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