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