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