minix/external/bsd/bind/dist/contrib/query-loc-0.4.0/loc.c
David van Moolenbroek 00b67f09dd Import NetBSD named(8)
Also known as ISC bind.  This import adds utilities such as host(1),
dig(1), and nslookup(1), as well as many other tools and libraries.

Change-Id: I035ca46e64f1965d57019e773f4ff0ef035e4aa3
2017-03-21 22:00:06 +00:00

593 lines
15 KiB
C

/* $NetBSD: loc.c,v 1.5 2014/12/10 04:37:56 christos Exp $ */
#include "loc.h"
/* Id: loc.c,v 1.1 2008/02/15 01:47:15 marka Exp */
/* Global variables */
short rr_errno;
/*
Prints the actual usage
*/
void
usage ()
{
(void) fprintf (stderr,
"Usage: %s: [-v] [-d nnn] hostname\n", progname);
exit (2);
}
/*
Panics
*/
void
panic (message)
char *message;
{
(void) fprintf (stderr,
"%s: %s\n", progname, message);
exit (2);
}
/*
** IN_ADDR_ARPA -- Convert dotted quad string to reverse in-addr.arpa
** ------------------------------------------------------------------
**
** Returns:
** Pointer to appropriate reverse in-addr.arpa name
** with trailing dot to force absolute domain name.
** NULL in case of invalid dotted quad input string.
*/
#ifndef ARPA_ROOT
#define ARPA_ROOT "in-addr.arpa"
#endif
char *
in_addr_arpa (dottedquad)
char *dottedquad; /* input string with dotted quad */
{
static char addrbuf[4 * 4 + sizeof (ARPA_ROOT) + 2];
unsigned int a[4];
register int n;
n = sscanf (dottedquad, "%u.%u.%u.%u", &a[0], &a[1], &a[2], &a[3]);
switch (n)
{
case 4:
(void) sprintf (addrbuf, "%u.%u.%u.%u.%s.",
a[3] & 0xff, a[2] & 0xff, a[1] & 0xff, a[0] & 0xff, ARPA_ROOT);
break;
case 3:
(void) sprintf (addrbuf, "%u.%u.%u.%s.",
a[2] & 0xff, a[1] & 0xff, a[0] & 0xff, ARPA_ROOT);
break;
case 2:
(void) sprintf (addrbuf, "%u.%u.%s.",
a[1] & 0xff, a[0] & 0xff, ARPA_ROOT);
break;
case 1:
(void) sprintf (addrbuf, "%u.%s.",
a[0] & 0xff, ARPA_ROOT);
break;
default:
return (NULL);
}
while (--n >= 0)
if (a[n] > 255)
return (NULL);
return (addrbuf);
}
/*
Returns a human-readable version of the LOC information or
NULL if it failed. Argument is a name (of a network or a machine)
and a boolean telling is it is a network name or a machine name.
*/
char *
getlocbyname (name, is_network)
const char *name;
short is_network;
{
char *result;
struct list_in_addr *list, *p;
result = findRR (name, T_LOC);
if (result != NULL)
{
if (debug >= 2)
printf ("LOC record found for the name %s\n", name);
return result;
}
else
{
if (!is_network)
{
list = findA (name);
if (debug >= 2)
printf ("No LOC record found for the name %s, trying addresses\n", name);
if (list != NULL)
{
for (p = list; p != NULL; p = p->next)
{
if (debug >= 2)
printf ("Trying address %s\n", inet_ntoa (p->addr));
result = getlocbyaddr (p->addr, NULL);
if (result != NULL)
return result;
}
return NULL;
}
else
{
if (debug >= 2)
printf (" No A record found for %s\n", name);
return NULL;
}
}
else
{
if (debug >= 2)
printf ("No LOC record found for the network name %s\n", name);
return NULL;
}
}
}
/*
Returns a human-readable version of the LOC information or
NULL if it failed. Argument is an IP address.
*/
char *
getlocbyaddr (addr, mask)
const struct in_addr addr;
const struct in_addr *mask;
{
struct in_addr netaddr;
u_int32_t a;
struct in_addr themask;
char text_addr[sizeof("255.255.255.255")],
text_mask[sizeof("255.255.255.255")];
if (mask == NULL)
{
themask.s_addr = (u_int32_t) 0;
}
else
{
themask = *mask;
}
strcpy (text_addr, inet_ntoa (addr));
strcpy (text_mask, inet_ntoa (themask));
if (debug >= 2)
printf ("Testing address %s/%s\n", text_addr, text_mask);
if (mask == NULL)
{
a = ntohl (addr.s_addr);
if (IN_CLASSA (a))
{
netaddr.s_addr = htonl (a & IN_CLASSA_NET);
themask.s_addr = htonl(IN_CLASSA_NET);
}
else if (IN_CLASSB (a))
{
netaddr.s_addr = htonl (a & IN_CLASSB_NET);
themask.s_addr = htonl(IN_CLASSB_NET);
}
else if (IN_CLASSC (a))
{
netaddr.s_addr = htonl (a & IN_CLASSC_NET);
themask.s_addr = htonl(IN_CLASSC_NET);
}
else
{
/* Error */
return NULL;
}
return getlocbynet (in_addr_arpa (inet_ntoa (netaddr)), addr, &themask);
}
else
{
netaddr.s_addr = addr.s_addr & themask.s_addr;
return getlocbynet (in_addr_arpa (inet_ntoa (netaddr)), addr, mask);
}
}
/*
Returns a human-readable LOC.
Argument is a network name in the 0.z.y.x.in-addr.arpa format
and the original address
*/
char *
getlocbynet (name, addr, mask)
char *name;
struct in_addr addr;
struct in_addr *mask;
{
char *network;
char *result;
struct list_in_addr *list;
struct in_addr newmask;
u_int32_t a;
char newname[4 * 4 + sizeof (ARPA_ROOT) + 2];
if (debug >= 2)
printf ("Testing network %s with mask %s\n", name, inet_ntoa(*mask));
/* Check if this network has an A RR */
list = findA (name);
if (list != NULL)
{
/* Yes, it does. This A record will be used as the
* new mask for recursion if it is longer than
* the actual mask. */
if (mask != NULL && mask->s_addr < list->addr.s_addr)
{
/* compute the new arguments for recursion
* - compute the new network by applying the new mask
* to the address and get the in_addr_arpa representation
* of it.
* - the address remains unchanged
* - the new mask is the one given in the A record
*/
a = ntohl(addr.s_addr); /* start from host address */
a &= ntohl(list->addr.s_addr); /* apply new mask */
newname[sizeof newname - 1] = 0;
strncpy(
newname,
in_addr_arpa(inet_ntoa(inet_makeaddr(a, 0))),
sizeof newname);
newmask = inet_makeaddr(ntohl(list->addr.s_addr), 0);
result = getlocbynet (newname, addr, &newmask);
if (result != NULL)
{
return result;
}
}
/* couldn't find a LOC. Fall through and try with name */
}
/* Check if this network has a name */
network = findRR (name, T_PTR);
if (network == NULL)
{
if (debug >= 2)
printf ("No name for network %s\n", name);
return NULL;
}
else
{
return getlocbyname (network, TRUE);
}
}
/*
The code for these two functions is stolen from the examples in Liu and Albitz
book "DNS and BIND" (O'Reilly).
*/
/****************************************************************
* skipName -- This routine skips over a domain name. If the *
* domain name expansion fails, it crashes. *
* dn_skipname() is probably not on your manual *
* page; it is similar to dn_expand() except that it just *
* skips over the name. dn_skipname() is in res_comp.c if *
* you need to find it. *
****************************************************************/
int
skipName (cp, endOfMsg)
u_char *cp;
u_char *endOfMsg;
{
int n;
if ((n = dn_skipname (cp, endOfMsg)) < 0)
{
panic ("dn_skipname failed\n");
}
return (n);
}
/****************************************************************
* skipToData -- This routine advances the cp pointer to the *
* start of the resource record data portion. On the way, *
* it fills in the type, class, ttl, and data length *
****************************************************************/
int
skipToData (cp, type, class, ttl, dlen, endOfMsg)
u_char *cp;
u_short *type;
u_short *class;
u_int32_t *ttl;
u_short *dlen;
u_char *endOfMsg;
{
u_char *tmp_cp = cp; /* temporary version of cp */
/* Skip the domain name; it matches the name we looked up */
tmp_cp += skipName (tmp_cp, endOfMsg);
/*
* Grab the type, class, and ttl. GETSHORT and GETLONG
* are macros defined in arpa/nameser.h.
*/
GETSHORT (*type, tmp_cp);
GETSHORT (*class, tmp_cp);
GETLONG (*ttl, tmp_cp);
GETSHORT (*dlen, tmp_cp);
return (tmp_cp - cp);
}
/*
Returns a human-readable version of a DNS RR (resource record)
associated with the name 'domain'.
If it does not find, ir returns NULL and sets rr_errno to explain why.
The code for this function is stolen from the examples in Liu and Albitz
book "DNS and BIND" (O'Reilly).
*/
char *
findRR (domain, requested_type)
char *domain;
int requested_type;
{
char *result, *message;
union
{
HEADER hdr; /* defined in resolv.h */
u_char buf[PACKETSZ]; /* defined in arpa/nameser.h */
}
response; /* response buffers */
short found = 0;
int responseLen; /* buffer length */
u_char *cp; /* character pointer to parse DNS packet */
u_char *endOfMsg; /* need to know the end of the message */
u_short class; /* classes defined in arpa/nameser.h */
u_short type; /* types defined in arpa/nameser.h */
u_int32_t ttl; /* resource record time to live */
u_short dlen; /* size of resource record data */
int i, count, dup; /* misc variables */
char *ptrList[1];
int ptrNum = 0;
struct in_addr addr;
result = (char *) malloc (256);
message = (char *) malloc (256);
/*
* Look up the records for the given domain name.
* We expect the domain to be a fully qualified name, so
* we use res_query(). If we wanted the resolver search
* algorithm, we would have used res_search() instead.
*/
if ((responseLen =
res_query (domain, /* the domain we care about */
C_IN, /* Internet class records */
requested_type, /* Look up name server records */
(u_char *) & response, /*response buffer */
sizeof (response))) /*buffer size */
< 0)
{ /*If negative */
rr_errno = h_errno;
return NULL;
}
/*
* Keep track of the end of the message so we don't
* pass it while parsing the response. responseLen is
* the value returned by res_query.
*/
endOfMsg = response.buf + responseLen;
/*
* Set a pointer to the start of the question section,
* which begins immediately AFTER the header.
*/
cp = response.buf + sizeof (HEADER);
/*
* Skip over the whole question section. The question
* section is comprised of a name, a type, and a class.
* QFIXEDSZ (defined in arpa/nameser.h) is the size of
* the type and class portions, which is fixed. Therefore,
* we can skip the question section by skipping the
* name (at the beginning) and then advancing QFIXEDSZ.
* After this calculation, cp points to the start of the
* answer section, which is a list of NS records.
*/
cp += skipName (cp, endOfMsg) + QFIXEDSZ;
count = ntohs (response.hdr.ancount) +
ntohs (response.hdr.nscount);
while ((--count >= 0) /* still more records */
&& (cp < endOfMsg))
{ /* still inside the packet */
/* Skip to the data portion of the resource record */
cp += skipToData (cp, &type, &class, &ttl, &dlen, endOfMsg);
if (type == requested_type)
{
switch (requested_type)
{
case (T_LOC):
loc_ntoa (cp, result);
return result;
break;
case (T_PTR):
ptrList[ptrNum] = (char *) malloc (MAXDNAME);
if (ptrList[ptrNum] == NULL)
{
panic ("Malloc failed");
}
if (dn_expand (response.buf, /* Start of the packet */
endOfMsg, /* End of the packet */
cp, /* Position in the packet */
(char *) ptrList[ptrNum], /* Result */
MAXDNAME) /* size of ptrList buffer */
< 0)
{ /* Negative: error */
panic ("dn_expand failed");
}
/*
* Check the name we've just unpacked and add it to
* the list if it is not a duplicate.
* If it is a duplicate, just ignore it.
*/
for (i = 0, dup = 0; (i < ptrNum) && !dup; i++)
dup = !strcasecmp (ptrList[i], ptrList[ptrNum]);
if (dup)
free (ptrList[ptrNum]);
else
ptrNum++;
strcpy (result, ptrList[0]);
return result;
break;
case (T_A):
bcopy ((char *) cp, (char *) &addr, INADDRSZ);
strcat (result, " ");
strcat (result, inet_ntoa (addr));
found = 1;
break;
default:
sprintf (message, "Unexpected type %u", requested_type);
panic (message);
}
}
/* Advance the pointer over the resource record data */
cp += dlen;
} /* end of while */
if (found)
return result;
else
return NULL;
}
struct list_in_addr *
findA (domain)
char *domain;
{
struct list_in_addr *result, *end;
union
{
HEADER hdr; /* defined in resolv.h */
u_char buf[PACKETSZ]; /* defined in arpa/nameser.h */
}
response; /* response buffers */
int responseLen; /* buffer length */
u_char *cp; /* character pointer to parse DNS packet */
u_char *endOfMsg; /* need to know the end of the message */
u_short class; /* classes defined in arpa/nameser.h */
u_short type; /* types defined in arpa/nameser.h */
u_int32_t ttl; /* resource record time to live */
u_short dlen; /* size of resource record data */
int count; /* misc variables */
struct in_addr addr;
end = NULL;
result = NULL;
/*
* Look up the records for the given domain name.
* We expect the domain to be a fully qualified name, so
* we use res_query(). If we wanted the resolver search
* algorithm, we would have used res_search() instead.
*/
if ((responseLen =
res_query (domain, /* the domain we care about */
C_IN, /* Internet class records */
T_A,
(u_char *) & response, /*response buffer */
sizeof (response))) /*buffer size */
< 0)
{ /*If negative */
rr_errno = h_errno;
return NULL;
}
/*
* Keep track of the end of the message so we don't
* pass it while parsing the response. responseLen is
* the value returned by res_query.
*/
endOfMsg = response.buf + responseLen;
/*
* Set a pointer to the start of the question section,
* which begins immediately AFTER the header.
*/
cp = response.buf + sizeof (HEADER);
/*
* Skip over the whole question section. The question
* section is comprised of a name, a type, and a class.
* QFIXEDSZ (defined in arpa/nameser.h) is the size of
* the type and class portions, which is fixed. Therefore,
* we can skip the question section by skipping the
* name (at the beginning) and then advancing QFIXEDSZ.
* After this calculation, cp points to the start of the
* answer section, which is a list of NS records.
*/
cp += skipName (cp, endOfMsg) + QFIXEDSZ;
count = ntohs (response.hdr.ancount) +
ntohs (response.hdr.nscount);
while ((--count >= 0) /* still more records */
&& (cp < endOfMsg))
{ /* still inside the packet */
/* Skip to the data portion of the resource record */
cp += skipToData (cp, &type, &class, &ttl, &dlen, endOfMsg);
if (type == T_A)
{
bcopy ((char *) cp, (char *) &addr, INADDRSZ);
if (end == NULL)
{
result = (void *) malloc (sizeof (struct list_in_addr));
result->addr = addr;
result->next = NULL;
end = result;
}
else
{
end->next = (void *) malloc (sizeof (struct list_in_addr));
end = end->next;
end->addr = addr;
end->next = NULL;
}
}
/* Advance the pointer over the resource record data */
cp += dlen;
} /* end of while */
return result;
}