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