1 /* $NetBSD: dns_lookup.c,v 1.2 2012/07/05 17:40:11 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* dns_lookup 3 6 /* SUMMARY 7 /* domain name service lookup 8 /* SYNOPSIS 9 /* #include <dns.h> 10 /* 11 /* int dns_lookup(name, type, rflags, list, fqdn, why) 12 /* const char *name; 13 /* unsigned type; 14 /* unsigned rflags; 15 /* DNS_RR **list; 16 /* VSTRING *fqdn; 17 /* VSTRING *why; 18 /* 19 /* int dns_lookup_l(name, rflags, list, fqdn, why, lflags, ltype, ...) 20 /* const char *name; 21 /* unsigned rflags; 22 /* DNS_RR **list; 23 /* VSTRING *fqdn; 24 /* VSTRING *why; 25 /* int lflags; 26 /* unsigned ltype; 27 /* 28 /* int dns_lookup_v(name, rflags, list, fqdn, why, lflags, ltype) 29 /* const char *name; 30 /* unsigned rflags; 31 /* DNS_RR **list; 32 /* VSTRING *fqdn; 33 /* VSTRING *why; 34 /* int lflags; 35 /* unsigned *ltype; 36 /* DESCRIPTION 37 /* dns_lookup() looks up DNS resource records. When requested to 38 /* look up data other than type CNAME, it will follow a limited 39 /* number of CNAME indirections. All result names (including 40 /* null terminator) will fit a buffer of size DNS_NAME_LEN. 41 /* All name results are validated by \fIvalid_hostname\fR(); 42 /* an invalid name is reported as a DNS_INVAL result, while 43 /* malformed replies are reported as transient errors. 44 /* 45 /* dns_lookup_l() and dns_lookup_v() allow the user to specify 46 /* a list of resource types. 47 /* INPUTS 48 /* .ad 49 /* .fi 50 /* .IP name 51 /* The name to be looked up in the domain name system. 52 /* This name must pass the valid_hostname() test; it 53 /* must not be an IP address. 54 /* .IP type 55 /* The resource record type to be looked up (T_A, T_MX etc.). 56 /* .IP rflags 57 /* Resolver flags. These are a bitwise OR of: 58 /* .RS 59 /* .IP RES_DEBUG 60 /* Print debugging information. 61 /* .IP RES_DNSRCH 62 /* Search local domain and parent domains. 63 /* .IP RES_DEFNAMES 64 /* Append local domain to unqualified names. 65 /* .RE 66 /* .IP lflags 67 /* Multi-type request control for dns_lookup_l() and dns_lookup_v(). 68 /* For convenience, DNS_REQ_FLAG_NONE requests no special 69 /* processing. Invoke dns_lookup() for all specified resource 70 /* record types in the specified order, and merge their results. 71 /* Otherwise, specify one or more of the following: 72 /* .RS 73 /* .IP DNS_REQ_FLAG_STOP_INVAL 74 /* Invoke dns_lookup() for the resource types in the order as 75 /* specified, and return when dns_lookup() returns DNS_INVAL. 76 /* .IP DNS_REQ_FLAG_STOP_OK 77 /* Invoke dns_lookup() for the resource types in the order as 78 /* specified, and return when dns_lookup() returns DNS_OK. 79 /* .RE 80 /* .IP ltype 81 /* The resource record types to be looked up. In the case of 82 /* dns_lookup_l(), this is a null-terminated argument list. 83 /* In the case of dns_lookup_v(), this is a null-terminated 84 /* integer array. 85 /* OUTPUTS 86 /* .ad 87 /* .fi 88 /* .IP list 89 /* A null pointer, or a pointer to a variable that receives a 90 /* list of requested resource records. 91 /* .IP fqdn 92 /* A null pointer, or storage for the fully-qualified domain 93 /* name found for \fIname\fR. 94 /* .IP why 95 /* A null pointer, or storage for the reason for failure. 96 /* DIAGNOSTICS 97 /* dns_lookup() returns one of the following codes and sets the 98 /* \fIwhy\fR argument accordingly: 99 /* .IP DNS_OK 100 /* The DNS query succeeded. 101 /* .IP DNS_NOTFOUND 102 /* The DNS query succeeded; the requested information was not found. 103 /* .IP DNS_INVAL 104 /* The DNS query succeeded; the result failed the valid_hostname() test. 105 /* 106 /* NOTE: the valid_hostname() test is skipped for results that 107 /* the caller suppresses explicitly. For example, when the 108 /* caller requests MX record lookup but specifies a null 109 /* resource record list argument, no syntax check will be done 110 /* for MX server names. 111 /* .IP DNS_RETRY 112 /* The query failed, or the reply was malformed. 113 /* The problem is considered transient. 114 /* .IP DNS_FAIL 115 /* The query failed. 116 /* BUGS 117 /* dns_lookup() implements a subset of all possible resource types: 118 /* CNAME, MX, A, and some records with similar formatting requirements. 119 /* It is unwise to specify the T_ANY wildcard resource type. 120 /* 121 /* It takes a surprising amount of code to accomplish what appears 122 /* to be a simple task. Later versions of the mail system may implement 123 /* their own DNS client software. 124 /* SEE ALSO 125 /* dns_rr(3) resource record memory and list management 126 /* LICENSE 127 /* .ad 128 /* .fi 129 /* The Secure Mailer license must be distributed with this software. 130 /* AUTHOR(S) 131 /* Wietse Venema 132 /* IBM T.J. Watson Research 133 /* P.O. Box 704 134 /* Yorktown Heights, NY 10598, USA 135 /*--*/ 136 137 /* System library. */ 138 139 #include <sys_defs.h> 140 #include <netdb.h> 141 #include <string.h> 142 #include <ctype.h> 143 144 /* Utility library. */ 145 146 #include <mymalloc.h> 147 #include <vstring.h> 148 #include <msg.h> 149 #include <valid_hostname.h> 150 #include <stringops.h> 151 152 /* DNS library. */ 153 154 #include "dns.h" 155 156 /* Local stuff. */ 157 158 /* 159 * Structure to keep track of things while decoding a name server reply. 160 */ 161 #define DEF_DNS_REPLY_SIZE 4096 /* in case we're using TCP */ 162 #define MAX_DNS_REPLY_SIZE 32768 /* in case we're using TCP */ 163 164 typedef struct DNS_REPLY { 165 unsigned char *buf; /* raw reply data */ 166 size_t buf_len; /* reply buffer length */ 167 int query_count; /* number of queries */ 168 int answer_count; /* number of answers */ 169 unsigned char *query_start; /* start of query data */ 170 unsigned char *answer_start; /* start of answer data */ 171 unsigned char *end; /* first byte past reply */ 172 } DNS_REPLY; 173 174 #define INET_ADDR_LEN 4 /* XXX */ 175 #define INET6_ADDR_LEN 16 /* XXX */ 176 177 /* dns_query - query name server and pre-parse the reply */ 178 179 #if __RES < 20030124 180 181 static int 182 res_ninit(res_state res) 183 { 184 int error; 185 186 if ((error = res_init()) < 0) 187 return error; 188 189 *res = _res; 190 return error; 191 } 192 193 static int 194 res_nsearch(res_state statp, const char *dname, int class, int type, 195 u_char *answer, int anslen) 196 { 197 return res_search(dname, class, type, answer, anslen); 198 } 199 200 #endif 201 202 static int dns_query(const char *name, int type, int flags, 203 DNS_REPLY *reply, VSTRING *why) 204 { 205 HEADER *reply_header; 206 int len; 207 unsigned long saved_options; 208 /* For efficiency, we are not called from multiple threads */ 209 static struct __res_state res; 210 211 /* 212 * Initialize the reply buffer. 213 */ 214 if (reply->buf == 0) { 215 reply->buf = (unsigned char *) mymalloc(DEF_DNS_REPLY_SIZE); 216 reply->buf_len = DEF_DNS_REPLY_SIZE; 217 } 218 219 /* 220 * Initialize the name service. 221 */ 222 if ((res.options & RES_INIT) == 0 && res_ninit(&res) < 0) { 223 if (why) 224 vstring_strcpy(why, "Name service initialization failure"); 225 return (DNS_FAIL); 226 } 227 228 /* 229 * Set search options: debugging, parent domain search, append local 230 * domain. Do not allow the user to control other features. 231 */ 232 #define USER_FLAGS (RES_DEBUG | RES_DNSRCH | RES_DEFNAMES) 233 234 if ((flags & USER_FLAGS) != flags) 235 msg_panic("dns_query: bad flags: %d", flags); 236 saved_options = (res.options & USER_FLAGS); 237 238 /* 239 * Perform the lookup. Claim that the information cannot be found if and 240 * only if the name server told us so. 241 */ 242 for (;;) { 243 res.options &= ~saved_options; 244 res.options |= flags; 245 len = res_nsearch(&res, name, C_IN, type, reply->buf, reply->buf_len); 246 res.options &= ~flags; 247 res.options |= saved_options; 248 if (len < 0) { 249 if (why) 250 vstring_sprintf(why, "Host or domain name not found. " 251 "Name service error for name=%s type=%s: %s", 252 name, dns_strtype(type), dns_strerror(h_errno)); 253 if (msg_verbose) 254 msg_info("dns_query: %s (%s): %s", 255 name, dns_strtype(type), dns_strerror(h_errno)); 256 switch (h_errno) { 257 case NO_RECOVERY: 258 return (DNS_FAIL); 259 case HOST_NOT_FOUND: 260 case NO_DATA: 261 return (DNS_NOTFOUND); 262 default: 263 return (DNS_RETRY); 264 } 265 } 266 if (msg_verbose) 267 msg_info("dns_query: %s (%s): OK", name, dns_strtype(type)); 268 269 reply_header = (HEADER *) reply->buf; 270 if (reply_header->tc == 0 || reply->buf_len >= MAX_DNS_REPLY_SIZE) 271 break; 272 reply->buf = (unsigned char *) 273 myrealloc((char *) reply->buf, 2 * reply->buf_len); 274 reply->buf_len *= 2; 275 } 276 277 /* 278 * Paranoia. 279 */ 280 if (len > reply->buf_len) { 281 msg_warn("reply length %d > buffer length %d for name=%s type=%s", 282 len, (int) reply->buf_len, name, dns_strtype(type)); 283 len = reply->buf_len; 284 } 285 286 /* 287 * Initialize the reply structure. Some structure members are filled on 288 * the fly while the reply is being parsed. 289 */ 290 reply->end = reply->buf + len; 291 reply->query_start = reply->buf + sizeof(HEADER); 292 reply->answer_start = 0; 293 reply->query_count = ntohs(reply_header->qdcount); 294 reply->answer_count = ntohs(reply_header->ancount); 295 return (DNS_OK); 296 } 297 298 /* dns_skip_query - skip query data in name server reply */ 299 300 static int dns_skip_query(DNS_REPLY *reply) 301 { 302 int query_count = reply->query_count; 303 unsigned char *pos = reply->query_start; 304 char temp[DNS_NAME_LEN]; 305 int len; 306 307 /* 308 * For each query, skip over the domain name and over the fixed query 309 * data. 310 */ 311 while (query_count-- > 0) { 312 if (pos >= reply->end) 313 return DNS_RETRY; 314 len = dn_expand(reply->buf, reply->end, pos, temp, DNS_NAME_LEN); 315 if (len < 0) 316 return (DNS_RETRY); 317 pos += len + QFIXEDSZ; 318 } 319 reply->answer_start = pos; 320 return (DNS_OK); 321 } 322 323 /* dns_get_fixed - extract fixed data from resource record */ 324 325 static int dns_get_fixed(unsigned char *pos, DNS_FIXED *fixed) 326 { 327 GETSHORT(fixed->type, pos); 328 GETSHORT(fixed->class, pos); 329 GETLONG(fixed->ttl, pos); 330 GETSHORT(fixed->length, pos); 331 332 if (fixed->class != C_IN) { 333 msg_warn("dns_get_fixed: bad class: %u", fixed->class); 334 return (DNS_RETRY); 335 } 336 return (DNS_OK); 337 } 338 339 /* valid_rr_name - validate hostname in resource record */ 340 341 static int valid_rr_name(const char *name, const char *location, 342 unsigned type, DNS_REPLY *reply) 343 { 344 char temp[DNS_NAME_LEN]; 345 char *query_name; 346 int len; 347 char *gripe; 348 int result; 349 350 /* 351 * People aren't supposed to specify numeric names where domain names are 352 * required, but it "works" with some mailers anyway, so people complain 353 * when software doesn't bend over backwards. 354 */ 355 #define PASS_NAME 1 356 #define REJECT_NAME 0 357 358 if (valid_hostaddr(name, DONT_GRIPE)) { 359 result = PASS_NAME; 360 gripe = "numeric domain name"; 361 } else if (!valid_hostname(name, DO_GRIPE)) { 362 result = REJECT_NAME; 363 gripe = "malformed domain name"; 364 } else { 365 result = PASS_NAME; 366 gripe = 0; 367 } 368 369 /* 370 * If we have a gripe, show some context, including the name used in the 371 * query and the type of reply that we're looking at. 372 */ 373 if (gripe) { 374 len = dn_expand(reply->buf, reply->end, reply->query_start, 375 temp, DNS_NAME_LEN); 376 query_name = (len < 0 ? "*unparsable*" : temp); 377 msg_warn("%s in %s of %s record for %s: %.100s", 378 gripe, location, dns_strtype(type), query_name, name); 379 } 380 return (result); 381 } 382 383 /* dns_get_rr - extract resource record from name server reply */ 384 385 static int dns_get_rr(DNS_RR **list, const char *orig_name, DNS_REPLY *reply, 386 unsigned char *pos, char *rr_name, 387 DNS_FIXED *fixed) 388 { 389 char temp[DNS_NAME_LEN]; 390 ssize_t data_len; 391 unsigned pref = 0; 392 unsigned char *src; 393 unsigned char *dst; 394 int ch; 395 396 #define MIN2(a, b) ((unsigned)(a) < (unsigned)(b) ? (a) : (b)) 397 398 *list = 0; 399 400 switch (fixed->type) { 401 default: 402 msg_panic("dns_get_rr: don't know how to extract resource type %s", 403 dns_strtype(fixed->type)); 404 case T_CNAME: 405 case T_MB: 406 case T_MG: 407 case T_MR: 408 case T_NS: 409 case T_PTR: 410 if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0) 411 return (DNS_RETRY); 412 if (!valid_rr_name(temp, "resource data", fixed->type, reply)) 413 return (DNS_INVAL); 414 data_len = strlen(temp) + 1; 415 break; 416 case T_MX: 417 GETSHORT(pref, pos); 418 if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0) 419 return (DNS_RETRY); 420 if (!valid_rr_name(temp, "resource data", fixed->type, reply)) 421 return (DNS_INVAL); 422 data_len = strlen(temp) + 1; 423 break; 424 case T_A: 425 if (fixed->length != INET_ADDR_LEN) { 426 msg_warn("extract_answer: bad address length: %d", fixed->length); 427 return (DNS_RETRY); 428 } 429 if (fixed->length > sizeof(temp)) 430 msg_panic("dns_get_rr: length %d > DNS_NAME_LEN", 431 fixed->length); 432 memcpy(temp, pos, fixed->length); 433 data_len = fixed->length; 434 break; 435 #ifdef T_AAAA 436 case T_AAAA: 437 if (fixed->length != INET6_ADDR_LEN) { 438 msg_warn("extract_answer: bad address length: %d", fixed->length); 439 return (DNS_RETRY); 440 } 441 if (fixed->length > sizeof(temp)) 442 msg_panic("dns_get_rr: length %d > DNS_NAME_LEN", 443 fixed->length); 444 memcpy(temp, pos, fixed->length); 445 data_len = fixed->length; 446 break; 447 #endif 448 case T_TXT: 449 data_len = MIN2(pos[0] + 1, MIN2(fixed->length + 1, sizeof(temp))); 450 for (src = pos + 1, dst = (unsigned char *) (temp); 451 dst < (unsigned char *) (temp) + data_len - 1; /* */ ) { 452 ch = *src++; 453 *dst++ = (ISPRINT(ch) ? ch : ' '); 454 } 455 *dst = 0; 456 break; 457 } 458 *list = dns_rr_create(orig_name, rr_name, fixed->type, fixed->class, 459 fixed->ttl, pref, temp, data_len); 460 return (DNS_OK); 461 } 462 463 /* dns_get_alias - extract CNAME from name server reply */ 464 465 static int dns_get_alias(DNS_REPLY *reply, unsigned char *pos, 466 DNS_FIXED *fixed, char *cname, int c_len) 467 { 468 if (fixed->type != T_CNAME) 469 msg_panic("dns_get_alias: bad type %s", dns_strtype(fixed->type)); 470 if (dn_expand(reply->buf, reply->end, pos, cname, c_len) < 0) 471 return (DNS_RETRY); 472 if (!valid_rr_name(cname, "resource data", fixed->type, reply)) 473 return (DNS_INVAL); 474 return (DNS_OK); 475 } 476 477 /* dns_get_answer - extract answers from name server reply */ 478 479 static int dns_get_answer(const char *orig_name, DNS_REPLY *reply, int type, 480 DNS_RR **rrlist, VSTRING *fqdn, char *cname, int c_len) 481 { 482 char rr_name[DNS_NAME_LEN]; 483 unsigned char *pos; 484 int answer_count = reply->answer_count; 485 int len; 486 DNS_FIXED fixed; 487 DNS_RR *rr; 488 int resource_found = 0; 489 int cname_found = 0; 490 int not_found_status = DNS_NOTFOUND; /* can't happen */ 491 int status; 492 493 /* 494 * Initialize. Skip over the name server query if we haven't yet. 495 */ 496 if (reply->answer_start == 0) 497 if ((status = dns_skip_query(reply)) < 0) 498 return (status); 499 pos = reply->answer_start; 500 if (rrlist) 501 *rrlist = 0; 502 503 /* 504 * Either this, or use a GOTO for emergency exits. The purpose is to 505 * prevent incomplete answers from being passed back to the caller. 506 */ 507 #define CORRUPT(status) { \ 508 if (rrlist && *rrlist) { \ 509 dns_rr_free(*rrlist); \ 510 *rrlist = 0; \ 511 } \ 512 return (status); \ 513 } 514 515 /* 516 * Iterate over all answers. 517 */ 518 while (answer_count-- > 0) { 519 520 /* 521 * Optionally extract the fully-qualified domain name. 522 */ 523 if (pos >= reply->end) 524 CORRUPT(DNS_RETRY); 525 len = dn_expand(reply->buf, reply->end, pos, rr_name, DNS_NAME_LEN); 526 if (len < 0) 527 CORRUPT(DNS_RETRY); 528 pos += len; 529 530 /* 531 * Extract the fixed reply data: type, class, ttl, length. 532 */ 533 if (pos + RRFIXEDSZ > reply->end) 534 CORRUPT(DNS_RETRY); 535 if ((status = dns_get_fixed(pos, &fixed)) != DNS_OK) 536 CORRUPT(status); 537 if (!valid_rr_name(rr_name, "resource name", fixed.type, reply)) 538 CORRUPT(DNS_INVAL); 539 if (fqdn) 540 vstring_strcpy(fqdn, rr_name); 541 if (msg_verbose) 542 msg_info("dns_get_answer: type %s for %s", 543 dns_strtype(fixed.type), rr_name); 544 pos += RRFIXEDSZ; 545 546 /* 547 * Optionally extract the requested resource or CNAME data. 548 */ 549 if (pos + fixed.length > reply->end) 550 CORRUPT(DNS_RETRY); 551 if (type == fixed.type || type == T_ANY) { /* requested type */ 552 if (rrlist) { 553 if ((status = dns_get_rr(&rr, orig_name, reply, pos, rr_name, 554 &fixed)) == DNS_OK) { 555 resource_found++; 556 *rrlist = dns_rr_append(*rrlist, rr); 557 } else if (not_found_status != DNS_RETRY) 558 not_found_status = status; 559 } else 560 resource_found++; 561 } else if (fixed.type == T_CNAME) { /* cname resource */ 562 cname_found++; 563 if (cname && c_len > 0) 564 if ((status = dns_get_alias(reply, pos, &fixed, cname, c_len)) != DNS_OK) 565 CORRUPT(status); 566 } 567 pos += fixed.length; 568 } 569 570 /* 571 * See what answer we came up with. Report success when the requested 572 * information was found. Otherwise, when a CNAME was found, report that 573 * more recursion is needed. Otherwise report failure. 574 */ 575 if (resource_found) 576 return (DNS_OK); 577 if (cname_found) 578 return (DNS_RECURSE); 579 return (not_found_status); 580 } 581 582 /* dns_lookup - DNS lookup user interface */ 583 584 int dns_lookup(const char *name, unsigned type, unsigned flags, 585 DNS_RR **rrlist, VSTRING *fqdn, VSTRING *why) 586 { 587 char cname[DNS_NAME_LEN]; 588 int c_len = sizeof(cname); 589 static DNS_REPLY reply; 590 int count; 591 int status; 592 const char *orig_name = name; 593 594 /* 595 * DJBDNS produces a bogus A record when given a numerical hostname. 596 */ 597 if (valid_hostaddr(name, DONT_GRIPE)) { 598 if (why) 599 vstring_sprintf(why, 600 "Name service error for %s: invalid host or domain name", 601 name); 602 SET_H_ERRNO(HOST_NOT_FOUND); 603 return (DNS_NOTFOUND); 604 } 605 606 /* 607 * The Linux resolver misbehaves when given an invalid domain name. 608 */ 609 if (!valid_hostname(name, DONT_GRIPE)) { 610 if (why) 611 vstring_sprintf(why, 612 "Name service error for %s: invalid host or domain name", 613 name); 614 SET_H_ERRNO(HOST_NOT_FOUND); 615 return (DNS_NOTFOUND); 616 } 617 618 /* 619 * Perform the lookup. Follow CNAME chains, but only up to a 620 * pre-determined maximum. 621 */ 622 for (count = 0; count < 10; count++) { 623 624 /* 625 * Perform the DNS lookup, and pre-parse the name server reply. 626 */ 627 if ((status = dns_query(name, type, flags, &reply, why)) != DNS_OK) 628 return (status); 629 630 /* 631 * Extract resource records of the requested type. Pick up CNAME 632 * information just in case the requested data is not found. 633 */ 634 status = dns_get_answer(orig_name, &reply, type, rrlist, fqdn, 635 cname, c_len); 636 switch (status) { 637 default: 638 if (why) 639 vstring_sprintf(why, "Name service error for name=%s type=%s: " 640 "Malformed or unexpected name server reply", 641 name, dns_strtype(type)); 642 /* FALLTHROUGH */ 643 case DNS_OK: 644 return (status); 645 case DNS_RECURSE: 646 if (msg_verbose) 647 msg_info("dns_lookup: %s aliased to %s", name, cname); 648 name = cname; 649 } 650 } 651 if (why) 652 vstring_sprintf(why, "Name server loop for %s", name); 653 msg_warn("dns_lookup: Name server loop for %s", name); 654 return (DNS_NOTFOUND); 655 } 656 657 /* dns_lookup_l - DNS lookup interface with types list */ 658 659 int dns_lookup_l(const char *name, unsigned flags, DNS_RR **rrlist, 660 VSTRING *fqdn, VSTRING *why, int lflags,...) 661 { 662 va_list ap; 663 unsigned type; 664 int status = DNS_NOTFOUND; 665 DNS_RR *rr; 666 int non_err = 0; 667 int soft_err = 0; 668 669 if (rrlist) 670 *rrlist = 0; 671 va_start(ap, lflags); 672 while ((type = va_arg(ap, unsigned)) != 0) { 673 if (msg_verbose) 674 msg_info("lookup %s type %s flags %d", 675 name, dns_strtype(type), flags); 676 status = dns_lookup(name, type, flags, rrlist ? &rr : (DNS_RR **) 0, 677 fqdn, why); 678 if (status == DNS_OK) { 679 non_err = 1; 680 if (rrlist) 681 *rrlist = dns_rr_append(*rrlist, rr); 682 if (lflags & DNS_REQ_FLAG_STOP_OK) 683 break; 684 } else if (status == DNS_INVAL) { 685 if (lflags & DNS_REQ_FLAG_STOP_INVAL) 686 break; 687 } else if (status == DNS_RETRY) { 688 soft_err = 1; 689 } 690 /* XXX Stop after NXDOMAIN error. */ 691 } 692 va_end(ap); 693 return (non_err ? DNS_OK : soft_err ? DNS_RETRY : status); 694 } 695 696 /* dns_lookup_v - DNS lookup interface with types vector */ 697 698 int dns_lookup_v(const char *name, unsigned flags, DNS_RR **rrlist, 699 VSTRING *fqdn, VSTRING *why, int lflags, 700 unsigned *types) 701 { 702 unsigned type; 703 int status = DNS_NOTFOUND; 704 DNS_RR *rr; 705 int non_err = 0; 706 int soft_err = 0; 707 708 if (rrlist) 709 *rrlist = 0; 710 while ((type = *types++) != 0) { 711 if (msg_verbose) 712 msg_info("lookup %s type %s flags %d", 713 name, dns_strtype(type), flags); 714 status = dns_lookup(name, type, flags, rrlist ? &rr : (DNS_RR **) 0, 715 fqdn, why); 716 if (status == DNS_OK) { 717 non_err = 1; 718 if (rrlist) 719 *rrlist = dns_rr_append(*rrlist, rr); 720 if (lflags & DNS_REQ_FLAG_STOP_OK) 721 break; 722 } else if (status == DNS_INVAL) { 723 if (lflags & DNS_REQ_FLAG_STOP_INVAL) 724 break; 725 } else if (status == DNS_RETRY) { 726 soft_err = 1; 727 } 728 /* XXX Stop after NXDOMAIN error. */ 729 } 730 return (non_err ? DNS_OK : soft_err ? DNS_RETRY : status); 731 } 732