1#include "loc.h"
2
3/* $Id: loc.c,v 1.1 2008/02/15 01:47:15 marka Exp $ */
4
5/* Global variables */
6
7short rr_errno;
8
9/*
10   Prints the actual usage
11 */
12void
13usage ()
14{
15  (void) fprintf (stderr,
16		  "Usage: %s: [-v] [-d nnn] hostname\n", progname);
17  exit (2);
18}
19
20/*
21   Panics
22 */
23void
24panic (message)
25     char *message;
26{
27  (void) fprintf (stderr,
28		  "%s: %s\n", progname, message);
29  exit (2);
30}
31
32/*
33   ** IN_ADDR_ARPA -- Convert dotted quad string to reverse in-addr.arpa
34   ** ------------------------------------------------------------------
35   **
36   **   Returns:
37   **           Pointer to appropriate reverse in-addr.arpa name
38   **           with trailing dot to force absolute domain name.
39   **           NULL in case of invalid dotted quad input string.
40 */
41
42#ifndef ARPA_ROOT
43#define ARPA_ROOT "in-addr.arpa"
44#endif
45
46char *
47in_addr_arpa (dottedquad)
48     char *dottedquad;		/* input string with dotted quad */
49{
50  static char addrbuf[4 * 4 + sizeof (ARPA_ROOT) + 2];
51  unsigned int a[4];
52  register int n;
53
54  n = sscanf (dottedquad, "%u.%u.%u.%u", &a[0], &a[1], &a[2], &a[3]);
55  switch (n)
56    {
57    case 4:
58      (void) sprintf (addrbuf, "%u.%u.%u.%u.%s.",
59	     a[3] & 0xff, a[2] & 0xff, a[1] & 0xff, a[0] & 0xff, ARPA_ROOT);
60      break;
61
62    case 3:
63      (void) sprintf (addrbuf, "%u.%u.%u.%s.",
64		      a[2] & 0xff, a[1] & 0xff, a[0] & 0xff, ARPA_ROOT);
65      break;
66
67    case 2:
68      (void) sprintf (addrbuf, "%u.%u.%s.",
69		      a[1] & 0xff, a[0] & 0xff, ARPA_ROOT);
70      break;
71
72    case 1:
73      (void) sprintf (addrbuf, "%u.%s.",
74		      a[0] & 0xff, ARPA_ROOT);
75      break;
76
77    default:
78      return (NULL);
79    }
80
81  while (--n >= 0)
82    if (a[n] > 255)
83      return (NULL);
84
85  return (addrbuf);
86}
87
88/*
89   Returns a human-readable version of the LOC information or
90   NULL if it failed. Argument is a name (of a network or a machine)
91   and a boolean telling is it is a network name or a machine name.
92 */
93char *
94getlocbyname (name, is_network)
95     const char *name;
96     short is_network;
97{
98  char *result;
99  struct list_in_addr *list, *p;
100  result = findRR (name, T_LOC);
101  if (result != NULL)
102    {
103      if (debug >= 2)
104	printf ("LOC record found for the name %s\n", name);
105      return result;
106    }
107  else
108    {
109      if (!is_network)
110	{
111	  list = findA (name);
112	  if (debug >= 2)
113	    printf ("No LOC record found for the name %s, trying addresses\n", name);
114	  if (list != NULL)
115	    {
116	      for (p = list; p != NULL; p = p->next)
117		{
118		  if (debug >= 2)
119		    printf ("Trying address %s\n", inet_ntoa (p->addr));
120		  result = getlocbyaddr (p->addr, NULL);
121		  if (result != NULL)
122		    return result;
123		}
124	      return NULL;
125	    }
126	  else
127	    {
128	      if (debug >= 2)
129		printf ("   No A record found for %s\n", name);
130	      return NULL;
131	    }
132	}
133      else
134	{
135	  if (debug >= 2)
136	    printf ("No LOC record found for the network name %s\n", name);
137	  return NULL;
138	}
139    }
140}
141
142/*
143   Returns a human-readable version of the LOC information or
144   NULL if it failed. Argument is an IP address.
145 */
146char *
147getlocbyaddr (addr, mask)
148     const struct in_addr addr;
149     const struct in_addr *mask;
150{
151  struct in_addr netaddr;
152  u_int32_t a;
153  struct in_addr themask;
154  char *text_addr, *text_mask;
155
156  if (mask == NULL)
157    {
158      themask.s_addr = (u_int32_t) 0;
159    }
160  else
161    {
162      themask = *mask;
163    }
164
165  text_addr = (char *) malloc (256);
166  text_mask = (char *) malloc (256);
167  strcpy (text_addr, inet_ntoa (addr));
168  strcpy (text_mask, inet_ntoa (themask));
169
170  if (debug >= 2)
171    printf ("Testing address %s/%s\n", text_addr, text_mask);
172  if (mask == NULL)
173    {
174      a = ntohl (addr.s_addr);
175      if (IN_CLASSA (a))
176	{
177	  netaddr.s_addr = htonl (a & IN_CLASSA_NET);
178	  themask.s_addr = htonl(IN_CLASSA_NET);
179	}
180      else if (IN_CLASSB (a))
181	{
182	  netaddr.s_addr = htonl (a & IN_CLASSB_NET);
183	  themask.s_addr = htonl(IN_CLASSB_NET);
184	}
185      else if (IN_CLASSC (a))
186	{
187	  netaddr.s_addr = htonl (a & IN_CLASSC_NET);
188	  themask.s_addr = htonl(IN_CLASSC_NET);
189	}
190      else
191	{
192	  /* Error */
193	  return NULL;
194	}
195      return getlocbynet (in_addr_arpa (inet_ntoa (netaddr)), addr, &themask);
196    }
197  else
198    {
199      netaddr.s_addr = addr.s_addr & themask.s_addr;
200      return getlocbynet (in_addr_arpa (inet_ntoa (netaddr)), addr, mask);
201    }
202}
203
204/*
205   Returns a human-readable LOC.
206   Argument is a network name in the 0.z.y.x.in-addr.arpa format
207   and the original address
208 */
209char *
210getlocbynet (name, addr, mask)
211     char *name;
212     struct in_addr addr;
213     struct in_addr *mask;
214{
215  char *network;
216  char *result;
217  struct list_in_addr *list;
218  struct in_addr newmask;
219  u_int32_t a;
220  char newname[4 * 4 + sizeof (ARPA_ROOT) + 2];
221
222  if (debug >= 2)
223    printf ("Testing network %s with mask %s\n", name, inet_ntoa(*mask));
224
225  /* Check if this network has an A RR */
226  list = findA (name);
227  if (list != NULL)
228    {
229      /* Yes, it does. This A record will be used as the
230       * new mask for recursion if it is longer than
231       * the actual mask. */
232      if (mask != NULL && mask->s_addr < list->addr.s_addr)
233	{
234	  /* compute the new arguments for recursion
235	   * - compute the new network by applying the new mask
236	   *   to the address and get the in_addr_arpa representation
237	   *   of it.
238	   * - the address remains unchanged
239	   * - the new mask is the one given in the A record
240	   */
241	  a = ntohl(addr.s_addr);        /* start from host address */
242	  a &= ntohl(list->addr.s_addr); /* apply new mask */
243	  newname[sizeof newname - 1] = 0;
244	  strncpy(
245		 newname,
246		 in_addr_arpa(inet_ntoa(inet_makeaddr(a, 0))),
247		 sizeof newname);
248	  newmask = inet_makeaddr(ntohl(list->addr.s_addr), 0);
249	  result = getlocbynet (newname, addr, &newmask);
250	  if (result != NULL)
251	    {
252	      return result;
253	    }
254	}
255      /* couldn't find a LOC. Fall through and try with name */
256    }
257
258  /* Check if this network has a name */
259  network = findRR (name, T_PTR);
260  if (network == NULL)
261    {
262      if (debug >= 2)
263	printf ("No name for network %s\n", name);
264      return NULL;
265    }
266  else
267    {
268      return getlocbyname (network, TRUE);
269    }
270}
271
272/*
273   The code for these two functions is stolen from the examples in Liu and Albitz
274   book "DNS and BIND" (O'Reilly).
275 */
276
277/****************************************************************
278 * skipName -- This routine skips over a domain name.  If the   *
279 *     domain name expansion fails, it crashes.                 *
280 *     dn_skipname() is probably not on your manual             *
281 *     page; it is similar to dn_expand() except that it just   *
282 *     skips over the name.  dn_skipname() is in res_comp.c if  *
283 *     you need to find it.                                     *
284 ****************************************************************/
285int
286skipName (cp, endOfMsg)
287     u_char *cp;
288     u_char *endOfMsg;
289{
290  int n;
291
292  if ((n = dn_skipname (cp, endOfMsg)) < 0)
293    {
294      panic ("dn_skipname failed\n");
295    }
296  return (n);
297}
298
299/****************************************************************
300 * skipToData -- This routine advances the cp pointer to the    *
301 *     start of the resource record data portion.  On the way,  *
302 *     it fills in the type, class, ttl, and data length        *
303 ****************************************************************/
304int
305skipToData (cp, type, class, ttl, dlen, endOfMsg)
306     u_char *cp;
307     u_short *type;
308     u_short *class;
309     u_int32_t *ttl;
310     u_short *dlen;
311     u_char *endOfMsg;
312{
313  u_char *tmp_cp = cp;		/* temporary version of cp */
314
315  /* Skip the domain name; it matches the name we looked up */
316  tmp_cp += skipName (tmp_cp, endOfMsg);
317
318  /*
319   * Grab the type, class, and ttl.  GETSHORT and GETLONG
320   * are macros defined in arpa/nameser.h.
321   */
322  GETSHORT (*type, tmp_cp);
323  GETSHORT (*class, tmp_cp);
324  GETLONG (*ttl, tmp_cp);
325  GETSHORT (*dlen, tmp_cp);
326
327  return (tmp_cp - cp);
328}
329
330
331/*
332   Returns a human-readable version of a DNS RR (resource record)
333   associated with the name 'domain'.
334   If it does not find, ir returns NULL and sets rr_errno to explain why.
335
336   The code for this function is stolen from the examples in Liu and Albitz
337   book "DNS and BIND" (O'Reilly).
338 */
339char *
340findRR (domain, requested_type)
341     char *domain;
342     int requested_type;
343{
344  char *result, *message;
345
346  union
347    {
348      HEADER hdr;		/* defined in resolv.h */
349      u_char buf[PACKETSZ];	/* defined in arpa/nameser.h */
350    }
351  response;			/* response buffers */
352short found = 0;
353int responseLen;		/* buffer length */
354
355  u_char *cp;			/* character pointer to parse DNS packet */
356  u_char *endOfMsg;		/* need to know the end of the message */
357  u_short class;		/* classes defined in arpa/nameser.h */
358  u_short type;			/* types defined in arpa/nameser.h */
359  u_int32_t ttl;		/* resource record time to live */
360  u_short dlen;			/* size of resource record data */
361
362  int i, count, dup;		/* misc variables */
363
364  char *ptrList[1];
365  int ptrNum = 0;
366  struct in_addr addr;
367
368  result = (char *) malloc (256);
369  message = (char *) malloc (256);
370  /*
371   * Look up the records for the given domain name.
372   * We expect the domain to be a fully qualified name, so
373   * we use res_query().  If we wanted the resolver search
374   * algorithm, we would have used res_search() instead.
375   */
376  if ((responseLen =
377       res_query (domain,	/* the domain we care about   */
378		  C_IN,		/* Internet class records     */
379		  requested_type,	/* Look up name server records */
380		  (u_char *) & response,	/*response buffer */
381		  sizeof (response)))	/*buffer size    */
382      < 0)
383    {				/*If negative    */
384      rr_errno = h_errno;
385      return NULL;
386    }
387
388  /*
389   * Keep track of the end of the message so we don't
390   * pass it while parsing the response.  responseLen is
391   * the value returned by res_query.
392   */
393  endOfMsg = response.buf + responseLen;
394
395  /*
396   * Set a pointer to the start of the question section,
397   * which begins immediately AFTER the header.
398   */
399  cp = response.buf + sizeof (HEADER);
400
401  /*
402   * Skip over the whole question section.  The question
403   * section is comprised of a name, a type, and a class.
404   * QFIXEDSZ (defined in arpa/nameser.h) is the size of
405   * the type and class portions, which is fixed.  Therefore,
406   * we can skip the question section by skipping the
407   * name (at the beginning) and then advancing QFIXEDSZ.
408   * After this calculation, cp points to the start of the
409   * answer section, which is a list of NS records.
410   */
411  cp += skipName (cp, endOfMsg) + QFIXEDSZ;
412
413  count = ntohs (response.hdr.ancount) +
414    ntohs (response.hdr.nscount);
415  while ((--count >= 0)		/* still more records     */
416	 && (cp < endOfMsg))
417    {				/* still inside the packet */
418
419
420      /* Skip to the data portion of the resource record */
421      cp += skipToData (cp, &type, &class, &ttl, &dlen, endOfMsg);
422
423      if (type == requested_type)
424	{
425	  switch (requested_type)
426	    {
427	    case (T_LOC):
428	      loc_ntoa (cp, result);
429	      return result;
430	      break;
431	    case (T_PTR):
432	      ptrList[ptrNum] = (char *) malloc (MAXDNAME);
433	      if (ptrList[ptrNum] == NULL)
434		{
435		  panic ("Malloc failed");
436		}
437
438	      if (dn_expand (response.buf,	/* Start of the packet   */
439			     endOfMsg,	/* End of the packet     */
440			     cp,	/* Position in the packet */
441			     (char *) ptrList[ptrNum],	/* Result    */
442			     MAXDNAME)	/* size of ptrList buffer */
443		  < 0)
444		{		/* Negative: error    */
445		  panic ("dn_expand failed");
446		}
447
448	      /*
449	       * Check the name we've just unpacked and add it to
450	       * the list if it is not a duplicate.
451	       * If it is a duplicate, just ignore it.
452	       */
453	      for (i = 0, dup = 0; (i < ptrNum) && !dup; i++)
454		dup = !strcasecmp (ptrList[i], ptrList[ptrNum]);
455	      if (dup)
456		free (ptrList[ptrNum]);
457	      else
458		ptrNum++;
459	      strcpy (result, ptrList[0]);
460	      return result;
461	      break;
462	    case (T_A):
463	      bcopy ((char *) cp, (char *) &addr, INADDRSZ);
464	      strcat (result, " ");
465	      strcat (result, inet_ntoa (addr));
466	      found = 1;
467	      break;
468	    default:
469	      sprintf (message, "Unexpected type %u", requested_type);
470	      panic (message);
471	    }
472	}
473
474      /* Advance the pointer over the resource record data */
475      cp += dlen;
476
477    }				/* end of while */
478  if (found)
479  return result;
480else
481return NULL;
482}
483
484struct list_in_addr *
485findA (domain)
486     char *domain;
487{
488
489  struct list_in_addr *result, *end;
490
491  union
492    {
493      HEADER hdr;		/* defined in resolv.h */
494      u_char buf[PACKETSZ];	/* defined in arpa/nameser.h */
495    }
496  response;			/* response buffers */
497  int responseLen;		/* buffer length */
498
499  u_char *cp;			/* character pointer to parse DNS packet */
500  u_char *endOfMsg;		/* need to know the end of the message */
501  u_short class;		/* classes defined in arpa/nameser.h */
502  u_short type;			/* types defined in arpa/nameser.h */
503  u_int32_t ttl;		/* resource record time to live */
504  u_short dlen;			/* size of resource record data */
505
506  int count;			/* misc variables */
507
508  struct in_addr addr;
509
510  end = NULL;
511  result = NULL;
512
513  /*
514   * Look up the records for the given domain name.
515   * We expect the domain to be a fully qualified name, so
516   * we use res_query().  If we wanted the resolver search
517   * algorithm, we would have used res_search() instead.
518   */
519  if ((responseLen =
520       res_query (domain,	/* the domain we care about   */
521		  C_IN,		/* Internet class records     */
522		  T_A,
523		  (u_char *) & response,	/*response buffer */
524		  sizeof (response)))	/*buffer size    */
525      < 0)
526    {				/*If negative    */
527      rr_errno = h_errno;
528      return NULL;
529    }
530
531  /*
532   * Keep track of the end of the message so we don't
533   * pass it while parsing the response.  responseLen is
534   * the value returned by res_query.
535   */
536  endOfMsg = response.buf + responseLen;
537
538  /*
539   * Set a pointer to the start of the question section,
540   * which begins immediately AFTER the header.
541   */
542  cp = response.buf + sizeof (HEADER);
543
544  /*
545   * Skip over the whole question section.  The question
546   * section is comprised of a name, a type, and a class.
547   * QFIXEDSZ (defined in arpa/nameser.h) is the size of
548   * the type and class portions, which is fixed.  Therefore,
549   * we can skip the question section by skipping the
550   * name (at the beginning) and then advancing QFIXEDSZ.
551   * After this calculation, cp points to the start of the
552   * answer section, which is a list of NS records.
553   */
554  cp += skipName (cp, endOfMsg) + QFIXEDSZ;
555
556  count = ntohs (response.hdr.ancount) +
557    ntohs (response.hdr.nscount);
558  while ((--count >= 0)		/* still more records     */
559	 && (cp < endOfMsg))
560    {				/* still inside the packet */
561
562
563      /* Skip to the data portion of the resource record */
564      cp += skipToData (cp, &type, &class, &ttl, &dlen, endOfMsg);
565
566      if (type == T_A)
567	{
568	  bcopy ((char *) cp, (char *) &addr, INADDRSZ);
569	  if (end == NULL)
570	    {
571	      result = (void *) malloc (sizeof (struct list_in_addr));
572	      result->addr = addr;
573	      result->next = NULL;
574	      end = result;
575	    }
576	  else
577	    {
578	      end->next = (void *) malloc (sizeof (struct list_in_addr));
579	      end = end->next;
580	      end->addr = addr;
581	      end->next = NULL;
582	    }
583	}
584
585      /* Advance the pointer over the resource record data */
586      cp += dlen;
587
588    }				/* end of while */
589  return result;
590}
591