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
smtp_print_addr(const char * what,DNS_RR * addr_list)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
smtp_addr_one(DNS_RR * addr_list,const char * host,int res_opt,unsigned pref,unsigned port,DSN_BUF * why)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
smtp_addr_list(DNS_RR * mx_names,DSN_BUF * why)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
smtp_find_self(DNS_RR * addr_list)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
smtp_truncate_self(DNS_RR * addr_list,unsigned pref)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
smtp_balance_inet_proto(DNS_RR * addr_list,int misc_flags,int addr_limit)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
smtp_domain_addr(const char * name,DNS_RR ** mxrr,int misc_flags,DSN_BUF * why,int * found_myself)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
smtp_host_addr(const char * host,int misc_flags,DSN_BUF * why)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
smtp_service_addr(const char * name,const char * service,DNS_RR ** mxrr,int misc_flags,DSN_BUF * why,int * found_myself)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