1 /* 2 * Copyright (c) 1995 - 2002 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.36.4.1 2002/10/21 14:48:15 joda 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 status = dn_expand(data, end_data, p + 2, host, sizeof(host)); 170 if(status < 0){ 171 free(*rr); 172 return -1; 173 } 174 if (status + 2 > size) { 175 free(*rr); 176 return -1; 177 } 178 179 (*rr)->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) + 180 strlen(host)); 181 if((*rr)->u.mx == NULL) { 182 free(*rr); 183 return -1; 184 } 185 (*rr)->u.mx->preference = (p[0] << 8) | p[1]; 186 strcpy((*rr)->u.mx->domain, host); 187 break; 188 } 189 case T_SRV:{ 190 status = dn_expand(data, end_data, p + 6, host, sizeof(host)); 191 if(status < 0){ 192 free(*rr); 193 return -1; 194 } 195 if (status + 6 > size) { 196 free(*rr); 197 return -1; 198 } 199 200 (*rr)->u.srv = 201 (struct srv_record*)malloc(sizeof(struct srv_record) + 202 strlen(host)); 203 if((*rr)->u.srv == NULL) { 204 free(*rr); 205 return -1; 206 } 207 (*rr)->u.srv->priority = (p[0] << 8) | p[1]; 208 (*rr)->u.srv->weight = (p[2] << 8) | p[3]; 209 (*rr)->u.srv->port = (p[4] << 8) | p[5]; 210 strcpy((*rr)->u.srv->target, host); 211 break; 212 } 213 case T_TXT:{ 214 if(size == 0 || size < *p + 1) { 215 free(*rr); 216 return -1; 217 } 218 (*rr)->u.txt = (char*)malloc(*p + 1); 219 if((*rr)->u.txt == NULL) { 220 free(*rr); 221 return -1; 222 } 223 strncpy((*rr)->u.txt, (char*)p + 1, *p); 224 (*rr)->u.txt[*p] = '\0'; 225 break; 226 } 227 case T_KEY : { 228 size_t key_len; 229 230 if (size < 4) { 231 free(*rr); 232 return -1; 233 } 234 235 key_len = size - 4; 236 (*rr)->u.key = malloc (sizeof(*(*rr)->u.key) + key_len - 1); 237 if ((*rr)->u.key == NULL) { 238 free(*rr); 239 return -1; 240 } 241 242 (*rr)->u.key->flags = (p[0] << 8) | p[1]; 243 (*rr)->u.key->protocol = p[2]; 244 (*rr)->u.key->algorithm = p[3]; 245 (*rr)->u.key->key_len = key_len; 246 memcpy ((*rr)->u.key->key_data, p + 4, key_len); 247 break; 248 } 249 case T_SIG : { 250 size_t sig_len; 251 252 if(size <= 18) { 253 free(*rr); 254 return -1; 255 } 256 status = dn_expand (data, end_data, p + 18, host, sizeof(host)); 257 if (status < 0) { 258 free(*rr); 259 return -1; 260 } 261 if (status + 18 > size) { 262 free(*rr); 263 return -1; 264 } 265 266 /* the signer name is placed after the sig_data, to make it 267 easy to free this struture; the size calculation below 268 includes the zero-termination if the structure itself. 269 don't you just love C? 270 */ 271 sig_len = size - 18 - status; 272 (*rr)->u.sig = malloc(sizeof(*(*rr)->u.sig) 273 + strlen(host) + sig_len); 274 if ((*rr)->u.sig == NULL) { 275 free(*rr); 276 return -1; 277 } 278 (*rr)->u.sig->type = (p[0] << 8) | p[1]; 279 (*rr)->u.sig->algorithm = p[2]; 280 (*rr)->u.sig->labels = p[3]; 281 (*rr)->u.sig->orig_ttl = (p[4] << 24) | (p[5] << 16) 282 | (p[6] << 8) | p[7]; 283 (*rr)->u.sig->sig_expiration = (p[8] << 24) | (p[9] << 16) 284 | (p[10] << 8) | p[11]; 285 (*rr)->u.sig->sig_inception = (p[12] << 24) | (p[13] << 16) 286 | (p[14] << 8) | p[15]; 287 (*rr)->u.sig->key_tag = (p[16] << 8) | p[17]; 288 (*rr)->u.sig->sig_len = sig_len; 289 memcpy ((*rr)->u.sig->sig_data, p + 18 + status, sig_len); 290 (*rr)->u.sig->signer = &(*rr)->u.sig->sig_data[sig_len]; 291 strcpy((*rr)->u.sig->signer, host); 292 break; 293 } 294 295 case T_CERT : { 296 size_t cert_len; 297 298 if (size < 5) { 299 free(*rr); 300 return -1; 301 } 302 303 cert_len = size - 5; 304 (*rr)->u.cert = malloc (sizeof(*(*rr)->u.cert) + cert_len - 1); 305 if ((*rr)->u.cert == NULL) { 306 free(*rr); 307 return -1; 308 } 309 310 (*rr)->u.cert->type = (p[0] << 8) | p[1]; 311 (*rr)->u.cert->tag = (p[2] << 8) | p[3]; 312 (*rr)->u.cert->algorithm = p[4]; 313 (*rr)->u.cert->cert_len = cert_len; 314 memcpy ((*rr)->u.cert->cert_data, p + 5, cert_len); 315 break; 316 } 317 default: 318 (*rr)->u.data = (unsigned char*)malloc(size); 319 if(size != 0 && (*rr)->u.data == NULL) { 320 free(*rr); 321 return -1; 322 } 323 memcpy((*rr)->u.data, p, size); 324 } 325 *pp = p + size; 326 return 0; 327 } 328 329 #ifndef TEST_RESOLVE 330 static 331 #endif 332 struct dns_reply* 333 parse_reply(const unsigned char *data, size_t len) 334 { 335 const unsigned char *p; 336 int status; 337 int i; 338 char host[MAXDNAME]; 339 const unsigned char *end_data = data + len; 340 struct dns_reply *r; 341 struct resource_record **rr; 342 343 r = calloc(1, sizeof(*r)); 344 if (r == NULL) 345 return NULL; 346 347 p = data; 348 #if 0 349 /* doesn't work on Crays */ 350 memcpy(&r->h, p, sizeof(HEADER)); 351 p += sizeof(HEADER); 352 #else 353 memcpy(&r->h, p, 12); /* XXX this will probably be mostly garbage */ 354 p += 12; 355 #endif 356 if(ntohs(r->h.qdcount) != 1) { 357 free(r); 358 return NULL; 359 } 360 status = dn_expand(data, end_data, p, host, sizeof(host)); 361 if(status < 0){ 362 dns_free_data(r); 363 return NULL; 364 } 365 r->q.domain = strdup(host); 366 if(r->q.domain == NULL) { 367 dns_free_data(r); 368 return NULL; 369 } 370 if (p + status + 4 > end_data) { 371 dns_free_data(r); 372 return NULL; 373 } 374 p += status; 375 r->q.type = (p[0] << 8 | p[1]); 376 p += 2; 377 r->q.class = (p[0] << 8 | p[1]); 378 p += 2; 379 380 rr = &r->head; 381 for(i = 0; i < ntohs(r->h.ancount); i++) { 382 if(parse_record(data, end_data, &p, rr) != 0) { 383 dns_free_data(r); 384 return NULL; 385 } 386 rr = &(*rr)->next; 387 } 388 for(i = 0; i < ntohs(r->h.nscount); i++) { 389 if(parse_record(data, end_data, &p, rr) != 0) { 390 dns_free_data(r); 391 return NULL; 392 } 393 rr = &(*rr)->next; 394 } 395 for(i = 0; i < ntohs(r->h.arcount); i++) { 396 if(parse_record(data, end_data, &p, rr) != 0) { 397 dns_free_data(r); 398 return NULL; 399 } 400 rr = &(*rr)->next; 401 } 402 *rr = NULL; 403 return r; 404 } 405 406 static struct dns_reply * 407 dns_lookup_int(const char *domain, int rr_class, int rr_type) 408 { 409 unsigned char reply[1024]; 410 int len; 411 #ifdef HAVE_RES_NSEARCH 412 struct __res_state stat; 413 memset(&stat, 0, sizeof(stat)); 414 if(res_ninit(&stat)) 415 return NULL; /* is this the best we can do? */ 416 #elif defined(HAVE__RES) 417 u_long old_options = 0; 418 #endif 419 420 if (_resolve_debug) { 421 #ifdef HAVE_RES_NSEARCH 422 stat.options |= RES_DEBUG; 423 #elif defined(HAVE__RES) 424 old_options = _res.options; 425 _res.options |= RES_DEBUG; 426 #endif 427 fprintf(stderr, "dns_lookup(%s, %d, %s)\n", domain, 428 rr_class, dns_type_to_string(rr_type)); 429 } 430 #ifdef HAVE_RES_NSEARCH 431 len = res_nsearch(&stat, domain, rr_class, rr_type, reply, sizeof(reply)); 432 #else 433 len = res_search(domain, rr_class, rr_type, reply, sizeof(reply)); 434 #endif 435 if (_resolve_debug) { 436 #if defined(HAVE__RES) && !defined(HAVE_RES_NSEARCH) 437 _res.options = old_options; 438 #endif 439 fprintf(stderr, "dns_lookup(%s, %d, %s) --> %d\n", 440 domain, rr_class, dns_type_to_string(rr_type), len); 441 } 442 #ifdef HAVE_RES_NSEARCH 443 res_nclose(&stat); 444 #endif 445 if(len < 0) { 446 return NULL; 447 } else { 448 len = min(len, sizeof(reply)); 449 return parse_reply(reply, len); 450 } 451 } 452 453 struct dns_reply * 454 dns_lookup(const char *domain, const char *type_name) 455 { 456 int type; 457 458 type = dns_string_to_type(type_name); 459 if(type == -1) { 460 if(_resolve_debug) 461 fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n", 462 type_name); 463 return NULL; 464 } 465 return dns_lookup_int(domain, C_IN, type); 466 } 467 468 static int 469 compare_srv(const void *a, const void *b) 470 { 471 const struct resource_record *const* aa = a, *const* bb = b; 472 473 if((*aa)->u.srv->priority == (*bb)->u.srv->priority) 474 return ((*aa)->u.srv->weight - (*bb)->u.srv->weight); 475 return ((*aa)->u.srv->priority - (*bb)->u.srv->priority); 476 } 477 478 #ifndef HAVE_RANDOM 479 #define random() rand() 480 #endif 481 482 /* try to rearrange the srv-records by the algorithm in RFC2782 */ 483 void 484 dns_srv_order(struct dns_reply *r) 485 { 486 struct resource_record **srvs, **ss, **headp; 487 struct resource_record *rr; 488 int num_srv = 0; 489 490 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE) 491 int state[256 / sizeof(int)]; 492 char *oldstate; 493 #endif 494 495 for(rr = r->head; rr; rr = rr->next) 496 if(rr->type == T_SRV) 497 num_srv++; 498 499 if(num_srv == 0) 500 return; 501 502 srvs = malloc(num_srv * sizeof(*srvs)); 503 if(srvs == NULL) 504 return; /* XXX not much to do here */ 505 506 /* unlink all srv-records from the linked list and put them in 507 a vector */ 508 for(ss = srvs, headp = &r->head; *headp; ) 509 if((*headp)->type == T_SRV) { 510 *ss = *headp; 511 *headp = (*headp)->next; 512 (*ss)->next = NULL; 513 ss++; 514 } else 515 headp = &(*headp)->next; 516 517 /* sort them by priority and weight */ 518 qsort(srvs, num_srv, sizeof(*srvs), compare_srv); 519 520 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE) 521 oldstate = initstate(time(NULL), (char*)state, sizeof(state)); 522 #endif 523 524 headp = &r->head; 525 526 for(ss = srvs; ss < srvs + num_srv; ) { 527 int sum, rnd, count; 528 struct resource_record **ee, **tt; 529 /* find the last record with the same priority and count the 530 sum of all weights */ 531 for(sum = 0, tt = ss; tt < srvs + num_srv; tt++) { 532 if(*tt == NULL) 533 continue; 534 if((*tt)->u.srv->priority != (*ss)->u.srv->priority) 535 break; 536 sum += (*tt)->u.srv->weight; 537 } 538 ee = tt; 539 /* ss is now the first record of this priority and ee is the 540 first of the next */ 541 while(ss < ee) { 542 rnd = random() % (sum + 1); 543 for(count = 0, tt = ss; ; tt++) { 544 if(*tt == NULL) 545 continue; 546 count += (*tt)->u.srv->weight; 547 if(count >= rnd) 548 break; 549 } 550 551 assert(tt < ee); 552 553 /* insert the selected record at the tail (of the head) of 554 the list */ 555 (*tt)->next = *headp; 556 *headp = *tt; 557 headp = &(*tt)->next; 558 sum -= (*tt)->u.srv->weight; 559 *tt = NULL; 560 while(ss < ee && *ss == NULL) 561 ss++; 562 } 563 } 564 565 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE) 566 setstate(oldstate); 567 #endif 568 free(srvs); 569 return; 570 } 571 572 #else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */ 573 574 struct dns_reply * 575 dns_lookup(const char *domain, const char *type_name) 576 { 577 return NULL; 578 } 579 580 void 581 dns_free_data(struct dns_reply *r) 582 { 583 } 584 585 void 586 dns_srv_order(struct dns_reply *r) 587 { 588 } 589 590 #endif 591 592 #ifdef TEST 593 int 594 main(int argc, char **argv) 595 { 596 struct dns_reply *r; 597 struct resource_record *rr; 598 r = dns_lookup(argv[1], argv[2]); 599 if(r == NULL){ 600 printf("No reply.\n"); 601 return 1; 602 } 603 if(r->q.type == T_SRV) 604 dns_srv_order(r); 605 606 for(rr = r->head; rr;rr=rr->next){ 607 printf("%-30s %-5s %-6d ", rr->domain, dns_type_to_string(rr->type), rr->ttl); 608 switch(rr->type){ 609 case T_NS: 610 case T_CNAME: 611 case T_PTR: 612 printf("%s\n", (char*)rr->u.data); 613 break; 614 case T_A: 615 printf("%s\n", inet_ntoa(*rr->u.a)); 616 break; 617 case T_MX: 618 case T_AFSDB:{ 619 printf("%d %s\n", rr->u.mx->preference, rr->u.mx->domain); 620 break; 621 } 622 case T_SRV:{ 623 struct srv_record *srv = rr->u.srv; 624 printf("%d %d %d %s\n", srv->priority, srv->weight, 625 srv->port, srv->target); 626 break; 627 } 628 case T_TXT: { 629 printf("%s\n", rr->u.txt); 630 break; 631 } 632 case T_SIG : { 633 struct sig_record *sig = rr->u.sig; 634 const char *type_string = dns_type_to_string (sig->type); 635 636 printf ("type %u (%s), algorithm %u, labels %u, orig_ttl %u, sig_expiration %u, sig_inception %u, key_tag %u, signer %s\n", 637 sig->type, type_string ? type_string : "", 638 sig->algorithm, sig->labels, sig->orig_ttl, 639 sig->sig_expiration, sig->sig_inception, sig->key_tag, 640 sig->signer); 641 break; 642 } 643 case T_KEY : { 644 struct key_record *key = rr->u.key; 645 646 printf ("flags %u, protocol %u, algorithm %u\n", 647 key->flags, key->protocol, key->algorithm); 648 break; 649 } 650 default: 651 printf("\n"); 652 break; 653 } 654 } 655 656 return 0; 657 } 658 #endif 659