593 lines
15 KiB
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;
|
|
}
|