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