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