NetBSD/dist/bind/contrib/query-loc-0.4.0/loc.c

593 lines
15 KiB
C

/* $NetBSD: loc.c,v 1.1.1.1 2008/06/21 18:30:23 christos Exp $ */
#include "loc.h"
/* Id: loc.c,v 1.1.2.1 2008/02/15 02:11:57 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, *text_mask;
if (mask == NULL)
{
themask.s_addr = (u_int32_t) 0;
}
else
{
themask = *mask;
}
text_addr = (char *) malloc (256);
text_mask = (char *) malloc (256);
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;
}