1 /* 2 * Copyright (c) 1995 - 2003 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifdef HAVE_CONFIG_H 35 #include <config.h> 36 #endif 37 #include "roken.h" 38 #ifdef HAVE_ARPA_NAMESER_H 39 #include <arpa/nameser.h> 40 #endif 41 #ifdef HAVE_RESOLV_H 42 #include <resolv.h> 43 #endif 44 #include "resolve.h" 45 46 #include <assert.h> 47 48 RCSID("$Id: resolve.c,v 1.38.2.1 2003/04/22 15:02:47 lha Exp $"); 49 50 #undef HAVE_RES_NSEARCH 51 #if (defined(HAVE_RES_SEARCH) || defined(HAVE_RES_NSEARCH)) && defined(HAVE_DN_EXPAND) 52 53 #define DECL(X) {#X, T_##X} 54 55 static struct stot{ 56 const char *name; 57 int type; 58 }stot[] = { 59 DECL(A), 60 DECL(NS), 61 DECL(CNAME), 62 DECL(SOA), 63 DECL(PTR), 64 DECL(MX), 65 DECL(TXT), 66 DECL(AFSDB), 67 DECL(SIG), 68 DECL(KEY), 69 DECL(SRV), 70 DECL(NAPTR), 71 {NULL, 0} 72 }; 73 74 int _resolve_debug = 0; 75 76 int 77 dns_string_to_type(const char *name) 78 { 79 struct stot *p = stot; 80 for(p = stot; p->name; p++) 81 if(strcasecmp(name, p->name) == 0) 82 return p->type; 83 return -1; 84 } 85 86 const char * 87 dns_type_to_string(int type) 88 { 89 struct stot *p = stot; 90 for(p = stot; p->name; p++) 91 if(type == p->type) 92 return p->name; 93 return NULL; 94 } 95 96 void 97 dns_free_data(struct dns_reply *r) 98 { 99 struct resource_record *rr; 100 if(r->q.domain) 101 free(r->q.domain); 102 for(rr = r->head; rr;){ 103 struct resource_record *tmp = rr; 104 if(rr->domain) 105 free(rr->domain); 106 if(rr->u.data) 107 free(rr->u.data); 108 rr = rr->next; 109 free(tmp); 110 } 111 free (r); 112 } 113 114 static int 115 parse_record(const unsigned char *data, const unsigned char *end_data, 116 const unsigned char **pp, struct resource_record **rr) 117 { 118 int type, class, ttl, size; 119 int status; 120 char host[MAXDNAME]; 121 const unsigned char *p = *pp; 122 status = dn_expand(data, end_data, p, host, sizeof(host)); 123 if(status < 0) 124 return -1; 125 if (p + status + 10 > end_data) 126 return -1; 127 p += status; 128 type = (p[0] << 8) | p[1]; 129 p += 2; 130 class = (p[0] << 8) | p[1]; 131 p += 2; 132 ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; 133 p += 4; 134 size = (p[0] << 8) | p[1]; 135 p += 2; 136 137 if (p + size > end_data) 138 return -1; 139 140 *rr = calloc(1, sizeof(**rr)); 141 if(*rr == NULL) 142 return -1; 143 (*rr)->domain = strdup(host); 144 if((*rr)->domain == NULL) { 145 free(*rr); 146 return -1; 147 } 148 (*rr)->type = type; 149 (*rr)->class = class; 150 (*rr)->ttl = ttl; 151 (*rr)->size = size; 152 switch(type){ 153 case T_NS: 154 case T_CNAME: 155 case T_PTR: 156 status = dn_expand(data, end_data, p, host, sizeof(host)); 157 if(status < 0) { 158 free(*rr); 159 return -1; 160 } 161 (*rr)->u.txt = strdup(host); 162 if((*rr)->u.txt == NULL) { 163 free(*rr); 164 return -1; 165 } 166 break; 167 case T_MX: 168 case T_AFSDB:{ 169 size_t hostlen; 170 171 status = dn_expand(data, end_data, p + 2, host, sizeof(host)); 172 if(status < 0){ 173 free(*rr); 174 return -1; 175 } 176 if (status + 2 > size) { 177 free(*rr); 178 return -1; 179 } 180 181 hostlen = strlen(host); 182 (*rr)->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) + 183 hostlen); 184 if((*rr)->u.mx == NULL) { 185 free(*rr); 186 return -1; 187 } 188 (*rr)->u.mx->preference = (p[0] << 8) | p[1]; 189 strlcpy((*rr)->u.mx->domain, host, hostlen + 1); 190 break; 191 } 192 case T_SRV:{ 193 size_t hostlen; 194 status = dn_expand(data, end_data, p + 6, host, sizeof(host)); 195 if(status < 0){ 196 free(*rr); 197 return -1; 198 } 199 if (status + 6 > size) { 200 free(*rr); 201 return -1; 202 } 203 204 hostlen = strlen(host); 205 (*rr)->u.srv = 206 (struct srv_record*)malloc(sizeof(struct srv_record) + 207 hostlen); 208 if((*rr)->u.srv == NULL) { 209 free(*rr); 210 return -1; 211 } 212 (*rr)->u.srv->priority = (p[0] << 8) | p[1]; 213 (*rr)->u.srv->weight = (p[2] << 8) | p[3]; 214 (*rr)->u.srv->port = (p[4] << 8) | p[5]; 215 strlcpy((*rr)->u.srv->target, host, hostlen + 1); 216 break; 217 } 218 case T_TXT:{ 219 if(size == 0 || size < *p + 1) { 220 free(*rr); 221 return -1; 222 } 223 (*rr)->u.txt = (char*)malloc(*p + 1); 224 if((*rr)->u.txt == NULL) { 225 free(*rr); 226 return -1; 227 } 228 strncpy((*rr)->u.txt, (char*)p + 1, *p); 229 (*rr)->u.txt[*p] = '\0'; 230 break; 231 } 232 case T_KEY : { 233 size_t key_len; 234 235 if (size < 4) { 236 free(*rr); 237 return -1; 238 } 239 240 key_len = size - 4; 241 (*rr)->u.key = malloc (sizeof(*(*rr)->u.key) + key_len - 1); 242 if ((*rr)->u.key == NULL) { 243 free(*rr); 244 return -1; 245 } 246 247 (*rr)->u.key->flags = (p[0] << 8) | p[1]; 248 (*rr)->u.key->protocol = p[2]; 249 (*rr)->u.key->algorithm = p[3]; 250 (*rr)->u.key->key_len = key_len; 251 memcpy ((*rr)->u.key->key_data, p + 4, key_len); 252 break; 253 } 254 case T_SIG : { 255 size_t sig_len, hostlen; 256 257 if(size <= 18) { 258 free(*rr); 259 return -1; 260 } 261 status = dn_expand (data, end_data, p + 18, host, sizeof(host)); 262 if (status < 0) { 263 free(*rr); 264 return -1; 265 } 266 if (status + 18 > size) { 267 free(*rr); 268 return -1; 269 } 270 271 /* the signer name is placed after the sig_data, to make it 272 easy to free this struture; the size calculation below 273 includes the zero-termination if the structure itself. 274 don't you just love C? 275 */ 276 sig_len = size - 18 - status; 277 hostlen = strlen(host); 278 (*rr)->u.sig = malloc(sizeof(*(*rr)->u.sig) 279 + hostlen + sig_len); 280 if ((*rr)->u.sig == NULL) { 281 free(*rr); 282 return -1; 283 } 284 (*rr)->u.sig->type = (p[0] << 8) | p[1]; 285 (*rr)->u.sig->algorithm = p[2]; 286 (*rr)->u.sig->labels = p[3]; 287 (*rr)->u.sig->orig_ttl = (p[4] << 24) | (p[5] << 16) 288 | (p[6] << 8) | p[7]; 289 (*rr)->u.sig->sig_expiration = (p[8] << 24) | (p[9] << 16) 290 | (p[10] << 8) | p[11]; 291 (*rr)->u.sig->sig_inception = (p[12] << 24) | (p[13] << 16) 292 | (p[14] << 8) | p[15]; 293 (*rr)->u.sig->key_tag = (p[16] << 8) | p[17]; 294 (*rr)->u.sig->sig_len = sig_len; 295 memcpy ((*rr)->u.sig->sig_data, p + 18 + status, sig_len); 296 (*rr)->u.sig->signer = &(*rr)->u.sig->sig_data[sig_len]; 297 strlcpy((*rr)->u.sig->signer, host, hostlen + 1); 298 break; 299 } 300 301 case T_CERT : { 302 size_t cert_len; 303 304 if (size < 5) { 305 free(*rr); 306 return -1; 307 } 308 309 cert_len = size - 5; 310 (*rr)->u.cert = malloc (sizeof(*(*rr)->u.cert) + cert_len - 1); 311 if ((*rr)->u.cert == NULL) { 312 free(*rr); 313 return -1; 314 } 315 316 (*rr)->u.cert->type = (p[0] << 8) | p[1]; 317 (*rr)->u.cert->tag = (p[2] << 8) | p[3]; 318 (*rr)->u.cert->algorithm = p[4]; 319 (*rr)->u.cert->cert_len = cert_len; 320 memcpy ((*rr)->u.cert->cert_data, p + 5, cert_len); 321 break; 322 } 323 default: 324 (*rr)->u.data = (unsigned char*)malloc(size); 325 if(size != 0 && (*rr)->u.data == NULL) { 326 free(*rr); 327 return -1; 328 } 329 memcpy((*rr)->u.data, p, size); 330 } 331 *pp = p + size; 332 return 0; 333 } 334 335 #ifndef TEST_RESOLVE 336 static 337 #endif 338 struct dns_reply* 339 parse_reply(const unsigned char *data, size_t len) 340 { 341 const unsigned char *p; 342 int status; 343 int i; 344 char host[MAXDNAME]; 345 const unsigned char *end_data = data + len; 346 struct dns_reply *r; 347 struct resource_record **rr; 348 349 r = calloc(1, sizeof(*r)); 350 if (r == NULL) 351 return NULL; 352 353 p = data; 354 #if 0 355 /* doesn't work on Crays */ 356 memcpy(&r->h, p, sizeof(HEADER)); 357 p += sizeof(HEADER); 358 #else 359 memcpy(&r->h, p, 12); /* XXX this will probably be mostly garbage */ 360 p += 12; 361 #endif 362 if(ntohs(r->h.qdcount) != 1) { 363 free(r); 364 return NULL; 365 } 366 status = dn_expand(data, end_data, p, host, sizeof(host)); 367 if(status < 0){ 368 dns_free_data(r); 369 return NULL; 370 } 371 r->q.domain = strdup(host); 372 if(r->q.domain == NULL) { 373 dns_free_data(r); 374 return NULL; 375 } 376 if (p + status + 4 > end_data) { 377 dns_free_data(r); 378 return NULL; 379 } 380 p += status; 381 r->q.type = (p[0] << 8 | p[1]); 382 p += 2; 383 r->q.class = (p[0] << 8 | p[1]); 384 p += 2; 385 386 rr = &r->head; 387 for(i = 0; i < ntohs(r->h.ancount); i++) { 388 if(parse_record(data, end_data, &p, rr) != 0) { 389 dns_free_data(r); 390 return NULL; 391 } 392 rr = &(*rr)->next; 393 } 394 for(i = 0; i < ntohs(r->h.nscount); i++) { 395 if(parse_record(data, end_data, &p, rr) != 0) { 396 dns_free_data(r); 397 return NULL; 398 } 399 rr = &(*rr)->next; 400 } 401 for(i = 0; i < ntohs(r->h.arcount); i++) { 402 if(parse_record(data, end_data, &p, rr) != 0) { 403 dns_free_data(r); 404 return NULL; 405 } 406 rr = &(*rr)->next; 407 } 408 *rr = NULL; 409 return r; 410 } 411 412 static struct dns_reply * 413 dns_lookup_int(const char *domain, int rr_class, int rr_type) 414 { 415 unsigned char reply[1024]; 416 int len; 417 #ifdef HAVE_RES_NSEARCH 418 struct __res_state stat; 419 memset(&stat, 0, sizeof(stat)); 420 if(res_ninit(&stat)) 421 return NULL; /* is this the best we can do? */ 422 #elif defined(HAVE__RES) 423 u_long old_options = 0; 424 #endif 425 426 if (_resolve_debug) { 427 #ifdef HAVE_RES_NSEARCH 428 stat.options |= RES_DEBUG; 429 #elif defined(HAVE__RES) 430 old_options = _res.options; 431 _res.options |= RES_DEBUG; 432 #endif 433 fprintf(stderr, "dns_lookup(%s, %d, %s)\n", domain, 434 rr_class, dns_type_to_string(rr_type)); 435 } 436 #ifdef HAVE_RES_NSEARCH 437 len = res_nsearch(&stat, domain, rr_class, rr_type, reply, sizeof(reply)); 438 #else 439 len = res_search(domain, rr_class, rr_type, reply, sizeof(reply)); 440 #endif 441 if (_resolve_debug) { 442 #if defined(HAVE__RES) && !defined(HAVE_RES_NSEARCH) 443 _res.options = old_options; 444 #endif 445 fprintf(stderr, "dns_lookup(%s, %d, %s) --> %d\n", 446 domain, rr_class, dns_type_to_string(rr_type), len); 447 } 448 #ifdef HAVE_RES_NSEARCH 449 res_nclose(&stat); 450 #endif 451 if(len < 0) { 452 return NULL; 453 } else { 454 len = min(len, sizeof(reply)); 455 return parse_reply(reply, len); 456 } 457 } 458 459 struct dns_reply * 460 dns_lookup(const char *domain, const char *type_name) 461 { 462 int type; 463 464 type = dns_string_to_type(type_name); 465 if(type == -1) { 466 if(_resolve_debug) 467 fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n", 468 type_name); 469 return NULL; 470 } 471 return dns_lookup_int(domain, C_IN, type); 472 } 473 474 static int 475 compare_srv(const void *a, const void *b) 476 { 477 const struct resource_record *const* aa = a, *const* bb = b; 478 479 if((*aa)->u.srv->priority == (*bb)->u.srv->priority) 480 return ((*aa)->u.srv->weight - (*bb)->u.srv->weight); 481 return ((*aa)->u.srv->priority - (*bb)->u.srv->priority); 482 } 483 484 #ifndef HAVE_RANDOM 485 #define random() rand() 486 #endif 487 488 /* try to rearrange the srv-records by the algorithm in RFC2782 */ 489 void 490 dns_srv_order(struct dns_reply *r) 491 { 492 struct resource_record **srvs, **ss, **headp; 493 struct resource_record *rr; 494 int num_srv = 0; 495 496 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE) 497 int state[256 / sizeof(int)]; 498 char *oldstate; 499 #endif 500 501 for(rr = r->head; rr; rr = rr->next) 502 if(rr->type == T_SRV) 503 num_srv++; 504 505 if(num_srv == 0) 506 return; 507 508 srvs = malloc(num_srv * sizeof(*srvs)); 509 if(srvs == NULL) 510 return; /* XXX not much to do here */ 511 512 /* unlink all srv-records from the linked list and put them in 513 a vector */ 514 for(ss = srvs, headp = &r->head; *headp; ) 515 if((*headp)->type == T_SRV) { 516 *ss = *headp; 517 *headp = (*headp)->next; 518 (*ss)->next = NULL; 519 ss++; 520 } else 521 headp = &(*headp)->next; 522 523 /* sort them by priority and weight */ 524 qsort(srvs, num_srv, sizeof(*srvs), compare_srv); 525 526 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE) 527 oldstate = initstate(time(NULL), (char*)state, sizeof(state)); 528 #endif 529 530 headp = &r->head; 531 532 for(ss = srvs; ss < srvs + num_srv; ) { 533 int sum, rnd, count; 534 struct resource_record **ee, **tt; 535 /* find the last record with the same priority and count the 536 sum of all weights */ 537 for(sum = 0, tt = ss; tt < srvs + num_srv; tt++) { 538 if(*tt == NULL) 539 continue; 540 if((*tt)->u.srv->priority != (*ss)->u.srv->priority) 541 break; 542 sum += (*tt)->u.srv->weight; 543 } 544 ee = tt; 545 /* ss is now the first record of this priority and ee is the 546 first of the next */ 547 while(ss < ee) { 548 rnd = random() % (sum + 1); 549 for(count = 0, tt = ss; ; tt++) { 550 if(*tt == NULL) 551 continue; 552 count += (*tt)->u.srv->weight; 553 if(count >= rnd) 554 break; 555 } 556 557 assert(tt < ee); 558 559 /* insert the selected record at the tail (of the head) of 560 the list */ 561 (*tt)->next = *headp; 562 *headp = *tt; 563 headp = &(*tt)->next; 564 sum -= (*tt)->u.srv->weight; 565 *tt = NULL; 566 while(ss < ee && *ss == NULL) 567 ss++; 568 } 569 } 570 571 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE) 572 setstate(oldstate); 573 #endif 574 free(srvs); 575 return; 576 } 577 578 #else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */ 579 580 struct dns_reply * 581 dns_lookup(const char *domain, const char *type_name) 582 { 583 return NULL; 584 } 585 586 void 587 dns_free_data(struct dns_reply *r) 588 { 589 } 590 591 void 592 dns_srv_order(struct dns_reply *r) 593 { 594 } 595 596 #endif 597 598 #ifdef TEST 599 int 600 main(int argc, char **argv) 601 { 602 struct dns_reply *r; 603 struct resource_record *rr; 604 r = dns_lookup(argv[1], argv[2]); 605 if(r == NULL){ 606 printf("No reply.\n"); 607 return 1; 608 } 609 if(r->q.type == T_SRV) 610 dns_srv_order(r); 611 612 for(rr = r->head; rr;rr=rr->next){ 613 printf("%-30s %-5s %-6d ", rr->domain, dns_type_to_string(rr->type), rr->ttl); 614 switch(rr->type){ 615 case T_NS: 616 case T_CNAME: 617 case T_PTR: 618 printf("%s\n", (char*)rr->u.data); 619 break; 620 case T_A: 621 printf("%s\n", inet_ntoa(*rr->u.a)); 622 break; 623 case T_MX: 624 case T_AFSDB:{ 625 printf("%d %s\n", rr->u.mx->preference, rr->u.mx->domain); 626 break; 627 } 628 case T_SRV:{ 629 struct srv_record *srv = rr->u.srv; 630 printf("%d %d %d %s\n", srv->priority, srv->weight, 631 srv->port, srv->target); 632 break; 633 } 634 case T_TXT: { 635 printf("%s\n", rr->u.txt); 636 break; 637 } 638 case T_SIG : { 639 struct sig_record *sig = rr->u.sig; 640 const char *type_string = dns_type_to_string (sig->type); 641 642 printf ("type %u (%s), algorithm %u, labels %u, orig_ttl %u, sig_expiration %u, sig_inception %u, key_tag %u, signer %s\n", 643 sig->type, type_string ? type_string : "", 644 sig->algorithm, sig->labels, sig->orig_ttl, 645 sig->sig_expiration, sig->sig_inception, sig->key_tag, 646 sig->signer); 647 break; 648 } 649 case T_KEY : { 650 struct key_record *key = rr->u.key; 651 652 printf ("flags %u, protocol %u, algorithm %u\n", 653 key->flags, key->protocol, key->algorithm); 654 break; 655 } 656 default: 657 printf("\n"); 658 break; 659 } 660 } 661 662 return 0; 663 } 664 #endif 665