1/* $OpenBSD: getrrsetbyname_async.c,v 1.14 2024/05/07 23:40:53 djm Exp $ */ 2/* 3 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/types.h> 19#include <sys/socket.h> 20#include <sys/uio.h> 21#include <netinet/in.h> 22#include <arpa/nameser.h> 23#include <netdb.h> 24 25#include <asr.h> 26#include <errno.h> 27#include <resolv.h> 28#include <stdlib.h> 29#include <string.h> 30#include <unistd.h> 31 32#include "asr_private.h" 33 34static int getrrsetbyname_async_run(struct asr_query *, struct asr_result *); 35static void get_response(struct asr_result *, const char *, int); 36 37struct asr_query * 38getrrsetbyname_async(const char *hostname, unsigned int rdclass, 39 unsigned int rdtype, unsigned int flags, void *asr) 40{ 41 struct asr_ctx *ac; 42 struct asr_query *as; 43 44 ac = _asr_use_resolver(asr); 45 if ((as = _asr_async_new(ac, ASR_GETRRSETBYNAME)) == NULL) 46 goto abort; /* errno set */ 47 as->as_run = getrrsetbyname_async_run; 48 49 as->as.rrset.flags = flags; 50 as->as.rrset.class = rdclass; 51 as->as.rrset.type = rdtype; 52 as->as.rrset.name = strdup(hostname); 53 if (as->as.rrset.name == NULL) 54 goto abort; /* errno set */ 55 56 _asr_ctx_unref(ac); 57 return (as); 58 abort: 59 if (as) 60 _asr_async_free(as); 61 62 _asr_ctx_unref(ac); 63 return (NULL); 64} 65DEF_WEAK(getrrsetbyname_async); 66 67static int 68getrrsetbyname_async_run(struct asr_query *as, struct asr_result *ar) 69{ 70 next: 71 switch (as->as_state) { 72 73 case ASR_STATE_INIT: 74 75 /* Check for invalid class and type. */ 76 if (as->as.rrset.class > 0xffff || as->as.rrset.type > 0xffff) { 77 ar->ar_rrset_errno = ERRSET_INVAL; 78 async_set_state(as, ASR_STATE_HALT); 79 break; 80 } 81 82 /* Do not allow queries of class or type ANY. */ 83 if (as->as.rrset.class == 0xff || as->as.rrset.type == 0xff) { 84 ar->ar_rrset_errno = ERRSET_INVAL; 85 async_set_state(as, ASR_STATE_HALT); 86 break; 87 } 88 89 /* Do not allow flags yet, unimplemented. */ 90 if (as->as.rrset.flags) { 91 ar->ar_rrset_errno = ERRSET_INVAL; 92 async_set_state(as, ASR_STATE_HALT); 93 break; 94 } 95 96 /* Create a delegate the lookup to a subquery. */ 97 as->as_subq = _res_query_async_ctx( 98 as->as.rrset.name, 99 as->as.rrset.class, 100 as->as.rrset.type, 101 as->as_ctx); 102 if (as->as_subq == NULL) { 103 ar->ar_rrset_errno = ERRSET_FAIL; 104 async_set_state(as, ASR_STATE_HALT); 105 break; 106 } 107 108 async_set_state(as, ASR_STATE_SUBQUERY); 109 break; 110 111 case ASR_STATE_SUBQUERY: 112 113 if ((asr_run(as->as_subq, ar)) == ASYNC_COND) 114 return (ASYNC_COND); 115 116 as->as_subq = NULL; 117 118 /* No packet received.*/ 119 if (ar->ar_datalen == -1) { 120 switch (ar->ar_h_errno) { 121 case HOST_NOT_FOUND: 122 ar->ar_rrset_errno = ERRSET_NONAME; 123 break; 124 case NO_DATA: 125 ar->ar_rrset_errno = ERRSET_NODATA; 126 break; 127 default: 128 ar->ar_rrset_errno = ERRSET_FAIL; 129 break; 130 } 131 async_set_state(as, ASR_STATE_HALT); 132 break; 133 } 134 135 /* Got a packet but no answer. */ 136 if (ar->ar_count == 0) { 137 free(ar->ar_data); 138 switch (ar->ar_rcode) { 139 case NXDOMAIN: 140 ar->ar_rrset_errno = ERRSET_NONAME; 141 break; 142 case NOERROR: 143 ar->ar_rrset_errno = ERRSET_NODATA; 144 break; 145 default: 146 ar->ar_rrset_errno = ERRSET_FAIL; 147 break; 148 } 149 async_set_state(as, ASR_STATE_HALT); 150 break; 151 } 152 153 get_response(ar, ar->ar_data, ar->ar_datalen); 154 free(ar->ar_data); 155 async_set_state(as, ASR_STATE_HALT); 156 break; 157 158 case ASR_STATE_HALT: 159 if (ar->ar_rrset_errno) 160 ar->ar_rrsetinfo = NULL; 161 return (ASYNC_DONE); 162 163 default: 164 ar->ar_rrset_errno = ERRSET_FAIL; 165 async_set_state(as, ASR_STATE_HALT); 166 break; 167 } 168 goto next; 169} 170 171/* The rest of this file is taken from the original implementation. */ 172 173/* $OpenBSD: getrrsetbyname_async.c,v 1.14 2024/05/07 23:40:53 djm Exp $ */ 174 175/* 176 * Copyright (c) 2001 Jakob Schlyter. All rights reserved. 177 * 178 * Redistribution and use in source and binary forms, with or without 179 * modification, are permitted provided that the following conditions 180 * are met: 181 * 182 * 1. Redistributions of source code must retain the above copyright 183 * notice, this list of conditions and the following disclaimer. 184 * 185 * 2. Redistributions in binary form must reproduce the above copyright 186 * notice, this list of conditions and the following disclaimer in the 187 * documentation and/or other materials provided with the distribution. 188 * 189 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 190 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 191 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 192 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 193 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 194 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 195 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 196 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 197 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 198 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 199 */ 200 201/* 202 * Portions Copyright (c) 1999-2001 Internet Software Consortium. 203 * 204 * Permission to use, copy, modify, and distribute this software for any 205 * purpose with or without fee is hereby granted, provided that the above 206 * copyright notice and this permission notice appear in all copies. 207 * 208 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM 209 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 210 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 211 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, 212 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING 213 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 214 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 215 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 216 */ 217 218#define MAXPACKET 1024*64 219 220struct dns_query { 221 char *name; 222 u_int16_t type; 223 u_int16_t class; 224 struct dns_query *next; 225}; 226 227struct dns_rr { 228 char *name; 229 u_int16_t type; 230 u_int16_t class; 231 u_int16_t ttl; 232 u_int16_t size; 233 void *rdata; 234 struct dns_rr *next; 235}; 236 237struct dns_response { 238 HEADER header; 239 struct dns_query *query; 240 struct dns_rr *answer; 241 struct dns_rr *authority; 242 struct dns_rr *additional; 243}; 244 245static struct dns_response *parse_dns_response(const u_char *, int); 246static struct dns_query *parse_dns_qsection(const u_char *, int, 247 const u_char **, int); 248static struct dns_rr *parse_dns_rrsection(const u_char *, int, const u_char **, 249 int); 250 251static void free_dns_query(struct dns_query *); 252static void free_dns_rr(struct dns_rr *); 253static void free_dns_response(struct dns_response *); 254 255static int count_dns_rr(struct dns_rr *, u_int16_t, u_int16_t); 256 257static void 258get_response(struct asr_result *ar, const char *pkt, int pktlen) 259{ 260 struct rrsetinfo *rrset = NULL; 261 struct dns_response *response = NULL; 262 struct dns_rr *rr; 263 struct rdatainfo *rdata; 264 unsigned int index_ans, index_sig; 265 266 /* parse result */ 267 response = parse_dns_response(pkt, pktlen); 268 if (response == NULL) { 269 ar->ar_rrset_errno = ERRSET_FAIL; 270 goto fail; 271 } 272 273 if (response->header.qdcount != 1) { 274 ar->ar_rrset_errno = ERRSET_FAIL; 275 goto fail; 276 } 277 278 /* initialize rrset */ 279 rrset = calloc(1, sizeof(struct rrsetinfo)); 280 if (rrset == NULL) { 281 ar->ar_rrset_errno = ERRSET_NOMEMORY; 282 goto fail; 283 } 284 rrset->rri_rdclass = response->query->class; 285 rrset->rri_rdtype = response->query->type; 286 rrset->rri_ttl = response->answer->ttl; 287 rrset->rri_nrdatas = response->header.ancount; 288 289 /* check for authenticated data */ 290 if (response->header.ad == 1) 291 rrset->rri_flags |= RRSET_VALIDATED; 292 293 /* copy name from answer section */ 294 rrset->rri_name = strdup(response->answer->name); 295 if (rrset->rri_name == NULL) { 296 ar->ar_rrset_errno = ERRSET_NOMEMORY; 297 goto fail; 298 } 299 300 /* count answers */ 301 rrset->rri_nrdatas = count_dns_rr(response->answer, rrset->rri_rdclass, 302 rrset->rri_rdtype); 303 rrset->rri_nsigs = count_dns_rr(response->answer, rrset->rri_rdclass, 304 T_RRSIG); 305 306 /* allocate memory for answers */ 307 rrset->rri_rdatas = calloc(rrset->rri_nrdatas, 308 sizeof(struct rdatainfo)); 309 if (rrset->rri_rdatas == NULL) { 310 ar->ar_rrset_errno = ERRSET_NOMEMORY; 311 goto fail; 312 } 313 314 /* allocate memory for signatures */ 315 rrset->rri_sigs = calloc(rrset->rri_nsigs, sizeof(struct rdatainfo)); 316 if (rrset->rri_sigs == NULL) { 317 ar->ar_rrset_errno = ERRSET_NOMEMORY; 318 goto fail; 319 } 320 321 /* copy answers & signatures */ 322 for (rr = response->answer, index_ans = 0, index_sig = 0; 323 rr; rr = rr->next) { 324 325 rdata = NULL; 326 327 if (rr->class == rrset->rri_rdclass && 328 rr->type == rrset->rri_rdtype) 329 rdata = &rrset->rri_rdatas[index_ans++]; 330 331 if (rr->class == rrset->rri_rdclass && 332 rr->type == T_RRSIG) 333 rdata = &rrset->rri_sigs[index_sig++]; 334 335 if (rdata) { 336 rdata->rdi_length = rr->size; 337 if (rr->size != 0) { 338 rdata->rdi_data = malloc(rr->size); 339 if (rdata->rdi_data == NULL) { 340 ar->ar_rrset_errno = ERRSET_NOMEMORY; 341 goto fail; 342 } 343 memcpy(rdata->rdi_data, rr->rdata, rr->size); 344 } 345 } 346 } 347 free_dns_response(response); 348 349 ar->ar_rrsetinfo = rrset; 350 ar->ar_rrset_errno = ERRSET_SUCCESS; 351 return; 352 353fail: 354 if (rrset != NULL) 355 freerrset(rrset); 356 if (response != NULL) 357 free_dns_response(response); 358} 359 360/* 361 * DNS response parsing routines 362 */ 363static struct dns_response * 364parse_dns_response(const u_char *answer, int size) 365{ 366 struct dns_response *resp; 367 const u_char *cp; 368 369 if (size <= HFIXEDSZ) 370 return (NULL); 371 372 /* allocate memory for the response */ 373 resp = calloc(1, sizeof(*resp)); 374 if (resp == NULL) 375 return (NULL); 376 377 /* initialize current pointer */ 378 cp = answer; 379 380 /* copy header */ 381 memcpy(&resp->header, cp, HFIXEDSZ); 382 cp += HFIXEDSZ; 383 384 /* fix header byte order */ 385 resp->header.qdcount = ntohs(resp->header.qdcount); 386 resp->header.ancount = ntohs(resp->header.ancount); 387 resp->header.nscount = ntohs(resp->header.nscount); 388 resp->header.arcount = ntohs(resp->header.arcount); 389 390 /* there must be at least one query */ 391 if (resp->header.qdcount < 1) { 392 free_dns_response(resp); 393 return (NULL); 394 } 395 396 /* parse query section */ 397 resp->query = parse_dns_qsection(answer, size, &cp, 398 resp->header.qdcount); 399 if (resp->header.qdcount && resp->query == NULL) { 400 free_dns_response(resp); 401 return (NULL); 402 } 403 404 /* parse answer section */ 405 resp->answer = parse_dns_rrsection(answer, size, &cp, 406 resp->header.ancount); 407 if (resp->header.ancount && resp->answer == NULL) { 408 free_dns_response(resp); 409 return (NULL); 410 } 411 412 /* parse authority section */ 413 resp->authority = parse_dns_rrsection(answer, size, &cp, 414 resp->header.nscount); 415 if (resp->header.nscount && resp->authority == NULL) { 416 free_dns_response(resp); 417 return (NULL); 418 } 419 420 /* parse additional section */ 421 resp->additional = parse_dns_rrsection(answer, size, &cp, 422 resp->header.arcount); 423 if (resp->header.arcount && resp->additional == NULL) { 424 free_dns_response(resp); 425 return (NULL); 426 } 427 428 return (resp); 429} 430 431static struct dns_query * 432parse_dns_qsection(const u_char *answer, int size, const u_char **cp, int count) 433{ 434 struct dns_query *head, *curr, *prev; 435 int i, length; 436 char name[MAXDNAME]; 437 438#define NEED(need) \ 439 do { \ 440 if (*cp + need > answer + size) \ 441 goto fail; \ 442 } while (0) 443 444 for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { 445 if (*cp >= answer + size) { 446 fail: 447 free_dns_query(head); 448 return (NULL); 449 } 450 /* allocate and initialize struct */ 451 curr = calloc(1, sizeof(struct dns_query)); 452 if (curr == NULL) 453 goto fail; 454 if (head == NULL) 455 head = curr; 456 if (prev != NULL) 457 prev->next = curr; 458 459 /* name */ 460 length = dn_expand(answer, answer + size, *cp, name, 461 sizeof(name)); 462 if (length < 0) { 463 free_dns_query(head); 464 return (NULL); 465 } 466 curr->name = strdup(name); 467 if (curr->name == NULL) { 468 free_dns_query(head); 469 return (NULL); 470 } 471 NEED(length); 472 *cp += length; 473 474 /* type */ 475 NEED(INT16SZ); 476 curr->type = _getshort(*cp); 477 *cp += INT16SZ; 478 479 /* class */ 480 NEED(INT16SZ); 481 curr->class = _getshort(*cp); 482 *cp += INT16SZ; 483 } 484#undef NEED 485 486 return (head); 487} 488 489static struct dns_rr * 490parse_dns_rrsection(const u_char *answer, int size, const u_char **cp, 491 int count) 492{ 493 struct dns_rr *head, *curr, *prev; 494 int i, length; 495 char name[MAXDNAME]; 496 497#define NEED(need) \ 498 do { \ 499 if (*cp + need > answer + size) \ 500 goto fail; \ 501 } while (0) 502 503 for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { 504 if (*cp >= answer + size) { 505 fail: 506 free_dns_rr(head); 507 return (NULL); 508 } 509 510 /* allocate and initialize struct */ 511 curr = calloc(1, sizeof(struct dns_rr)); 512 if (curr == NULL) 513 goto fail; 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 NEED(length); 532 *cp += length; 533 534 /* type */ 535 NEED(INT16SZ); 536 curr->type = _getshort(*cp); 537 *cp += INT16SZ; 538 539 /* class */ 540 NEED(INT16SZ); 541 curr->class = _getshort(*cp); 542 *cp += INT16SZ; 543 544 /* ttl */ 545 NEED(INT32SZ); 546 curr->ttl = _getlong(*cp); 547 *cp += INT32SZ; 548 549 /* rdata size */ 550 NEED(INT16SZ); 551 curr->size = _getshort(*cp); 552 *cp += INT16SZ; 553 554 /* rdata itself */ 555 NEED(curr->size); 556 if (curr->size != 0) { 557 if ((curr->rdata = malloc(curr->size)) == NULL) { 558 free_dns_rr(head); 559 return (NULL); 560 } 561 memcpy(curr->rdata, *cp, curr->size); 562 } 563 *cp += curr->size; 564 } 565#undef NEED 566 567 return (head); 568} 569 570static void 571free_dns_query(struct dns_query *p) 572{ 573 if (p == NULL) 574 return; 575 576 if (p->name) 577 free(p->name); 578 free_dns_query(p->next); 579 free(p); 580} 581 582static void 583free_dns_rr(struct dns_rr *p) 584{ 585 if (p == NULL) 586 return; 587 588 if (p->name) 589 free(p->name); 590 if (p->rdata) 591 free(p->rdata); 592 free_dns_rr(p->next); 593 free(p); 594} 595 596static void 597free_dns_response(struct dns_response *p) 598{ 599 if (p == NULL) 600 return; 601 602 free_dns_query(p->query); 603 free_dns_rr(p->answer); 604 free_dns_rr(p->authority); 605 free_dns_rr(p->additional); 606 free(p); 607} 608 609static int 610count_dns_rr(struct dns_rr *p, u_int16_t class, u_int16_t type) 611{ 612 int n = 0; 613 614 while (p) { 615 if (p->class == class && p->type == type) 616 n++; 617 p = p->next; 618 } 619 620 return (n); 621} 622