1 /*	$NetBSD: smtp_addr.c,v 1.4 2022/10/08 16:12: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 /* 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 /*	Wietse Venema
68 /*	Google, Inc.
69 /*	111 8th Avenue
70 /*	New York, NY 10011, USA
71 /*--*/
72 
73 /* System library. */
74 
75 #include <sys_defs.h>
76 #include <sys/socket.h>
77 #include <netinet/in.h>
78 #include <arpa/inet.h>
79 #include <stdlib.h>
80 #include <netdb.h>
81 #include <ctype.h>
82 #include <string.h>
83 #include <unistd.h>
84 #include <errno.h>
85 
86 /* Utility library. */
87 
88 #include <msg.h>
89 #include <vstring.h>
90 #include <mymalloc.h>
91 #include <inet_addr_list.h>
92 #include <stringops.h>
93 #include <myaddrinfo.h>
94 #include <inet_proto.h>
95 #include <midna_domain.h>
96 
97 /* Global library. */
98 
99 #include <mail_params.h>
100 #include <own_inet_addr.h>
101 #include <dsn_buf.h>
102 
103 /* DNS library. */
104 
105 #include <dns.h>
106 
107 /* Application-specific. */
108 
109 #include "smtp.h"
110 #include "smtp_addr.h"
111 
112 /* smtp_print_addr - print address list */
113 
smtp_print_addr(const char * what,DNS_RR * addr_list)114 static void smtp_print_addr(const char *what, DNS_RR *addr_list)
115 {
116     DNS_RR *addr;
117     MAI_HOSTADDR_STR hostaddr;
118 
119     msg_info("begin %s address list", what);
120     for (addr = addr_list; addr; addr = addr->next) {
121 	if (dns_rr_to_pa(addr, &hostaddr) == 0) {
122 	    msg_warn("skipping record type %s: %m", dns_strtype(addr->type));
123 	} else {
124 	    msg_info("pref %4d host %s/%s",
125 		     addr->pref, SMTP_HNAME(addr),
126 		     hostaddr.buf);
127 	}
128     }
129     msg_info("end %s address list", what);
130 }
131 
132 /* smtp_addr_one - address lookup for one host name */
133 
smtp_addr_one(DNS_RR * addr_list,const char * host,int res_opt,unsigned pref,DSN_BUF * why)134 static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt,
135 			             unsigned pref, DSN_BUF *why)
136 {
137     const char *myname = "smtp_addr_one";
138     DNS_RR *addr = 0;
139     DNS_RR *rr;
140     int     aierr;
141     struct addrinfo *res0;
142     struct addrinfo *res;
143     const INET_PROTO_INFO *proto_info = inet_proto_info();
144     unsigned char *proto_family_list = proto_info->sa_family_list;
145     int     found;
146 
147     if (msg_verbose)
148 	msg_info("%s: host %s", myname, host);
149 
150     /*
151      * Interpret a numerical name as an address.
152      */
153     if (hostaddr_to_sockaddr(host, (char *) 0, 0, &res0) == 0) {
154 	if (strchr((char *) proto_family_list, res0->ai_family) != 0) {
155 	    if ((addr = dns_sa_to_rr(host, pref, res0->ai_addr)) == 0)
156 		msg_fatal("host %s: conversion error for address family "
157 			  "%d: %m", host, res0->ai_addr->sa_family);
158 	    addr_list = dns_rr_append(addr_list, addr);
159 	    if (msg_verbose)
160 		msg_info("%s: using numerical host %s", myname, host);
161 	    freeaddrinfo(res0);
162 	    return (addr_list);
163 	}
164 	freeaddrinfo(res0);
165     }
166 
167     /*
168      * Use DNS lookup, but keep the option open to use native name service.
169      *
170      * XXX A soft error dominates past and future hard errors. Therefore we
171      * should not clobber a soft error text and status code.
172      */
173     if (smtp_host_lookup_mask & SMTP_HOST_FLAG_DNS) {
174 	res_opt |= smtp_dns_res_opt;
175 	switch (dns_lookup_v(host, res_opt, &addr, (VSTRING *) 0,
176 			     why->reason, DNS_REQ_FLAG_NONE,
177 			     proto_info->dns_atype_list)) {
178 	case DNS_OK:
179 	    for (rr = addr; rr; rr = rr->next)
180 		rr->pref = pref;
181 	    addr_list = dns_rr_append(addr_list, addr);
182 	    return (addr_list);
183 	default:
184 	    dsb_status(why, "4.4.3");
185 	    return (addr_list);
186 	case DNS_FAIL:
187 	    dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.3" : "5.4.3");
188 	    return (addr_list);
189 	case DNS_INVAL:
190 	    dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4");
191 	    return (addr_list);
192 	case DNS_POLICY:
193 	    dsb_status(why, "4.7.0");
194 	    return (addr_list);
195 	case DNS_NOTFOUND:
196 	    dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4");
197 	    /* maybe native naming service will succeed */
198 	    break;
199 	}
200     }
201 
202     /*
203      * Use the native name service which also looks in /etc/hosts.
204      *
205      * XXX A soft error dominates past and future hard errors. Therefore we
206      * should not clobber a soft error text and status code.
207      */
208 #define RETRY_AI_ERROR(e) \
209         ((e) == EAI_AGAIN || (e) == EAI_MEMORY || (e) == EAI_SYSTEM)
210 #ifdef EAI_NODATA
211 #define DSN_NOHOST(e) \
212 	((e) == EAI_AGAIN || (e) == EAI_NODATA || (e) == EAI_NONAME)
213 #else
214 #define DSN_NOHOST(e) \
215 	((e) == EAI_AGAIN || (e) == EAI_NONAME)
216 #endif
217 
218     if (smtp_host_lookup_mask & SMTP_HOST_FLAG_NATIVE) {
219 	if ((aierr = hostname_to_sockaddr(host, (char *) 0, 0, &res0)) != 0) {
220 	    dsb_simple(why, (SMTP_HAS_SOFT_DSN(why) || RETRY_AI_ERROR(aierr)) ?
221 		       (DSN_NOHOST(aierr) ? "4.4.4" : "4.3.0") :
222 		       (DSN_NOHOST(aierr) ? "5.4.4" : "5.3.0"),
223 		       "unable to look up host %s: %s",
224 		       host, MAI_STRERROR(aierr));
225 	} else {
226 	    for (found = 0, res = res0; res != 0; res = res->ai_next) {
227 		if (strchr((char *) proto_family_list, res->ai_family) == 0) {
228 		    msg_info("skipping address family %d for host %s",
229 			     res->ai_family, host);
230 		    continue;
231 		}
232 		found++;
233 		if ((addr = dns_sa_to_rr(host, pref, res->ai_addr)) == 0)
234 		    msg_fatal("host %s: conversion error for address family "
235 			      "%d: %m", host, res0->ai_addr->sa_family);
236 		addr_list = dns_rr_append(addr_list, addr);
237 		if (msg_verbose) {
238 		    MAI_HOSTADDR_STR hostaddr_str;
239 
240 		    SOCKADDR_TO_HOSTADDR(res->ai_addr, res->ai_addrlen,
241 				  &hostaddr_str, (MAI_SERVPORT_STR *) 0, 0);
242 		    msg_info("%s: native lookup result: %s",
243 			     myname, hostaddr_str.buf);
244 		}
245 	    }
246 	    freeaddrinfo(res0);
247 	    if (found == 0) {
248 		dsb_simple(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4",
249 			   "%s: host not found", host);
250 	    }
251 	    return (addr_list);
252 	}
253     }
254 
255     /*
256      * No further alternatives for host lookup.
257      */
258     return (addr_list);
259 }
260 
261 /* smtp_addr_list - address lookup for a list of mail exchangers */
262 
smtp_addr_list(DNS_RR * mx_names,DSN_BUF * why)263 static DNS_RR *smtp_addr_list(DNS_RR *mx_names, DSN_BUF *why)
264 {
265     DNS_RR *addr_list = 0;
266     DNS_RR *rr;
267     int     res_opt = 0;
268 
269     if (mx_names->dnssec_valid)
270 	res_opt = RES_USE_DNSSEC;
271 #ifdef USE_TLS
272     else if (smtp_tls_insecure_mx_policy > TLS_LEV_MAY)
273 	res_opt = RES_USE_DNSSEC;
274 #endif
275 
276     /*
277      * As long as we are able to look up any host address, we ignore problems
278      * with DNS lookups (except if we're backup MX, and all the better MX
279      * hosts can't be found).
280      *
281      * XXX 2821: update the error status (0->FAIL upon unrecoverable lookup
282      * error, any->RETRY upon temporary lookup error) so that we can
283      * correctly handle the case of no resolvable MX host. Currently this is
284      * always treated as a soft error. RFC 2821 wants a more precise
285      * response.
286      *
287      * XXX dns_lookup() enables RES_DEFNAMES. This is wrong for names found in
288      * MX records - we should not append the local domain to dot-less names.
289      *
290      * XXX However, this is not the only problem. If we use the native name
291      * service for host lookup, then it will usually enable RES_DNSRCH which
292      * appends local domain information to all lookups. In particular,
293      * getaddrinfo() may invoke a resolver that runs in a different process
294      * (NIS server, nscd), so we can't even reliably turn this off by
295      * tweaking the in-process resolver flags.
296      */
297     for (rr = mx_names; rr; rr = rr->next) {
298 	if (rr->type != T_MX)
299 	    msg_panic("smtp_addr_list: bad resource type: %d", rr->type);
300 	addr_list = smtp_addr_one(addr_list, (char *) rr->data, res_opt,
301 				  rr->pref, why);
302     }
303     return (addr_list);
304 }
305 
306 /* smtp_find_self - spot myself in a crowd of mail exchangers */
307 
smtp_find_self(DNS_RR * addr_list)308 static DNS_RR *smtp_find_self(DNS_RR *addr_list)
309 {
310     const char *myname = "smtp_find_self";
311     INET_ADDR_LIST *self;
312     INET_ADDR_LIST *proxy;
313     DNS_RR *addr;
314     int     i;
315 
316     self = own_inet_addr_list();
317     proxy = proxy_inet_addr_list();
318 
319     for (addr = addr_list; addr; addr = addr->next) {
320 
321 	/*
322 	 * Find out if this mail system is listening on this address.
323 	 */
324 	for (i = 0; i < self->used; i++)
325 	    if (DNS_RR_EQ_SA(addr, (struct sockaddr *) (self->addrs + i))) {
326 		if (msg_verbose)
327 		    msg_info("%s: found self at pref %d", myname, addr->pref);
328 		return (addr);
329 	    }
330 
331 	/*
332 	 * Find out if this mail system has a proxy listening on this
333 	 * address.
334 	 */
335 	for (i = 0; i < proxy->used; i++)
336 	    if (DNS_RR_EQ_SA(addr, (struct sockaddr *) (proxy->addrs + i))) {
337 		if (msg_verbose)
338 		    msg_info("%s: found proxy at pref %d", myname, addr->pref);
339 		return (addr);
340 	    }
341     }
342 
343     /*
344      * Didn't find myself, or my proxy.
345      */
346     if (msg_verbose)
347 	msg_info("%s: not found", myname);
348     return (0);
349 }
350 
351 /* smtp_truncate_self - truncate address list at self and equivalents */
352 
smtp_truncate_self(DNS_RR * addr_list,unsigned pref)353 static DNS_RR *smtp_truncate_self(DNS_RR *addr_list, unsigned pref)
354 {
355     DNS_RR *addr;
356     DNS_RR *last;
357 
358     for (last = 0, addr = addr_list; addr; last = addr, addr = addr->next) {
359 	if (pref == addr->pref) {
360 	    if (msg_verbose)
361 		smtp_print_addr("truncated", addr);
362 	    dns_rr_free(addr);
363 	    if (last == 0) {
364 		addr_list = 0;
365 	    } else {
366 		last->next = 0;
367 	    }
368 	    break;
369 	}
370     }
371     return (addr_list);
372 }
373 
374 /* smtp_balance_inet_proto - balance IPv4/6 protocols within address limit */
375 
smtp_balance_inet_proto(DNS_RR * addr_list,int misc_flags,int addr_limit)376 static DNS_RR *smtp_balance_inet_proto(DNS_RR *addr_list, int misc_flags,
377 				               int addr_limit)
378 {
379     const char myname[] = "smtp_balance_inet_proto";
380     DNS_RR *rr;
381     DNS_RR *stripped_list;
382     DNS_RR *next;
383     int     v6_count;
384     int     v4_count;
385     int     v6_target, v4_target;
386     int    *p;
387 
388     /*
389      * Precondition: the input is sorted by MX preference (not necessarily IP
390      * address family preference), and addresses with the same or worse
391      * preference than 'myself' have been eliminated. Postcondition: the
392      * relative list order is unchanged, but some elements are removed.
393      */
394 
395     /*
396      * Count the number of IPv6 and IPv4 addresses.
397      */
398     for (v4_count = v6_count = 0, rr = addr_list; rr != 0; rr = rr->next) {
399 	if (rr->type == T_A) {
400 	    v4_count++;
401 	} else if (rr->type == T_AAAA) {
402 	    v6_count++;
403 	} else {
404 	    msg_panic("%s: unexpected record type: %s",
405 		      myname, dns_strtype(rr->type));
406 	}
407     }
408 
409     /*
410      * Ensure that one address type will not out-crowd the other, while
411      * enforcing the address count limit. This works around a current problem
412      * where some destination announces primarily IPv6 MX addresses, the
413      * smtp_address_limit eliminates most or all IPv4 addresses, and the
414      * destination is not reachable over IPv6.
415      *
416      * Maybe: do all smtp_mx_address_limit enforcement here, and remove
417      * pre-existing enforcement elsewhere. That would obsolete the
418      * smtp_balance_inet_protocols configuration parameter.
419      */
420     if (v4_count > 0 && v6_count > 0 && v4_count + v6_count > addr_limit) {
421 
422 	/*-
423          * Decide how many IPv6 and IPv4 addresses to keep. The code below
424          * has three branches, corresponding to the regions R1, R2 and R3
425          * in the figure.
426          *
427          *  L = addr_limit
428          *  X = excluded by condition (v4_count + v6_count > addr_limit)
429          *
430          * v4_count
431          *     ^
432          *     |
433          *  L  \  R1
434          *     |X\     |
435          *     |XXX\   |
436          *     |XXXXX\ | R2
437          * L/2 +-------\-------
438          *     |XXXXXXX|X\
439          *     |XXXXXXX|XXX\  R3
440          *     |XXXXXXX|XXXXX\
441          *   0 +-------+-------\--> v6_count
442          *     0      L/2      L
443          */
444 	if (v6_count <= addr_limit / 2) {	/* Region R1 */
445 	    v6_target = v6_count;
446 	    v4_target = addr_limit - v6_target;
447 	} else if (v4_count <= addr_limit / 2) {/* Region R3 */
448 	    v4_target = v4_count;
449 	    v6_target = addr_limit - v4_target;
450 	} else {				/* Region R2 */
451 	    /* v4_count > addr_limit / 2 && v6_count > addr_limit / 2 */
452 	    v4_target = (addr_limit + (addr_list->type == T_A)) / 2;
453 	    v6_target = addr_limit - v4_target;
454 	}
455 	if (msg_verbose)
456 	    msg_info("v6_target=%d, v4_target=%d", v6_target, v4_target);
457 
458 	/* Enforce the address count targets. */
459 	stripped_list = 0;
460 	for (rr = addr_list; rr != 0; rr = next) {
461 	    next = rr->next;
462 	    rr->next = 0;
463 	    if (rr->type == T_A) {
464 		p = &v4_target;
465 	    } else if (rr->type == T_AAAA) {
466 		p = &v6_target;
467 	    } else {
468 		msg_panic("%s: unexpected record type: %s",
469 			  myname, dns_strtype(rr->type));
470 	    }
471 	    if (*p > 0) {
472 		stripped_list = dns_rr_append(stripped_list, rr);
473 		*p -= 1;
474 	    } else {
475 		dns_rr_free(rr);
476 	    }
477 	}
478 	if (v4_target > 0 || v6_target > 0)
479 	    msg_panic("%s: bad target count: v4_target=%d, v6_target=%d",
480 		      myname, v4_target, v6_target);
481 	if (msg_verbose)
482 	    smtp_print_addr("smtp_balance_inet_proto result", stripped_list);
483 	return (stripped_list);
484     } else {
485 	return (addr_list);
486     }
487 }
488 
489 /* smtp_domain_addr - mail exchanger address lookup */
490 
smtp_domain_addr(const char * name,DNS_RR ** mxrr,int misc_flags,DSN_BUF * why,int * found_myself)491 DNS_RR *smtp_domain_addr(const char *name, DNS_RR **mxrr, int misc_flags,
492 			         DSN_BUF *why, int *found_myself)
493 {
494     DNS_RR *mx_names;
495     DNS_RR *addr_list = 0;
496     DNS_RR *self = 0;
497     unsigned best_pref;
498     unsigned best_found;
499     int     r = 0;			/* Resolver flags */
500     const char *aname;
501 
502     dsb_reset(why);				/* Paranoia */
503 
504     /*
505      * Preferences from DNS use 0..32767, fall-backs use 32768+.
506      */
507 #define IMPOSSIBLE_PREFERENCE	(~0)
508 
509     /*
510      * Sanity check.
511      */
512     if (smtp_dns_support == SMTP_DNS_DISABLED)
513 	msg_panic("smtp_domain_addr: DNS lookup is disabled");
514     if (smtp_dns_support == SMTP_DNS_DNSSEC)
515 	r |= RES_USE_DNSSEC;
516 
517     /*
518      * IDNA support.
519      */
520 #ifndef NO_EAI
521     if (!allascii(name) && (aname = midna_domain_to_ascii(name)) != 0) {
522 	if (msg_verbose)
523 	    msg_info("%s asciified to %s", name, aname);
524     } else
525 #endif
526 	aname = name;
527 
528     /*
529      * Look up the mail exchanger hosts listed for this name. Sort the
530      * results by preference. Look up the corresponding host addresses, and
531      * truncate the list so that it contains only hosts that are more
532      * preferred than myself. When no MX resource records exist, look up the
533      * addresses listed for this name.
534      *
535      * According to RFC 974: "It is possible that the list of MXs in the
536      * response to the query will be empty.  This is a special case.  If the
537      * list is empty, mailers should treat it as if it contained one RR, an
538      * MX RR with a preference value of 0, and a host name of REMOTE.  (I.e.,
539      * REMOTE is its only MX).  In addition, the mailer should do no further
540      * processing on the list, but should attempt to deliver the message to
541      * REMOTE."
542      *
543      * Normally it is OK if an MX host cannot be found in the DNS; we'll just
544      * use a backup one, and silently ignore the better MX host. However, if
545      * the best backup that we can find in the DNS is the local machine, then
546      * we must remember that the local machine is not the primary MX host, or
547      * else we will claim that mail loops back.
548      *
549      * XXX Optionally do A lookups even when the MX lookup didn't complete.
550      * Unfortunately with some DNS servers this is not a transient problem.
551      *
552      * XXX Ideally we would perform A lookups only as far as needed. But as long
553      * as we're looking up all the hosts, it would be better to look up the
554      * least preferred host first, so that DNS lookup error messages make
555      * more sense.
556      *
557      * XXX 2821: RFC 2821 says that the sender must shuffle equal-preference MX
558      * hosts, whereas multiple A records per hostname must be used in the
559      * order as received. They make the bogus assumption that a hostname with
560      * multiple A records corresponds to one machine with multiple network
561      * interfaces.
562      *
563      * XXX 2821: Postfix recognizes the local machine by looking for its own IP
564      * address in the list of mail exchangers. RFC 2821 says one has to look
565      * at the mail exchanger hostname as well, making the bogus assumption
566      * that an IP address is listed only under one hostname. However, looking
567      * at hostnames provides a partial solution for MX hosts behind a NAT
568      * gateway.
569      */
570     switch (dns_lookup(aname, T_MX, r, &mx_names, (VSTRING *) 0, why->reason)) {
571     default:
572 	dsb_status(why, "4.4.3");
573 	if (var_ign_mx_lookup_err)
574 	    addr_list = smtp_host_addr(aname, misc_flags, why);
575 	break;
576     case DNS_INVAL:
577 	dsb_status(why, "5.4.4");
578 	if (var_ign_mx_lookup_err)
579 	    addr_list = smtp_host_addr(aname, misc_flags, why);
580 	break;
581     case DNS_NULLMX:
582 	dsb_status(why, "5.1.0");
583 	break;
584     case DNS_POLICY:
585 	dsb_status(why, "4.7.0");
586 	break;
587     case DNS_FAIL:
588 	dsb_status(why, "5.4.3");
589 	if (var_ign_mx_lookup_err)
590 	    addr_list = smtp_host_addr(aname, misc_flags, why);
591 	break;
592     case DNS_OK:
593 	mx_names = dns_rr_sort(mx_names, dns_rr_compare_pref_any);
594 	best_pref = (mx_names ? mx_names->pref : IMPOSSIBLE_PREFERENCE);
595 	addr_list = smtp_addr_list(mx_names, why);
596 	if (mxrr)
597 	    *mxrr = dns_rr_copy(mx_names);	/* copies one record! */
598 	dns_rr_free(mx_names);
599 	if (addr_list == 0) {
600 	    /* Text does not change. */
601 	    if (var_smtp_defer_mxaddr) {
602 		/* Don't clobber the null terminator. */
603 		if (SMTP_HAS_HARD_DSN(why))
604 		    SMTP_SET_SOFT_DSN(why);	/* XXX */
605 		/* Require some error status. */
606 		else if (!SMTP_HAS_SOFT_DSN(why))
607 		    msg_panic("smtp_domain_addr: bad status");
608 	    }
609 	    msg_warn("no MX host for %s has a valid address record", name);
610 	    break;
611 	}
612 	best_found = (addr_list ? addr_list->pref : IMPOSSIBLE_PREFERENCE);
613 	if (msg_verbose)
614 	    smtp_print_addr(name, addr_list);
615 	if ((misc_flags & SMTP_MISC_FLAG_LOOP_DETECT)
616 	    && (self = smtp_find_self(addr_list)) != 0) {
617 	    addr_list = smtp_truncate_self(addr_list, self->pref);
618 	    if (addr_list == 0) {
619 		if (best_pref != best_found) {
620 		    dsb_simple(why, "4.4.4",
621 			       "unable to find primary relay for %s", name);
622 		} else {
623 		    dsb_simple(why, "5.4.6", "mail for %s loops back to myself",
624 			       name);
625 		}
626 	    }
627 	}
628 #define SMTP_COMPARE_ADDR(flags) \
629 	(((flags) & SMTP_MISC_FLAG_PREF_IPV6) ? dns_rr_compare_pref_ipv6 : \
630 	 ((flags) & SMTP_MISC_FLAG_PREF_IPV4) ? dns_rr_compare_pref_ipv4 : \
631 	 dns_rr_compare_pref_any)
632 
633 	if (addr_list && addr_list->next) {
634 	    if (var_smtp_rand_addr)
635 		addr_list = dns_rr_shuffle(addr_list);
636 	    addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags));
637 	    if (var_smtp_mxaddr_limit > 0 && var_smtp_balance_inet_proto)
638 		addr_list = smtp_balance_inet_proto(addr_list, misc_flags,
639 						    var_smtp_mxaddr_limit);
640 	}
641 	break;
642     case DNS_NOTFOUND:
643 	addr_list = smtp_host_addr(aname, misc_flags, why);
644 	break;
645     }
646 
647     /*
648      * Clean up.
649      */
650     *found_myself |= (self != 0);
651     return (addr_list);
652 }
653 
654 /* smtp_host_addr - direct host lookup */
655 
smtp_host_addr(const char * host,int misc_flags,DSN_BUF * why)656 DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why)
657 {
658     DNS_RR *addr_list;
659     int     res_opt = 0;
660     const char *ahost;
661 
662     dsb_reset(why);				/* Paranoia */
663 
664     if (smtp_dns_support == SMTP_DNS_DNSSEC)
665 	res_opt |= RES_USE_DNSSEC;
666 
667     /*
668      * IDNA support.
669      */
670 #ifndef NO_EAI
671     if (!allascii(host) && (ahost = midna_domain_to_ascii(host)) != 0) {
672 	if (msg_verbose)
673 	    msg_info("%s asciified to %s", host, ahost);
674     } else
675 #endif
676 	ahost = host;
677 
678     /*
679      * If the host is specified by numerical address, just convert the
680      * address to internal form. Otherwise, the host is specified by name.
681      */
682 #define PREF0	0
683     addr_list = smtp_addr_one((DNS_RR *) 0, ahost, res_opt, PREF0, why);
684     if (addr_list
685 	&& (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT)
686 	&& smtp_find_self(addr_list) != 0) {
687 	dns_rr_free(addr_list);
688 	dsb_simple(why, "5.4.6", "mail for %s loops back to myself", host);
689 	return (0);
690     }
691     if (addr_list && addr_list->next) {
692 	if (var_smtp_rand_addr)
693 	    addr_list = dns_rr_shuffle(addr_list);
694 	/* The following changes the order of equal-preference hosts. */
695 	if (inet_proto_info()->ai_family_list[1] != 0)
696 	    addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags));
697 	if (var_smtp_mxaddr_limit > 0 && var_smtp_balance_inet_proto)
698 	    addr_list = smtp_balance_inet_proto(addr_list, misc_flags,
699 						var_smtp_mxaddr_limit);
700     }
701     if (msg_verbose)
702 	smtp_print_addr(host, addr_list);
703     return (addr_list);
704 }
705