1 /* $NetBSD: smtp_addr.c,v 1.5 2023/12/23 20:30:45 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_list = dns_rr_append(addr_list, addr); 185 addr->pref = pref; 186 addr->port = port; 187 if (msg_verbose) 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 (msg_verbose) { 268 MAI_HOSTADDR_STR hostaddr_str; 269 270 SOCKADDR_TO_HOSTADDR(res->ai_addr, res->ai_addrlen, 271 &hostaddr_str, (MAI_SERVPORT_STR *) 0, 0); 272 msg_info("%s: native lookup result: %s", 273 myname, hostaddr_str.buf); 274 } 275 } 276 freeaddrinfo(res0); 277 if (found == 0) { 278 dsb_simple(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4", 279 "%s: host not found", host); 280 } 281 return (addr_list); 282 } 283 } 284 285 /* 286 * No further alternatives for host lookup. 287 */ 288 return (addr_list); 289 } 290 291 /* smtp_addr_list - address lookup for a list of mail exchangers */ 292 293 static DNS_RR *smtp_addr_list(DNS_RR *mx_names, DSN_BUF *why) 294 { 295 DNS_RR *addr_list = 0; 296 DNS_RR *rr; 297 int res_opt = 0; 298 299 if (mx_names->dnssec_valid) 300 res_opt = RES_USE_DNSSEC; 301 #ifdef USE_TLS 302 else if (smtp_tls_insecure_mx_policy > TLS_LEV_MAY) 303 res_opt = RES_USE_DNSSEC; 304 #endif 305 306 /* 307 * As long as we are able to look up any host address, we ignore problems 308 * with DNS lookups (except if we're backup MX, and all the better MX 309 * hosts can't be found). 310 * 311 * XXX 2821: update the error status (0->FAIL upon unrecoverable lookup 312 * error, any->RETRY upon temporary lookup error) so that we can 313 * correctly handle the case of no resolvable MX host. Currently this is 314 * always treated as a soft error. RFC 2821 wants a more precise 315 * response. 316 * 317 * XXX dns_lookup() enables RES_DEFNAMES. This is wrong for names found in 318 * MX records - we should not append the local domain to dot-less names. 319 * 320 * XXX However, this is not the only problem. If we use the native name 321 * service for host lookup, then it will usually enable RES_DNSRCH which 322 * appends local domain information to all lookups. In particular, 323 * getaddrinfo() may invoke a resolver that runs in a different process 324 * (NIS server, nscd), so we can't even reliably turn this off by 325 * tweaking the in-process resolver flags. 326 */ 327 for (rr = mx_names; rr; rr = rr->next) { 328 if (rr->type != T_MX && rr->type != T_SRV) 329 msg_panic("smtp_addr_list: bad resource type: %d", rr->type); 330 addr_list = smtp_addr_one(addr_list, (char *) rr->data, res_opt, 331 rr->pref, rr->port, why); 332 } 333 return (addr_list); 334 } 335 336 /* smtp_find_self - spot myself in a crowd of mail exchangers */ 337 338 static DNS_RR *smtp_find_self(DNS_RR *addr_list) 339 { 340 const char *myname = "smtp_find_self"; 341 INET_ADDR_LIST *self; 342 INET_ADDR_LIST *proxy; 343 DNS_RR *addr; 344 int i; 345 346 self = own_inet_addr_list(); 347 proxy = proxy_inet_addr_list(); 348 349 for (addr = addr_list; addr; addr = addr->next) { 350 351 /* 352 * Find out if this mail system is listening on this address. 353 */ 354 for (i = 0; i < self->used; i++) 355 if (DNS_RR_EQ_SA(addr, (struct sockaddr *) (self->addrs + i))) { 356 if (msg_verbose) 357 msg_info("%s: found self at pref %d", myname, addr->pref); 358 return (addr); 359 } 360 361 /* 362 * Find out if this mail system has a proxy listening on this 363 * address. 364 */ 365 for (i = 0; i < proxy->used; i++) 366 if (DNS_RR_EQ_SA(addr, (struct sockaddr *) (proxy->addrs + i))) { 367 if (msg_verbose) 368 msg_info("%s: found proxy at pref %d", myname, addr->pref); 369 return (addr); 370 } 371 } 372 373 /* 374 * Didn't find myself, or my proxy. 375 */ 376 if (msg_verbose) 377 msg_info("%s: not found", myname); 378 return (0); 379 } 380 381 /* smtp_truncate_self - truncate address list at self and equivalents */ 382 383 static DNS_RR *smtp_truncate_self(DNS_RR *addr_list, unsigned pref) 384 { 385 DNS_RR *addr; 386 DNS_RR *last; 387 388 for (last = 0, addr = addr_list; addr; last = addr, addr = addr->next) { 389 if (pref == addr->pref) { 390 if (msg_verbose) 391 smtp_print_addr("truncated", addr); 392 dns_rr_free(addr); 393 if (last == 0) { 394 addr_list = 0; 395 } else { 396 last->next = 0; 397 } 398 break; 399 } 400 } 401 return (addr_list); 402 } 403 404 /* smtp_balance_inet_proto - balance IPv4/6 protocols within address limit */ 405 406 static DNS_RR *smtp_balance_inet_proto(DNS_RR *addr_list, int misc_flags, 407 int addr_limit) 408 { 409 const char myname[] = "smtp_balance_inet_proto"; 410 DNS_RR *rr; 411 DNS_RR *stripped_list; 412 DNS_RR *next; 413 int v6_count; 414 int v4_count; 415 int v6_target, v4_target; 416 int *p; 417 418 /* 419 * Precondition: the input is sorted by MX preference (not necessarily IP 420 * address family preference), and addresses with the same or worse 421 * preference than 'myself' have been eliminated. Postcondition: the 422 * relative list order is unchanged, but some elements are removed. 423 */ 424 425 /* 426 * Count the number of IPv6 and IPv4 addresses. 427 */ 428 for (v4_count = v6_count = 0, rr = addr_list; rr != 0; rr = rr->next) { 429 if (rr->type == T_A) { 430 v4_count++; 431 } else if (rr->type == T_AAAA) { 432 v6_count++; 433 } else { 434 msg_panic("%s: unexpected record type: %s", 435 myname, dns_strtype(rr->type)); 436 } 437 } 438 439 /* 440 * Ensure that one address type will not out-crowd the other, while 441 * enforcing the address count limit. This works around a current problem 442 * where some destination announces primarily IPv6 MX addresses, the 443 * smtp_address_limit eliminates most or all IPv4 addresses, and the 444 * destination is not reachable over IPv6. 445 * 446 * Maybe: do all smtp_mx_address_limit enforcement here, and remove 447 * pre-existing enforcement elsewhere. That would obsolete the 448 * smtp_balance_inet_protocols configuration parameter. 449 */ 450 if (v4_count > 0 && v6_count > 0 && v4_count + v6_count > addr_limit) { 451 452 /*- 453 * Decide how many IPv6 and IPv4 addresses to keep. The code below 454 * has three branches, corresponding to the regions R1, R2 and R3 455 * in the figure. 456 * 457 * L = addr_limit 458 * X = excluded by condition (v4_count + v6_count > addr_limit) 459 * 460 * v4_count 461 * ^ 462 * | 463 * L \ R1 464 * |X\ | 465 * |XXX\ | 466 * |XXXXX\ | R2 467 * L/2 +-------\------- 468 * |XXXXXXX|X\ 469 * |XXXXXXX|XXX\ R3 470 * |XXXXXXX|XXXXX\ 471 * 0 +-------+-------\--> v6_count 472 * 0 L/2 L 473 */ 474 if (v6_count <= addr_limit / 2) { /* Region R1 */ 475 v6_target = v6_count; 476 v4_target = addr_limit - v6_target; 477 } else if (v4_count <= addr_limit / 2) {/* Region R3 */ 478 v4_target = v4_count; 479 v6_target = addr_limit - v4_target; 480 } else { /* Region R2 */ 481 /* v4_count > addr_limit / 2 && v6_count > addr_limit / 2 */ 482 v4_target = (addr_limit + (addr_list->type == T_A)) / 2; 483 v6_target = addr_limit - v4_target; 484 } 485 if (msg_verbose) 486 msg_info("v6_target=%d, v4_target=%d", v6_target, v4_target); 487 488 /* Enforce the address count targets. */ 489 stripped_list = 0; 490 for (rr = addr_list; rr != 0; rr = next) { 491 next = rr->next; 492 rr->next = 0; 493 if (rr->type == T_A) { 494 p = &v4_target; 495 } else if (rr->type == T_AAAA) { 496 p = &v6_target; 497 } else { 498 msg_panic("%s: unexpected record type: %s", 499 myname, dns_strtype(rr->type)); 500 } 501 if (*p > 0) { 502 stripped_list = dns_rr_append(stripped_list, rr); 503 *p -= 1; 504 } else { 505 dns_rr_free(rr); 506 } 507 } 508 if (v4_target > 0 || v6_target > 0) 509 msg_panic("%s: bad target count: v4_target=%d, v6_target=%d", 510 myname, v4_target, v6_target); 511 if (msg_verbose) 512 smtp_print_addr("smtp_balance_inet_proto result", stripped_list); 513 return (stripped_list); 514 } else { 515 return (addr_list); 516 } 517 } 518 519 /* smtp_domain_addr - mail exchanger address lookup */ 520 521 DNS_RR *smtp_domain_addr(const char *name, DNS_RR **mxrr, int misc_flags, 522 DSN_BUF *why, int *found_myself) 523 { 524 DNS_RR *mx_names; 525 DNS_RR *addr_list = 0; 526 DNS_RR *self = 0; 527 unsigned best_pref; 528 unsigned best_found; 529 int r = 0; /* Resolver flags */ 530 const char *aname; 531 532 dsb_reset(why); /* Paranoia */ 533 534 /* 535 * Preferences from DNS use 0..32767, fall-backs use 32768+. 536 */ 537 #define IMPOSSIBLE_PREFERENCE (~0) 538 539 /* 540 * Sanity check. 541 */ 542 if (smtp_dns_support == SMTP_DNS_DISABLED) 543 msg_panic("smtp_domain_addr: DNS lookup is disabled"); 544 if (smtp_dns_support == SMTP_DNS_DNSSEC) 545 r |= RES_USE_DNSSEC; 546 547 /* 548 * IDNA support. 549 */ 550 #ifndef NO_EAI 551 if (!allascii(name) && (aname = midna_domain_to_ascii(name)) != 0) { 552 if (msg_verbose) 553 msg_info("%s asciified to %s", name, aname); 554 } else 555 #endif 556 aname = name; 557 558 /* 559 * Look up the mail exchanger hosts listed for this name. Sort the 560 * results by preference. Look up the corresponding host addresses, and 561 * truncate the list so that it contains only hosts that are more 562 * preferred than myself. When no MX resource records exist, look up the 563 * addresses listed for this name. 564 * 565 * According to RFC 974: "It is possible that the list of MXs in the 566 * response to the query will be empty. This is a special case. If the 567 * list is empty, mailers should treat it as if it contained one RR, an 568 * MX RR with a preference value of 0, and a host name of REMOTE. (I.e., 569 * REMOTE is its only MX). In addition, the mailer should do no further 570 * processing on the list, but should attempt to deliver the message to 571 * REMOTE." 572 * 573 * Normally it is OK if an MX host cannot be found in the DNS; we'll just 574 * use a backup one, and silently ignore the better MX host. However, if 575 * the best backup that we can find in the DNS is the local machine, then 576 * we must remember that the local machine is not the primary MX host, or 577 * else we will claim that mail loops back. 578 * 579 * XXX Optionally do A lookups even when the MX lookup didn't complete. 580 * Unfortunately with some DNS servers this is not a transient problem. 581 * 582 * XXX Ideally we would perform A lookups only as far as needed. But as long 583 * as we're looking up all the hosts, it would be better to look up the 584 * least preferred host first, so that DNS lookup error messages make 585 * more sense. 586 * 587 * XXX 2821: RFC 2821 says that the sender must shuffle equal-preference MX 588 * hosts, whereas multiple A records per hostname must be used in the 589 * order as received. They make the bogus assumption that a hostname with 590 * multiple A records corresponds to one machine with multiple network 591 * interfaces. 592 * 593 * XXX 2821: Postfix recognizes the local machine by looking for its own IP 594 * address in the list of mail exchangers. RFC 2821 says one has to look 595 * at the mail exchanger hostname as well, making the bogus assumption 596 * that an IP address is listed only under one hostname. However, looking 597 * at hostnames provides a partial solution for MX hosts behind a NAT 598 * gateway. 599 */ 600 switch (dns_lookup(aname, T_MX, r, &mx_names, (VSTRING *) 0, why->reason)) { 601 default: 602 dsb_status(why, "4.4.3"); 603 if (var_ign_mx_lookup_err) 604 addr_list = smtp_host_addr(aname, misc_flags, why); 605 break; 606 case DNS_INVAL: 607 dsb_status(why, "5.4.4"); 608 if (var_ign_mx_lookup_err) 609 addr_list = smtp_host_addr(aname, misc_flags, why); 610 break; 611 case DNS_NULLMX: 612 dsb_status(why, "5.1.0"); 613 break; 614 case DNS_POLICY: 615 dsb_status(why, "4.7.0"); 616 break; 617 case DNS_FAIL: 618 dsb_status(why, "5.4.3"); 619 if (var_ign_mx_lookup_err) 620 addr_list = smtp_host_addr(aname, misc_flags, why); 621 break; 622 case DNS_OK: 623 mx_names = dns_rr_sort(mx_names, dns_rr_compare_pref_any); 624 best_pref = (mx_names ? mx_names->pref : IMPOSSIBLE_PREFERENCE); 625 addr_list = smtp_addr_list(mx_names, why); 626 if (mxrr) 627 *mxrr = dns_rr_copy(mx_names); /* copies one record! */ 628 dns_rr_free(mx_names); 629 if (addr_list == 0) { 630 /* Text does not change. */ 631 if (var_smtp_defer_mxaddr) { 632 /* Don't clobber the null terminator. */ 633 if (SMTP_HAS_HARD_DSN(why)) 634 SMTP_SET_SOFT_DSN(why); /* XXX */ 635 /* Require some error status. */ 636 else if (!SMTP_HAS_SOFT_DSN(why)) 637 msg_panic("smtp_domain_addr: bad status"); 638 } 639 msg_warn("no MX host for %s has a valid address record", name); 640 break; 641 } 642 best_found = (addr_list ? addr_list->pref : IMPOSSIBLE_PREFERENCE); 643 if (msg_verbose) 644 smtp_print_addr(name, addr_list); 645 if ((misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) 646 && (self = smtp_find_self(addr_list)) != 0) { 647 addr_list = smtp_truncate_self(addr_list, self->pref); 648 if (addr_list == 0) { 649 if (best_pref != best_found) { 650 dsb_simple(why, "4.4.4", 651 "unable to find primary relay for %s", name); 652 } else { 653 dsb_simple(why, "5.4.6", "mail for %s loops back to myself", 654 name); 655 } 656 } 657 } 658 #define SMTP_COMPARE_ADDR(flags) \ 659 (((flags) & SMTP_MISC_FLAG_PREF_IPV6) ? dns_rr_compare_pref_ipv6 : \ 660 ((flags) & SMTP_MISC_FLAG_PREF_IPV4) ? dns_rr_compare_pref_ipv4 : \ 661 dns_rr_compare_pref_any) 662 663 if (addr_list && addr_list->next) { 664 if (var_smtp_rand_addr) 665 addr_list = dns_rr_shuffle(addr_list); 666 addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags)); 667 if (var_smtp_mxaddr_limit > 0 && var_smtp_balance_inet_proto) 668 addr_list = smtp_balance_inet_proto(addr_list, misc_flags, 669 var_smtp_mxaddr_limit); 670 } 671 break; 672 case DNS_NOTFOUND: 673 addr_list = smtp_host_addr(aname, misc_flags, why); 674 break; 675 } 676 677 /* 678 * Clean up. 679 */ 680 *found_myself |= (self != 0); 681 return (addr_list); 682 } 683 684 /* smtp_host_addr - direct host lookup */ 685 686 DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why) 687 { 688 DNS_RR *addr_list; 689 int res_opt = 0; 690 const char *ahost; 691 692 dsb_reset(why); /* Paranoia */ 693 694 if (smtp_dns_support == SMTP_DNS_DNSSEC) 695 res_opt |= RES_USE_DNSSEC; 696 697 /* 698 * IDNA support. 699 */ 700 #ifndef NO_EAI 701 if (!allascii(host) && (ahost = midna_domain_to_ascii(host)) != 0) { 702 if (msg_verbose) 703 msg_info("%s asciified to %s", host, ahost); 704 } else 705 #endif 706 ahost = host; 707 708 /* 709 * If the host is specified by numerical address, just convert the 710 * address to internal form. Otherwise, the host is specified by name. 711 */ 712 #define PREF0 0 713 addr_list = smtp_addr_one((DNS_RR *) 0, ahost, res_opt, PREF0, 0, why); 714 if (addr_list 715 && (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) 716 && smtp_find_self(addr_list) != 0) { 717 dns_rr_free(addr_list); 718 dsb_simple(why, "5.4.6", "mail for %s loops back to myself", host); 719 return (0); 720 } 721 if (addr_list && addr_list->next) { 722 if (var_smtp_rand_addr) 723 addr_list = dns_rr_shuffle(addr_list); 724 /* The following changes the order of equal-preference hosts. */ 725 if (inet_proto_info()->ai_family_list[1] != 0) 726 addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags)); 727 if (var_smtp_mxaddr_limit > 0 && var_smtp_balance_inet_proto) 728 addr_list = smtp_balance_inet_proto(addr_list, misc_flags, 729 var_smtp_mxaddr_limit); 730 } 731 if (msg_verbose) 732 smtp_print_addr(host, addr_list); 733 return (addr_list); 734 } 735 736 /* smtp_service_addr - service address lookup */ 737 738 DNS_RR *smtp_service_addr(const char *name, const char *service, DNS_RR **mxrr, 739 int misc_flags, DSN_BUF *why, 740 int *found_myself) 741 { 742 static VSTRING *srv_qname = 0; 743 const char *str_srv_qname; 744 DNS_RR *srv_names = 0; 745 DNS_RR *addr_list = 0; 746 DNS_RR *self = 0; 747 unsigned best_pref; 748 unsigned best_found; 749 int r = 0; 750 const char *aname; 751 int allow_non_srv_fallback = var_allow_srv_fallback; 752 753 dsb_reset(why); 754 755 /* 756 * Sanity check. 757 */ 758 if (smtp_dns_support == SMTP_DNS_DISABLED) 759 msg_panic("smtp_service_addr: DNS lookup is disabled"); 760 761 if (smtp_dns_support == SMTP_DNS_DNSSEC) { 762 r |= RES_USE_DNSSEC; 763 } 764 if (srv_qname == 0) 765 srv_qname = vstring_alloc(100); 766 vstring_sprintf(srv_qname, "_%s._tcp.%s", service, name); 767 str_srv_qname = STR(srv_qname); 768 769 /* 770 * IDNA support. 771 */ 772 #ifndef NO_EAI 773 if (!allascii(str_srv_qname) 774 && (aname = midna_domain_to_ascii(str_srv_qname)) != 0) { 775 if (msg_verbose) 776 msg_info("%s asciified to %s", str_srv_qname, aname); 777 } else 778 #endif 779 aname = str_srv_qname; 780 781 switch (dns_lookup(aname, T_SRV, r, &srv_names, (VSTRING *) 0, 782 why->reason)) { 783 default: 784 dsb_status(why, "4.4.3"); 785 allow_non_srv_fallback |= var_ign_srv_lookup_err; 786 break; 787 case DNS_INVAL: 788 dsb_status(why, "5.4.4"); 789 allow_non_srv_fallback |= var_ign_srv_lookup_err; 790 break; 791 case DNS_POLICY: 792 dsb_status(why, "4.7.0"); 793 break; 794 case DNS_FAIL: 795 dsb_status(why, "5.4.3"); 796 allow_non_srv_fallback |= var_ign_srv_lookup_err; 797 break; 798 case DNS_NULLSRV: 799 dsb_status(why, "5.1.0"); 800 break; 801 case DNS_OK: 802 /* Shuffle then sort the SRV rr records by priority and weight. */ 803 srv_names = dns_srv_rr_sort(srv_names); 804 best_pref = (srv_names ? srv_names->pref : IMPOSSIBLE_PREFERENCE); 805 addr_list = smtp_addr_list(srv_names, why); 806 if (mxrr) 807 *mxrr = dns_rr_copy(srv_names); /* copies one record! */ 808 dns_rr_free(srv_names); 809 if (addr_list == 0) { 810 msg_warn("no SRV host for %s has a valid address record", 811 str_srv_qname); 812 break; 813 } 814 /* Optional loop prevention, similar to smtp_domain_addr(). */ 815 best_found = (addr_list ? addr_list->pref : IMPOSSIBLE_PREFERENCE); 816 if (msg_verbose) 817 smtp_print_addr(aname, addr_list); 818 if ((misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) 819 && (self = smtp_find_self(addr_list)) != 0) { 820 addr_list = smtp_truncate_self(addr_list, self->pref); 821 if (addr_list == 0) { 822 if (best_pref != best_found) { 823 dsb_simple(why, "4.4.4", 824 "unable to find primary relay for %s", 825 str_srv_qname); 826 } else { 827 dsb_simple(why, "5.4.6", "mail for %s loops back to myself", 828 str_srv_qname); 829 } 830 } 831 } 832 /* TODO: sort by priority, weight, and address family preference. */ 833 834 /* Optional address family balancing, as in smtp_domain_addr(). */ 835 if (addr_list && addr_list->next) { 836 if (var_smtp_mxaddr_limit > 0 && var_smtp_balance_inet_proto) 837 addr_list = smtp_balance_inet_proto(addr_list, misc_flags, 838 var_smtp_mxaddr_limit); 839 } 840 break; 841 case DNS_NOTFOUND: 842 dsb_status(why, "5.4.4"); 843 break; 844 } 845 846 /* 847 * If permitted, fall back to non-SRV record lookups. 848 */ 849 if (addr_list == 0 && allow_non_srv_fallback) { 850 msg_info("skipping SRV lookup for %s: %s", 851 str_srv_qname, STR(why->reason)); 852 if (misc_flags & SMTP_MISC_FLAG_FALLBACK_SRV_TO_MX) 853 addr_list = smtp_domain_addr(name, mxrr, misc_flags, why, 854 found_myself); 855 else 856 addr_list = smtp_host_addr(name, misc_flags, why); 857 } 858 859 /* 860 * Only if we're not falling back. 861 */ 862 else { 863 *found_myself |= (self != 0); 864 } 865 return (addr_list); 866 } 867