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 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 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 * 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 * 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 * 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 * 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 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 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 * 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 * 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