1 /* $OpenBSD: getrrsetbyname.c,v 1.11 2007/10/11 18:36:41 jakob Exp $ */ 2 3 /* 4 * Copyright (c) 2001 Jakob Schlyter. 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 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Portions Copyright (c) 1999-2001 Internet Software Consortium. 31 * 32 * Permission to use, copy, modify, and distribute this software for any 33 * purpose with or without fee is hereby granted, provided that the above 34 * copyright notice and this permission notice appear in all copies. 35 * 36 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM 37 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 38 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 39 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, 40 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING 41 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 42 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 43 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 44 */ 45 46 /* OPENBSD ORIGINAL: lib/libc/net/getrrsetbyname.c */ 47 48 #include "includes.h" 49 50 #if !defined (HAVE_GETRRSETBYNAME) && !defined (HAVE_LDNS) 51 52 #include <stdlib.h> 53 #include <string.h> 54 55 #include <netinet/in.h> 56 #include <arpa/inet.h> 57 58 #include "getrrsetbyname.h" 59 60 #if defined(HAVE_DECL_H_ERRNO) && !HAVE_DECL_H_ERRNO 61 extern int h_errno; 62 #endif 63 64 /* We don't need multithread support here */ 65 #ifdef _THREAD_PRIVATE 66 # undef _THREAD_PRIVATE 67 #endif 68 #define _THREAD_PRIVATE(a,b,c) (c) 69 70 #ifndef HAVE__RES_EXTERN 71 struct __res_state _res; 72 #endif 73 74 /* Necessary functions and macros */ 75 76 /* 77 * Inline versions of get/put short/long. Pointer is advanced. 78 * 79 * These macros demonstrate the property of C whereby it can be 80 * portable or it can be elegant but rarely both. 81 */ 82 83 #ifndef INT32SZ 84 # define INT32SZ 4 85 #endif 86 #ifndef INT16SZ 87 # define INT16SZ 2 88 #endif 89 90 #ifndef GETSHORT 91 #define GETSHORT(s, cp) { \ 92 u_char *t_cp = (u_char *)(cp); \ 93 (s) = ((u_int16_t)t_cp[0] << 8) \ 94 | ((u_int16_t)t_cp[1]) \ 95 ; \ 96 (cp) += INT16SZ; \ 97 } 98 #endif 99 100 #ifndef GETLONG 101 #define GETLONG(l, cp) { \ 102 u_char *t_cp = (u_char *)(cp); \ 103 (l) = ((u_int32_t)t_cp[0] << 24) \ 104 | ((u_int32_t)t_cp[1] << 16) \ 105 | ((u_int32_t)t_cp[2] << 8) \ 106 | ((u_int32_t)t_cp[3]) \ 107 ; \ 108 (cp) += INT32SZ; \ 109 } 110 #endif 111 112 /* 113 * If the system doesn't have _getshort/_getlong or that are not exactly what 114 * we need then use local replacements, avoiding name collisions. 115 */ 116 #if !defined(HAVE__GETSHORT) || !defined(HAVE__GETLONG) || \ 117 !defined(HAVE_DECL__GETSHORT) || HAVE_DECL__GETSHORT == 0 || \ 118 !defined(HAVE_DECL__GETLONG) || HAVE_DECL__GETLONG == 0 119 # ifdef _getshort 120 # undef _getshort 121 # endif 122 # ifdef _getlong 123 # undef _getlong 124 # endif 125 # define _getshort(x) (_ssh_compat_getshort(x)) 126 # define _getlong(x) (_ssh_compat_getlong(x)) 127 /* 128 * Routines to insert/extract short/long's. 129 */ 130 static u_int16_t 131 _getshort(const u_char *msgp) 132 { 133 u_int16_t u; 134 135 GETSHORT(u, msgp); 136 return (u); 137 } 138 139 static u_int32_t 140 _getlong(const u_char *msgp) 141 { 142 u_int32_t u; 143 144 GETLONG(u, msgp); 145 return (u); 146 } 147 #endif /* missing _getshort/_getlong */ 148 149 /* ************** */ 150 151 #define ANSWER_BUFFER_SIZE 0xffff 152 153 struct dns_query { 154 char *name; 155 u_int16_t type; 156 u_int16_t class; 157 struct dns_query *next; 158 }; 159 160 struct dns_rr { 161 char *name; 162 u_int16_t type; 163 u_int16_t class; 164 u_int16_t ttl; 165 u_int16_t size; 166 void *rdata; 167 struct dns_rr *next; 168 }; 169 170 struct dns_response { 171 HEADER header; 172 struct dns_query *query; 173 struct dns_rr *answer; 174 struct dns_rr *authority; 175 struct dns_rr *additional; 176 }; 177 178 static struct dns_response *parse_dns_response(const u_char *, int); 179 static struct dns_query *parse_dns_qsection(const u_char *, int, 180 const u_char **, int); 181 static struct dns_rr *parse_dns_rrsection(const u_char *, int, const u_char **, 182 int); 183 184 static void free_dns_query(struct dns_query *); 185 static void free_dns_rr(struct dns_rr *); 186 static void free_dns_response(struct dns_response *); 187 188 static int count_dns_rr(struct dns_rr *, u_int16_t, u_int16_t); 189 190 int 191 getrrsetbyname(const char *hostname, unsigned int rdclass, 192 unsigned int rdtype, unsigned int flags, 193 struct rrsetinfo **res) 194 { 195 struct __res_state *_resp = _THREAD_PRIVATE(_res, _res, &_res); 196 int result; 197 struct rrsetinfo *rrset = NULL; 198 struct dns_response *response = NULL; 199 struct dns_rr *rr; 200 struct rdatainfo *rdata; 201 int length; 202 unsigned int index_ans, index_sig; 203 u_char answer[ANSWER_BUFFER_SIZE]; 204 205 /* check for invalid class and type */ 206 if (rdclass > 0xffff || rdtype > 0xffff) { 207 result = ERRSET_INVAL; 208 goto fail; 209 } 210 211 /* don't allow queries of class or type ANY */ 212 if (rdclass == 0xff || rdtype == 0xff) { 213 result = ERRSET_INVAL; 214 goto fail; 215 } 216 217 /* don't allow flags yet, unimplemented */ 218 if (flags) { 219 result = ERRSET_INVAL; 220 goto fail; 221 } 222 223 /* initialize resolver */ 224 if ((_resp->options & RES_INIT) == 0 && res_init() == -1) { 225 result = ERRSET_FAIL; 226 goto fail; 227 } 228 229 #ifdef DEBUG 230 _resp->options |= RES_DEBUG; 231 #endif /* DEBUG */ 232 233 #ifdef RES_USE_DNSSEC 234 /* turn on DNSSEC if EDNS0 is configured */ 235 if (_resp->options & RES_USE_EDNS0) 236 _resp->options |= RES_USE_DNSSEC; 237 #endif /* RES_USE_DNSEC */ 238 239 /* make query */ 240 length = res_query(hostname, (signed int) rdclass, (signed int) rdtype, 241 answer, sizeof(answer)); 242 if (length < 0) { 243 switch(h_errno) { 244 case HOST_NOT_FOUND: 245 result = ERRSET_NONAME; 246 goto fail; 247 case NO_DATA: 248 result = ERRSET_NODATA; 249 goto fail; 250 default: 251 result = ERRSET_FAIL; 252 goto fail; 253 } 254 } 255 256 /* parse result */ 257 response = parse_dns_response(answer, length); 258 if (response == NULL) { 259 result = ERRSET_FAIL; 260 goto fail; 261 } 262 263 if (response->header.qdcount != 1) { 264 result = ERRSET_FAIL; 265 goto fail; 266 } 267 268 /* initialize rrset */ 269 rrset = calloc(1, sizeof(struct rrsetinfo)); 270 if (rrset == NULL) { 271 result = ERRSET_NOMEMORY; 272 goto fail; 273 } 274 rrset->rri_rdclass = response->query->class; 275 rrset->rri_rdtype = response->query->type; 276 rrset->rri_ttl = response->answer->ttl; 277 rrset->rri_nrdatas = response->header.ancount; 278 279 #ifdef HAVE_HEADER_AD 280 /* check for authenticated data */ 281 if (response->header.ad == 1) 282 rrset->rri_flags |= RRSET_VALIDATED; 283 #endif 284 285 /* copy name from answer section */ 286 rrset->rri_name = strdup(response->answer->name); 287 if (rrset->rri_name == NULL) { 288 result = ERRSET_NOMEMORY; 289 goto fail; 290 } 291 292 /* count answers */ 293 rrset->rri_nrdatas = count_dns_rr(response->answer, rrset->rri_rdclass, 294 rrset->rri_rdtype); 295 rrset->rri_nsigs = count_dns_rr(response->answer, rrset->rri_rdclass, 296 T_RRSIG); 297 298 /* allocate memory for answers */ 299 rrset->rri_rdatas = calloc(rrset->rri_nrdatas, 300 sizeof(struct rdatainfo)); 301 if (rrset->rri_rdatas == NULL) { 302 result = ERRSET_NOMEMORY; 303 goto fail; 304 } 305 306 /* allocate memory for signatures */ 307 if (rrset->rri_nsigs > 0) { 308 rrset->rri_sigs = calloc(rrset->rri_nsigs, sizeof(struct rdatainfo)); 309 if (rrset->rri_sigs == NULL) { 310 result = ERRSET_NOMEMORY; 311 goto fail; 312 } 313 } 314 315 /* copy answers & signatures */ 316 for (rr = response->answer, index_ans = 0, index_sig = 0; 317 rr; rr = rr->next) { 318 319 rdata = NULL; 320 321 if (rr->class == rrset->rri_rdclass && 322 rr->type == rrset->rri_rdtype) 323 rdata = &rrset->rri_rdatas[index_ans++]; 324 325 if (rr->class == rrset->rri_rdclass && 326 rr->type == T_RRSIG) 327 rdata = &rrset->rri_sigs[index_sig++]; 328 329 if (rdata) { 330 rdata->rdi_length = rr->size; 331 if (rr->size != 0) { 332 rdata->rdi_data = malloc(rr->size); 333 if (rdata->rdi_data == NULL) { 334 result = ERRSET_NOMEMORY; 335 goto fail; 336 } 337 memcpy(rdata->rdi_data, rr->rdata, rr->size); 338 } 339 } 340 } 341 free_dns_response(response); 342 343 *res = rrset; 344 return (ERRSET_SUCCESS); 345 346 fail: 347 if (rrset != NULL) 348 freerrset(rrset); 349 if (response != NULL) 350 free_dns_response(response); 351 return (result); 352 } 353 354 void 355 freerrset(struct rrsetinfo *rrset) 356 { 357 u_int16_t i; 358 359 if (rrset == NULL) 360 return; 361 362 if (rrset->rri_rdatas) { 363 for (i = 0; i < rrset->rri_nrdatas; i++) { 364 if (rrset->rri_rdatas[i].rdi_data == NULL) 365 break; 366 free(rrset->rri_rdatas[i].rdi_data); 367 } 368 free(rrset->rri_rdatas); 369 } 370 371 if (rrset->rri_sigs) { 372 for (i = 0; i < rrset->rri_nsigs; i++) { 373 if (rrset->rri_sigs[i].rdi_data == NULL) 374 break; 375 free(rrset->rri_sigs[i].rdi_data); 376 } 377 free(rrset->rri_sigs); 378 } 379 380 if (rrset->rri_name) 381 free(rrset->rri_name); 382 free(rrset); 383 } 384 385 /* 386 * DNS response parsing routines 387 */ 388 static struct dns_response * 389 parse_dns_response(const u_char *answer, int size) 390 { 391 struct dns_response *resp; 392 const u_char *cp; 393 394 if (size < HFIXEDSZ) 395 return (NULL); 396 397 /* allocate memory for the response */ 398 resp = calloc(1, sizeof(*resp)); 399 if (resp == NULL) 400 return (NULL); 401 402 /* initialize current pointer */ 403 cp = answer; 404 405 /* copy header */ 406 memcpy(&resp->header, cp, HFIXEDSZ); 407 cp += HFIXEDSZ; 408 409 /* fix header byte order */ 410 resp->header.qdcount = ntohs(resp->header.qdcount); 411 resp->header.ancount = ntohs(resp->header.ancount); 412 resp->header.nscount = ntohs(resp->header.nscount); 413 resp->header.arcount = ntohs(resp->header.arcount); 414 415 /* there must be at least one query */ 416 if (resp->header.qdcount < 1) { 417 free_dns_response(resp); 418 return (NULL); 419 } 420 421 /* parse query section */ 422 resp->query = parse_dns_qsection(answer, size, &cp, 423 resp->header.qdcount); 424 if (resp->header.qdcount && resp->query == NULL) { 425 free_dns_response(resp); 426 return (NULL); 427 } 428 429 /* parse answer section */ 430 resp->answer = parse_dns_rrsection(answer, size, &cp, 431 resp->header.ancount); 432 if (resp->header.ancount && resp->answer == NULL) { 433 free_dns_response(resp); 434 return (NULL); 435 } 436 437 /* parse authority section */ 438 resp->authority = parse_dns_rrsection(answer, size, &cp, 439 resp->header.nscount); 440 if (resp->header.nscount && resp->authority == NULL) { 441 free_dns_response(resp); 442 return (NULL); 443 } 444 445 /* parse additional section */ 446 resp->additional = parse_dns_rrsection(answer, size, &cp, 447 resp->header.arcount); 448 if (resp->header.arcount && resp->additional == NULL) { 449 free_dns_response(resp); 450 return (NULL); 451 } 452 453 return (resp); 454 } 455 456 static struct dns_query * 457 parse_dns_qsection(const u_char *answer, int size, const u_char **cp, int count) 458 { 459 struct dns_query *head, *curr, *prev; 460 int i, length; 461 char name[MAXDNAME]; 462 463 #define NEED(need) \ 464 do { \ 465 if (*cp + need > answer + size) \ 466 goto fail; \ 467 } while (0) 468 469 for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { 470 if (*cp >= answer + size) { 471 fail: 472 free_dns_query(head); 473 return (NULL); 474 } 475 /* allocate and initialize struct */ 476 curr = calloc(1, sizeof(struct dns_query)); 477 if (curr == NULL) 478 goto fail; 479 if (head == NULL) 480 head = curr; 481 if (prev != NULL) 482 prev->next = curr; 483 484 /* name */ 485 length = dn_expand(answer, answer + size, *cp, name, 486 sizeof(name)); 487 if (length < 0) { 488 free_dns_query(head); 489 return (NULL); 490 } 491 curr->name = strdup(name); 492 if (curr->name == NULL) { 493 free_dns_query(head); 494 return (NULL); 495 } 496 NEED(length); 497 *cp += length; 498 499 /* type */ 500 NEED(INT16SZ); 501 curr->type = _getshort(*cp); 502 *cp += INT16SZ; 503 504 /* class */ 505 NEED(INT16SZ); 506 curr->class = _getshort(*cp); 507 *cp += INT16SZ; 508 } 509 #undef NEED 510 511 return (head); 512 } 513 514 static struct dns_rr * 515 parse_dns_rrsection(const u_char *answer, int size, const u_char **cp, 516 int count) 517 { 518 struct dns_rr *head, *curr, *prev; 519 int i, length; 520 char name[MAXDNAME]; 521 522 #define NEED(need) \ 523 do { \ 524 if (*cp + need > answer + size) \ 525 goto fail; \ 526 } while (0) 527 528 for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { 529 if (*cp >= answer + size) { 530 fail: 531 free_dns_rr(head); 532 return (NULL); 533 } 534 535 /* allocate and initialize struct */ 536 curr = calloc(1, sizeof(struct dns_rr)); 537 if (curr == NULL) 538 goto fail; 539 if (head == NULL) 540 head = curr; 541 if (prev != NULL) 542 prev->next = curr; 543 544 /* name */ 545 length = dn_expand(answer, answer + size, *cp, name, 546 sizeof(name)); 547 if (length < 0) { 548 free_dns_rr(head); 549 return (NULL); 550 } 551 curr->name = strdup(name); 552 if (curr->name == NULL) { 553 free_dns_rr(head); 554 return (NULL); 555 } 556 NEED(length); 557 *cp += length; 558 559 /* type */ 560 NEED(INT16SZ); 561 curr->type = _getshort(*cp); 562 *cp += INT16SZ; 563 564 /* class */ 565 NEED(INT16SZ); 566 curr->class = _getshort(*cp); 567 *cp += INT16SZ; 568 569 /* ttl */ 570 NEED(INT32SZ); 571 curr->ttl = _getlong(*cp); 572 *cp += INT32SZ; 573 574 /* rdata size */ 575 NEED(INT16SZ); 576 curr->size = _getshort(*cp); 577 *cp += INT16SZ; 578 579 /* rdata itself */ 580 NEED(curr->size); 581 if (curr->size != 0) { 582 if ((curr->rdata = malloc(curr->size)) == NULL) { 583 free_dns_rr(head); 584 return (NULL); 585 } 586 memcpy(curr->rdata, *cp, curr->size); 587 } 588 *cp += curr->size; 589 } 590 #undef NEED 591 592 return (head); 593 } 594 595 static void 596 free_dns_query(struct dns_query *p) 597 { 598 if (p == NULL) 599 return; 600 601 if (p->name) 602 free(p->name); 603 free_dns_query(p->next); 604 free(p); 605 } 606 607 static void 608 free_dns_rr(struct dns_rr *p) 609 { 610 if (p == NULL) 611 return; 612 613 if (p->name) 614 free(p->name); 615 if (p->rdata) 616 free(p->rdata); 617 free_dns_rr(p->next); 618 free(p); 619 } 620 621 static void 622 free_dns_response(struct dns_response *p) 623 { 624 if (p == NULL) 625 return; 626 627 free_dns_query(p->query); 628 free_dns_rr(p->answer); 629 free_dns_rr(p->authority); 630 free_dns_rr(p->additional); 631 free(p); 632 } 633 634 static int 635 count_dns_rr(struct dns_rr *p, u_int16_t class, u_int16_t type) 636 { 637 int n = 0; 638 639 while(p) { 640 if (p->class == class && p->type == type) 641 n++; 642 p = p->next; 643 } 644 645 return (n); 646 } 647 648 #endif /* !defined (HAVE_GETRRSETBYNAME) && !defined (HAVE_LDNS) */ 649