1 /* 2 * Copyright (c) 1995 - 2001 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.30 2001/10/02 15:39:41 joda Exp $"); 49 50 #if defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) 51 52 #define DECL(X) {#X, T_##X} 53 54 static struct stot{ 55 const char *name; 56 int type; 57 }stot[] = { 58 DECL(A), 59 DECL(NS), 60 DECL(CNAME), 61 DECL(SOA), 62 DECL(PTR), 63 DECL(MX), 64 DECL(TXT), 65 DECL(AFSDB), 66 DECL(SIG), 67 DECL(KEY), 68 DECL(SRV), 69 DECL(NAPTR), 70 {NULL, 0} 71 }; 72 73 int _resolve_debug = 0; 74 75 int 76 dns_string_to_type(const char *name) 77 { 78 struct stot *p = stot; 79 for(p = stot; p->name; p++) 80 if(strcasecmp(name, p->name) == 0) 81 return p->type; 82 return -1; 83 } 84 85 const char * 86 dns_type_to_string(int type) 87 { 88 struct stot *p = stot; 89 for(p = stot; p->name; p++) 90 if(type == p->type) 91 return p->name; 92 return NULL; 93 } 94 95 void 96 dns_free_data(struct dns_reply *r) 97 { 98 struct resource_record *rr; 99 if(r->q.domain) 100 free(r->q.domain); 101 for(rr = r->head; rr;){ 102 struct resource_record *tmp = rr; 103 if(rr->domain) 104 free(rr->domain); 105 if(rr->u.data) 106 free(rr->u.data); 107 rr = rr->next; 108 free(tmp); 109 } 110 free (r); 111 } 112 113 static struct dns_reply* 114 parse_reply(unsigned char *data, int len) 115 { 116 unsigned char *p; 117 char host[128]; 118 int status; 119 120 struct dns_reply *r; 121 struct resource_record **rr; 122 123 r = calloc(1, sizeof(*r)); 124 if (r == NULL) 125 return NULL; 126 127 p = data; 128 #if 0 129 /* doesn't work on Crays */ 130 memcpy(&r->h, p, sizeof(HEADER)); 131 p += sizeof(HEADER); 132 #else 133 memcpy(&r->h, p, 12); /* XXX this will probably be mostly garbage */ 134 p += 12; 135 #endif 136 status = dn_expand(data, data + len, p, host, sizeof(host)); 137 if(status < 0){ 138 dns_free_data(r); 139 return NULL; 140 } 141 r->q.domain = strdup(host); 142 if(r->q.domain == NULL) { 143 dns_free_data(r); 144 return NULL; 145 } 146 p += status; 147 r->q.type = (p[0] << 8 | p[1]); 148 p += 2; 149 r->q.class = (p[0] << 8 | p[1]); 150 p += 2; 151 rr = &r->head; 152 while(p < data + len){ 153 int type, class, ttl, size; 154 status = dn_expand(data, data + len, p, host, sizeof(host)); 155 if(status < 0){ 156 dns_free_data(r); 157 return NULL; 158 } 159 p += status; 160 type = (p[0] << 8) | p[1]; 161 p += 2; 162 class = (p[0] << 8) | p[1]; 163 p += 2; 164 ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; 165 p += 4; 166 size = (p[0] << 8) | p[1]; 167 p += 2; 168 *rr = (struct resource_record*)calloc(1, 169 sizeof(struct resource_record)); 170 if(*rr == NULL) { 171 dns_free_data(r); 172 return NULL; 173 } 174 (*rr)->domain = strdup(host); 175 if((*rr)->domain == NULL) { 176 dns_free_data(r); 177 return NULL; 178 } 179 (*rr)->type = type; 180 (*rr)->class = class; 181 (*rr)->ttl = ttl; 182 (*rr)->size = size; 183 switch(type){ 184 case T_NS: 185 case T_CNAME: 186 case T_PTR: 187 status = dn_expand(data, data + len, p, host, sizeof(host)); 188 if(status < 0){ 189 dns_free_data(r); 190 return NULL; 191 } 192 (*rr)->u.txt = strdup(host); 193 if((*rr)->u.txt == NULL) { 194 dns_free_data(r); 195 return NULL; 196 } 197 break; 198 case T_MX: 199 case T_AFSDB:{ 200 status = dn_expand(data, data + len, p + 2, host, sizeof(host)); 201 if(status < 0){ 202 dns_free_data(r); 203 return NULL; 204 } 205 (*rr)->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) + 206 strlen(host)); 207 if((*rr)->u.mx == NULL) { 208 dns_free_data(r); 209 return NULL; 210 } 211 (*rr)->u.mx->preference = (p[0] << 8) | p[1]; 212 strcpy((*rr)->u.mx->domain, host); 213 break; 214 } 215 case T_SRV:{ 216 status = dn_expand(data, data + len, p + 6, host, sizeof(host)); 217 if(status < 0){ 218 dns_free_data(r); 219 return NULL; 220 } 221 (*rr)->u.srv = 222 (struct srv_record*)malloc(sizeof(struct srv_record) + 223 strlen(host)); 224 if((*rr)->u.srv == NULL) { 225 dns_free_data(r); 226 return NULL; 227 } 228 (*rr)->u.srv->priority = (p[0] << 8) | p[1]; 229 (*rr)->u.srv->weight = (p[2] << 8) | p[3]; 230 (*rr)->u.srv->port = (p[4] << 8) | p[5]; 231 strcpy((*rr)->u.srv->target, host); 232 break; 233 } 234 case T_TXT:{ 235 (*rr)->u.txt = (char*)malloc(size + 1); 236 if((*rr)->u.txt == NULL) { 237 dns_free_data(r); 238 return NULL; 239 } 240 strncpy((*rr)->u.txt, (char*)p + 1, *p); 241 (*rr)->u.txt[*p] = 0; 242 break; 243 } 244 case T_KEY : { 245 size_t key_len; 246 247 key_len = size - 4; 248 (*rr)->u.key = malloc (sizeof(*(*rr)->u.key) + key_len - 1); 249 if ((*rr)->u.key == NULL) { 250 dns_free_data (r); 251 return NULL; 252 } 253 254 (*rr)->u.key->flags = (p[0] << 8) | p[1]; 255 (*rr)->u.key->protocol = p[2]; 256 (*rr)->u.key->algorithm = p[3]; 257 (*rr)->u.key->key_len = key_len; 258 memcpy ((*rr)->u.key->key_data, p + 4, key_len); 259 break; 260 } 261 case T_SIG : { 262 size_t sig_len; 263 264 status = dn_expand (data, data + len, p + 18, host, sizeof(host)); 265 if (status < 0) { 266 dns_free_data (r); 267 return NULL; 268 } 269 sig_len = len - 18 - status; 270 (*rr)->u.sig = malloc(sizeof(*(*rr)->u.sig) 271 + strlen(host) + sig_len); 272 if ((*rr)->u.sig == NULL) { 273 dns_free_data (r); 274 return NULL; 275 } 276 (*rr)->u.sig->type = (p[0] << 8) | p[1]; 277 (*rr)->u.sig->algorithm = p[2]; 278 (*rr)->u.sig->labels = p[3]; 279 (*rr)->u.sig->orig_ttl = (p[4] << 24) | (p[5] << 16) 280 | (p[6] << 8) | p[7]; 281 (*rr)->u.sig->sig_expiration = (p[8] << 24) | (p[9] << 16) 282 | (p[10] << 8) | p[11]; 283 (*rr)->u.sig->sig_inception = (p[12] << 24) | (p[13] << 16) 284 | (p[14] << 8) | p[15]; 285 (*rr)->u.sig->key_tag = (p[16] << 8) | p[17]; 286 (*rr)->u.sig->sig_len = sig_len; 287 memcpy ((*rr)->u.sig->sig_data, p + 18 + status, sig_len); 288 (*rr)->u.sig->signer = &(*rr)->u.sig->sig_data[sig_len]; 289 strcpy((*rr)->u.sig->signer, host); 290 break; 291 } 292 293 case T_CERT : { 294 size_t cert_len; 295 296 cert_len = size - 5; 297 (*rr)->u.cert = malloc (sizeof(*(*rr)->u.cert) + cert_len - 1); 298 if ((*rr)->u.cert == NULL) { 299 dns_free_data (r); 300 return NULL; 301 } 302 303 (*rr)->u.cert->type = (p[0] << 8) | p[1]; 304 (*rr)->u.cert->tag = (p[2] << 8) | p[3]; 305 (*rr)->u.cert->algorithm = p[4]; 306 (*rr)->u.cert->cert_len = cert_len; 307 memcpy ((*rr)->u.cert->cert_data, p + 5, cert_len); 308 break; 309 } 310 default: 311 (*rr)->u.data = (unsigned char*)malloc(size); 312 if(size != 0 && (*rr)->u.data == NULL) { 313 dns_free_data(r); 314 return NULL; 315 } 316 memcpy((*rr)->u.data, p, size); 317 } 318 p += size; 319 rr = &(*rr)->next; 320 } 321 *rr = NULL; 322 return r; 323 } 324 325 static struct dns_reply * 326 dns_lookup_int(const char *domain, int rr_class, int rr_type) 327 { 328 unsigned char reply[1024]; 329 int len; 330 struct dns_reply *r = NULL; 331 u_long old_options = 0; 332 333 if (_resolve_debug) { 334 old_options = _res.options; 335 _res.options |= RES_DEBUG; 336 fprintf(stderr, "dns_lookup(%s, %d, %s)\n", domain, 337 rr_class, dns_type_to_string(rr_type)); 338 } 339 len = res_search(domain, rr_class, rr_type, reply, sizeof(reply)); 340 if (_resolve_debug) { 341 _res.options = old_options; 342 fprintf(stderr, "dns_lookup(%s, %d, %s) --> %d\n", 343 domain, rr_class, dns_type_to_string(rr_type), len); 344 } 345 if (len >= 0) 346 r = parse_reply(reply, len); 347 return r; 348 } 349 350 struct dns_reply * 351 dns_lookup(const char *domain, const char *type_name) 352 { 353 int type; 354 355 type = dns_string_to_type(type_name); 356 if(type == -1) { 357 if(_resolve_debug) 358 fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n", 359 type_name); 360 return NULL; 361 } 362 return dns_lookup_int(domain, C_IN, type); 363 } 364 365 static int 366 compare_srv(const void *a, const void *b) 367 { 368 const struct resource_record *const* aa = a, *const* bb = b; 369 370 if((*aa)->u.srv->priority == (*bb)->u.srv->priority) 371 return ((*aa)->u.srv->weight - (*bb)->u.srv->weight); 372 return ((*aa)->u.srv->priority - (*bb)->u.srv->priority); 373 } 374 375 #ifndef HAVE_RANDOM 376 #define random() rand() 377 #endif 378 379 /* try to rearrange the srv-records by the algorithm in RFC2782 */ 380 void 381 dns_srv_order(struct dns_reply *r) 382 { 383 struct resource_record **srvs, **ss, **headp; 384 struct resource_record *rr; 385 int num_srv = 0; 386 387 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE) 388 char state[256], *oldstate; 389 #endif 390 391 for(rr = r->head; rr; rr = rr->next) 392 if(rr->type == T_SRV) 393 num_srv++; 394 395 if(num_srv == 0) 396 return; 397 398 srvs = malloc(num_srv * sizeof(*srvs)); 399 if(srvs == NULL) 400 return; /* XXX not much to do here */ 401 402 /* unlink all srv-records from the linked list and put them in 403 a vector */ 404 for(ss = srvs, headp = &r->head; *headp; ) 405 if((*headp)->type == T_SRV) { 406 *ss = *headp; 407 *headp = (*headp)->next; 408 (*ss)->next = NULL; 409 ss++; 410 } else 411 headp = &(*headp)->next; 412 413 /* sort them by priority and weight */ 414 qsort(srvs, num_srv, sizeof(*srvs), compare_srv); 415 416 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE) 417 oldstate = initstate(time(NULL), state, sizeof(state)); 418 #endif 419 420 headp = &r->head; 421 422 for(ss = srvs; ss < srvs + num_srv; ) { 423 int sum, rnd, count; 424 struct resource_record **ee, **tt; 425 /* find the last record with the same priority and count the 426 sum of all weights */ 427 for(sum = 0, tt = ss; tt < srvs + num_srv; tt++) { 428 if(*tt == NULL) 429 continue; 430 if((*tt)->u.srv->priority != (*ss)->u.srv->priority) 431 break; 432 sum += (*tt)->u.srv->weight; 433 } 434 ee = tt; 435 /* ss is now the first record of this priority and ee is the 436 first of the next */ 437 while(ss < ee) { 438 rnd = random() % (sum + 1); 439 for(count = 0, tt = ss; ; tt++) { 440 if(*tt == NULL) 441 continue; 442 count += (*tt)->u.srv->weight; 443 if(count >= rnd) 444 break; 445 } 446 447 assert(tt < ee); 448 449 /* insert the selected record at the tail (of the head) of 450 the list */ 451 (*tt)->next = *headp; 452 *headp = *tt; 453 headp = &(*tt)->next; 454 sum -= (*tt)->u.srv->weight; 455 *tt = NULL; 456 while(ss < ee && *ss == NULL) 457 ss++; 458 } 459 } 460 461 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE) 462 setstate(oldstate); 463 #endif 464 free(srvs); 465 return; 466 } 467 468 #else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */ 469 470 struct dns_reply * 471 dns_lookup(const char *domain, const char *type_name) 472 { 473 return NULL; 474 } 475 476 void 477 dns_free_data(struct dns_reply *r) 478 { 479 } 480 481 void 482 dns_srv_order(struct dns_reply *r) 483 { 484 } 485 486 #endif 487 488 #ifdef TEST 489 int 490 main(int argc, char **argv) 491 { 492 struct dns_reply *r; 493 struct resource_record *rr; 494 r = dns_lookup(argv[1], argv[2]); 495 if(r == NULL){ 496 printf("No reply.\n"); 497 return 1; 498 } 499 if(r->q.type == T_SRV) 500 dns_srv_order(r); 501 502 for(rr = r->head; rr;rr=rr->next){ 503 printf("%s %s %d ", rr->domain, dns_type_to_string(rr->type), rr->ttl); 504 switch(rr->type){ 505 case T_NS: 506 case T_CNAME: 507 case T_PTR: 508 printf("%s\n", (char*)rr->u.data); 509 break; 510 case T_A: 511 printf("%s\n", inet_ntoa(*rr->u.a)); 512 break; 513 case T_MX: 514 case T_AFSDB:{ 515 printf("%d %s\n", rr->u.mx->preference, rr->u.mx->domain); 516 break; 517 } 518 case T_SRV:{ 519 struct srv_record *srv = rr->u.srv; 520 printf("%d %d %d %s\n", srv->priority, srv->weight, 521 srv->port, srv->target); 522 break; 523 } 524 case T_TXT: { 525 printf("%s\n", rr->u.txt); 526 break; 527 } 528 case T_SIG : { 529 struct sig_record *sig = rr->u.sig; 530 const char *type_string = dns_type_to_string (sig->type); 531 532 printf ("type %u (%s), algorithm %u, labels %u, orig_ttl %u, sig_expiration %u, sig_inception %u, key_tag %u, signer %s\n", 533 sig->type, type_string ? type_string : "", 534 sig->algorithm, sig->labels, sig->orig_ttl, 535 sig->sig_expiration, sig->sig_inception, sig->key_tag, 536 sig->signer); 537 break; 538 } 539 case T_KEY : { 540 struct key_record *key = rr->u.key; 541 542 printf ("flags %u, protocol %u, algorithm %u\n", 543 key->flags, key->protocol, key->algorithm); 544 break; 545 } 546 default: 547 printf("\n"); 548 break; 549 } 550 } 551 552 return 0; 553 } 554 #endif 555