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