7 /* SMTP server address lookup
9 /* #include "smtp_addr.h"
11 /* DNS_RR *smtp_domain_addr(name, misc_flags, why, found_myself)
17 /* DNS_RR *smtp_host_addr(name, misc_flags, why)
22 /* This module implements Internet address lookups. By default,
23 /* lookups are done via the Internet domain name service (DNS).
24 /* A reasonable number of CNAME indirections is permitted. When
25 /* DNS lookups are disabled, host address lookup is done with
26 /* getnameinfo() or gethostbyname().
28 /* smtp_domain_addr() looks up the network addresses for mail
29 /* exchanger hosts listed for the named domain. Addresses are
30 /* returned in most-preferred first order. The result is truncated
31 /* so that it contains only hosts that are more preferred than the
32 /* local mail server itself. The found_myself result parameter
33 /* is updated when the local MTA is MX host for the specified
36 /* When no mail exchanger is listed in the DNS for \fIname\fR, the
37 /* request is passed to smtp_host_addr().
39 /* It is an error to call smtp_domain_addr() when DNS lookups are
42 /* smtp_host_addr() looks up all addresses listed for the named
43 /* host. The host can be specified as a numerical Internet network
44 /* address, or as a symbolic host name.
46 /* Results from smtp_domain_addr() or smtp_host_addr() are
47 /* destroyed by dns_rr_free(), including null lists.
49 /* Panics: interface violations. For example, calling smtp_domain_addr()
50 /* when DNS lookups are explicitly disabled.
52 /* All routines either return a DNS_RR pointer, or return a null
53 /* pointer and update the \fIwhy\fR argument accordingly.
57 /* The Secure Mailer license must be distributed with this software.
60 /* IBM T.J. Watson Research
62 /* Yorktown Heights, NY 10598, USA
68 #include <sys/socket.h>
69 #include <netinet/in.h>
70 #include <arpa/inet.h>
78 /* Utility library. */
83 #include <inet_addr_list.h>
84 #include <stringops.h>
85 #include <myaddrinfo.h>
86 #include <inet_proto.h>
90 #include <mail_params.h>
91 #include <own_inet_addr.h>
98 /* Application-specific. */
101 #include "smtp_addr.h"
103 /* smtp_print_addr - print address list */
105 static void smtp_print_addr(const char *what
, DNS_RR
*addr_list
)
108 MAI_HOSTADDR_STR hostaddr
;
110 msg_info("begin %s address list", what
);
111 for (addr
= addr_list
; addr
; addr
= addr
->next
) {
112 if (dns_rr_to_pa(addr
, &hostaddr
) == 0) {
113 msg_warn("skipping record type %s: %m", dns_strtype(addr
->type
));
115 msg_info("pref %4d host %s/%s",
116 addr
->pref
, SMTP_HNAME(addr
),
120 msg_info("end %s address list", what
);
123 /* smtp_addr_one - address lookup for one host name */
125 static DNS_RR
*smtp_addr_one(DNS_RR
*addr_list
, const char *host
,
126 unsigned pref
, DSN_BUF
*why
)
128 const char *myname
= "smtp_addr_one";
132 struct addrinfo
*res0
;
133 struct addrinfo
*res
;
134 INET_PROTO_INFO
*proto_info
= inet_proto_info();
138 msg_info("%s: host %s", myname
, host
);
141 * Interpret a numerical name as an address.
143 if (hostaddr_to_sockaddr(host
, (char *) 0, 0, &res0
) == 0
144 && strchr((char *) proto_info
->sa_family_list
, res0
->ai_family
) != 0) {
145 if ((addr
= dns_sa_to_rr(host
, pref
, res0
->ai_addr
)) == 0)
146 msg_fatal("host %s: conversion error for address family %d: %m",
147 host
, ((struct sockaddr
*) (res0
->ai_addr
))->sa_family
);
148 addr_list
= dns_rr_append(addr_list
, addr
);
154 * Use DNS lookup, but keep the option open to use native name service.
156 * XXX A soft error dominates past and future hard errors. Therefore we
157 * should not clobber a soft error text and status code.
159 if (smtp_host_lookup_mask
& SMTP_HOST_FLAG_DNS
) {
160 switch (dns_lookup_v(host
, RES_DEFNAMES
, &addr
, (VSTRING
*) 0,
161 why
->reason
, DNS_REQ_FLAG_NONE
,
162 proto_info
->dns_atype_list
)) {
164 for (rr
= addr
; rr
; rr
= rr
->next
)
166 addr_list
= dns_rr_append(addr_list
, addr
);
169 dsb_status(why
, "4.4.3");
172 dsb_status(why
, SMTP_HAS_SOFT_DSN(why
) ? "4.4.3" : "5.4.3");
175 dsb_status(why
, SMTP_HAS_SOFT_DSN(why
) ? "4.4.4" : "5.4.4");
178 dsb_status(why
, SMTP_HAS_SOFT_DSN(why
) ? "4.4.4" : "5.4.4");
179 /* maybe native naming service will succeed */
185 * Use the native name service which also looks in /etc/hosts.
187 * XXX A soft error dominates past and future hard errors. Therefore we
188 * should not clobber a soft error text and status code.
190 #define RETRY_AI_ERROR(e) \
191 ((e) == EAI_AGAIN || (e) == EAI_MEMORY || (e) == EAI_SYSTEM)
193 #define DSN_NOHOST(e) \
194 ((e) == EAI_AGAIN || (e) == EAI_NODATA || (e) == EAI_NONAME)
196 #define DSN_NOHOST(e) \
197 ((e) == EAI_AGAIN || (e) == EAI_NONAME)
200 if (smtp_host_lookup_mask
& SMTP_HOST_FLAG_NATIVE
) {
201 if ((aierr
= hostname_to_sockaddr(host
, (char *) 0, 0, &res0
)) != 0) {
202 dsb_simple(why
, (SMTP_HAS_SOFT_DSN(why
) || RETRY_AI_ERROR(aierr
)) ?
203 (DSN_NOHOST(aierr
) ? "4.4.4" : "4.3.0") :
204 (DSN_NOHOST(aierr
) ? "5.4.4" : "5.3.0"),
205 "unable to look up host %s: %s",
206 host
, MAI_STRERROR(aierr
));
208 for (found
= 0, res
= res0
; res
!= 0; res
= res
->ai_next
) {
209 if (strchr((char *) proto_info
->sa_family_list
, res
->ai_family
) == 0) {
210 msg_info("skipping address family %d for host %s",
211 res
->ai_family
, host
);
215 if ((addr
= dns_sa_to_rr(host
, pref
, res
->ai_addr
)) == 0)
216 msg_fatal("host %s: conversion error for address family %d: %m",
217 host
, ((struct sockaddr
*) (res0
->ai_addr
))->sa_family
);
218 addr_list
= dns_rr_append(addr_list
, addr
);
222 dsb_simple(why
, SMTP_HAS_SOFT_DSN(why
) ? "4.4.4" : "5.4.4",
223 "%s: host not found", host
);
230 * No further alternatives for host lookup.
235 /* smtp_addr_list - address lookup for a list of mail exchangers */
237 static DNS_RR
*smtp_addr_list(DNS_RR
*mx_names
, DSN_BUF
*why
)
239 DNS_RR
*addr_list
= 0;
243 * As long as we are able to look up any host address, we ignore problems
244 * with DNS lookups (except if we're backup MX, and all the better MX
245 * hosts can't be found).
247 * XXX 2821: update the error status (0->FAIL upon unrecoverable lookup
248 * error, any->RETRY upon temporary lookup error) so that we can
249 * correctly handle the case of no resolvable MX host. Currently this is
250 * always treated as a soft error. RFC 2821 wants a more precise
253 * XXX dns_lookup() enables RES_DEFNAMES. This is wrong for names found in
254 * MX records - we should not append the local domain to dot-less names.
256 * XXX However, this is not the only problem. If we use the native name
257 * service for host lookup, then it will usually enable RES_DNSRCH which
258 * appends local domain information to all lookups. In particular,
259 * getaddrinfo() may invoke a resolver that runs in a different process
260 * (NIS server, nscd), so we can't even reliably turn this off by
261 * tweaking the in-process resolver flags.
263 for (rr
= mx_names
; rr
; rr
= rr
->next
) {
264 if (rr
->type
!= T_MX
)
265 msg_panic("smtp_addr_list: bad resource type: %d", rr
->type
);
266 addr_list
= smtp_addr_one(addr_list
, (char *) rr
->data
, rr
->pref
, why
);
271 /* smtp_find_self - spot myself in a crowd of mail exchangers */
273 static DNS_RR
*smtp_find_self(DNS_RR
*addr_list
)
275 const char *myname
= "smtp_find_self";
276 INET_ADDR_LIST
*self
;
277 INET_ADDR_LIST
*proxy
;
281 self
= own_inet_addr_list();
282 proxy
= proxy_inet_addr_list();
284 for (addr
= addr_list
; addr
; addr
= addr
->next
) {
287 * Find out if this mail system is listening on this address.
289 for (i
= 0; i
< self
->used
; i
++)
290 if (DNS_RR_EQ_SA(addr
, (struct sockaddr
*) (self
->addrs
+ i
))) {
292 msg_info("%s: found self at pref %d", myname
, addr
->pref
);
297 * Find out if this mail system has a proxy listening on this
300 for (i
= 0; i
< proxy
->used
; i
++)
301 if (DNS_RR_EQ_SA(addr
, (struct sockaddr
*) (proxy
->addrs
+ i
))) {
303 msg_info("%s: found proxy at pref %d", myname
, addr
->pref
);
309 * Didn't find myself, or my proxy.
312 msg_info("%s: not found", myname
);
316 /* smtp_truncate_self - truncate address list at self and equivalents */
318 static DNS_RR
*smtp_truncate_self(DNS_RR
*addr_list
, unsigned pref
)
323 for (last
= 0, addr
= addr_list
; addr
; last
= addr
, addr
= addr
->next
) {
324 if (pref
== addr
->pref
) {
326 smtp_print_addr("truncated", addr
);
339 /* smtp_domain_addr - mail exchanger address lookup */
341 DNS_RR
*smtp_domain_addr(char *name
, int misc_flags
, DSN_BUF
*why
,
345 DNS_RR
*addr_list
= 0;
350 dsb_reset(why
); /* Paranoia */
353 * Preferences from DNS use 0..32767, fall-backs use 32768+.
355 #define IMPOSSIBLE_PREFERENCE (~0)
361 msg_panic("smtp_domain_addr: DNS lookup is disabled");
364 * Look up the mail exchanger hosts listed for this name. Sort the
365 * results by preference. Look up the corresponding host addresses, and
366 * truncate the list so that it contains only hosts that are more
367 * preferred than myself. When no MX resource records exist, look up the
368 * addresses listed for this name.
370 * According to RFC 974: "It is possible that the list of MXs in the
371 * response to the query will be empty. This is a special case. If the
372 * list is empty, mailers should treat it as if it contained one RR, an
373 * MX RR with a preference value of 0, and a host name of REMOTE. (I.e.,
374 * REMOTE is its only MX). In addition, the mailer should do no further
375 * processing on the list, but should attempt to deliver the message to
378 * Normally it is OK if an MX host cannot be found in the DNS; we'll just
379 * use a backup one, and silently ignore the better MX host. However, if
380 * the best backup that we can find in the DNS is the local machine, then
381 * we must remember that the local machine is not the primary MX host, or
382 * else we will claim that mail loops back.
384 * XXX Optionally do A lookups even when the MX lookup didn't complete.
385 * Unfortunately with some DNS servers this is not a transient problem.
387 * XXX Ideally we would perform A lookups only as far as needed. But as long
388 * as we're looking up all the hosts, it would be better to look up the
389 * least preferred host first, so that DNS lookup error messages make
392 * XXX 2821: RFC 2821 says that the sender must shuffle equal-preference MX
393 * hosts, whereas multiple A records per hostname must be used in the
394 * order as received. They make the bogus assumption that a hostname with
395 * multiple A records corresponds to one machine with multiple network
398 * XXX 2821: Postfix recognizes the local machine by looking for its own IP
399 * address in the list of mail exchangers. RFC 2821 says one has to look
400 * at the mail exchanger hostname as well, making the bogus assumption
401 * that an IP address is listed only under one hostname. However, looking
402 * at hostnames provides a partial solution for MX hosts behind a NAT
405 switch (dns_lookup(name
, T_MX
, 0, &mx_names
, (VSTRING
*) 0, why
->reason
)) {
407 dsb_status(why
, "4.4.3");
408 if (var_ign_mx_lookup_err
)
409 addr_list
= smtp_host_addr(name
, misc_flags
, why
);
412 dsb_status(why
, "5.4.4");
413 if (var_ign_mx_lookup_err
)
414 addr_list
= smtp_host_addr(name
, misc_flags
, why
);
417 dsb_status(why
, "5.4.3");
418 if (var_ign_mx_lookup_err
)
419 addr_list
= smtp_host_addr(name
, misc_flags
, why
);
422 mx_names
= dns_rr_sort(mx_names
, dns_rr_compare_pref
);
423 best_pref
= (mx_names
? mx_names
->pref
: IMPOSSIBLE_PREFERENCE
);
424 addr_list
= smtp_addr_list(mx_names
, why
);
425 dns_rr_free(mx_names
);
426 if (addr_list
== 0) {
427 /* Text does not change. */
428 if (var_smtp_defer_mxaddr
) {
429 /* Don't clobber the null terminator. */
430 if (SMTP_HAS_HARD_DSN(why
))
431 SMTP_SET_SOFT_DSN(why
); /* XXX */
432 /* Require some error status. */
433 else if (!SMTP_HAS_SOFT_DSN(why
))
434 msg_panic("smtp_domain_addr: bad status");
436 msg_warn("no MX host for %s has a valid address record", name
);
439 best_found
= (addr_list
? addr_list
->pref
: IMPOSSIBLE_PREFERENCE
);
441 smtp_print_addr(name
, addr_list
);
442 if ((misc_flags
& SMTP_MISC_FLAG_LOOP_DETECT
)
443 && (self
= smtp_find_self(addr_list
)) != 0) {
444 addr_list
= smtp_truncate_self(addr_list
, self
->pref
);
445 if (addr_list
== 0) {
446 if (best_pref
!= best_found
) {
447 dsb_simple(why
, "4.4.4",
448 "unable to find primary relay for %s", name
);
450 dsb_simple(why
, "5.4.6", "mail for %s loops back to myself",
455 if (addr_list
&& addr_list
->next
&& var_smtp_rand_addr
) {
456 addr_list
= dns_rr_shuffle(addr_list
);
457 addr_list
= dns_rr_sort(addr_list
, dns_rr_compare_pref
);
461 addr_list
= smtp_host_addr(name
, misc_flags
, why
);
468 *found_myself
|= (self
!= 0);
472 /* smtp_host_addr - direct host lookup */
474 DNS_RR
*smtp_host_addr(const char *host
, int misc_flags
, DSN_BUF
*why
)
478 dsb_reset(why
); /* Paranoia */
481 * If the host is specified by numerical address, just convert the
482 * address to internal form. Otherwise, the host is specified by name.
485 addr_list
= smtp_addr_one((DNS_RR
*) 0, host
, PREF0
, why
);
487 && (misc_flags
& SMTP_MISC_FLAG_LOOP_DETECT
)
488 && smtp_find_self(addr_list
) != 0) {
489 dns_rr_free(addr_list
);
490 dsb_simple(why
, "5.4.6", "mail for %s loops back to myself", host
);
493 if (addr_list
&& addr_list
->next
) {
494 if (var_smtp_rand_addr
)
495 addr_list
= dns_rr_shuffle(addr_list
);
496 /* The following changes the order of equal-preference hosts. */
497 if (inet_proto_info()->ai_family_list
[1] != 0)
498 addr_list
= dns_rr_sort(addr_list
, dns_rr_compare_pref
);
501 smtp_print_addr(host
, addr_list
);