1 /* $NetBSD: dns_lookup.c,v 1.1.1.2 2010/06/17 18:06:44 tron 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 static int dns_query(const char *name, int type, int flags, 180 DNS_REPLY *reply, VSTRING *why) 181 { 182 HEADER *reply_header; 183 int len; 184 unsigned long saved_options; 185 186 /* 187 * Initialize the reply buffer. 188 */ 189 if (reply->buf == 0) { 190 reply->buf = (unsigned char *) mymalloc(DEF_DNS_REPLY_SIZE); 191 reply->buf_len = DEF_DNS_REPLY_SIZE; 192 } 193 194 /* 195 * Initialize the name service. 196 */ 197 if ((_res.options & RES_INIT) == 0 && res_init() < 0) { 198 if (why) 199 vstring_strcpy(why, "Name service initialization failure"); 200 return (DNS_FAIL); 201 } 202 203 /* 204 * Set search options: debugging, parent domain search, append local 205 * domain. Do not allow the user to control other features. 206 */ 207 #define USER_FLAGS (RES_DEBUG | RES_DNSRCH | RES_DEFNAMES) 208 209 if ((flags & USER_FLAGS) != flags) 210 msg_panic("dns_query: bad flags: %d", flags); 211 saved_options = (_res.options & USER_FLAGS); 212 213 /* 214 * Perform the lookup. Claim that the information cannot be found if and 215 * only if the name server told us so. 216 */ 217 for (;;) { 218 _res.options &= ~saved_options; 219 _res.options |= flags; 220 len = res_search((char *) name, C_IN, type, reply->buf, reply->buf_len); 221 _res.options &= ~flags; 222 _res.options |= saved_options; 223 if (len < 0) { 224 if (why) 225 vstring_sprintf(why, "Host or domain name not found. " 226 "Name service error for name=%s type=%s: %s", 227 name, dns_strtype(type), dns_strerror(h_errno)); 228 if (msg_verbose) 229 msg_info("dns_query: %s (%s): %s", 230 name, dns_strtype(type), dns_strerror(h_errno)); 231 switch (h_errno) { 232 case NO_RECOVERY: 233 return (DNS_FAIL); 234 case HOST_NOT_FOUND: 235 case NO_DATA: 236 return (DNS_NOTFOUND); 237 default: 238 return (DNS_RETRY); 239 } 240 } 241 if (msg_verbose) 242 msg_info("dns_query: %s (%s): OK", name, dns_strtype(type)); 243 244 reply_header = (HEADER *) reply->buf; 245 if (reply_header->tc == 0 || reply->buf_len >= MAX_DNS_REPLY_SIZE) 246 break; 247 reply->buf = (unsigned char *) 248 myrealloc((char *) reply->buf, 2 * reply->buf_len); 249 reply->buf_len *= 2; 250 } 251 252 /* 253 * Paranoia. 254 */ 255 if (len > reply->buf_len) { 256 msg_warn("reply length %d > buffer length %d for name=%s type=%s", 257 len, (int) reply->buf_len, name, dns_strtype(type)); 258 len = reply->buf_len; 259 } 260 261 /* 262 * Initialize the reply structure. Some structure members are filled on 263 * the fly while the reply is being parsed. 264 */ 265 reply->end = reply->buf + len; 266 reply->query_start = reply->buf + sizeof(HEADER); 267 reply->answer_start = 0; 268 reply->query_count = ntohs(reply_header->qdcount); 269 reply->answer_count = ntohs(reply_header->ancount); 270 return (DNS_OK); 271 } 272 273 /* dns_skip_query - skip query data in name server reply */ 274 275 static int dns_skip_query(DNS_REPLY *reply) 276 { 277 int query_count = reply->query_count; 278 unsigned char *pos = reply->query_start; 279 char temp[DNS_NAME_LEN]; 280 int len; 281 282 /* 283 * For each query, skip over the domain name and over the fixed query 284 * data. 285 */ 286 while (query_count-- > 0) { 287 if (pos >= reply->end) 288 return DNS_RETRY; 289 len = dn_expand(reply->buf, reply->end, pos, temp, DNS_NAME_LEN); 290 if (len < 0) 291 return (DNS_RETRY); 292 pos += len + QFIXEDSZ; 293 } 294 reply->answer_start = pos; 295 return (DNS_OK); 296 } 297 298 /* dns_get_fixed - extract fixed data from resource record */ 299 300 static int dns_get_fixed(unsigned char *pos, DNS_FIXED *fixed) 301 { 302 GETSHORT(fixed->type, pos); 303 GETSHORT(fixed->class, pos); 304 GETLONG(fixed->ttl, pos); 305 GETSHORT(fixed->length, pos); 306 307 if (fixed->class != C_IN) { 308 msg_warn("dns_get_fixed: bad class: %u", fixed->class); 309 return (DNS_RETRY); 310 } 311 return (DNS_OK); 312 } 313 314 /* valid_rr_name - validate hostname in resource record */ 315 316 static int valid_rr_name(const char *name, const char *location, 317 unsigned type, DNS_REPLY *reply) 318 { 319 char temp[DNS_NAME_LEN]; 320 char *query_name; 321 int len; 322 char *gripe; 323 int result; 324 325 /* 326 * People aren't supposed to specify numeric names where domain names are 327 * required, but it "works" with some mailers anyway, so people complain 328 * when software doesn't bend over backwards. 329 */ 330 #define PASS_NAME 1 331 #define REJECT_NAME 0 332 333 if (valid_hostaddr(name, DONT_GRIPE)) { 334 result = PASS_NAME; 335 gripe = "numeric domain name"; 336 } else if (!valid_hostname(name, DO_GRIPE)) { 337 result = REJECT_NAME; 338 gripe = "malformed domain name"; 339 } else { 340 result = PASS_NAME; 341 gripe = 0; 342 } 343 344 /* 345 * If we have a gripe, show some context, including the name used in the 346 * query and the type of reply that we're looking at. 347 */ 348 if (gripe) { 349 len = dn_expand(reply->buf, reply->end, reply->query_start, 350 temp, DNS_NAME_LEN); 351 query_name = (len < 0 ? "*unparsable*" : temp); 352 msg_warn("%s in %s of %s record for %s: %.100s", 353 gripe, location, dns_strtype(type), query_name, name); 354 } 355 return (result); 356 } 357 358 /* dns_get_rr - extract resource record from name server reply */ 359 360 static int dns_get_rr(DNS_RR **list, const char *orig_name, DNS_REPLY *reply, 361 unsigned char *pos, char *rr_name, 362 DNS_FIXED *fixed) 363 { 364 char temp[DNS_NAME_LEN]; 365 ssize_t data_len; 366 unsigned pref = 0; 367 unsigned char *src; 368 unsigned char *dst; 369 int ch; 370 371 #define MIN2(a, b) ((unsigned)(a) < (unsigned)(b) ? (a) : (b)) 372 373 *list = 0; 374 375 switch (fixed->type) { 376 default: 377 msg_panic("dns_get_rr: don't know how to extract resource type %s", 378 dns_strtype(fixed->type)); 379 case T_CNAME: 380 case T_MB: 381 case T_MG: 382 case T_MR: 383 case T_NS: 384 case T_PTR: 385 if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0) 386 return (DNS_RETRY); 387 if (!valid_rr_name(temp, "resource data", fixed->type, reply)) 388 return (DNS_INVAL); 389 data_len = strlen(temp) + 1; 390 break; 391 case T_MX: 392 GETSHORT(pref, pos); 393 if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0) 394 return (DNS_RETRY); 395 if (!valid_rr_name(temp, "resource data", fixed->type, reply)) 396 return (DNS_INVAL); 397 data_len = strlen(temp) + 1; 398 break; 399 case T_A: 400 if (fixed->length != INET_ADDR_LEN) { 401 msg_warn("extract_answer: bad address length: %d", fixed->length); 402 return (DNS_RETRY); 403 } 404 if (fixed->length > sizeof(temp)) 405 msg_panic("dns_get_rr: length %d > DNS_NAME_LEN", 406 fixed->length); 407 memcpy(temp, pos, fixed->length); 408 data_len = fixed->length; 409 break; 410 #ifdef T_AAAA 411 case T_AAAA: 412 if (fixed->length != INET6_ADDR_LEN) { 413 msg_warn("extract_answer: bad address length: %d", fixed->length); 414 return (DNS_RETRY); 415 } 416 if (fixed->length > sizeof(temp)) 417 msg_panic("dns_get_rr: length %d > DNS_NAME_LEN", 418 fixed->length); 419 memcpy(temp, pos, fixed->length); 420 data_len = fixed->length; 421 break; 422 #endif 423 case T_TXT: 424 data_len = MIN2(pos[0] + 1, MIN2(fixed->length + 1, sizeof(temp))); 425 for (src = pos + 1, dst = (unsigned char *) (temp); 426 dst < (unsigned char *) (temp) + data_len - 1; /* */ ) { 427 ch = *src++; 428 *dst++ = (ISPRINT(ch) ? ch : ' '); 429 } 430 *dst = 0; 431 break; 432 } 433 *list = dns_rr_create(orig_name, rr_name, fixed->type, fixed->class, 434 fixed->ttl, pref, temp, data_len); 435 return (DNS_OK); 436 } 437 438 /* dns_get_alias - extract CNAME from name server reply */ 439 440 static int dns_get_alias(DNS_REPLY *reply, unsigned char *pos, 441 DNS_FIXED *fixed, char *cname, int c_len) 442 { 443 if (fixed->type != T_CNAME) 444 msg_panic("dns_get_alias: bad type %s", dns_strtype(fixed->type)); 445 if (dn_expand(reply->buf, reply->end, pos, cname, c_len) < 0) 446 return (DNS_RETRY); 447 if (!valid_rr_name(cname, "resource data", fixed->type, reply)) 448 return (DNS_INVAL); 449 return (DNS_OK); 450 } 451 452 /* dns_get_answer - extract answers from name server reply */ 453 454 static int dns_get_answer(const char *orig_name, DNS_REPLY *reply, int type, 455 DNS_RR **rrlist, VSTRING *fqdn, char *cname, int c_len) 456 { 457 char rr_name[DNS_NAME_LEN]; 458 unsigned char *pos; 459 int answer_count = reply->answer_count; 460 int len; 461 DNS_FIXED fixed; 462 DNS_RR *rr; 463 int resource_found = 0; 464 int cname_found = 0; 465 int not_found_status = DNS_NOTFOUND; /* can't happen */ 466 int status; 467 468 /* 469 * Initialize. Skip over the name server query if we haven't yet. 470 */ 471 if (reply->answer_start == 0) 472 if ((status = dns_skip_query(reply)) < 0) 473 return (status); 474 pos = reply->answer_start; 475 if (rrlist) 476 *rrlist = 0; 477 478 /* 479 * Either this, or use a GOTO for emergency exits. The purpose is to 480 * prevent incomplete answers from being passed back to the caller. 481 */ 482 #define CORRUPT(status) { \ 483 if (rrlist && *rrlist) { \ 484 dns_rr_free(*rrlist); \ 485 *rrlist = 0; \ 486 } \ 487 return (status); \ 488 } 489 490 /* 491 * Iterate over all answers. 492 */ 493 while (answer_count-- > 0) { 494 495 /* 496 * Optionally extract the fully-qualified domain name. 497 */ 498 if (pos >= reply->end) 499 CORRUPT(DNS_RETRY); 500 len = dn_expand(reply->buf, reply->end, pos, rr_name, DNS_NAME_LEN); 501 if (len < 0) 502 CORRUPT(DNS_RETRY); 503 pos += len; 504 505 /* 506 * Extract the fixed reply data: type, class, ttl, length. 507 */ 508 if (pos + RRFIXEDSZ > reply->end) 509 CORRUPT(DNS_RETRY); 510 if ((status = dns_get_fixed(pos, &fixed)) != DNS_OK) 511 CORRUPT(status); 512 if (!valid_rr_name(rr_name, "resource name", fixed.type, reply)) 513 CORRUPT(DNS_INVAL); 514 if (fqdn) 515 vstring_strcpy(fqdn, rr_name); 516 if (msg_verbose) 517 msg_info("dns_get_answer: type %s for %s", 518 dns_strtype(fixed.type), rr_name); 519 pos += RRFIXEDSZ; 520 521 /* 522 * Optionally extract the requested resource or CNAME data. 523 */ 524 if (pos + fixed.length > reply->end) 525 CORRUPT(DNS_RETRY); 526 if (type == fixed.type || type == T_ANY) { /* requested type */ 527 if (rrlist) { 528 if ((status = dns_get_rr(&rr, orig_name, reply, pos, rr_name, 529 &fixed)) == DNS_OK) { 530 resource_found++; 531 *rrlist = dns_rr_append(*rrlist, rr); 532 } else if (not_found_status != DNS_RETRY) 533 not_found_status = status; 534 } else 535 resource_found++; 536 } else if (fixed.type == T_CNAME) { /* cname resource */ 537 cname_found++; 538 if (cname && c_len > 0) 539 if ((status = dns_get_alias(reply, pos, &fixed, cname, c_len)) != DNS_OK) 540 CORRUPT(status); 541 } 542 pos += fixed.length; 543 } 544 545 /* 546 * See what answer we came up with. Report success when the requested 547 * information was found. Otherwise, when a CNAME was found, report that 548 * more recursion is needed. Otherwise report failure. 549 */ 550 if (resource_found) 551 return (DNS_OK); 552 if (cname_found) 553 return (DNS_RECURSE); 554 return (not_found_status); 555 } 556 557 /* dns_lookup - DNS lookup user interface */ 558 559 int dns_lookup(const char *name, unsigned type, unsigned flags, 560 DNS_RR **rrlist, VSTRING *fqdn, VSTRING *why) 561 { 562 char cname[DNS_NAME_LEN]; 563 int c_len = sizeof(cname); 564 static DNS_REPLY reply; 565 int count; 566 int status; 567 const char *orig_name = name; 568 569 /* 570 * DJBDNS produces a bogus A record when given a numerical hostname. 571 */ 572 if (valid_hostaddr(name, DONT_GRIPE)) { 573 if (why) 574 vstring_sprintf(why, 575 "Name service error for %s: invalid host or domain name", 576 name); 577 SET_H_ERRNO(HOST_NOT_FOUND); 578 return (DNS_NOTFOUND); 579 } 580 581 /* 582 * The Linux resolver misbehaves when given an invalid domain name. 583 */ 584 if (!valid_hostname(name, DONT_GRIPE)) { 585 if (why) 586 vstring_sprintf(why, 587 "Name service error for %s: invalid host or domain name", 588 name); 589 SET_H_ERRNO(HOST_NOT_FOUND); 590 return (DNS_NOTFOUND); 591 } 592 593 /* 594 * Perform the lookup. Follow CNAME chains, but only up to a 595 * pre-determined maximum. 596 */ 597 for (count = 0; count < 10; count++) { 598 599 /* 600 * Perform the DNS lookup, and pre-parse the name server reply. 601 */ 602 if ((status = dns_query(name, type, flags, &reply, why)) != DNS_OK) 603 return (status); 604 605 /* 606 * Extract resource records of the requested type. Pick up CNAME 607 * information just in case the requested data is not found. 608 */ 609 status = dns_get_answer(orig_name, &reply, type, rrlist, fqdn, 610 cname, c_len); 611 switch (status) { 612 default: 613 if (why) 614 vstring_sprintf(why, "Name service error for name=%s type=%s: " 615 "Malformed or unexpected name server reply", 616 name, dns_strtype(type)); 617 /* FALLTHROUGH */ 618 case DNS_OK: 619 return (status); 620 case DNS_RECURSE: 621 if (msg_verbose) 622 msg_info("dns_lookup: %s aliased to %s", name, cname); 623 name = cname; 624 } 625 } 626 if (why) 627 vstring_sprintf(why, "Name server loop for %s", name); 628 msg_warn("dns_lookup: Name server loop for %s", name); 629 return (DNS_NOTFOUND); 630 } 631 632 /* dns_lookup_l - DNS lookup interface with types list */ 633 634 int dns_lookup_l(const char *name, unsigned flags, DNS_RR **rrlist, 635 VSTRING *fqdn, VSTRING *why, int lflags,...) 636 { 637 va_list ap; 638 unsigned type; 639 int status = DNS_NOTFOUND; 640 DNS_RR *rr; 641 int non_err = 0; 642 int soft_err = 0; 643 644 if (rrlist) 645 *rrlist = 0; 646 va_start(ap, lflags); 647 while ((type = va_arg(ap, unsigned)) != 0) { 648 if (msg_verbose) 649 msg_info("lookup %s type %s flags %d", 650 name, dns_strtype(type), flags); 651 status = dns_lookup(name, type, flags, rrlist ? &rr : (DNS_RR **) 0, 652 fqdn, why); 653 if (status == DNS_OK) { 654 non_err = 1; 655 if (rrlist) 656 *rrlist = dns_rr_append(*rrlist, rr); 657 if (lflags & DNS_REQ_FLAG_STOP_OK) 658 break; 659 } else if (status == DNS_INVAL) { 660 if (lflags & DNS_REQ_FLAG_STOP_INVAL) 661 break; 662 } else if (status == DNS_RETRY) { 663 soft_err = 1; 664 } 665 /* XXX Stop after NXDOMAIN error. */ 666 } 667 va_end(ap); 668 return (non_err ? DNS_OK : soft_err ? DNS_RETRY : status); 669 } 670 671 /* dns_lookup_v - DNS lookup interface with types vector */ 672 673 int dns_lookup_v(const char *name, unsigned flags, DNS_RR **rrlist, 674 VSTRING *fqdn, VSTRING *why, int lflags, 675 unsigned *types) 676 { 677 unsigned type; 678 int status = DNS_NOTFOUND; 679 DNS_RR *rr; 680 int non_err = 0; 681 int soft_err = 0; 682 683 if (rrlist) 684 *rrlist = 0; 685 while ((type = *types++) != 0) { 686 if (msg_verbose) 687 msg_info("lookup %s type %s flags %d", 688 name, dns_strtype(type), flags); 689 status = dns_lookup(name, type, flags, rrlist ? &rr : (DNS_RR **) 0, 690 fqdn, why); 691 if (status == DNS_OK) { 692 non_err = 1; 693 if (rrlist) 694 *rrlist = dns_rr_append(*rrlist, rr); 695 if (lflags & DNS_REQ_FLAG_STOP_OK) 696 break; 697 } else if (status == DNS_INVAL) { 698 if (lflags & DNS_REQ_FLAG_STOP_INVAL) 699 break; 700 } else if (status == DNS_RETRY) { 701 soft_err = 1; 702 } 703 /* XXX Stop after NXDOMAIN error. */ 704 } 705 return (non_err ? DNS_OK : soft_err ? DNS_RETRY : status); 706 } 707