1 /* $NetBSD: dns_lookup.c,v 1.7 2022/10/08 16:12:45 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 /* 37 /* int dns_get_h_errno() 38 /* AUXILIARY FUNCTIONS 39 /* extern int var_dns_ncache_ttl_fix; 40 /* 41 /* int dns_lookup_r(name, type, rflags, list, fqdn, why, rcode) 42 /* const char *name; 43 /* unsigned type; 44 /* unsigned rflags; 45 /* DNS_RR **list; 46 /* VSTRING *fqdn; 47 /* VSTRING *why; 48 /* int *rcode; 49 /* 50 /* int dns_lookup_rl(name, rflags, list, fqdn, why, rcode, lflags, 51 /* ltype, ...) 52 /* const char *name; 53 /* unsigned rflags; 54 /* DNS_RR **list; 55 /* VSTRING *fqdn; 56 /* VSTRING *why; 57 /* int *rcode; 58 /* int lflags; 59 /* unsigned ltype; 60 /* 61 /* int dns_lookup_rv(name, rflags, list, fqdn, why, rcode, lflags, 62 /* ltype) 63 /* const char *name; 64 /* unsigned rflags; 65 /* DNS_RR **list; 66 /* VSTRING *fqdn; 67 /* VSTRING *why; 68 /* int *rcode; 69 /* int lflags; 70 /* unsigned *ltype; 71 /* 72 /* int dns_lookup_x(name, type, rflags, list, fqdn, why, rcode, lflags) 73 /* const char *name; 74 /* unsigned type; 75 /* unsigned rflags; 76 /* DNS_RR **list; 77 /* VSTRING *fqdn; 78 /* VSTRING *why; 79 /* int *rcode; 80 /* unsigned lflags; 81 /* DESCRIPTION 82 /* dns_lookup() looks up DNS resource records. When requested to 83 /* look up data other than type CNAME, it will follow a limited 84 /* number of CNAME indirections. All result names (including 85 /* null terminator) will fit a buffer of size DNS_NAME_LEN. 86 /* All name results are validated by \fIvalid_hostname\fR(); 87 /* an invalid name is reported as a DNS_INVAL result, while 88 /* malformed replies are reported as transient errors. 89 /* 90 /* dns_get_h_errno() returns the last error. This deprecates 91 /* usage of the global h_errno variable. We should not rely 92 /* on that being updated. 93 /* 94 /* dns_lookup_l() and dns_lookup_v() allow the user to specify 95 /* a list of resource types. 96 /* 97 /* dns_lookup_x, dns_lookup_r(), dns_lookup_rl() and dns_lookup_rv() 98 /* accept or return additional information. 99 /* 100 /* The var_dns_ncache_ttl_fix variable controls a workaround 101 /* for res_search(3) implementations that break the 102 /* DNS_REQ_FLAG_NCACHE_TTL feature. The workaround does not 103 /* support EDNS0 or DNSSEC, but it should be sufficient for 104 /* DNSBL/DNSWL lookups. 105 /* INPUTS 106 /* .ad 107 /* .fi 108 /* .IP name 109 /* The name to be looked up in the domain name system. 110 /* This name must pass the valid_hostname() test; it 111 /* must not be an IP address. 112 /* .IP type 113 /* The resource record type to be looked up (T_A, T_MX etc.). 114 /* .IP rflags 115 /* Resolver flags. These are a bitwise OR of: 116 /* .RS 117 /* .IP RES_DEBUG 118 /* Print debugging information. 119 /* .IP RES_DNSRCH 120 /* Search local domain and parent domains. 121 /* .IP RES_DEFNAMES 122 /* Append local domain to unqualified names. 123 /* .IP RES_USE_DNSSEC 124 /* Request DNSSEC validation. This flag is silently ignored 125 /* when the system stub resolver API, resolver(3), does not 126 /* implement DNSSEC. 127 /* Automatically turns on the RES_TRUSTAD flag on systems that 128 /* support this flag (this behavior will be more configurable 129 /* in a later release). 130 /* .RE 131 /* .IP lflags 132 /* Flags that control the operation of the dns_lookup*() 133 /* functions. DNS_REQ_FLAG_NONE requests no special processing. 134 /* Otherwise, specify one or more of the following: 135 /* .RS 136 /* .IP DNS_REQ_FLAG_STOP_INVAL 137 /* This flag is used by dns_lookup_l() and dns_lookup_v(). 138 /* Invoke dns_lookup() for the resource types in the order as 139 /* specified, and return when dns_lookup() returns DNS_INVAL. 140 /* .IP DNS_REQ_FLAG_STOP_NULLMX 141 /* This flag is used by dns_lookup_l() and dns_lookup_v(). 142 /* Invoke dns_lookup() for the resource types in the order as 143 /* specified, and return when dns_lookup() returns DNS_NULLMX. 144 /* .IP DNS_REQ_FLAG_STOP_MX_POLICY 145 /* This flag is used by dns_lookup_l() and dns_lookup_v(). 146 /* Invoke dns_lookup() for the resource types in the order as 147 /* specified, and return when dns_lookup() returns DNS_POLICY 148 /* for an MX query. 149 /* .IP DNS_REQ_FLAG_STOP_OK 150 /* This flag is used by dns_lookup_l() and dns_lookup_v(). 151 /* Invoke dns_lookup() for the resource types in the order as 152 /* specified, and return when dns_lookup() returns DNS_OK. 153 /* .IP DNS_REQ_FLAG_NCACHE_TTL 154 /* When the lookup result status is DNS_NOTFOUND, return the 155 /* SOA record(s) from the authority section in the reply, if 156 /* available. The per-record reply TTL specifies how long the 157 /* DNS_NOTFOUND answer is valid. The caller should pass the 158 /* record(s) to dns_rr_free(). 159 /* Logs a warning if the RES_DNSRCH or RES_DEFNAMES resolver 160 /* flags are set, and disables those flags. 161 /* .RE 162 /* .IP ltype 163 /* The resource record types to be looked up. In the case of 164 /* dns_lookup_l(), this is a null-terminated argument list. 165 /* In the case of dns_lookup_v(), this is a null-terminated 166 /* integer array. 167 /* OUTPUTS 168 /* .ad 169 /* .fi 170 /* .IP list 171 /* A null pointer, or a pointer to a variable that receives a 172 /* list of requested resource records. 173 /* .IP fqdn 174 /* A null pointer, or storage for the fully-qualified domain 175 /* name found for \fIname\fR. 176 /* .IP why 177 /* A null pointer, or storage for the reason for failure. 178 /* .IP rcode 179 /* Pointer to storage for the reply RCODE value. This gives 180 /* more detailed information than DNS_FAIL, DNS_RETRY, etc. 181 /* DIAGNOSTICS 182 /* If DNSSEC validation is requested but the response is not 183 /* DNSSEC validated, dns_lookup() will send a one-time probe 184 /* query as configured with the \fBdnssec_probe\fR configuration 185 /* parameter, and will log a warning when the probe response 186 /* was not DNSSEC validated. 187 /* .PP 188 /* dns_lookup() returns one of the following codes and sets the 189 /* \fIwhy\fR argument accordingly: 190 /* .IP DNS_OK 191 /* The DNS query succeeded. 192 /* .IP DNS_POLICY 193 /* The DNS query succeeded, but the answer did not pass the 194 /* policy filter. 195 /* .IP DNS_NOTFOUND 196 /* The DNS query succeeded; the requested information was not found. 197 /* .IP DNS_NULLMX 198 /* The DNS query succeeded; the requested service is unavailable. 199 /* This is returned when the list argument is not a null 200 /* pointer, and an MX lookup result contains a null server 201 /* name (so-called "nullmx" record). 202 /* .IP DNS_INVAL 203 /* The DNS query succeeded; the result failed the valid_hostname() test. 204 /* 205 /* NOTE: the valid_hostname() test is skipped for results that 206 /* the caller suppresses explicitly. For example, when the 207 /* caller requests MX record lookup but specifies a null 208 /* resource record list argument, no syntax check will be done 209 /* for MX server names. 210 /* .IP DNS_RETRY 211 /* The query failed, or the reply was malformed. 212 /* The problem is considered transient. 213 /* .IP DNS_FAIL 214 /* The query failed. 215 /* BUGS 216 /* dns_lookup() implements a subset of all possible resource types: 217 /* CNAME, MX, A, and some records with similar formatting requirements. 218 /* It is unwise to specify the T_ANY wildcard resource type. 219 /* 220 /* It takes a surprising amount of code to accomplish what appears 221 /* to be a simple task. Later versions of the mail system may implement 222 /* their own DNS client software. 223 /* SEE ALSO 224 /* dns_rr(3) resource record memory and list management 225 /* LICENSE 226 /* .ad 227 /* .fi 228 /* The Secure Mailer license must be distributed with this software. 229 /* AUTHOR(S) 230 /* Wietse Venema 231 /* IBM T.J. Watson Research 232 /* P.O. Box 704 233 /* Yorktown Heights, NY 10598, USA 234 /* 235 /* Wietse Venema 236 /* Google, Inc. 237 /* 111 8th Avenue 238 /* New York, NY 10011, USA 239 /*--*/ 240 241 /* System library. */ 242 243 #include <sys_defs.h> 244 #include <netdb.h> 245 #include <string.h> 246 #include <ctype.h> 247 248 /* Utility library. */ 249 250 #include <mymalloc.h> 251 #include <vstring.h> 252 #include <msg.h> 253 #include <valid_hostname.h> 254 #include <stringops.h> 255 256 /* Global library. */ 257 258 #include <mail_params.h> 259 260 /* DNS library. */ 261 262 #define LIBDNS_INTERNAL 263 #include "dns.h" 264 265 /* Local stuff. */ 266 267 /* 268 * Structure to keep track of things while decoding a name server reply. 269 */ 270 #define DEF_DNS_REPLY_SIZE 4096 /* in case we're using TCP */ 271 #define MAX_DNS_REPLY_SIZE 65536 /* in case we're using TCP */ 272 #define MAX_DNS_QUERY_SIZE 2048 /* XXX */ 273 274 typedef struct DNS_REPLY { 275 unsigned char *buf; /* raw reply data */ 276 size_t buf_len; /* reply buffer length */ 277 int rcode; /* unfiltered reply code */ 278 int dnssec_ad; /* DNSSEC AD bit */ 279 int query_count; /* number of queries */ 280 int answer_count; /* number of answers */ 281 int auth_count; /* number of authority records */ 282 unsigned char *query_start; /* start of query data */ 283 unsigned char *answer_start; /* start of answer data */ 284 unsigned char *end; /* first byte past reply */ 285 } DNS_REPLY; 286 287 /* 288 * Test/set primitives to determine if the reply buffer contains a server 289 * response. We use this when the caller requests DNS_REQ_FLAG_NCACHE_TTL, 290 * and the DNS server replies that the requested record does not exist. 291 */ 292 #define TEST_HAVE_DNS_REPLY_PACKET(r) ((r)->end > (r)->buf) 293 #define SET_HAVE_DNS_REPLY_PACKET(r, l) ((r)->end = (r)->buf + (l)) 294 #define SET_NO_DNS_REPLY_PACKET(r) ((r)->end = (r)->buf) 295 296 #define INET_ADDR_LEN 4 /* XXX */ 297 #define INET6_ADDR_LEN 16 /* XXX */ 298 299 /* 300 * Use the threadsafe resolver API if available, not because it is theadsafe, 301 * but because it has more functionality. 302 */ 303 #ifdef USE_RES_NCALLS 304 static struct __res_state dns_res_state; 305 306 #define DNS_RES_NINIT res_ninit 307 #define DNS_RES_NMKQUERY res_nmkquery 308 #define DNS_RES_NSEARCH res_nsearch 309 #define DNS_RES_NSEND res_nsend 310 #define DNS_GET_H_ERRNO(statp) ((statp)->res_h_errno) 311 312 /* 313 * Alias new resolver API calls to the legacy resolver API which stores 314 * resolver and error state in global variables. 315 */ 316 #else 317 #define dns_res_state _res 318 #define DNS_RES_NINIT(statp) res_init() 319 #define DNS_RES_NMKQUERY(statp, op, dname, class, type, data, datalen, \ 320 newrr, buf, buflen) \ 321 res_mkquery((op), (dname), (class), (type), (data), (datalen), \ 322 (newrr), (buf), (buflen)) 323 #define DNS_RES_NSEARCH(statp, dname, class, type, answer, anslen) \ 324 res_search((dname), (class), (type), (answer), (anslen)) 325 #define DNS_RES_NSEND(statp, msg, msglen, answer, anslen) \ 326 res_send((msg), (msglen), (answer), (anslen)) 327 #define DNS_GET_H_ERRNO(statp) (h_errno) 328 #endif 329 330 #ifdef USE_SET_H_ERRNO 331 #define DNS_SET_H_ERRNO(statp, err) (set_h_errno(err)) 332 #else 333 #define DNS_SET_H_ERRNO(statp, err) (DNS_GET_H_ERRNO(statp) = (err)) 334 #endif 335 336 /* 337 * To improve postscreen's allowlisting support, we need to know how long a 338 * DNSBL "not found" answer is valid. The 2010 implementation assumed it was 339 * valid for 3600 seconds. That is too long by 2015 standards. 340 * 341 * Instead of guessing, Postfix 3.1 and later implement RFC 2308 (DNS NCACHE), 342 * where a DNS server provides the TTL of a "not found" response as the TTL 343 * of an SOA record in the authority section. 344 * 345 * Unfortunately, the res_search() and res_query() API gets in the way. These 346 * functions overload their result value, the server reply length, and 347 * return -1 when the requested record does not exist. With libbind-based 348 * implementations, the server response is still available in an application 349 * buffer, thanks to the promise that res_query() and res_search() invoke 350 * res_send(), which returns the full server response even if the requested 351 * record does not exist. 352 * 353 * If this promise is broken (for example, res_search() does not call 354 * res_send(), but some non-libbind implementation that updates the 355 * application buffer only when the requested record exists), then we have a 356 * way out by setting the var_dns_ncache_ttl_fix variable. This enables a 357 * limited res_query() clone that should be sufficient for DNSBL / DNSWL 358 * lookups. 359 * 360 * The libunbound API does not comingle the reply length and reply status 361 * information, but that will have to wait until it is safe to make 362 * libunbound a mandatory dependency for Postfix. 363 */ 364 #ifdef HAVE_RES_SEND 365 366 /* dns_neg_query - a res_query() clone that can return negative replies */ 367 368 static int dns_neg_query(const char *name, int class, int type, 369 unsigned char *answer, int anslen) 370 { 371 unsigned char msg_buf[MAX_DNS_QUERY_SIZE]; 372 HEADER *reply_header = (HEADER *) answer; 373 int len; 374 375 /* 376 * Differences with res_query() from libbind: 377 * 378 * - This function returns a positive server reply length not only in case 379 * of success, but in all cases where a server reply is available that 380 * passes the preliminary checks in res_send(). 381 * 382 * - This function clears h_errno in case of success. The caller must use 383 * h_errno instead of the return value to decide if the lookup was 384 * successful. 385 * 386 * - No support for EDNS0 and DNSSEC (including turning off EDNS0 after 387 * error). That should be sufficient for DNS reputation lookups where the 388 * reply contains a small number of IP addresses. TXT records are out of 389 * scope for this workaround. 390 */ 391 reply_header->rcode = NOERROR; 392 393 #define NO_MKQUERY_DATA_BUF ((unsigned char *) 0) 394 #define NO_MKQUERY_DATA_LEN ((int) 0) 395 #define NO_MKQUERY_NEWRR ((unsigned char *) 0) 396 397 if ((len = DNS_RES_NMKQUERY(&dns_res_state, 398 QUERY, name, class, type, NO_MKQUERY_DATA_BUF, 399 NO_MKQUERY_DATA_LEN, NO_MKQUERY_NEWRR, 400 msg_buf, sizeof(msg_buf))) < 0) { 401 DNS_SET_H_ERRNO(&dns_res_state, NO_RECOVERY); 402 if (msg_verbose) 403 msg_info("res_nmkquery() failed"); 404 return (len); 405 } else if ((len = DNS_RES_NSEND(&dns_res_state, 406 msg_buf, len, answer, anslen)) < 0) { 407 DNS_SET_H_ERRNO(&dns_res_state, TRY_AGAIN); 408 if (msg_verbose) 409 msg_info("res_nsend() failed"); 410 return (len); 411 } else { 412 switch (reply_header->rcode) { 413 case NXDOMAIN: 414 DNS_SET_H_ERRNO(&dns_res_state, HOST_NOT_FOUND); 415 break; 416 case NOERROR: 417 if (reply_header->ancount != 0) 418 DNS_SET_H_ERRNO(&dns_res_state, 0); 419 else 420 DNS_SET_H_ERRNO(&dns_res_state, NO_DATA); 421 break; 422 case SERVFAIL: 423 DNS_SET_H_ERRNO(&dns_res_state, TRY_AGAIN); 424 break; 425 default: 426 DNS_SET_H_ERRNO(&dns_res_state, NO_RECOVERY); 427 break; 428 } 429 return (len); 430 } 431 } 432 433 #endif 434 435 /* dns_neg_search - res_search() that can return negative replies */ 436 437 static int dns_neg_search(const char *name, int class, int type, 438 unsigned char *answer, int anslen, int keep_notfound) 439 { 440 int len; 441 442 /* 443 * Differences with res_search() from libbind: 444 * 445 * - With a non-zero keep_notfound argument, this function returns a 446 * positive server reply length not only in case of success, but also in 447 * case of a "notfound" reply status. The keep_notfound argument is 448 * usually zero, which allows us to avoid an unnecessary memset() call in 449 * the most common use case. 450 * 451 * - This function clears h_errno in case of success. The caller must use 452 * h_errno instead of the return value to decide if a lookup was 453 * successful. 454 */ 455 #define NOT_FOUND_H_ERRNO(he) ((he) == HOST_NOT_FOUND || (he) == NO_DATA) 456 457 if (keep_notfound) 458 /* Prepare for returning a null-padded server reply. */ 459 memset(answer, 0, anslen); 460 len = DNS_RES_NSEARCH(&dns_res_state, name, class, type, answer, anslen); 461 /* Begin API creep workaround. */ 462 if (len < 0 && DNS_GET_H_ERRNO(&dns_res_state) == 0) { 463 DNS_SET_H_ERRNO(&dns_res_state, TRY_AGAIN); 464 msg_warn("res_nsearch(state, \"%s\", %d, %d, %p, %d) returns %d" 465 " with h_errno==0 -- setting h_errno=TRY_AGAIN", 466 name, class, type, answer, anslen, len); 467 } 468 /* End API creep workaround. */ 469 if (len > 0) { 470 DNS_SET_H_ERRNO(&dns_res_state, 0); 471 } else if (keep_notfound 472 && NOT_FOUND_H_ERRNO(DNS_GET_H_ERRNO(&dns_res_state))) { 473 /* Expect to return a null-padded server reply. */ 474 len = anslen; 475 } 476 return (len); 477 } 478 479 /* dns_query - query name server and pre-parse the reply */ 480 481 static int dns_query(const char *name, int type, unsigned flags, 482 DNS_REPLY *reply, VSTRING *why, unsigned lflags) 483 { 484 HEADER *reply_header; 485 int len; 486 unsigned long saved_options; 487 int keep_notfound = (lflags & DNS_REQ_FLAG_NCACHE_TTL); 488 489 /* 490 * Initialize the reply buffer. 491 */ 492 if (reply->buf == 0) { 493 reply->buf = (unsigned char *) mymalloc(DEF_DNS_REPLY_SIZE); 494 reply->buf_len = DEF_DNS_REPLY_SIZE; 495 } 496 497 /* 498 * Initialize the name service. 499 */ 500 if ((dns_res_state.options & RES_INIT) == 0 501 && DNS_RES_NINIT(&dns_res_state) < 0) { 502 if (why) 503 vstring_strcpy(why, "Name service initialization failure"); 504 return (DNS_FAIL); 505 } 506 507 /* 508 * Set search options: debugging, parent domain search, append local 509 * domain. Do not allow the user to control other features. 510 */ 511 #define USER_FLAGS (RES_DEBUG | RES_DNSRCH | RES_DEFNAMES | RES_USE_DNSSEC) 512 513 if ((flags & USER_FLAGS) != flags) 514 msg_panic("dns_query: bad flags: %d", flags); 515 516 /* 517 * Set extra options that aren't exposed to the application. 518 */ 519 #define XTRA_FLAGS (RES_USE_EDNS0 | RES_TRUSTAD) 520 521 if (DNS_WANT_DNSSEC_VALIDATION(flags)) 522 flags |= (RES_USE_EDNS0 | RES_TRUSTAD); 523 524 /* 525 * Can't append domains: we need the right SOA TTL. 526 */ 527 #define APPEND_DOMAIN_FLAGS (RES_DNSRCH | RES_DEFNAMES) 528 529 if (keep_notfound && (flags & APPEND_DOMAIN_FLAGS)) { 530 msg_warn("negative caching disables RES_DEFNAMES and RES_DNSRCH"); 531 flags &= ~APPEND_DOMAIN_FLAGS; 532 } 533 534 /* 535 * Save and restore resolver options that we overwrite, to avoid 536 * surprising behavior in other code that also invokes the resolver. 537 */ 538 #define SAVE_FLAGS (USER_FLAGS | XTRA_FLAGS) 539 540 saved_options = (dns_res_state.options & SAVE_FLAGS); 541 542 /* 543 * Perform the lookup. Claim that the information cannot be found if and 544 * only if the name server told us so. 545 */ 546 for (;;) { 547 dns_res_state.options &= ~saved_options; 548 dns_res_state.options |= flags; 549 if (keep_notfound && var_dns_ncache_ttl_fix) { 550 #ifdef HAVE_RES_SEND 551 len = dns_neg_query((char *) name, C_IN, type, reply->buf, 552 reply->buf_len); 553 #else 554 var_dns_ncache_ttl_fix = 0; 555 msg_warn("system library does not support %s=yes" 556 " -- ignoring this setting", VAR_DNS_NCACHE_TTL_FIX); 557 len = dns_neg_search((char *) name, C_IN, type, reply->buf, 558 reply->buf_len, keep_notfound); 559 #endif 560 } else { 561 len = dns_neg_search((char *) name, C_IN, type, reply->buf, 562 reply->buf_len, keep_notfound); 563 } 564 dns_res_state.options &= ~flags; 565 dns_res_state.options |= saved_options; 566 reply_header = (HEADER *) reply->buf; 567 reply->rcode = reply_header->rcode; 568 if ((reply->dnssec_ad = !!reply_header->ad) != 0) 569 DNS_SEC_STATS_SET(DNS_SEC_FLAG_AVAILABLE); 570 if (DNS_GET_H_ERRNO(&dns_res_state) != 0) { 571 if (why) 572 vstring_sprintf(why, "Host or domain name not found. " 573 "Name service error for name=%s type=%s: %s", 574 name, dns_strtype(type), 575 dns_strerror(DNS_GET_H_ERRNO(&dns_res_state))); 576 if (msg_verbose) 577 msg_info("dns_query: %s (%s): %s", 578 name, dns_strtype(type), 579 dns_strerror(DNS_GET_H_ERRNO(&dns_res_state))); 580 switch (DNS_GET_H_ERRNO(&dns_res_state)) { 581 case NO_RECOVERY: 582 return (DNS_FAIL); 583 case HOST_NOT_FOUND: 584 case NO_DATA: 585 if (keep_notfound) 586 break; 587 SET_NO_DNS_REPLY_PACKET(reply); 588 return (DNS_NOTFOUND); 589 default: 590 return (DNS_RETRY); 591 } 592 } else { 593 if (msg_verbose) 594 msg_info("dns_query: %s (%s): OK", name, dns_strtype(type)); 595 } 596 597 if (reply_header->tc == 0 || reply->buf_len >= MAX_DNS_REPLY_SIZE) 598 break; 599 reply->buf = (unsigned char *) 600 myrealloc((void *) reply->buf, 2 * reply->buf_len); 601 reply->buf_len *= 2; 602 } 603 604 /* 605 * Future proofing. If this reaches the panic call, then some code change 606 * introduced a bug. 607 */ 608 if (len < 0) 609 msg_panic("dns_query: bad length %d (h_errno=%s)", 610 len, dns_strerror(DNS_GET_H_ERRNO(&dns_res_state))); 611 612 /* 613 * Paranoia. 614 */ 615 if (len > reply->buf_len) { 616 msg_warn("reply length %d > buffer length %d for name=%s type=%s", 617 len, (int) reply->buf_len, name, dns_strtype(type)); 618 len = reply->buf_len; 619 } 620 621 /* 622 * Initialize the reply structure. Some structure members are filled on 623 * the fly while the reply is being parsed. 624 */ 625 SET_HAVE_DNS_REPLY_PACKET(reply, len); 626 reply->query_start = reply->buf + sizeof(HEADER); 627 reply->answer_start = 0; 628 reply->query_count = ntohs(reply_header->qdcount); 629 reply->answer_count = ntohs(reply_header->ancount); 630 reply->auth_count = ntohs(reply_header->nscount); 631 if (msg_verbose > 1) 632 msg_info("dns_query: reply len=%d ancount=%d nscount=%d", 633 len, reply->answer_count, reply->auth_count); 634 635 /* 636 * Future proofing. If this reaches the panic call, then some code change 637 * introduced a bug. 638 */ 639 if (DNS_GET_H_ERRNO(&dns_res_state) == 0) { 640 return (DNS_OK); 641 } else if (keep_notfound) { 642 return (DNS_NOTFOUND); 643 } else { 644 msg_panic("dns_query: unexpected reply status: %s", 645 dns_strerror(DNS_GET_H_ERRNO(&dns_res_state))); 646 } 647 } 648 649 /* dns_skip_query - skip query data in name server reply */ 650 651 static int dns_skip_query(DNS_REPLY *reply) 652 { 653 int query_count = reply->query_count; 654 unsigned char *pos = reply->query_start; 655 int len; 656 657 /* 658 * For each query, skip over the domain name and over the fixed query 659 * data. 660 */ 661 while (query_count-- > 0) { 662 if (pos >= reply->end) 663 return DNS_RETRY; 664 len = dn_skipname(pos, reply->end); 665 if (len < 0) 666 return (DNS_RETRY); 667 pos += len + QFIXEDSZ; 668 } 669 reply->answer_start = pos; 670 return (DNS_OK); 671 } 672 673 /* dns_get_fixed - extract fixed data from resource record */ 674 675 static int dns_get_fixed(unsigned char *pos, DNS_FIXED *fixed) 676 { 677 GETSHORT(fixed->type, pos); 678 GETSHORT(fixed->class, pos); 679 GETLONG(fixed->ttl, pos); 680 GETSHORT(fixed->length, pos); 681 682 if (fixed->class != C_IN) { 683 msg_warn("dns_get_fixed: bad class: %u", fixed->class); 684 return (DNS_RETRY); 685 } 686 return (DNS_OK); 687 } 688 689 /* valid_rr_name - validate hostname in resource record */ 690 691 static int valid_rr_name(const char *name, const char *location, 692 unsigned type, DNS_REPLY *reply) 693 { 694 char temp[DNS_NAME_LEN]; 695 char *query_name; 696 int len; 697 char *gripe; 698 int result; 699 700 /* 701 * People aren't supposed to specify numeric names where domain names are 702 * required, but it "works" with some mailers anyway, so people complain 703 * when software doesn't bend over backwards. 704 */ 705 #define PASS_NAME 1 706 #define REJECT_NAME 0 707 708 if (valid_hostaddr(name, DONT_GRIPE)) { 709 result = PASS_NAME; 710 gripe = "numeric domain name"; 711 } else if (!valid_hostname(name, DO_GRIPE)) { 712 result = REJECT_NAME; 713 gripe = "malformed domain name"; 714 } else { 715 result = PASS_NAME; 716 gripe = 0; 717 } 718 719 /* 720 * If we have a gripe, show some context, including the name used in the 721 * query and the type of reply that we're looking at. 722 */ 723 if (gripe) { 724 len = dn_expand(reply->buf, reply->end, reply->query_start, 725 temp, DNS_NAME_LEN); 726 query_name = (len < 0 ? "*unparsable*" : temp); 727 msg_warn("%s in %s of %s record for %s: %.100s", 728 gripe, location, dns_strtype(type), query_name, name); 729 } 730 return (result); 731 } 732 733 /* dns_get_rr - extract resource record from name server reply */ 734 735 static int dns_get_rr(DNS_RR **list, const char *orig_name, DNS_REPLY *reply, 736 unsigned char *pos, char *rr_name, 737 DNS_FIXED *fixed) 738 { 739 char temp[DNS_NAME_LEN]; 740 char *tempbuf = temp; 741 UINT32_TYPE soa_buf[5]; 742 int comp_len; 743 ssize_t data_len; 744 unsigned pref = 0; 745 unsigned char *src; 746 unsigned char *dst; 747 int ch; 748 749 #define MIN2(a, b) ((unsigned)(a) < (unsigned)(b) ? (a) : (b)) 750 751 *list = 0; 752 753 switch (fixed->type) { 754 default: 755 msg_panic("dns_get_rr: don't know how to extract resource type %s", 756 dns_strtype(fixed->type)); 757 case T_CNAME: 758 case T_DNAME: 759 case T_MB: 760 case T_MG: 761 case T_MR: 762 case T_NS: 763 case T_PTR: 764 if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0) 765 return (DNS_RETRY); 766 if (!valid_rr_name(temp, "resource data", fixed->type, reply)) 767 return (DNS_INVAL); 768 data_len = strlen(temp) + 1; 769 break; 770 case T_MX: 771 GETSHORT(pref, pos); 772 if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0) 773 return (DNS_RETRY); 774 /* Don't even think of returning an invalid hostname to the caller. */ 775 if (*temp == 0) 776 return (DNS_NULLMX); /* TODO: descriptive text */ 777 if (!valid_rr_name(temp, "resource data", fixed->type, reply)) 778 return (DNS_INVAL); 779 data_len = strlen(temp) + 1; 780 break; 781 case T_A: 782 if (fixed->length != INET_ADDR_LEN) { 783 msg_warn("extract_answer: bad address length: %d", fixed->length); 784 return (DNS_RETRY); 785 } 786 if (fixed->length > sizeof(temp)) 787 msg_panic("dns_get_rr: length %d > DNS_NAME_LEN", 788 fixed->length); 789 memcpy(temp, pos, fixed->length); 790 data_len = fixed->length; 791 break; 792 #ifdef T_AAAA 793 case T_AAAA: 794 if (fixed->length != INET6_ADDR_LEN) { 795 msg_warn("extract_answer: bad address length: %d", fixed->length); 796 return (DNS_RETRY); 797 } 798 if (fixed->length > sizeof(temp)) 799 msg_panic("dns_get_rr: length %d > DNS_NAME_LEN", 800 fixed->length); 801 memcpy(temp, pos, fixed->length); 802 data_len = fixed->length; 803 break; 804 #endif 805 806 /* 807 * We impose the same length limit here as for DNS names. However, 808 * see T_TLSA discussion below. 809 */ 810 case T_TXT: 811 data_len = MIN2(pos[0] + 1, MIN2(fixed->length + 1, sizeof(temp))); 812 for (src = pos + 1, dst = (unsigned char *) (temp); 813 dst < (unsigned char *) (temp) + data_len - 1; /* */ ) { 814 ch = *src++; 815 *dst++ = (ISPRINT(ch) ? ch : ' '); 816 } 817 *dst = 0; 818 break; 819 820 /* 821 * For a full certificate, fixed->length may be longer than 822 * sizeof(tmpbuf) == DNS_NAME_LEN. Since we don't need a decode 823 * buffer, just copy the raw data into the rr. 824 * 825 * XXX Reject replies with bogus length < 3. 826 * 827 * XXX What about enforcing a sane upper bound? The RFC 1035 hard 828 * protocol limit is the RRDATA length limit of 65535. 829 */ 830 case T_TLSA: 831 data_len = fixed->length; 832 tempbuf = (char *) pos; 833 break; 834 835 /* 836 * We use the SOA record TTL to determine the negative reply TTL. We 837 * save the time fields in the SOA record for debugging, but for now 838 * we don't bother saving the source host and mailbox information, as 839 * that would require changes to the DNS_RR structure and APIs. See 840 * also code in dns_strrecord(). 841 */ 842 case T_SOA: 843 comp_len = dn_skipname(pos, reply->end); 844 if (comp_len < 0) 845 return (DNS_RETRY); 846 pos += comp_len; 847 comp_len = dn_skipname(pos, reply->end); 848 if (comp_len < 0) 849 return (DNS_RETRY); 850 pos += comp_len; 851 if (reply->end - pos < sizeof(soa_buf)) { 852 msg_warn("extract_answer: bad SOA length: %d", fixed->length); 853 return (DNS_RETRY); 854 } 855 GETLONG(soa_buf[0], pos); /* Serial */ 856 GETLONG(soa_buf[1], pos); /* Refresh */ 857 GETLONG(soa_buf[2], pos); /* Retry */ 858 GETLONG(soa_buf[3], pos); /* Expire */ 859 GETLONG(soa_buf[4], pos); /* Ncache TTL */ 860 tempbuf = (char *) soa_buf; 861 data_len = sizeof(soa_buf); 862 break; 863 } 864 *list = dns_rr_create(orig_name, rr_name, fixed->type, fixed->class, 865 fixed->ttl, pref, tempbuf, data_len); 866 return (DNS_OK); 867 } 868 869 /* dns_get_alias - extract CNAME from name server reply */ 870 871 static int dns_get_alias(DNS_REPLY *reply, unsigned char *pos, 872 DNS_FIXED *fixed, char *cname, int c_len) 873 { 874 if (fixed->type != T_CNAME) 875 msg_panic("dns_get_alias: bad type %s", dns_strtype(fixed->type)); 876 if (dn_expand(reply->buf, reply->end, pos, cname, c_len) < 0) 877 return (DNS_RETRY); 878 if (!valid_rr_name(cname, "resource data", fixed->type, reply)) 879 return (DNS_INVAL); 880 return (DNS_OK); 881 } 882 883 /* dns_get_answer - extract answers from name server reply */ 884 885 static int dns_get_answer(const char *orig_name, DNS_REPLY *reply, int type, 886 DNS_RR **rrlist, VSTRING *fqdn, char *cname, int c_len, 887 int *maybe_secure) 888 { 889 char rr_name[DNS_NAME_LEN]; 890 unsigned char *pos; 891 int answer_count = reply->answer_count; 892 int len; 893 DNS_FIXED fixed; 894 DNS_RR *rr; 895 int resource_found = 0; 896 int cname_found = 0; 897 int not_found_status = DNS_NOTFOUND; /* can't happen */ 898 int status; 899 900 /* 901 * Initialize. Skip over the name server query if we haven't yet. 902 */ 903 if (reply->answer_start == 0) 904 if ((status = dns_skip_query(reply)) < 0) 905 return (status); 906 pos = reply->answer_start; 907 908 /* 909 * Either this, or use a GOTO for emergency exits. The purpose is to 910 * prevent incomplete answers from being passed back to the caller. 911 */ 912 #define CORRUPT(status) { \ 913 if (rrlist && *rrlist) { \ 914 dns_rr_free(*rrlist); \ 915 *rrlist = 0; \ 916 } \ 917 return (status); \ 918 } 919 920 /* 921 * Iterate over all answers. 922 */ 923 while (answer_count-- > 0) { 924 925 /* 926 * Optionally extract the fully-qualified domain name. 927 */ 928 if (pos >= reply->end) 929 CORRUPT(DNS_RETRY); 930 len = dn_expand(reply->buf, reply->end, pos, rr_name, DNS_NAME_LEN); 931 if (len < 0) 932 CORRUPT(DNS_RETRY); 933 pos += len; 934 935 /* 936 * Extract the fixed reply data: type, class, ttl, length. 937 */ 938 if (pos + RRFIXEDSZ > reply->end) 939 CORRUPT(DNS_RETRY); 940 if ((status = dns_get_fixed(pos, &fixed)) != DNS_OK) 941 CORRUPT(status); 942 if (strcmp(orig_name, ".") == 0 && *rr_name == 0) 943 /* Allow empty response name for root queries. */ ; 944 else if (!valid_rr_name(rr_name, "resource name", fixed.type, reply)) 945 CORRUPT(DNS_INVAL); 946 if (fqdn) 947 vstring_strcpy(fqdn, rr_name); 948 if (msg_verbose) 949 msg_info("dns_get_answer: type %s for %s", 950 dns_strtype(fixed.type), rr_name); 951 pos += RRFIXEDSZ; 952 953 /* 954 * Optionally extract the requested resource or CNAME data. 955 */ 956 if (pos + fixed.length > reply->end) 957 CORRUPT(DNS_RETRY); 958 if (type == fixed.type || type == T_ANY) { /* requested type */ 959 if (rrlist) { 960 if ((status = dns_get_rr(&rr, orig_name, reply, pos, rr_name, 961 &fixed)) == DNS_OK) { 962 resource_found++; 963 rr->dnssec_valid = *maybe_secure ? reply->dnssec_ad : 0; 964 *rrlist = dns_rr_append(*rrlist, rr); 965 } else if (status == DNS_NULLMX) { 966 CORRUPT(status); /* TODO: use better name */ 967 } else if (not_found_status != DNS_RETRY) 968 not_found_status = status; 969 } else 970 resource_found++; 971 } else if (fixed.type == T_CNAME) { /* cname resource */ 972 cname_found++; 973 if (cname && c_len > 0) 974 if ((status = dns_get_alias(reply, pos, &fixed, cname, c_len)) != DNS_OK) 975 CORRUPT(status); 976 if (!reply->dnssec_ad) 977 *maybe_secure = 0; 978 } 979 pos += fixed.length; 980 } 981 982 /* 983 * See what answer we came up with. Report success when the requested 984 * information was found. Otherwise, when a CNAME was found, report that 985 * more recursion is needed. Otherwise report failure. 986 */ 987 if (resource_found) 988 return (DNS_OK); 989 if (cname_found) 990 return (DNS_RECURSE); 991 return (not_found_status); 992 } 993 994 /* dns_lookup_x - DNS lookup user interface */ 995 996 int dns_lookup_x(const char *name, unsigned type, unsigned flags, 997 DNS_RR **rrlist, VSTRING *fqdn, VSTRING *why, 998 int *rcode, unsigned lflags) 999 { 1000 char cname[DNS_NAME_LEN]; 1001 int c_len = sizeof(cname); 1002 static DNS_REPLY reply; 1003 int count; 1004 int status; 1005 int maybe_secure = 1; /* Query name presumed secure */ 1006 const char *orig_name = name; 1007 1008 /* 1009 * Reset results early. DNS_OK is not the only status that returns 1010 * resource records; DNS_NOTFOUND will do that too, if requested. 1011 */ 1012 if (rrlist) 1013 *rrlist = 0; 1014 1015 /* 1016 * DJBDNS produces a bogus A record when given a numerical hostname. 1017 */ 1018 if (valid_hostaddr(name, DONT_GRIPE)) { 1019 if (why) 1020 vstring_sprintf(why, 1021 "Name service error for %s: invalid host or domain name", 1022 name); 1023 if (rcode) 1024 *rcode = NXDOMAIN; 1025 DNS_SET_H_ERRNO(&dns_res_state, HOST_NOT_FOUND); 1026 return (DNS_NOTFOUND); 1027 } 1028 1029 /* 1030 * The Linux resolver misbehaves when given an invalid domain name. 1031 */ 1032 if (strcmp(name, ".") && !valid_hostname(name, DONT_GRIPE)) { 1033 if (why) 1034 vstring_sprintf(why, 1035 "Name service error for %s: invalid host or domain name", 1036 name); 1037 if (rcode) 1038 *rcode = NXDOMAIN; 1039 DNS_SET_H_ERRNO(&dns_res_state, HOST_NOT_FOUND); 1040 return (DNS_NOTFOUND); 1041 } 1042 1043 /* 1044 * Perform the lookup. Follow CNAME chains, but only up to a 1045 * pre-determined maximum. 1046 */ 1047 for (count = 0; count < 10; count++) { 1048 1049 /* 1050 * Perform the DNS lookup, and pre-parse the name server reply. 1051 */ 1052 status = dns_query(name, type, flags, &reply, why, lflags); 1053 if (rcode) 1054 *rcode = reply.rcode; 1055 if (status != DNS_OK) { 1056 1057 /* 1058 * If the record does not exist, and we have a copy of the server 1059 * response, try to extract the negative caching TTL for the SOA 1060 * record in the authority section. DO NOT return an error if an 1061 * SOA record is malformed. 1062 */ 1063 if (status == DNS_NOTFOUND && TEST_HAVE_DNS_REPLY_PACKET(&reply) 1064 && reply.auth_count > 0) { 1065 reply.answer_count = reply.auth_count; /* XXX TODO: Fix API */ 1066 (void) dns_get_answer(orig_name, &reply, T_SOA, rrlist, fqdn, 1067 cname, c_len, &maybe_secure); 1068 } 1069 if (DNS_WANT_DNSSEC_VALIDATION(flags) 1070 && !DNS_SEC_STATS_TEST(DNS_SEC_FLAG_AVAILABLE | \ 1071 DNS_SEC_FLAG_DONT_PROBE)) 1072 dns_sec_probe(flags); /* XXX Clobbers 'reply' */ 1073 return (status); 1074 } 1075 1076 /* 1077 * Extract resource records of the requested type. Pick up CNAME 1078 * information just in case the requested data is not found. 1079 */ 1080 status = dns_get_answer(orig_name, &reply, type, rrlist, fqdn, 1081 cname, c_len, &maybe_secure); 1082 if (DNS_WANT_DNSSEC_VALIDATION(flags) 1083 && !DNS_SEC_STATS_TEST(DNS_SEC_FLAG_AVAILABLE | \ 1084 DNS_SEC_FLAG_DONT_PROBE)) 1085 dns_sec_probe(flags); /* XXX Clobbers 'reply' */ 1086 switch (status) { 1087 default: 1088 if (why) 1089 vstring_sprintf(why, "Name service error for name=%s type=%s: " 1090 "Malformed or unexpected name server reply", 1091 name, dns_strtype(type)); 1092 return (status); 1093 case DNS_NULLMX: 1094 if (why) 1095 vstring_sprintf(why, "Domain %s does not accept mail (nullMX)", 1096 name); 1097 DNS_SET_H_ERRNO(&dns_res_state, NO_DATA); 1098 return (status); 1099 case DNS_OK: 1100 if (rrlist && dns_rr_filter_maps) { 1101 if (dns_rr_filter_execute(rrlist) < 0) { 1102 if (why) 1103 vstring_sprintf(why, 1104 "Error looking up name=%s type=%s: " 1105 "Invalid DNS reply filter syntax", 1106 name, dns_strtype(type)); 1107 dns_rr_free(*rrlist); 1108 *rrlist = 0; 1109 status = DNS_RETRY; 1110 } else if (*rrlist == 0) { 1111 if (why) 1112 vstring_sprintf(why, 1113 "Error looking up name=%s type=%s: " 1114 "DNS reply filter drops all results", 1115 name, dns_strtype(type)); 1116 status = DNS_POLICY; 1117 } 1118 } 1119 return (status); 1120 case DNS_RECURSE: 1121 if (msg_verbose) 1122 msg_info("dns_lookup: %s aliased to %s", name, cname); 1123 #if RES_USE_DNSSEC 1124 1125 /* 1126 * Once an intermediate CNAME reply is not validated, all 1127 * consequent RRs are deemed not validated, so we don't ask for 1128 * further DNSSEC replies. 1129 */ 1130 if (maybe_secure == 0) 1131 flags &= ~RES_USE_DNSSEC; 1132 #endif 1133 name = cname; 1134 } 1135 } 1136 if (why) 1137 vstring_sprintf(why, "Name server loop for %s", name); 1138 msg_warn("dns_lookup: Name server loop for %s", name); 1139 return (DNS_NOTFOUND); 1140 } 1141 1142 /* dns_lookup_rl - DNS lookup interface with types list */ 1143 1144 int dns_lookup_rl(const char *name, unsigned flags, DNS_RR **rrlist, 1145 VSTRING *fqdn, VSTRING *why, int *rcode, 1146 int lflags,...) 1147 { 1148 va_list ap; 1149 unsigned type, next; 1150 int status = DNS_NOTFOUND; 1151 int hpref_status = INT_MIN; 1152 VSTRING *hpref_rtext = 0; 1153 int hpref_rcode; 1154 int hpref_h_errno; 1155 DNS_RR *rr; 1156 1157 /* Save intermediate highest-priority result. */ 1158 #define SAVE_HPREF_STATUS() do { \ 1159 hpref_status = status; \ 1160 if (rcode) \ 1161 hpref_rcode = *rcode; \ 1162 if (why && status != DNS_OK) \ 1163 vstring_strcpy(hpref_rtext ? hpref_rtext : \ 1164 (hpref_rtext = vstring_alloc(VSTRING_LEN(why))), \ 1165 vstring_str(why)); \ 1166 hpref_h_errno = DNS_GET_H_ERRNO(&dns_res_state); \ 1167 } while (0) 1168 1169 /* Restore intermediate highest-priority result. */ 1170 #define RESTORE_HPREF_STATUS() do { \ 1171 status = hpref_status; \ 1172 if (rcode) \ 1173 *rcode = hpref_rcode; \ 1174 if (why && status != DNS_OK) \ 1175 vstring_strcpy(why, vstring_str(hpref_rtext)); \ 1176 DNS_SET_H_ERRNO(&dns_res_state, hpref_h_errno); \ 1177 } while (0) 1178 1179 if (rrlist) 1180 *rrlist = 0; 1181 va_start(ap, lflags); 1182 for (type = va_arg(ap, unsigned); type != 0; type = next) { 1183 next = va_arg(ap, unsigned); 1184 if (msg_verbose) 1185 msg_info("lookup %s type %s flags %s", 1186 name, dns_strtype(type), dns_str_resflags(flags)); 1187 status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0, 1188 fqdn, why, rcode, lflags); 1189 if (rrlist && rr) 1190 *rrlist = dns_rr_append(*rrlist, rr); 1191 if (status == DNS_OK) { 1192 if (lflags & DNS_REQ_FLAG_STOP_OK) 1193 break; 1194 } else if (status == DNS_INVAL) { 1195 if (lflags & DNS_REQ_FLAG_STOP_INVAL) 1196 break; 1197 } else if (status == DNS_POLICY) { 1198 if (type == T_MX && (lflags & DNS_REQ_FLAG_STOP_MX_POLICY)) 1199 break; 1200 } else if (status == DNS_NULLMX) { 1201 if (lflags & DNS_REQ_FLAG_STOP_NULLMX) 1202 break; 1203 } 1204 /* XXX Stop after NXDOMAIN error. */ 1205 if (next == 0) 1206 break; 1207 if (status >= hpref_status) 1208 SAVE_HPREF_STATUS(); /* save last info */ 1209 } 1210 va_end(ap); 1211 if (status < hpref_status) 1212 RESTORE_HPREF_STATUS(); /* else report last info */ 1213 if (hpref_rtext) 1214 vstring_free(hpref_rtext); 1215 return (status); 1216 } 1217 1218 /* dns_lookup_rv - DNS lookup interface with types vector */ 1219 1220 int dns_lookup_rv(const char *name, unsigned flags, DNS_RR **rrlist, 1221 VSTRING *fqdn, VSTRING *why, int *rcode, 1222 int lflags, unsigned *types) 1223 { 1224 unsigned type, next; 1225 int status = DNS_NOTFOUND; 1226 int hpref_status = INT_MIN; 1227 VSTRING *hpref_rtext = 0; 1228 int hpref_rcode; 1229 int hpref_h_errno; 1230 DNS_RR *rr; 1231 1232 if (rrlist) 1233 *rrlist = 0; 1234 for (type = *types++; type != 0; type = next) { 1235 next = *types++; 1236 if (msg_verbose) 1237 msg_info("lookup %s type %s flags %s", 1238 name, dns_strtype(type), dns_str_resflags(flags)); 1239 status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0, 1240 fqdn, why, rcode, lflags); 1241 if (rrlist && rr) 1242 *rrlist = dns_rr_append(*rrlist, rr); 1243 if (status == DNS_OK) { 1244 if (lflags & DNS_REQ_FLAG_STOP_OK) 1245 break; 1246 } else if (status == DNS_INVAL) { 1247 if (lflags & DNS_REQ_FLAG_STOP_INVAL) 1248 break; 1249 } else if (status == DNS_POLICY) { 1250 if (type == T_MX && (lflags & DNS_REQ_FLAG_STOP_MX_POLICY)) 1251 break; 1252 } else if (status == DNS_NULLMX) { 1253 if (lflags & DNS_REQ_FLAG_STOP_NULLMX) 1254 break; 1255 } 1256 /* XXX Stop after NXDOMAIN error. */ 1257 if (next == 0) 1258 break; 1259 if (status >= hpref_status) 1260 SAVE_HPREF_STATUS(); /* save last info */ 1261 } 1262 if (status < hpref_status) 1263 RESTORE_HPREF_STATUS(); /* else report last info */ 1264 if (hpref_rtext) 1265 vstring_free(hpref_rtext); 1266 return (status); 1267 } 1268 1269 /* dns_get_h_errno - get the last lookup status */ 1270 1271 int dns_get_h_errno(void) 1272 { 1273 return (DNS_GET_H_ERRNO(&dns_res_state)); 1274 } 1275