1 /* $NetBSD: smtp_addr.c,v 1.3 2020/03/18 19:05:20 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* smtp_addr 3 6 /* SUMMARY 7 /* SMTP server address lookup 8 /* SYNOPSIS 9 /* #include "smtp_addr.h" 10 /* 11 /* DNS_RR *smtp_domain_addr(name, mxrr, misc_flags, why, found_myself) 12 /* char *name; 13 /* DNS_RR **mxrr; 14 /* int misc_flags; 15 /* DSN_BUF *why; 16 /* int *found_myself; 17 /* 18 /* DNS_RR *smtp_host_addr(name, misc_flags, why) 19 /* char *name; 20 /* int misc_flags; 21 /* DSN_BUF *why; 22 /* DESCRIPTION 23 /* This module implements Internet address lookups. By default, 24 /* lookups are done via the Internet domain name service (DNS). 25 /* A reasonable number of CNAME indirections is permitted. When 26 /* DNS lookups are disabled, host address lookup is done with 27 /* getnameinfo() or gethostbyname(). 28 /* 29 /* smtp_domain_addr() looks up the network addresses for mail 30 /* exchanger hosts listed for the named domain. Addresses are 31 /* returned in most-preferred first order. The result is truncated 32 /* so that it contains only hosts that are more preferred than the 33 /* local mail server itself. The found_myself result parameter 34 /* is updated when the local MTA is MX host for the specified 35 /* destination. If MX records were found, the rname, qname, 36 /* and dnssec validation status of the MX RRset are returned 37 /* via mxrr, which the caller must free with dns_rr_free(). 38 /* 39 /* When no mail exchanger is listed in the DNS for \fIname\fR, the 40 /* request is passed to smtp_host_addr(). 41 /* 42 /* It is an error to call smtp_domain_addr() when DNS lookups are 43 /* disabled. 44 /* 45 /* smtp_host_addr() looks up all addresses listed for the named 46 /* host. The host can be specified as a numerical Internet network 47 /* address, or as a symbolic host name. 48 /* 49 /* Results from smtp_domain_addr() or smtp_host_addr() are 50 /* destroyed by dns_rr_free(), including null lists. 51 /* DIAGNOSTICS 52 /* Panics: interface violations. For example, calling smtp_domain_addr() 53 /* when DNS lookups are explicitly disabled. 54 /* 55 /* All routines either return a DNS_RR pointer, or return a null 56 /* pointer and update the \fIwhy\fR argument accordingly. 57 /* LICENSE 58 /* .ad 59 /* .fi 60 /* The Secure Mailer license must be distributed with this software. 61 /* AUTHOR(S) 62 /* Wietse Venema 63 /* IBM T.J. Watson Research 64 /* P.O. Box 704 65 /* Yorktown Heights, NY 10598, USA 66 /* 67 /* Wietse Venema 68 /* Google, Inc. 69 /* 111 8th Avenue 70 /* New York, NY 10011, USA 71 /*--*/ 72 73 /* System library. */ 74 75 #include <sys_defs.h> 76 #include <sys/socket.h> 77 #include <netinet/in.h> 78 #include <arpa/inet.h> 79 #include <stdlib.h> 80 #include <netdb.h> 81 #include <ctype.h> 82 #include <string.h> 83 #include <unistd.h> 84 #include <errno.h> 85 86 /* Utility library. */ 87 88 #include <msg.h> 89 #include <vstring.h> 90 #include <mymalloc.h> 91 #include <inet_addr_list.h> 92 #include <stringops.h> 93 #include <myaddrinfo.h> 94 #include <inet_proto.h> 95 #include <midna_domain.h> 96 97 /* Global library. */ 98 99 #include <mail_params.h> 100 #include <own_inet_addr.h> 101 #include <dsn_buf.h> 102 103 /* DNS library. */ 104 105 #include <dns.h> 106 107 /* Application-specific. */ 108 109 #include "smtp.h" 110 #include "smtp_addr.h" 111 112 /* smtp_print_addr - print address list */ 113 114 static void smtp_print_addr(const char *what, DNS_RR *addr_list) 115 { 116 DNS_RR *addr; 117 MAI_HOSTADDR_STR hostaddr; 118 119 msg_info("begin %s address list", what); 120 for (addr = addr_list; addr; addr = addr->next) { 121 if (dns_rr_to_pa(addr, &hostaddr) == 0) { 122 msg_warn("skipping record type %s: %m", dns_strtype(addr->type)); 123 } else { 124 msg_info("pref %4d host %s/%s", 125 addr->pref, SMTP_HNAME(addr), 126 hostaddr.buf); 127 } 128 } 129 msg_info("end %s address list", what); 130 } 131 132 /* smtp_addr_one - address lookup for one host name */ 133 134 static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt, 135 unsigned pref, DSN_BUF *why) 136 { 137 const char *myname = "smtp_addr_one"; 138 DNS_RR *addr = 0; 139 DNS_RR *rr; 140 int aierr; 141 struct addrinfo *res0; 142 struct addrinfo *res; 143 INET_PROTO_INFO *proto_info = inet_proto_info(); 144 unsigned char *proto_family_list = proto_info->sa_family_list; 145 int found; 146 147 if (msg_verbose) 148 msg_info("%s: host %s", myname, host); 149 150 /* 151 * Interpret a numerical name as an address. 152 */ 153 if (hostaddr_to_sockaddr(host, (char *) 0, 0, &res0) == 0) { 154 if (strchr((char *) proto_family_list, res0->ai_family) != 0) { 155 if ((addr = dns_sa_to_rr(host, pref, res0->ai_addr)) == 0) 156 msg_fatal("host %s: conversion error for address family " 157 "%d: %m", host, res0->ai_addr->sa_family); 158 addr_list = dns_rr_append(addr_list, addr); 159 freeaddrinfo(res0); 160 return (addr_list); 161 } 162 freeaddrinfo(res0); 163 } 164 165 /* 166 * Use DNS lookup, but keep the option open to use native name service. 167 * 168 * XXX A soft error dominates past and future hard errors. Therefore we 169 * should not clobber a soft error text and status code. 170 */ 171 if (smtp_host_lookup_mask & SMTP_HOST_FLAG_DNS) { 172 res_opt |= smtp_dns_res_opt; 173 switch (dns_lookup_v(host, res_opt, &addr, (VSTRING *) 0, 174 why->reason, DNS_REQ_FLAG_NONE, 175 proto_info->dns_atype_list)) { 176 case DNS_OK: 177 for (rr = addr; rr; rr = rr->next) 178 rr->pref = pref; 179 addr_list = dns_rr_append(addr_list, addr); 180 return (addr_list); 181 default: 182 dsb_status(why, "4.4.3"); 183 return (addr_list); 184 case DNS_FAIL: 185 dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.3" : "5.4.3"); 186 return (addr_list); 187 case DNS_INVAL: 188 dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4"); 189 return (addr_list); 190 case DNS_POLICY: 191 dsb_status(why, "4.7.0"); 192 return (addr_list); 193 case DNS_NOTFOUND: 194 dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4"); 195 /* maybe native naming service will succeed */ 196 break; 197 } 198 } 199 200 /* 201 * Use the native name service which also looks in /etc/hosts. 202 * 203 * XXX A soft error dominates past and future hard errors. Therefore we 204 * should not clobber a soft error text and status code. 205 */ 206 #define RETRY_AI_ERROR(e) \ 207 ((e) == EAI_AGAIN || (e) == EAI_MEMORY || (e) == EAI_SYSTEM) 208 #ifdef EAI_NODATA 209 #define DSN_NOHOST(e) \ 210 ((e) == EAI_AGAIN || (e) == EAI_NODATA || (e) == EAI_NONAME) 211 #else 212 #define DSN_NOHOST(e) \ 213 ((e) == EAI_AGAIN || (e) == EAI_NONAME) 214 #endif 215 216 if (smtp_host_lookup_mask & SMTP_HOST_FLAG_NATIVE) { 217 if ((aierr = hostname_to_sockaddr(host, (char *) 0, 0, &res0)) != 0) { 218 dsb_simple(why, (SMTP_HAS_SOFT_DSN(why) || RETRY_AI_ERROR(aierr)) ? 219 (DSN_NOHOST(aierr) ? "4.4.4" : "4.3.0") : 220 (DSN_NOHOST(aierr) ? "5.4.4" : "5.3.0"), 221 "unable to look up host %s: %s", 222 host, MAI_STRERROR(aierr)); 223 } else { 224 for (found = 0, res = res0; res != 0; res = res->ai_next) { 225 if (strchr((char *) proto_family_list, res->ai_family) == 0) { 226 msg_info("skipping address family %d for host %s", 227 res->ai_family, host); 228 continue; 229 } 230 found++; 231 if ((addr = dns_sa_to_rr(host, pref, res->ai_addr)) == 0) 232 msg_fatal("host %s: conversion error for address family " 233 "%d: %m", host, res0->ai_addr->sa_family); 234 addr_list = dns_rr_append(addr_list, addr); 235 } 236 freeaddrinfo(res0); 237 if (found == 0) { 238 dsb_simple(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4", 239 "%s: host not found", host); 240 } 241 return (addr_list); 242 } 243 } 244 245 /* 246 * No further alternatives for host lookup. 247 */ 248 return (addr_list); 249 } 250 251 /* smtp_addr_list - address lookup for a list of mail exchangers */ 252 253 static DNS_RR *smtp_addr_list(DNS_RR *mx_names, DSN_BUF *why) 254 { 255 DNS_RR *addr_list = 0; 256 DNS_RR *rr; 257 int res_opt = 0; 258 259 if (mx_names->dnssec_valid) 260 res_opt = RES_USE_DNSSEC; 261 #ifdef USE_TLS 262 else if (smtp_tls_insecure_mx_policy > TLS_LEV_MAY) 263 res_opt = RES_USE_DNSSEC; 264 #endif 265 266 /* 267 * As long as we are able to look up any host address, we ignore problems 268 * with DNS lookups (except if we're backup MX, and all the better MX 269 * hosts can't be found). 270 * 271 * XXX 2821: update the error status (0->FAIL upon unrecoverable lookup 272 * error, any->RETRY upon temporary lookup error) so that we can 273 * correctly handle the case of no resolvable MX host. Currently this is 274 * always treated as a soft error. RFC 2821 wants a more precise 275 * response. 276 * 277 * XXX dns_lookup() enables RES_DEFNAMES. This is wrong for names found in 278 * MX records - we should not append the local domain to dot-less names. 279 * 280 * XXX However, this is not the only problem. If we use the native name 281 * service for host lookup, then it will usually enable RES_DNSRCH which 282 * appends local domain information to all lookups. In particular, 283 * getaddrinfo() may invoke a resolver that runs in a different process 284 * (NIS server, nscd), so we can't even reliably turn this off by 285 * tweaking the in-process resolver flags. 286 */ 287 for (rr = mx_names; rr; rr = rr->next) { 288 if (rr->type != T_MX) 289 msg_panic("smtp_addr_list: bad resource type: %d", rr->type); 290 addr_list = smtp_addr_one(addr_list, (char *) rr->data, res_opt, 291 rr->pref, why); 292 } 293 return (addr_list); 294 } 295 296 /* smtp_find_self - spot myself in a crowd of mail exchangers */ 297 298 static DNS_RR *smtp_find_self(DNS_RR *addr_list) 299 { 300 const char *myname = "smtp_find_self"; 301 INET_ADDR_LIST *self; 302 INET_ADDR_LIST *proxy; 303 DNS_RR *addr; 304 int i; 305 306 self = own_inet_addr_list(); 307 proxy = proxy_inet_addr_list(); 308 309 for (addr = addr_list; addr; addr = addr->next) { 310 311 /* 312 * Find out if this mail system is listening on this address. 313 */ 314 for (i = 0; i < self->used; i++) 315 if (DNS_RR_EQ_SA(addr, (struct sockaddr *) (self->addrs + i))) { 316 if (msg_verbose) 317 msg_info("%s: found self at pref %d", myname, addr->pref); 318 return (addr); 319 } 320 321 /* 322 * Find out if this mail system has a proxy listening on this 323 * address. 324 */ 325 for (i = 0; i < proxy->used; i++) 326 if (DNS_RR_EQ_SA(addr, (struct sockaddr *) (proxy->addrs + i))) { 327 if (msg_verbose) 328 msg_info("%s: found proxy at pref %d", myname, addr->pref); 329 return (addr); 330 } 331 } 332 333 /* 334 * Didn't find myself, or my proxy. 335 */ 336 if (msg_verbose) 337 msg_info("%s: not found", myname); 338 return (0); 339 } 340 341 /* smtp_truncate_self - truncate address list at self and equivalents */ 342 343 static DNS_RR *smtp_truncate_self(DNS_RR *addr_list, unsigned pref) 344 { 345 DNS_RR *addr; 346 DNS_RR *last; 347 348 for (last = 0, addr = addr_list; addr; last = addr, addr = addr->next) { 349 if (pref == addr->pref) { 350 if (msg_verbose) 351 smtp_print_addr("truncated", addr); 352 dns_rr_free(addr); 353 if (last == 0) { 354 addr_list = 0; 355 } else { 356 last->next = 0; 357 } 358 break; 359 } 360 } 361 return (addr_list); 362 } 363 364 /* smtp_balance_inet_proto - balance IPv4/6 protocols within address limit */ 365 366 static DNS_RR *smtp_balance_inet_proto(DNS_RR *addr_list, int misc_flags, 367 int addr_limit) 368 { 369 const char myname[] = "smtp_balance_inet_proto"; 370 DNS_RR *rr; 371 DNS_RR *stripped_list; 372 DNS_RR *next; 373 int v6_count; 374 int v4_count; 375 int v6_target, 376 v4_target; 377 int *p; 378 379 /* 380 * Precondition: the input is sorted by MX preference (not necessarily IP 381 * address family preference), and addresses with the same or worse 382 * preference than 'myself' have been eliminated. Postcondition: the 383 * relative list order is unchanged, but some elements are removed. 384 */ 385 386 /* 387 * Count the number of IPv6 and IPv4 addresses. 388 */ 389 for (v4_count = v6_count = 0, rr = addr_list; rr != 0; rr = rr->next) { 390 if (rr->type == T_A) { 391 v4_count++; 392 } else if (rr->type == T_AAAA) { 393 v6_count++; 394 } else { 395 msg_panic("%s: unexpected record type: %s", 396 myname, dns_strtype(rr->type)); 397 } 398 } 399 400 /* 401 * Ensure that one address type will not out-crowd the other, while 402 * enforcing the address count limit. This works around a current problem 403 * where some destination announces primarily IPv6 MX addresses, the 404 * smtp_address_limit eliminates most or all IPv4 addresses, and the 405 * destination is not reachable over IPv6. 406 * 407 * Maybe: do all smtp_mx_address_limit enforcement here, and remove 408 * pre-existing enforcement elsewhere. That would obsolete the 409 * smtp_balance_inet_protocols configuration parameter. 410 */ 411 if (v4_count > 0 && v6_count > 0 && v4_count + v6_count > addr_limit) { 412 413 /*- 414 * Decide how many IPv6 and IPv4 addresses to keep. The code below 415 * has three branches, corresponding to the regions R1, R2 and R3 416 * in the figure. 417 * 418 * L = addr_limit 419 * X = excluded by condition (v4_count + v6_count > addr_limit) 420 * 421 * v4_count 422 * ^ 423 * | 424 * L \ R1 425 * |X\ | 426 * |XXX\ | 427 * |XXXXX\ | R2 428 * L/2 +-------\------- 429 * |XXXXXXX|X\ 430 * |XXXXXXX|XXX\ R3 431 * |XXXXXXX|XXXXX\ 432 * 0 +-------+-------\--> v6_count 433 * 0 L/2 L 434 */ 435 if (v6_count <= addr_limit / 2) { /* Region R1 */ 436 v6_target = v6_count; 437 v4_target = addr_limit - v6_target; 438 } else if (v4_count <= addr_limit / 2) {/* Region R3 */ 439 v4_target = v4_count; 440 v6_target = addr_limit - v4_target; 441 } else { /* Region R2 */ 442 /* v4_count > addr_limit / 2 && v6_count > addr_limit / 2 */ 443 v4_target = (addr_limit + (addr_list->type == T_A)) / 2; 444 v6_target = addr_limit - v4_target; 445 } 446 if (msg_verbose) 447 msg_info("v6_target=%d, v4_target=%d", v6_target, v4_target); 448 449 /* Enforce the address count targets. */ 450 stripped_list = 0; 451 for (rr = addr_list; rr != 0; rr = next) { 452 next = rr->next; 453 rr->next = 0; 454 if (rr->type == T_A) { 455 p = &v4_target; 456 } else if (rr->type == T_AAAA) { 457 p = &v6_target; 458 } else { 459 msg_panic("%s: unexpected record type: %s", 460 myname, dns_strtype(rr->type)); 461 } 462 if (*p > 0) { 463 stripped_list = dns_rr_append(stripped_list, rr); 464 *p -= 1; 465 } else { 466 dns_rr_free(rr); 467 } 468 } 469 if (v4_target > 0 || v6_target > 0) 470 msg_panic("%s: bad target count: v4_target=%d, v6_target=%d", 471 myname, v4_target, v6_target); 472 if (msg_verbose) 473 smtp_print_addr("smtp_balance_inet_proto result", stripped_list); 474 return (stripped_list); 475 } else { 476 return (addr_list); 477 } 478 } 479 480 /* smtp_domain_addr - mail exchanger address lookup */ 481 482 DNS_RR *smtp_domain_addr(const char *name, DNS_RR **mxrr, int misc_flags, 483 DSN_BUF *why, int *found_myself) 484 { 485 DNS_RR *mx_names; 486 DNS_RR *addr_list = 0; 487 DNS_RR *self = 0; 488 unsigned best_pref; 489 unsigned best_found; 490 int r = 0; /* Resolver flags */ 491 const char *aname; 492 493 dsb_reset(why); /* Paranoia */ 494 495 /* 496 * Preferences from DNS use 0..32767, fall-backs use 32768+. 497 */ 498 #define IMPOSSIBLE_PREFERENCE (~0) 499 500 /* 501 * Sanity check. 502 */ 503 if (smtp_dns_support == SMTP_DNS_DISABLED) 504 msg_panic("smtp_domain_addr: DNS lookup is disabled"); 505 if (smtp_dns_support == SMTP_DNS_DNSSEC) 506 r |= RES_USE_DNSSEC; 507 508 /* 509 * IDNA support. 510 */ 511 #ifndef NO_EAI 512 if (!allascii(name) && (aname = midna_domain_to_ascii(name)) != 0) { 513 if (msg_verbose) 514 msg_info("%s asciified to %s", name, aname); 515 } else 516 #endif 517 aname = name; 518 519 /* 520 * Look up the mail exchanger hosts listed for this name. Sort the 521 * results by preference. Look up the corresponding host addresses, and 522 * truncate the list so that it contains only hosts that are more 523 * preferred than myself. When no MX resource records exist, look up the 524 * addresses listed for this name. 525 * 526 * According to RFC 974: "It is possible that the list of MXs in the 527 * response to the query will be empty. This is a special case. If the 528 * list is empty, mailers should treat it as if it contained one RR, an 529 * MX RR with a preference value of 0, and a host name of REMOTE. (I.e., 530 * REMOTE is its only MX). In addition, the mailer should do no further 531 * processing on the list, but should attempt to deliver the message to 532 * REMOTE." 533 * 534 * Normally it is OK if an MX host cannot be found in the DNS; we'll just 535 * use a backup one, and silently ignore the better MX host. However, if 536 * the best backup that we can find in the DNS is the local machine, then 537 * we must remember that the local machine is not the primary MX host, or 538 * else we will claim that mail loops back. 539 * 540 * XXX Optionally do A lookups even when the MX lookup didn't complete. 541 * Unfortunately with some DNS servers this is not a transient problem. 542 * 543 * XXX Ideally we would perform A lookups only as far as needed. But as long 544 * as we're looking up all the hosts, it would be better to look up the 545 * least preferred host first, so that DNS lookup error messages make 546 * more sense. 547 * 548 * XXX 2821: RFC 2821 says that the sender must shuffle equal-preference MX 549 * hosts, whereas multiple A records per hostname must be used in the 550 * order as received. They make the bogus assumption that a hostname with 551 * multiple A records corresponds to one machine with multiple network 552 * interfaces. 553 * 554 * XXX 2821: Postfix recognizes the local machine by looking for its own IP 555 * address in the list of mail exchangers. RFC 2821 says one has to look 556 * at the mail exchanger hostname as well, making the bogus assumption 557 * that an IP address is listed only under one hostname. However, looking 558 * at hostnames provides a partial solution for MX hosts behind a NAT 559 * gateway. 560 */ 561 switch (dns_lookup(aname, T_MX, r, &mx_names, (VSTRING *) 0, why->reason)) { 562 default: 563 dsb_status(why, "4.4.3"); 564 if (var_ign_mx_lookup_err) 565 addr_list = smtp_host_addr(aname, misc_flags, why); 566 break; 567 case DNS_INVAL: 568 dsb_status(why, "5.4.4"); 569 if (var_ign_mx_lookup_err) 570 addr_list = smtp_host_addr(aname, misc_flags, why); 571 break; 572 case DNS_NULLMX: 573 dsb_status(why, "5.1.0"); 574 break; 575 case DNS_POLICY: 576 dsb_status(why, "4.7.0"); 577 break; 578 case DNS_FAIL: 579 dsb_status(why, "5.4.3"); 580 if (var_ign_mx_lookup_err) 581 addr_list = smtp_host_addr(aname, misc_flags, why); 582 break; 583 case DNS_OK: 584 mx_names = dns_rr_sort(mx_names, dns_rr_compare_pref_any); 585 best_pref = (mx_names ? mx_names->pref : IMPOSSIBLE_PREFERENCE); 586 addr_list = smtp_addr_list(mx_names, why); 587 if (mxrr) 588 *mxrr = dns_rr_copy(mx_names); /* copies one record! */ 589 dns_rr_free(mx_names); 590 if (addr_list == 0) { 591 /* Text does not change. */ 592 if (var_smtp_defer_mxaddr) { 593 /* Don't clobber the null terminator. */ 594 if (SMTP_HAS_HARD_DSN(why)) 595 SMTP_SET_SOFT_DSN(why); /* XXX */ 596 /* Require some error status. */ 597 else if (!SMTP_HAS_SOFT_DSN(why)) 598 msg_panic("smtp_domain_addr: bad status"); 599 } 600 msg_warn("no MX host for %s has a valid address record", name); 601 break; 602 } 603 best_found = (addr_list ? addr_list->pref : IMPOSSIBLE_PREFERENCE); 604 if (msg_verbose) 605 smtp_print_addr(name, addr_list); 606 if ((misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) 607 && (self = smtp_find_self(addr_list)) != 0) { 608 addr_list = smtp_truncate_self(addr_list, self->pref); 609 if (addr_list == 0) { 610 if (best_pref != best_found) { 611 dsb_simple(why, "4.4.4", 612 "unable to find primary relay for %s", name); 613 } else { 614 dsb_simple(why, "5.4.6", "mail for %s loops back to myself", 615 name); 616 } 617 } 618 } 619 #define SMTP_COMPARE_ADDR(flags) \ 620 (((flags) & SMTP_MISC_FLAG_PREF_IPV6) ? dns_rr_compare_pref_ipv6 : \ 621 ((flags) & SMTP_MISC_FLAG_PREF_IPV4) ? dns_rr_compare_pref_ipv4 : \ 622 dns_rr_compare_pref_any) 623 624 if (addr_list && addr_list->next) { 625 if (var_smtp_rand_addr) 626 addr_list = dns_rr_shuffle(addr_list); 627 addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags)); 628 if (var_smtp_mxaddr_limit > 0 && var_smtp_balance_inet_proto) 629 addr_list = smtp_balance_inet_proto(addr_list, misc_flags, 630 var_smtp_mxaddr_limit); 631 } 632 break; 633 case DNS_NOTFOUND: 634 addr_list = smtp_host_addr(aname, misc_flags, why); 635 break; 636 } 637 638 /* 639 * Clean up. 640 */ 641 *found_myself |= (self != 0); 642 return (addr_list); 643 } 644 645 /* smtp_host_addr - direct host lookup */ 646 647 DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why) 648 { 649 DNS_RR *addr_list; 650 int res_opt = 0; 651 const char *ahost; 652 653 dsb_reset(why); /* Paranoia */ 654 655 if (smtp_dns_support == SMTP_DNS_DNSSEC) 656 res_opt |= RES_USE_DNSSEC; 657 658 /* 659 * IDNA support. 660 */ 661 #ifndef NO_EAI 662 if (!allascii(host) && (ahost = midna_domain_to_ascii(host)) != 0) { 663 if (msg_verbose) 664 msg_info("%s asciified to %s", host, ahost); 665 } else 666 #endif 667 ahost = host; 668 669 /* 670 * If the host is specified by numerical address, just convert the 671 * address to internal form. Otherwise, the host is specified by name. 672 */ 673 #define PREF0 0 674 addr_list = smtp_addr_one((DNS_RR *) 0, ahost, res_opt, PREF0, why); 675 if (addr_list 676 && (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) 677 && smtp_find_self(addr_list) != 0) { 678 dns_rr_free(addr_list); 679 dsb_simple(why, "5.4.6", "mail for %s loops back to myself", host); 680 return (0); 681 } 682 if (addr_list && addr_list->next) { 683 if (var_smtp_rand_addr) 684 addr_list = dns_rr_shuffle(addr_list); 685 /* The following changes the order of equal-preference hosts. */ 686 if (inet_proto_info()->ai_family_list[1] != 0) 687 addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags)); 688 if (var_smtp_mxaddr_limit > 0 && var_smtp_balance_inet_proto) 689 addr_list = smtp_balance_inet_proto(addr_list, misc_flags, 690 var_smtp_mxaddr_limit); 691 } 692 if (msg_verbose) 693 smtp_print_addr(host, addr_list); 694 return (addr_list); 695 } 696