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 rdata->rdi_data = malloc(rr->size); 332 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 free_dns_response(response); 341 342 *res = rrset; 343 return (ERRSET_SUCCESS); 344 345 fail: 346 if (rrset != NULL) 347 freerrset(rrset); 348 if (response != NULL) 349 free_dns_response(response); 350 return (result); 351 } 352 353 void 354 freerrset(struct rrsetinfo *rrset) 355 { 356 u_int16_t i; 357 358 if (rrset == NULL) 359 return; 360 361 if (rrset->rri_rdatas) { 362 for (i = 0; i < rrset->rri_nrdatas; i++) { 363 if (rrset->rri_rdatas[i].rdi_data == NULL) 364 break; 365 free(rrset->rri_rdatas[i].rdi_data); 366 } 367 free(rrset->rri_rdatas); 368 } 369 370 if (rrset->rri_sigs) { 371 for (i = 0; i < rrset->rri_nsigs; i++) { 372 if (rrset->rri_sigs[i].rdi_data == NULL) 373 break; 374 free(rrset->rri_sigs[i].rdi_data); 375 } 376 free(rrset->rri_sigs); 377 } 378 379 if (rrset->rri_name) 380 free(rrset->rri_name); 381 free(rrset); 382 } 383 384 /* 385 * DNS response parsing routines 386 */ 387 static struct dns_response * 388 parse_dns_response(const u_char *answer, int size) 389 { 390 struct dns_response *resp; 391 const u_char *cp; 392 393 /* allocate memory for the response */ 394 resp = calloc(1, sizeof(*resp)); 395 if (resp == NULL) 396 return (NULL); 397 398 /* initialize current pointer */ 399 cp = answer; 400 401 /* copy header */ 402 memcpy(&resp->header, cp, HFIXEDSZ); 403 cp += HFIXEDSZ; 404 405 /* fix header byte order */ 406 resp->header.qdcount = ntohs(resp->header.qdcount); 407 resp->header.ancount = ntohs(resp->header.ancount); 408 resp->header.nscount = ntohs(resp->header.nscount); 409 resp->header.arcount = ntohs(resp->header.arcount); 410 411 /* there must be at least one query */ 412 if (resp->header.qdcount < 1) { 413 free_dns_response(resp); 414 return (NULL); 415 } 416 417 /* parse query section */ 418 resp->query = parse_dns_qsection(answer, size, &cp, 419 resp->header.qdcount); 420 if (resp->header.qdcount && resp->query == NULL) { 421 free_dns_response(resp); 422 return (NULL); 423 } 424 425 /* parse answer section */ 426 resp->answer = parse_dns_rrsection(answer, size, &cp, 427 resp->header.ancount); 428 if (resp->header.ancount && resp->answer == NULL) { 429 free_dns_response(resp); 430 return (NULL); 431 } 432 433 /* parse authority section */ 434 resp->authority = parse_dns_rrsection(answer, size, &cp, 435 resp->header.nscount); 436 if (resp->header.nscount && resp->authority == NULL) { 437 free_dns_response(resp); 438 return (NULL); 439 } 440 441 /* parse additional section */ 442 resp->additional = parse_dns_rrsection(answer, size, &cp, 443 resp->header.arcount); 444 if (resp->header.arcount && resp->additional == NULL) { 445 free_dns_response(resp); 446 return (NULL); 447 } 448 449 return (resp); 450 } 451 452 static struct dns_query * 453 parse_dns_qsection(const u_char *answer, int size, const u_char **cp, int count) 454 { 455 struct dns_query *head, *curr, *prev; 456 int i, length; 457 char name[MAXDNAME]; 458 459 for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { 460 461 /* allocate and initialize struct */ 462 curr = calloc(1, sizeof(struct dns_query)); 463 if (curr == NULL) { 464 free_dns_query(head); 465 return (NULL); 466 } 467 if (head == NULL) 468 head = curr; 469 if (prev != NULL) 470 prev->next = curr; 471 472 /* name */ 473 length = dn_expand(answer, answer + size, *cp, name, 474 sizeof(name)); 475 if (length < 0) { 476 free_dns_query(head); 477 return (NULL); 478 } 479 curr->name = strdup(name); 480 if (curr->name == NULL) { 481 free_dns_query(head); 482 return (NULL); 483 } 484 *cp += length; 485 486 /* type */ 487 curr->type = _getshort(*cp); 488 *cp += INT16SZ; 489 490 /* class */ 491 curr->class = _getshort(*cp); 492 *cp += INT16SZ; 493 } 494 495 return (head); 496 } 497 498 static struct dns_rr * 499 parse_dns_rrsection(const u_char *answer, int size, const u_char **cp, 500 int count) 501 { 502 struct dns_rr *head, *curr, *prev; 503 int i, length; 504 char name[MAXDNAME]; 505 506 for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { 507 508 /* allocate and initialize struct */ 509 curr = calloc(1, sizeof(struct dns_rr)); 510 if (curr == NULL) { 511 free_dns_rr(head); 512 return (NULL); 513 } 514 if (head == NULL) 515 head = curr; 516 if (prev != NULL) 517 prev->next = curr; 518 519 /* name */ 520 length = dn_expand(answer, answer + size, *cp, name, 521 sizeof(name)); 522 if (length < 0) { 523 free_dns_rr(head); 524 return (NULL); 525 } 526 curr->name = strdup(name); 527 if (curr->name == NULL) { 528 free_dns_rr(head); 529 return (NULL); 530 } 531 *cp += length; 532 533 /* type */ 534 curr->type = _getshort(*cp); 535 *cp += INT16SZ; 536 537 /* class */ 538 curr->class = _getshort(*cp); 539 *cp += INT16SZ; 540 541 /* ttl */ 542 curr->ttl = _getlong(*cp); 543 *cp += INT32SZ; 544 545 /* rdata size */ 546 curr->size = _getshort(*cp); 547 *cp += INT16SZ; 548 549 /* rdata itself */ 550 curr->rdata = malloc(curr->size); 551 if (curr->rdata == NULL) { 552 free_dns_rr(head); 553 return (NULL); 554 } 555 memcpy(curr->rdata, *cp, curr->size); 556 *cp += curr->size; 557 } 558 559 return (head); 560 } 561 562 static void 563 free_dns_query(struct dns_query *p) 564 { 565 if (p == NULL) 566 return; 567 568 if (p->name) 569 free(p->name); 570 free_dns_query(p->next); 571 free(p); 572 } 573 574 static void 575 free_dns_rr(struct dns_rr *p) 576 { 577 if (p == NULL) 578 return; 579 580 if (p->name) 581 free(p->name); 582 if (p->rdata) 583 free(p->rdata); 584 free_dns_rr(p->next); 585 free(p); 586 } 587 588 static void 589 free_dns_response(struct dns_response *p) 590 { 591 if (p == NULL) 592 return; 593 594 free_dns_query(p->query); 595 free_dns_rr(p->answer); 596 free_dns_rr(p->authority); 597 free_dns_rr(p->additional); 598 free(p); 599 } 600 601 static int 602 count_dns_rr(struct dns_rr *p, u_int16_t class, u_int16_t type) 603 { 604 int n = 0; 605 606 while(p) { 607 if (p->class == class && p->type == type) 608 n++; 609 p = p->next; 610 } 611 612 return (n); 613 } 614 615 #endif /* !defined (HAVE_GETRRSETBYNAME) && !defined (HAVE_LDNS) */ 616