1/*++ 2/* NAME 3/* smtp_addr 3 4/* SUMMARY 5/* SMTP server address lookup 6/* SYNOPSIS 7/* #include "smtp_addr.h" 8/* 9/* DNS_RR *smtp_domain_addr(name, mxrr, misc_flags, why, found_myself) 10/* char *name; 11/* DNS_RR **mxrr; 12/* int misc_flags; 13/* DSN_BUF *why; 14/* int *found_myself; 15/* 16/* DNS_RR *smtp_host_addr(name, misc_flags, why) 17/* char *name; 18/* int misc_flags; 19/* DSN_BUF *why; 20/* DESCRIPTION 21/* This module implements Internet address lookups. By default, 22/* lookups are done via the Internet domain name service (DNS). 23/* A reasonable number of CNAME indirections is permitted. When 24/* DNS lookups are disabled, host address lookup is done with 25/* getnameinfo() or gethostbyname(). 26/* 27/* smtp_domain_addr() looks up the network addresses for mail 28/* exchanger hosts listed for the named domain. Addresses are 29/* returned in most-preferred first order. The result is truncated 30/* so that it contains only hosts that are more preferred than the 31/* local mail server itself. The found_myself result parameter 32/* is updated when the local MTA is MX host for the specified 33/* destination. If MX records were found, the rname, qname, 34/* and dnssec validation status of the MX RRset are returned 35/* via mxrr, which the caller must free with dns_rr_free(). 36/* 37/* When no mail exchanger is listed in the DNS for \fIname\fR, the 38/* request is passed to smtp_host_addr(). 39/* 40/* It is an error to call smtp_domain_addr() when DNS lookups are 41/* disabled. 42/* 43/* smtp_host_addr() looks up all addresses listed for the named 44/* host. The host can be specified as a numerical Internet network 45/* address, or as a symbolic host name. 46/* 47/* Results from smtp_domain_addr() or smtp_host_addr() are 48/* destroyed by dns_rr_free(), including null lists. 49/* DIAGNOSTICS 50/* Panics: interface violations. For example, calling smtp_domain_addr() 51/* when DNS lookups are explicitly disabled. 52/* 53/* All routines either return a DNS_RR pointer, or return a null 54/* pointer and update the \fIwhy\fR argument accordingly. 55/* LICENSE 56/* .ad 57/* .fi 58/* The Secure Mailer license must be distributed with this software. 59/* AUTHOR(S) 60/* Wietse Venema 61/* IBM T.J. Watson Research 62/* P.O. Box 704 63/* Yorktown Heights, NY 10598, USA 64/*--*/ 65 66/* System library. */ 67 68#include <sys_defs.h> 69#include <sys/socket.h> 70#include <netinet/in.h> 71#include <arpa/inet.h> 72#include <stdlib.h> 73#include <netdb.h> 74#include <ctype.h> 75#include <string.h> 76#include <unistd.h> 77#include <errno.h> 78 79/* Utility library. */ 80 81#include <msg.h> 82#include <vstring.h> 83#include <mymalloc.h> 84#include <inet_addr_list.h> 85#include <stringops.h> 86#include <myaddrinfo.h> 87#include <inet_proto.h> 88 89/* Global library. */ 90 91#include <mail_params.h> 92#include <own_inet_addr.h> 93#include <dsn_buf.h> 94 95/* DNS library. */ 96 97#include <dns.h> 98 99/* Application-specific. */ 100 101#include "smtp.h" 102#include "smtp_addr.h" 103 104/* smtp_print_addr - print address list */ 105 106static void smtp_print_addr(const char *what, DNS_RR *addr_list) 107{ 108 DNS_RR *addr; 109 MAI_HOSTADDR_STR hostaddr; 110 111 msg_info("begin %s address list", what); 112 for (addr = addr_list; addr; addr = addr->next) { 113 if (dns_rr_to_pa(addr, &hostaddr) == 0) { 114 msg_warn("skipping record type %s: %m", dns_strtype(addr->type)); 115 } else { 116 msg_info("pref %4d host %s/%s", 117 addr->pref, SMTP_HNAME(addr), 118 hostaddr.buf); 119 } 120 } 121 msg_info("end %s address list", what); 122} 123 124/* smtp_addr_one - address lookup for one host name */ 125 126static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt, 127 unsigned pref, DSN_BUF *why) 128{ 129 const char *myname = "smtp_addr_one"; 130 DNS_RR *addr = 0; 131 DNS_RR *rr; 132 int aierr; 133 struct addrinfo *res0; 134 struct addrinfo *res; 135 INET_PROTO_INFO *proto_info = inet_proto_info(); 136 int found; 137 138 if (msg_verbose) 139 msg_info("%s: host %s", myname, host); 140 141 /* 142 * Interpret a numerical name as an address. 143 */ 144 if (hostaddr_to_sockaddr(host, (char *) 0, 0, &res0) == 0 145 && strchr((char *) proto_info->sa_family_list, res0->ai_family) != 0) { 146 if ((addr = dns_sa_to_rr(host, pref, res0->ai_addr)) == 0) 147 msg_fatal("host %s: conversion error for address family %d: %m", 148 host, ((struct sockaddr *) (res0->ai_addr))->sa_family); 149 addr_list = dns_rr_append(addr_list, addr); 150 freeaddrinfo(res0); 151 return (addr_list); 152 } 153 154 /* 155 * Use DNS lookup, but keep the option open to use native name service. 156 * 157 * XXX A soft error dominates past and future hard errors. Therefore we 158 * should not clobber a soft error text and status code. 159 */ 160 if (smtp_host_lookup_mask & SMTP_HOST_FLAG_DNS) { 161 res_opt |= smtp_dns_res_opt; 162 switch (dns_lookup_v(host, res_opt, &addr, (VSTRING *) 0, 163 why->reason, DNS_REQ_FLAG_NONE, 164 proto_info->dns_atype_list)) { 165 case DNS_OK: 166 for (rr = addr; rr; rr = rr->next) 167 rr->pref = pref; 168 addr_list = dns_rr_append(addr_list, addr); 169 return (addr_list); 170 default: 171 dsb_status(why, "4.4.3"); 172 return (addr_list); 173 case DNS_FAIL: 174 dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.3" : "5.4.3"); 175 return (addr_list); 176 case DNS_INVAL: 177 dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4"); 178 return (addr_list); 179 case DNS_NOTFOUND: 180 dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4"); 181 /* maybe native naming service will succeed */ 182 break; 183 } 184 } 185 186 /* 187 * Use the native name service which also looks in /etc/hosts. 188 * 189 * XXX A soft error dominates past and future hard errors. Therefore we 190 * should not clobber a soft error text and status code. 191 */ 192#define RETRY_AI_ERROR(e) \ 193 ((e) == EAI_AGAIN || (e) == EAI_MEMORY || (e) == EAI_SYSTEM) 194#ifdef EAI_NODATA 195#define DSN_NOHOST(e) \ 196 ((e) == EAI_AGAIN || (e) == EAI_NODATA || (e) == EAI_NONAME) 197#else 198#define DSN_NOHOST(e) \ 199 ((e) == EAI_AGAIN || (e) == EAI_NONAME) 200#endif 201 202 if (smtp_host_lookup_mask & SMTP_HOST_FLAG_NATIVE) { 203 if ((aierr = hostname_to_sockaddr(host, (char *) 0, 0, &res0)) != 0) { 204 dsb_simple(why, (SMTP_HAS_SOFT_DSN(why) || RETRY_AI_ERROR(aierr)) ? 205 (DSN_NOHOST(aierr) ? "4.4.4" : "4.3.0") : 206 (DSN_NOHOST(aierr) ? "5.4.4" : "5.3.0"), 207 "unable to look up host %s: %s", 208 host, MAI_STRERROR(aierr)); 209 } else { 210 for (found = 0, res = res0; res != 0; res = res->ai_next) { 211 if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) { 212 msg_info("skipping address family %d for host %s", 213 res->ai_family, host); 214 continue; 215 } 216 found++; 217 if ((addr = dns_sa_to_rr(host, pref, res->ai_addr)) == 0) 218 msg_fatal("host %s: conversion error for address family %d: %m", 219 host, ((struct sockaddr *) (res0->ai_addr))->sa_family); 220 addr_list = dns_rr_append(addr_list, addr); 221 } 222 freeaddrinfo(res0); 223 if (found == 0) { 224 dsb_simple(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4", 225 "%s: host not found", host); 226 } 227 return (addr_list); 228 } 229 } 230 231 /* 232 * No further alternatives for host lookup. 233 */ 234 return (addr_list); 235} 236 237/* smtp_addr_list - address lookup for a list of mail exchangers */ 238 239static DNS_RR *smtp_addr_list(DNS_RR *mx_names, DSN_BUF *why) 240{ 241 DNS_RR *addr_list = 0; 242 DNS_RR *rr; 243 int res_opt = mx_names->dnssec_valid ? RES_USE_DNSSEC : 0; 244 245 /* 246 * As long as we are able to look up any host address, we ignore problems 247 * with DNS lookups (except if we're backup MX, and all the better MX 248 * hosts can't be found). 249 * 250 * XXX 2821: update the error status (0->FAIL upon unrecoverable lookup 251 * error, any->RETRY upon temporary lookup error) so that we can 252 * correctly handle the case of no resolvable MX host. Currently this is 253 * always treated as a soft error. RFC 2821 wants a more precise 254 * response. 255 * 256 * XXX dns_lookup() enables RES_DEFNAMES. This is wrong for names found in 257 * MX records - we should not append the local domain to dot-less names. 258 * 259 * XXX However, this is not the only problem. If we use the native name 260 * service for host lookup, then it will usually enable RES_DNSRCH which 261 * appends local domain information to all lookups. In particular, 262 * getaddrinfo() may invoke a resolver that runs in a different process 263 * (NIS server, nscd), so we can't even reliably turn this off by 264 * tweaking the in-process resolver flags. 265 */ 266 for (rr = mx_names; rr; rr = rr->next) { 267 if (rr->type != T_MX) 268 msg_panic("smtp_addr_list: bad resource type: %d", rr->type); 269 addr_list = smtp_addr_one(addr_list, (char *) rr->data, res_opt, 270 rr->pref, why); 271 } 272 return (addr_list); 273} 274 275/* smtp_find_self - spot myself in a crowd of mail exchangers */ 276 277static DNS_RR *smtp_find_self(DNS_RR *addr_list) 278{ 279 const char *myname = "smtp_find_self"; 280 INET_ADDR_LIST *self; 281 INET_ADDR_LIST *proxy; 282 DNS_RR *addr; 283 int i; 284 285 self = own_inet_addr_list(); 286 proxy = proxy_inet_addr_list(); 287 288 for (addr = addr_list; addr; addr = addr->next) { 289 290 /* 291 * Find out if this mail system is listening on this address. 292 */ 293 for (i = 0; i < self->used; i++) 294 if (DNS_RR_EQ_SA(addr, (struct sockaddr *) (self->addrs + i))) { 295 if (msg_verbose) 296 msg_info("%s: found self at pref %d", myname, addr->pref); 297 return (addr); 298 } 299 300 /* 301 * Find out if this mail system has a proxy listening on this 302 * address. 303 */ 304 for (i = 0; i < proxy->used; i++) 305 if (DNS_RR_EQ_SA(addr, (struct sockaddr *) (proxy->addrs + i))) { 306 if (msg_verbose) 307 msg_info("%s: found proxy at pref %d", myname, addr->pref); 308 return (addr); 309 } 310 } 311 312 /* 313 * Didn't find myself, or my proxy. 314 */ 315 if (msg_verbose) 316 msg_info("%s: not found", myname); 317 return (0); 318} 319 320/* smtp_truncate_self - truncate address list at self and equivalents */ 321 322static DNS_RR *smtp_truncate_self(DNS_RR *addr_list, unsigned pref) 323{ 324 DNS_RR *addr; 325 DNS_RR *last; 326 327 for (last = 0, addr = addr_list; addr; last = addr, addr = addr->next) { 328 if (pref == addr->pref) { 329 if (msg_verbose) 330 smtp_print_addr("truncated", addr); 331 dns_rr_free(addr); 332 if (last == 0) { 333 addr_list = 0; 334 } else { 335 last->next = 0; 336 } 337 break; 338 } 339 } 340 return (addr_list); 341} 342 343/* smtp_domain_addr - mail exchanger address lookup */ 344 345DNS_RR *smtp_domain_addr(char *name, DNS_RR **mxrr, int misc_flags, 346 DSN_BUF *why, int *found_myself) 347{ 348 DNS_RR *mx_names; 349 DNS_RR *addr_list = 0; 350 DNS_RR *self = 0; 351 unsigned best_pref; 352 unsigned best_found; 353 int r = 0; /* Resolver flags */ 354 355 dsb_reset(why); /* Paranoia */ 356 357 /* 358 * Preferences from DNS use 0..32767, fall-backs use 32768+. 359 */ 360#define IMPOSSIBLE_PREFERENCE (~0) 361 362 /* 363 * Sanity check. 364 */ 365 if (smtp_dns_support == SMTP_DNS_DISABLED) 366 msg_panic("smtp_domain_addr: DNS lookup is disabled"); 367 if (smtp_dns_support == SMTP_DNS_DNSSEC) 368 r |= RES_USE_DNSSEC; 369 370 /* 371 * Look up the mail exchanger hosts listed for this name. Sort the 372 * results by preference. Look up the corresponding host addresses, and 373 * truncate the list so that it contains only hosts that are more 374 * preferred than myself. When no MX resource records exist, look up the 375 * addresses listed for this name. 376 * 377 * According to RFC 974: "It is possible that the list of MXs in the 378 * response to the query will be empty. This is a special case. If the 379 * list is empty, mailers should treat it as if it contained one RR, an 380 * MX RR with a preference value of 0, and a host name of REMOTE. (I.e., 381 * REMOTE is its only MX). In addition, the mailer should do no further 382 * processing on the list, but should attempt to deliver the message to 383 * REMOTE." 384 * 385 * Normally it is OK if an MX host cannot be found in the DNS; we'll just 386 * use a backup one, and silently ignore the better MX host. However, if 387 * the best backup that we can find in the DNS is the local machine, then 388 * we must remember that the local machine is not the primary MX host, or 389 * else we will claim that mail loops back. 390 * 391 * XXX Optionally do A lookups even when the MX lookup didn't complete. 392 * Unfortunately with some DNS servers this is not a transient problem. 393 * 394 * XXX Ideally we would perform A lookups only as far as needed. But as long 395 * as we're looking up all the hosts, it would be better to look up the 396 * least preferred host first, so that DNS lookup error messages make 397 * more sense. 398 * 399 * XXX 2821: RFC 2821 says that the sender must shuffle equal-preference MX 400 * hosts, whereas multiple A records per hostname must be used in the 401 * order as received. They make the bogus assumption that a hostname with 402 * multiple A records corresponds to one machine with multiple network 403 * interfaces. 404 * 405 * XXX 2821: Postfix recognizes the local machine by looking for its own IP 406 * address in the list of mail exchangers. RFC 2821 says one has to look 407 * at the mail exchanger hostname as well, making the bogus assumption 408 * that an IP address is listed only under one hostname. However, looking 409 * at hostnames provides a partial solution for MX hosts behind a NAT 410 * gateway. 411 */ 412 switch (dns_lookup(name, T_MX, r, &mx_names, (VSTRING *) 0, why->reason)) { 413 default: 414 dsb_status(why, "4.4.3"); 415 if (var_ign_mx_lookup_err) 416 addr_list = smtp_host_addr(name, misc_flags, why); 417 break; 418 case DNS_INVAL: 419 dsb_status(why, "5.4.4"); 420 if (var_ign_mx_lookup_err) 421 addr_list = smtp_host_addr(name, misc_flags, why); 422 break; 423 case DNS_FAIL: 424 dsb_status(why, "5.4.3"); 425 if (var_ign_mx_lookup_err) 426 addr_list = smtp_host_addr(name, misc_flags, why); 427 break; 428 case DNS_OK: 429 mx_names = dns_rr_sort(mx_names, dns_rr_compare_pref_any); 430 best_pref = (mx_names ? mx_names->pref : IMPOSSIBLE_PREFERENCE); 431 addr_list = smtp_addr_list(mx_names, why); 432 if (mxrr) 433 *mxrr = dns_rr_copy(mx_names); /* copies one record! */ 434 dns_rr_free(mx_names); 435 if (addr_list == 0) { 436 /* Text does not change. */ 437 if (var_smtp_defer_mxaddr) { 438 /* Don't clobber the null terminator. */ 439 if (SMTP_HAS_HARD_DSN(why)) 440 SMTP_SET_SOFT_DSN(why); /* XXX */ 441 /* Require some error status. */ 442 else if (!SMTP_HAS_SOFT_DSN(why)) 443 msg_panic("smtp_domain_addr: bad status"); 444 } 445 msg_warn("no MX host for %s has a valid address record", name); 446 break; 447 } 448 best_found = (addr_list ? addr_list->pref : IMPOSSIBLE_PREFERENCE); 449 if (msg_verbose) 450 smtp_print_addr(name, addr_list); 451 if ((misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) 452 && (self = smtp_find_self(addr_list)) != 0) { 453 addr_list = smtp_truncate_self(addr_list, self->pref); 454 if (addr_list == 0) { 455 if (best_pref != best_found) { 456 dsb_simple(why, "4.4.4", 457 "unable to find primary relay for %s", name); 458 } else { 459 dsb_simple(why, "5.4.6", "mail for %s loops back to myself", 460 name); 461 } 462 } 463 } 464#define SMTP_COMPARE_ADDR(flags) \ 465 (((flags) & SMTP_MISC_FLAG_PREF_IPV6) ? dns_rr_compare_pref_ipv6 : \ 466 ((flags) & SMTP_MISC_FLAG_PREF_IPV4) ? dns_rr_compare_pref_ipv4 : \ 467 dns_rr_compare_pref_any) 468 469 if (addr_list && addr_list->next && var_smtp_rand_addr) { 470 addr_list = dns_rr_shuffle(addr_list); 471 addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags)); 472 } 473 break; 474 case DNS_NOTFOUND: 475 addr_list = smtp_host_addr(name, misc_flags, why); 476 break; 477 } 478 479 /* 480 * Clean up. 481 */ 482 *found_myself |= (self != 0); 483 return (addr_list); 484} 485 486/* smtp_host_addr - direct host lookup */ 487 488DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why) 489{ 490 DNS_RR *addr_list; 491 int res_opt = 0; 492 493 dsb_reset(why); /* Paranoia */ 494 495 if (smtp_dns_support == SMTP_DNS_DNSSEC) 496 res_opt |= RES_USE_DNSSEC; 497 498 /* 499 * If the host is specified by numerical address, just convert the 500 * address to internal form. Otherwise, the host is specified by name. 501 */ 502#define PREF0 0 503 addr_list = smtp_addr_one((DNS_RR *) 0, host, res_opt, PREF0, why); 504 if (addr_list 505 && (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) 506 && smtp_find_self(addr_list) != 0) { 507 dns_rr_free(addr_list); 508 dsb_simple(why, "5.4.6", "mail for %s loops back to myself", host); 509 return (0); 510 } 511 if (addr_list && addr_list->next) { 512 if (var_smtp_rand_addr) 513 addr_list = dns_rr_shuffle(addr_list); 514 /* The following changes the order of equal-preference hosts. */ 515 if (inet_proto_info()->ai_family_list[1] != 0) 516 addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags)); 517 } 518 if (msg_verbose) 519 smtp_print_addr(host, addr_list); 520 return (addr_list); 521} 522