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