xref: /openbsd/usr.sbin/smtpd/mta.c (revision e6c7c102)
1*e6c7c102Sjsg /*	$OpenBSD: mta.c,v 1.248 2024/04/23 13:34:51 jsg Exp $	*/
21f3c2bcfSsobrado 
33ef9cbf7Sgilles /*
43ef9cbf7Sgilles  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
565c4fdfbSgilles  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
6e5b07014Sgilles  * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
7b04e6f27Seric  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
83ef9cbf7Sgilles  *
93ef9cbf7Sgilles  * Permission to use, copy, modify, and distribute this software for any
103ef9cbf7Sgilles  * purpose with or without fee is hereby granted, provided that the above
113ef9cbf7Sgilles  * copyright notice and this permission notice appear in all copies.
123ef9cbf7Sgilles  *
133ef9cbf7Sgilles  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
143ef9cbf7Sgilles  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
153ef9cbf7Sgilles  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
163ef9cbf7Sgilles  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
173ef9cbf7Sgilles  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
183ef9cbf7Sgilles  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
193ef9cbf7Sgilles  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
203ef9cbf7Sgilles  */
213ef9cbf7Sgilles 
22c50073caSchl #include <inttypes.h>
233ef9cbf7Sgilles #include <stdlib.h>
243ef9cbf7Sgilles #include <string.h>
250dcffd0dSop #include <time.h>
26eed85469Seric #include <tls.h>
273ef9cbf7Sgilles 
283ef9cbf7Sgilles #include "smtpd.h"
295eb8dddaSgilles #include "log.h"
30eed85469Seric #include "ssl.h"
313ef9cbf7Sgilles 
325d758dd0Seric #define MAXERROR_PER_ROUTE	4
33ac5aa4b9Seric 
34c5acbec8Seric #define DELAY_CHECK_SOURCE	1
35c5acbec8Seric #define DELAY_CHECK_SOURCE_SLOW	10
36c5acbec8Seric #define DELAY_CHECK_SOURCE_FAST 0
37c5acbec8Seric #define DELAY_CHECK_LIMIT	5
38ac5aa4b9Seric 
39c5acbec8Seric #define	DELAY_QUADRATIC		1
402aa70c64Sgilles #define DELAY_ROUTE_BASE	15
412aa70c64Sgilles #define DELAY_ROUTE_MAX		3600
42ac5aa4b9Seric 
436dc81a07Seric #define RELAY_ONHOLD		0x01
44704aa3edSeric #define RELAY_HOLDQ		0x02
456dc81a07Seric 
46eed85469Seric static void mta_setup_dispatcher(struct dispatcher *);
47a8e22235Sgilles static void mta_handle_envelope(struct envelope *, const char *);
48a8e22235Sgilles static void mta_query_smarthost(struct envelope *);
49a8e22235Sgilles static void mta_on_smarthost(struct envelope *, const char *);
5065c4fdfbSgilles static void mta_query_mx(struct mta_relay *);
5165c4fdfbSgilles static void mta_query_secret(struct mta_relay *);
5265c4fdfbSgilles static void mta_query_preference(struct mta_relay *);
5365c4fdfbSgilles static void mta_query_source(struct mta_relay *);
5465c4fdfbSgilles static void mta_on_mx(void *, void *, void *);
55c5acbec8Seric static void mta_on_secret(struct mta_relay *, const char *);
56bc504fceSeric static void mta_on_preference(struct mta_relay *, int);
5765c4fdfbSgilles static void mta_on_source(struct mta_relay *, struct mta_source *);
58c5acbec8Seric static void mta_on_timeout(struct runq *, void *);
5965c4fdfbSgilles static void mta_connect(struct mta_connector *);
60c5acbec8Seric static void mta_route_enable(struct mta_route *);
61c5acbec8Seric static void mta_route_disable(struct mta_route *, int, int);
6265c4fdfbSgilles static void mta_drain(struct mta_relay *);
63af241c42Seric static void mta_delivery_flush_event(int, short, void *);
6465c4fdfbSgilles static void mta_flush(struct mta_relay *, int, const char *);
65c5acbec8Seric static struct mta_route *mta_find_route(struct mta_connector *, time_t, int*,
66d6ee62d2Seric     time_t*, struct mta_mx **);
67399c053eSeric static void mta_log(const struct mta_envelope *, const char *, const char *,
68299c4efeSeric     const char *, const char *);
69ac5aa4b9Seric 
7065c4fdfbSgilles SPLAY_HEAD(mta_relay_tree, mta_relay);
71ead27be3Seric static struct mta_relay *mta_relay(struct envelope *, struct relayhost *);
7265c4fdfbSgilles static void mta_relay_ref(struct mta_relay *);
7365c4fdfbSgilles static void mta_relay_unref(struct mta_relay *);
74f9a337f4Seric static void mta_relay_show(struct mta_relay *, struct mproc *, uint32_t, time_t);
7565c4fdfbSgilles static int mta_relay_cmp(const struct mta_relay *, const struct mta_relay *);
7665c4fdfbSgilles SPLAY_PROTOTYPE(mta_relay_tree, mta_relay, entry, mta_relay_cmp);
7765c4fdfbSgilles 
7865c4fdfbSgilles SPLAY_HEAD(mta_host_tree, mta_host);
7965c4fdfbSgilles static struct mta_host *mta_host(const struct sockaddr *);
8065c4fdfbSgilles static void mta_host_ref(struct mta_host *);
8165c4fdfbSgilles static void mta_host_unref(struct mta_host *);
8265c4fdfbSgilles static int mta_host_cmp(const struct mta_host *, const struct mta_host *);
8365c4fdfbSgilles SPLAY_PROTOTYPE(mta_host_tree, mta_host, entry, mta_host_cmp);
8465c4fdfbSgilles 
8565c4fdfbSgilles SPLAY_HEAD(mta_domain_tree, mta_domain);
8665c4fdfbSgilles static struct mta_domain *mta_domain(char *, int);
8765c4fdfbSgilles #if 0
8865c4fdfbSgilles static void mta_domain_ref(struct mta_domain *);
8965c4fdfbSgilles #endif
9065c4fdfbSgilles static void mta_domain_unref(struct mta_domain *);
9165c4fdfbSgilles static int mta_domain_cmp(const struct mta_domain *, const struct mta_domain *);
9265c4fdfbSgilles SPLAY_PROTOTYPE(mta_domain_tree, mta_domain, entry, mta_domain_cmp);
9365c4fdfbSgilles 
9465c4fdfbSgilles SPLAY_HEAD(mta_source_tree, mta_source);
9565c4fdfbSgilles static struct mta_source *mta_source(const struct sockaddr *);
9665c4fdfbSgilles static void mta_source_ref(struct mta_source *);
9765c4fdfbSgilles static void mta_source_unref(struct mta_source *);
9865c4fdfbSgilles static const char *mta_source_to_text(struct mta_source *);
9965c4fdfbSgilles static int mta_source_cmp(const struct mta_source *, const struct mta_source *);
10065c4fdfbSgilles SPLAY_PROTOTYPE(mta_source_tree, mta_source, entry, mta_source_cmp);
10165c4fdfbSgilles 
10265c4fdfbSgilles static struct mta_connector *mta_connector(struct mta_relay *,
10365c4fdfbSgilles     struct mta_source *);
10465c4fdfbSgilles static void mta_connector_free(struct mta_connector *);
10565c4fdfbSgilles static const char *mta_connector_to_text(struct mta_connector *);
10665c4fdfbSgilles 
10765c4fdfbSgilles SPLAY_HEAD(mta_route_tree, mta_route);
10865c4fdfbSgilles static struct mta_route *mta_route(struct mta_source *, struct mta_host *);
10965c4fdfbSgilles static void mta_route_ref(struct mta_route *);
11065c4fdfbSgilles static void mta_route_unref(struct mta_route *);
11165c4fdfbSgilles static const char *mta_route_to_text(struct mta_route *);
11265c4fdfbSgilles static int mta_route_cmp(const struct mta_route *, const struct mta_route *);
113ac5aa4b9Seric SPLAY_PROTOTYPE(mta_route_tree, mta_route, entry, mta_route_cmp);
114ac5aa4b9Seric 
1155b6a9ce9Seric struct mta_block {
1165b6a9ce9Seric 	SPLAY_ENTRY(mta_block)	 entry;
1175b6a9ce9Seric 	struct mta_source	*source;
1185b6a9ce9Seric 	char			*domain;
1195b6a9ce9Seric };
1205b6a9ce9Seric 
1215b6a9ce9Seric SPLAY_HEAD(mta_block_tree, mta_block);
1225b6a9ce9Seric void mta_block(struct mta_source *, char *);
1235b6a9ce9Seric void mta_unblock(struct mta_source *, char *);
1245b6a9ce9Seric int mta_is_blocked(struct mta_source *, char *);
1255b6a9ce9Seric static int mta_block_cmp(const struct mta_block *, const struct mta_block *);
1265b6a9ce9Seric SPLAY_PROTOTYPE(mta_block_tree, mta_block, entry, mta_block_cmp);
1275b6a9ce9Seric 
12841b8cf0bSmillert /*
12941b8cf0bSmillert  * This function is not publicy exported because it is a hack until libtls
13041b8cf0bSmillert  * has a proper privsep setup
13141b8cf0bSmillert  */
13241b8cf0bSmillert void tls_config_use_fake_private_key(struct tls_config *config);
13341b8cf0bSmillert 
13465c4fdfbSgilles static struct mta_relay_tree		relays;
13565c4fdfbSgilles static struct mta_domain_tree		domains;
13665c4fdfbSgilles static struct mta_host_tree		hosts;
13765c4fdfbSgilles static struct mta_source_tree		sources;
13865c4fdfbSgilles static struct mta_route_tree		routes;
1395b6a9ce9Seric static struct mta_block_tree		blocks;
14065c4fdfbSgilles 
14165c4fdfbSgilles static struct tree wait_mx;
14265c4fdfbSgilles static struct tree wait_preference;
14365c4fdfbSgilles static struct tree wait_secret;
144a8e22235Sgilles static struct tree wait_smarthost;
14565c4fdfbSgilles static struct tree wait_source;
146af241c42Seric static struct tree flush_evp;
147af241c42Seric static struct event ev_flush_evp;
148ac5aa4b9Seric 
149c5acbec8Seric static struct runq *runq_relay;
150c5acbec8Seric static struct runq *runq_connector;
151c5acbec8Seric static struct runq *runq_route;
152c5acbec8Seric static struct runq *runq_hoststat;
153c5acbec8Seric 
154c5acbec8Seric static time_t	max_seen_conndelay_route;
155c5acbec8Seric static time_t	max_seen_discdelay_route;
156c5acbec8Seric 
157c5acbec8Seric #define	HOSTSTAT_EXPIRE_DELAY	(4 * 3600)
158c5acbec8Seric struct hoststat {
159953aae25Sderaadt 	char			 name[HOST_NAME_MAX+1];
160c5acbec8Seric 	time_t			 tm;
161953aae25Sderaadt 	char			 error[LINE_MAX];
162c5acbec8Seric 	struct tree		 deferred;
163c5acbec8Seric };
164c5acbec8Seric static struct dict hoststat;
165c5acbec8Seric 
166c5acbec8Seric void mta_hoststat_update(const char *, const char *);
167c5acbec8Seric void mta_hoststat_cache(const char *, uint64_t);
168c5acbec8Seric void mta_hoststat_uncache(const char *, uint64_t);
169c5acbec8Seric void mta_hoststat_reschedule(const char *);
170c5acbec8Seric static void mta_hoststat_remove_entry(struct hoststat *);
171c5acbec8Seric 
172ac5aa4b9Seric void
mta_imsg(struct mproc * p,struct imsg * imsg)17365c4fdfbSgilles mta_imsg(struct mproc *p, struct imsg *imsg)
1743ef9cbf7Sgilles {
17565c4fdfbSgilles 	struct mta_relay	*relay;
17665c4fdfbSgilles 	struct mta_domain	*domain;
177f9a337f4Seric 	struct mta_host		*host;
178c5acbec8Seric 	struct mta_route	*route;
1795b6a9ce9Seric 	struct mta_block	*block;
18065c4fdfbSgilles 	struct mta_mx		*mx, *imx;
1815b6a9ce9Seric 	struct mta_source	*source;
182c5acbec8Seric 	struct hoststat		*hs;
18365c4fdfbSgilles 	struct sockaddr_storage	 ss;
184a8e22235Sgilles 	struct envelope		 evp, *e;
18565c4fdfbSgilles 	struct msg		 m;
18665c4fdfbSgilles 	const char		*secret;
187c5acbec8Seric 	const char		*hostname;
1885b6a9ce9Seric 	const char		*dom;
189a8e22235Sgilles 	const char		*smarthost;
19065c4fdfbSgilles 	uint64_t		 reqid;
191c5acbec8Seric 	time_t			 t;
192953aae25Sderaadt 	char			 buf[LINE_MAX];
19365c4fdfbSgilles 	int			 dnserror, preference, v, status;
194c5acbec8Seric 	void			*iter;
195c5acbec8Seric 	uint64_t		 u64;
196eb143ecfSjacekm 
197ed1929b6Sjacekm 	switch (imsg->hdr.type) {
198aa1d5973Seric 	case IMSG_QUEUE_TRANSFER:
19965c4fdfbSgilles 		m_msg(&m, imsg);
200399c053eSeric 		m_get_envelope(&m, &evp);
20165c4fdfbSgilles 		m_end(&m);
202a8e22235Sgilles 		mta_handle_envelope(&evp, NULL);
203ac5aa4b9Seric 		return;
204ac5aa4b9Seric 
205aa1d5973Seric 	case IMSG_MTA_OPEN_MESSAGE:
20665c4fdfbSgilles 		mta_session_imsg(p, imsg);
207b28a97afSjacekm 		return;
20865c4fdfbSgilles 
209aa1d5973Seric 	case IMSG_MTA_LOOKUP_CREDENTIALS:
21065c4fdfbSgilles 		m_msg(&m, imsg);
21165c4fdfbSgilles 		m_get_id(&m, &reqid);
21265c4fdfbSgilles 		m_get_string(&m, &secret);
21365c4fdfbSgilles 		m_end(&m);
21465c4fdfbSgilles 		relay = tree_xpop(&wait_secret, reqid);
215c5acbec8Seric 		mta_on_secret(relay, secret[0] ? secret : NULL);
21665c4fdfbSgilles 		return;
21765c4fdfbSgilles 
218aa1d5973Seric 	case IMSG_MTA_LOOKUP_SOURCE:
21965c4fdfbSgilles 		m_msg(&m, imsg);
22065c4fdfbSgilles 		m_get_id(&m, &reqid);
22165c4fdfbSgilles 		m_get_int(&m, &status);
222c5acbec8Seric 		if (status == LKA_OK)
22365c4fdfbSgilles 			m_get_sockaddr(&m, (struct sockaddr*)&ss);
22465c4fdfbSgilles 		m_end(&m);
22565c4fdfbSgilles 
226c5acbec8Seric 		relay = tree_xpop(&wait_source, reqid);
227c5acbec8Seric 		mta_on_source(relay, (status == LKA_OK) ?
228c5acbec8Seric 		    mta_source((struct sockaddr *)&ss) : NULL);
22965c4fdfbSgilles 		return;
23065c4fdfbSgilles 
231a8e22235Sgilles 	case IMSG_MTA_LOOKUP_SMARTHOST:
232a8e22235Sgilles 		m_msg(&m, imsg);
233a8e22235Sgilles 		m_get_id(&m, &reqid);
234a8e22235Sgilles 		m_get_int(&m, &status);
235a8e22235Sgilles 		smarthost = NULL;
236a8e22235Sgilles 		if (status == LKA_OK)
237a8e22235Sgilles 			m_get_string(&m, &smarthost);
238a8e22235Sgilles 		m_end(&m);
239a8e22235Sgilles 
240a8e22235Sgilles 		e = tree_xpop(&wait_smarthost, reqid);
241a8e22235Sgilles 		mta_on_smarthost(e, smarthost);
242a8e22235Sgilles 		return;
243a8e22235Sgilles 
244aa1d5973Seric 	case IMSG_MTA_LOOKUP_HELO:
24565c4fdfbSgilles 		mta_session_imsg(p, imsg);
24665c4fdfbSgilles 		return;
24765c4fdfbSgilles 
248aa1d5973Seric 	case IMSG_MTA_DNS_HOST:
24965c4fdfbSgilles 		m_msg(&m, imsg);
25065c4fdfbSgilles 		m_get_id(&m, &reqid);
251d6ee62d2Seric 		m_get_string(&m, &hostname);
25265c4fdfbSgilles 		m_get_sockaddr(&m, (struct sockaddr*)&ss);
25365c4fdfbSgilles 		m_get_int(&m, &preference);
25465c4fdfbSgilles 		m_end(&m);
25565c4fdfbSgilles 		domain = tree_xget(&wait_mx, reqid);
256118c16f3Sgilles 		mx = xcalloc(1, sizeof *mx);
257d6ee62d2Seric 		mx->mxname = xstrdup(hostname);
25865c4fdfbSgilles 		mx->host = mta_host((struct sockaddr*)&ss);
25965c4fdfbSgilles 		mx->preference = preference;
26065c4fdfbSgilles 		TAILQ_FOREACH(imx, &domain->mxs, entry) {
261e06daefdSeric 			if (imx->preference > mx->preference) {
26265c4fdfbSgilles 				TAILQ_INSERT_BEFORE(imx, mx, entry);
26365c4fdfbSgilles 				return;
26465c4fdfbSgilles 			}
26565c4fdfbSgilles 		}
26665c4fdfbSgilles 		TAILQ_INSERT_TAIL(&domain->mxs, mx, entry);
26765c4fdfbSgilles 		return;
26865c4fdfbSgilles 
269aa1d5973Seric 	case IMSG_MTA_DNS_HOST_END:
27065c4fdfbSgilles 		m_msg(&m, imsg);
27165c4fdfbSgilles 		m_get_id(&m, &reqid);
27265c4fdfbSgilles 		m_get_int(&m, &dnserror);
27365c4fdfbSgilles 		m_end(&m);
27465c4fdfbSgilles 		domain = tree_xpop(&wait_mx, reqid);
27565c4fdfbSgilles 		domain->mxstatus = dnserror;
27665c4fdfbSgilles 		if (domain->mxstatus == DNS_OK) {
27765c4fdfbSgilles 			log_debug("debug: MXs for domain %s:",
27865c4fdfbSgilles 			    domain->name);
27965c4fdfbSgilles 			TAILQ_FOREACH(mx, &domain->mxs, entry)
280d7bcae4dSeric 				log_debug("	%s preference %d",
28165c4fdfbSgilles 				    sa_to_text(mx->host->sa),
28265c4fdfbSgilles 				    mx->preference);
28365c4fdfbSgilles 		}
28465c4fdfbSgilles 		else {
28565c4fdfbSgilles 			log_debug("debug: Failed MX query for %s:",
28665c4fdfbSgilles 			    domain->name);
28765c4fdfbSgilles 		}
288afc7e0ecSeric 		domain->lastmxquery = time(NULL);
28965c4fdfbSgilles 		waitq_run(&domain->mxs, domain);
29065c4fdfbSgilles 		return;
29165c4fdfbSgilles 
292aa1d5973Seric 	case IMSG_MTA_DNS_MX_PREFERENCE:
29365c4fdfbSgilles 		m_msg(&m, imsg);
29465c4fdfbSgilles 		m_get_id(&m, &reqid);
29565c4fdfbSgilles 		m_get_int(&m, &dnserror);
296c5acbec8Seric 		if (dnserror == 0)
297c5acbec8Seric 			m_get_int(&m, &preference);
29865c4fdfbSgilles 		m_end(&m);
299c5acbec8Seric 
300c5acbec8Seric 		relay = tree_xpop(&wait_preference, reqid);
301bc504fceSeric 		if (dnserror) {
302bc504fceSeric 			log_warnx("warn: Couldn't find backup "
303bc504fceSeric 			    "preference for %s: error %d",
304bc504fceSeric 			    mta_relay_to_text(relay), dnserror);
305bc504fceSeric 			preference = INT_MAX;
306bc504fceSeric 		}
307bc504fceSeric 		mta_on_preference(relay, preference);
30865c4fdfbSgilles 		return;
30965c4fdfbSgilles 
310c5acbec8Seric 	case IMSG_CTL_RESUME_ROUTE:
311c5acbec8Seric 		u64 = *((uint64_t *)imsg->data);
312c5acbec8Seric 		if (u64)
313c5acbec8Seric 			log_debug("resuming route: %llu",
314c5acbec8Seric 			    (unsigned long long)u64);
315c5acbec8Seric 		else
316c5acbec8Seric 			log_debug("resuming all routes");
317c5acbec8Seric 		SPLAY_FOREACH(route, mta_route_tree, &routes) {
318c5acbec8Seric 			if (u64 && route->id != u64)
319c5acbec8Seric 				continue;
32022cff71cSeric 
32122cff71cSeric 			if (route->flags & ROUTE_DISABLED) {
32222cff71cSeric 				log_info("smtp-out: Enabling route %s per admin request",
32322cff71cSeric 				    mta_route_to_text(route));
32428efdb08Seric 				if (!runq_cancel(runq_route, route)) {
32522cff71cSeric 					log_warnx("warn: route not on runq");
32622cff71cSeric 					fatalx("exiting");
32722cff71cSeric 				}
32822cff71cSeric 				route->flags &= ~ROUTE_DISABLED;
32922cff71cSeric 				route->flags |= ROUTE_NEW;
33022cff71cSeric 				route->nerror = 0;
33122cff71cSeric 				route->penalty = 0;
33222cff71cSeric 				mta_route_unref(route); /* from mta_route_disable */
33322cff71cSeric 			}
33422cff71cSeric 
335c5acbec8Seric 			if (u64)
336c5acbec8Seric 				break;
337c5acbec8Seric 		}
338c5acbec8Seric 		return;
339c5acbec8Seric 
340f9a337f4Seric 	case IMSG_CTL_MTA_SHOW_HOSTS:
341f9a337f4Seric 		t = time(NULL);
342f9a337f4Seric 		SPLAY_FOREACH(host, mta_host_tree, &hosts) {
343769fc316Sgilles 			(void)snprintf(buf, sizeof(buf),
344f9a337f4Seric 			    "%s %s refcount=%d nconn=%zu lastconn=%s",
345f9a337f4Seric 			    sockaddr_to_text(host->sa),
346f9a337f4Seric 			    host->ptrname,
347f9a337f4Seric 			    host->refcount,
348f9a337f4Seric 			    host->nconn,
349f9a337f4Seric 			    host->lastconn ? duration_to_text(t - host->lastconn) : "-");
350f9a337f4Seric 			m_compose(p, IMSG_CTL_MTA_SHOW_HOSTS,
351f9a337f4Seric 			    imsg->hdr.peerid, 0, -1,
352f9a337f4Seric 			    buf, strlen(buf) + 1);
353f9a337f4Seric 		}
354f9a337f4Seric 		m_compose(p, IMSG_CTL_MTA_SHOW_HOSTS, imsg->hdr.peerid,
355f9a337f4Seric 		    0, -1, NULL, 0);
356f9a337f4Seric 		return;
357f9a337f4Seric 
358f9a337f4Seric 	case IMSG_CTL_MTA_SHOW_RELAYS:
359f9a337f4Seric 		t = time(NULL);
360f9a337f4Seric 		SPLAY_FOREACH(relay, mta_relay_tree, &relays)
361f9a337f4Seric 			mta_relay_show(relay, p, imsg->hdr.peerid, t);
362f9a337f4Seric 		m_compose(p, IMSG_CTL_MTA_SHOW_RELAYS, imsg->hdr.peerid,
363f9a337f4Seric 		    0, -1, NULL, 0);
364f9a337f4Seric 		return;
365f9a337f4Seric 
366c5acbec8Seric 	case IMSG_CTL_MTA_SHOW_ROUTES:
367c5acbec8Seric 		SPLAY_FOREACH(route, mta_route_tree, &routes) {
36828efdb08Seric 			v = runq_pending(runq_route, route, &t);
369769fc316Sgilles 			(void)snprintf(buf, sizeof(buf),
3705d758dd0Seric 			    "%llu. %s %c%c%c%c nconn=%zu nerror=%d penalty=%d timeout=%s",
371c5acbec8Seric 			    (unsigned long long)route->id,
372c5acbec8Seric 			    mta_route_to_text(route),
373c5acbec8Seric 			    route->flags & ROUTE_NEW ? 'N' : '-',
374c5acbec8Seric 			    route->flags & ROUTE_DISABLED ? 'D' : '-',
375c5acbec8Seric 			    route->flags & ROUTE_RUNQ ? 'Q' : '-',
376c5acbec8Seric 			    route->flags & ROUTE_KEEPALIVE ? 'K' : '-',
377c5acbec8Seric 			    route->nconn,
3785d758dd0Seric 			    route->nerror,
379c5acbec8Seric 			    route->penalty,
380c5acbec8Seric 			    v ? duration_to_text(t - time(NULL)) : "-");
381c5acbec8Seric 			m_compose(p, IMSG_CTL_MTA_SHOW_ROUTES,
382c5acbec8Seric 			    imsg->hdr.peerid, 0, -1,
383c5acbec8Seric 			    buf, strlen(buf) + 1);
384c5acbec8Seric 		}
385c5acbec8Seric 		m_compose(p, IMSG_CTL_MTA_SHOW_ROUTES, imsg->hdr.peerid,
386c5acbec8Seric 		    0, -1, NULL, 0);
387c5acbec8Seric 		return;
3885d758dd0Seric 
389c5acbec8Seric 	case IMSG_CTL_MTA_SHOW_HOSTSTATS:
390c5acbec8Seric 		iter = NULL;
391c5acbec8Seric 		while (dict_iter(&hoststat, &iter, &hostname,
392c5acbec8Seric 			(void **)&hs)) {
393769fc316Sgilles 			(void)snprintf(buf, sizeof(buf),
394c5acbec8Seric 			    "%s|%llu|%s",
395c5acbec8Seric 			    hostname, (unsigned long long) hs->tm,
396c5acbec8Seric 			    hs->error);
397c5acbec8Seric 			m_compose(p, IMSG_CTL_MTA_SHOW_HOSTSTATS,
398c5acbec8Seric 			    imsg->hdr.peerid, 0, -1,
399c5acbec8Seric 			    buf, strlen(buf) + 1);
400c5acbec8Seric 		}
401c5acbec8Seric 		m_compose(p, IMSG_CTL_MTA_SHOW_HOSTSTATS,
402c5acbec8Seric 		    imsg->hdr.peerid,
403c5acbec8Seric 		    0, -1, NULL, 0);
404c5acbec8Seric 		return;
4055b6a9ce9Seric 
4065b6a9ce9Seric 	case IMSG_CTL_MTA_BLOCK:
4075b6a9ce9Seric 		m_msg(&m, imsg);
4085b6a9ce9Seric 		m_get_sockaddr(&m, (struct sockaddr*)&ss);
4095b6a9ce9Seric 		m_get_string(&m, &dom);
4105b6a9ce9Seric 		m_end(&m);
4115b6a9ce9Seric 		source = mta_source((struct sockaddr*)&ss);
412e3317d18Smmcc 		if (*dom != '\0') {
413769fc316Sgilles 			if (!(strlcpy(buf, dom, sizeof(buf))
414769fc316Sgilles 				>= sizeof(buf)))
4155b6a9ce9Seric 				mta_block(source, buf);
4165b6a9ce9Seric 		}
4175b6a9ce9Seric 		else
4185b6a9ce9Seric 			mta_block(source, NULL);
4195b6a9ce9Seric 		mta_source_unref(source);
4205b6a9ce9Seric 		m_compose(p, IMSG_CTL_OK, imsg->hdr.peerid, 0, -1, NULL, 0);
4215b6a9ce9Seric 		return;
4225b6a9ce9Seric 
4235b6a9ce9Seric 	case IMSG_CTL_MTA_UNBLOCK:
4245b6a9ce9Seric 		m_msg(&m, imsg);
4255b6a9ce9Seric 		m_get_sockaddr(&m, (struct sockaddr*)&ss);
4265b6a9ce9Seric 		m_get_string(&m, &dom);
4275b6a9ce9Seric 		m_end(&m);
4285b6a9ce9Seric 		source = mta_source((struct sockaddr*)&ss);
429e3317d18Smmcc 		if (*dom != '\0') {
430769fc316Sgilles 			if (!(strlcpy(buf, dom, sizeof(buf))
431769fc316Sgilles 				>= sizeof(buf)))
4325b6a9ce9Seric 				mta_unblock(source, buf);
4335b6a9ce9Seric 		}
4345b6a9ce9Seric 		else
4355b6a9ce9Seric 			mta_unblock(source, NULL);
4365b6a9ce9Seric 		mta_source_unref(source);
4375b6a9ce9Seric 		m_compose(p, IMSG_CTL_OK, imsg->hdr.peerid, 0, -1, NULL, 0);
4385b6a9ce9Seric 		return;
4395b6a9ce9Seric 
4405b6a9ce9Seric 	case IMSG_CTL_MTA_SHOW_BLOCK:
4415b6a9ce9Seric 		SPLAY_FOREACH(block, mta_block_tree, &blocks) {
442769fc316Sgilles 			(void)snprintf(buf, sizeof(buf), "%s -> %s",
4435b6a9ce9Seric 			    mta_source_to_text(block->source),
4445b6a9ce9Seric 			    block->domain ? block->domain : "*");
4455b6a9ce9Seric 			m_compose(p, IMSG_CTL_MTA_SHOW_BLOCK,
4465b6a9ce9Seric 			    imsg->hdr.peerid, 0, -1, buf, strlen(buf) + 1);
4475b6a9ce9Seric 		}
4485b6a9ce9Seric 		m_compose(p, IMSG_CTL_MTA_SHOW_BLOCK, imsg->hdr.peerid,
4495b6a9ce9Seric 		    0, -1, NULL, 0);
4505b6a9ce9Seric 		return;
451c5acbec8Seric 	}
452c5acbec8Seric 
453ff01b044Seric 	fatalx("mta_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
454ed1929b6Sjacekm }
455ed1929b6Sjacekm 
456aa1d5973Seric void
mta_postfork(void)457aa1d5973Seric mta_postfork(void)
458ed1929b6Sjacekm {
459eed85469Seric 	struct dispatcher *dispatcher;
460eed85469Seric 	const char *key;
461eed85469Seric 	void *iter;
462eed85469Seric 
463eed85469Seric 	iter = NULL;
464eed85469Seric 	while (dict_iter(env->sc_dispatchers, &iter, &key, (void **)&dispatcher)) {
465eed85469Seric 		log_debug("%s: %s", __func__, key);
466eed85469Seric 		mta_setup_dispatcher(dispatcher);
467eed85469Seric 	}
468eed85469Seric }
469eed85469Seric 
470eed85469Seric static void
mta_setup_dispatcher(struct dispatcher * dispatcher)471eed85469Seric mta_setup_dispatcher(struct dispatcher *dispatcher)
472eed85469Seric {
473eed85469Seric 	struct dispatcher_remote *remote;
474eed85469Seric 	static const char *dheparams[] = { "none", "auto", "legacy" };
475eed85469Seric 	struct tls_config *config;
476eed85469Seric 	struct pki *pki;
477eed85469Seric 	struct ca *ca;
478a2bac8cfSeric 	const char *ciphers;
47932bd51d0Seric 	uint32_t protos;
480eed85469Seric 
481eed85469Seric 	if (dispatcher->type != DISPATCHER_REMOTE)
482eed85469Seric 		return;
483eed85469Seric 
484eed85469Seric 	remote = &dispatcher->u.remote;
485eed85469Seric 
486eed85469Seric 	if ((config = tls_config_new()) == NULL)
487eed85469Seric 		fatal("smtpd: tls_config_new");
488eed85469Seric 
489a2bac8cfSeric 	ciphers = env->sc_tls_ciphers;
490a2bac8cfSeric 	if (remote->tls_ciphers)
491a2bac8cfSeric 		ciphers = remote->tls_ciphers;
492a2bac8cfSeric 	if (ciphers && tls_config_set_ciphers(config, ciphers) == -1)
49362e04d05Sop 		fatalx("%s", tls_config_error(config));
49432bd51d0Seric 
495a040621aSeric 	if (remote->tls_protocols) {
496a040621aSeric 		if (tls_config_parse_protocols(&protos,
497a040621aSeric 		    remote->tls_protocols) == -1)
49862e04d05Sop 			fatalx("failed to parse protocols \"%s\"",
499a040621aSeric 			    remote->tls_protocols);
500a040621aSeric 		if (tls_config_set_protocols(config, protos) == -1)
50162e04d05Sop 			fatalx("%s", tls_config_error(config));
502a040621aSeric 	}
503eed85469Seric 
504eed85469Seric 	if (remote->pki) {
505eed85469Seric 		pki = dict_get(env->sc_pki_dict, remote->pki);
506eed85469Seric 		if (pki == NULL)
50762e04d05Sop 			fatalx("client pki \"%s\" not found", remote->pki);
508eed85469Seric 
509eed85469Seric 		tls_config_set_dheparams(config, dheparams[pki->pki_dhe]);
51041b8cf0bSmillert 		tls_config_use_fake_private_key(config);
511eed85469Seric 		if (tls_config_set_keypair_mem(config, pki->pki_cert,
512eed85469Seric 		    pki->pki_cert_len, NULL, 0) == -1)
51362e04d05Sop 			fatalx("tls_config_set_keypair_mem: %s",
51462e04d05Sop 			    tls_config_error(config));
515eed85469Seric 	}
516eed85469Seric 
517eed85469Seric 	if (remote->ca) {
518eed85469Seric 		ca = dict_get(env->sc_ca_dict, remote->ca);
519eed85469Seric 		if (tls_config_set_ca_mem(config, ca->ca_cert, ca->ca_cert_len)
520eed85469Seric 		    == -1)
52162e04d05Sop 			fatalx("tls_config_set_ca_mem: %s",
52262e04d05Sop 			    tls_config_error(config));
523eed85469Seric 	}
524eed85469Seric 	else if (tls_config_set_ca_file(config, tls_default_ca_cert_file())
525eed85469Seric 	    == -1)
52662e04d05Sop 		fatalx("tls_config_set_ca_file: %s",
52762e04d05Sop 		    tls_config_error(config));
528eed85469Seric 
52989818320Smillert 	if (remote->tls_verify) {
53089818320Smillert 		tls_config_verify(config);
53189818320Smillert 	} else {
532eed85469Seric 		tls_config_insecure_noverifycert(config);
533eed85469Seric 		tls_config_insecure_noverifyname(config);
534eed85469Seric 		tls_config_insecure_noverifytime(config);
535eed85469Seric 	}
536eed85469Seric 
537eed85469Seric 	remote->tls_config = config;
538af8de4b6Sgilles }
539af8de4b6Sgilles 
540aa1d5973Seric void
mta_postprivdrop(void)541aa1d5973Seric mta_postprivdrop(void)
5423ef9cbf7Sgilles {
54365c4fdfbSgilles 	SPLAY_INIT(&relays);
54465c4fdfbSgilles 	SPLAY_INIT(&domains);
54565c4fdfbSgilles 	SPLAY_INIT(&hosts);
54665c4fdfbSgilles 	SPLAY_INIT(&sources);
54765c4fdfbSgilles 	SPLAY_INIT(&routes);
5485b6a9ce9Seric 	SPLAY_INIT(&blocks);
54965c4fdfbSgilles 
55065c4fdfbSgilles 	tree_init(&wait_secret);
551a8e22235Sgilles 	tree_init(&wait_smarthost);
55265c4fdfbSgilles 	tree_init(&wait_mx);
55365c4fdfbSgilles 	tree_init(&wait_preference);
55465c4fdfbSgilles 	tree_init(&wait_source);
555af241c42Seric 	tree_init(&flush_evp);
556c5acbec8Seric 	dict_init(&hoststat);
55765c4fdfbSgilles 
558af241c42Seric 	evtimer_set(&ev_flush_evp, mta_delivery_flush_event, NULL);
559af241c42Seric 
560c5acbec8Seric 	runq_init(&runq_relay, mta_on_timeout);
561c5acbec8Seric 	runq_init(&runq_connector, mta_on_timeout);
562c5acbec8Seric 	runq_init(&runq_route, mta_on_timeout);
563c5acbec8Seric 	runq_init(&runq_hoststat, mta_on_timeout);
5643ef9cbf7Sgilles }
565ac5aa4b9Seric 
566aa1d5973Seric 
56765c4fdfbSgilles /*
56865c4fdfbSgilles  * Local error on the given source.
56965c4fdfbSgilles  */
57065c4fdfbSgilles void
mta_source_error(struct mta_relay * relay,struct mta_route * route,const char * e)57165c4fdfbSgilles mta_source_error(struct mta_relay *relay, struct mta_route *route, const char *e)
572ac5aa4b9Seric {
57365c4fdfbSgilles 	struct mta_connector	*c;
574c5acbec8Seric 
57565c4fdfbSgilles 	/*
57665c4fdfbSgilles 	 * Remember the source as broken for this connector.
57765c4fdfbSgilles 	 */
57865c4fdfbSgilles 	c = mta_connector(relay, route->src);
579c5acbec8Seric 	if (!(c->flags & CONNECTOR_ERROR_SOURCE))
580c5acbec8Seric 		log_info("smtp-out: Error on %s: %s",
581c5acbec8Seric 		    mta_route_to_text(route), e);
582c5acbec8Seric 	c->flags |= CONNECTOR_ERROR_SOURCE;
583ac5aa4b9Seric }
584ac5aa4b9Seric 
58565c4fdfbSgilles void
mta_route_error(struct mta_relay * relay,struct mta_route * route)5860cf935dfSgilles mta_route_error(struct mta_relay *relay, struct mta_route *route)
587ac5aa4b9Seric {
588d6b3bcf4Seric #if 0
5895d758dd0Seric 	route->nerror += 1;
590ac5aa4b9Seric 
5915d758dd0Seric 	if (route->nerror > MAXERROR_PER_ROUTE) {
5925d758dd0Seric 		log_info("smtp-out: Too many errors on %s: "
5935d758dd0Seric 		    "disabling for a while", mta_route_to_text(route));
5945d758dd0Seric 		mta_route_disable(route, 2, ROUTE_DISABLED_SMTP);
59565c4fdfbSgilles 	}
596d6b3bcf4Seric #endif
597ac5aa4b9Seric }
598ac5aa4b9Seric 
599ac5aa4b9Seric void
mta_route_ok(struct mta_relay * relay,struct mta_route * route)60065c4fdfbSgilles mta_route_ok(struct mta_relay *relay, struct mta_route *route)
601ac5aa4b9Seric {
60265c4fdfbSgilles 	struct mta_connector	*c;
60365c4fdfbSgilles 
604c5acbec8Seric 	if (!(route->flags & ROUTE_NEW))
605c5acbec8Seric 		return;
60665c4fdfbSgilles 
607c5acbec8Seric 	log_debug("debug: mta-routing: route %s is now valid.",
608c5acbec8Seric 	    mta_route_to_text(route));
609c5acbec8Seric 
6105d758dd0Seric 	route->nerror = 0;
611c5acbec8Seric 	route->flags &= ~ROUTE_NEW;
61265c4fdfbSgilles 
61365c4fdfbSgilles 	c = mta_connector(relay, route->src);
614c5acbec8Seric 	mta_connect(c);
615c5acbec8Seric }
616c5acbec8Seric 
617c5acbec8Seric void
mta_route_down(struct mta_relay * relay,struct mta_route * route)618c5acbec8Seric mta_route_down(struct mta_relay *relay, struct mta_route *route)
619c5acbec8Seric {
620d6b3bcf4Seric #if 0
621c5acbec8Seric 	mta_route_disable(route, 2, ROUTE_DISABLED_SMTP);
622d6b3bcf4Seric #endif
623ac5aa4b9Seric }
624ac5aa4b9Seric 
625ac5aa4b9Seric void
mta_route_collect(struct mta_relay * relay,struct mta_route * route)62665c4fdfbSgilles mta_route_collect(struct mta_relay *relay, struct mta_route *route)
627ac5aa4b9Seric {
62865c4fdfbSgilles 	struct mta_connector	*c;
62965c4fdfbSgilles 
630c5acbec8Seric 	log_debug("debug: mta_route_collect(%s)",
631c5acbec8Seric 	    mta_route_to_text(route));
63265c4fdfbSgilles 
63365c4fdfbSgilles 	relay->nconn -= 1;
634c5acbec8Seric 	relay->domain->nconn -= 1;
63565c4fdfbSgilles 	route->nconn -= 1;
63665c4fdfbSgilles 	route->src->nconn -= 1;
63765c4fdfbSgilles 	route->dst->nconn -= 1;
638c5acbec8Seric 	route->lastdisc = time(NULL);
639c5acbec8Seric 
640c5acbec8Seric 	/* First connection failed */
641c5acbec8Seric 	if (route->flags & ROUTE_NEW)
6422aa70c64Sgilles 		mta_route_disable(route, 1, ROUTE_DISABLED_NET);
64365c4fdfbSgilles 
64465c4fdfbSgilles 	c = mta_connector(relay, route->src);
64565c4fdfbSgilles 	c->nconn -= 1;
646c5acbec8Seric 	mta_connect(c);
647c5acbec8Seric 	mta_route_unref(route); /* from mta_find_route() */
64865c4fdfbSgilles 	mta_relay_unref(relay); /* from mta_connect() */
64965c4fdfbSgilles }
65065c4fdfbSgilles 
65165c4fdfbSgilles struct mta_task *
mta_route_next_task(struct mta_relay * relay,struct mta_route * route)65265c4fdfbSgilles mta_route_next_task(struct mta_relay *relay, struct mta_route *route)
65365c4fdfbSgilles {
65465c4fdfbSgilles 	struct mta_task	*task;
65565c4fdfbSgilles 
65665c4fdfbSgilles 	if ((task = TAILQ_FIRST(&relay->tasks))) {
65765c4fdfbSgilles 		TAILQ_REMOVE(&relay->tasks, task, entry);
65865c4fdfbSgilles 		relay->ntask -= 1;
65965c4fdfbSgilles 		task->relay = NULL;
6606dc81a07Seric 
6616dc81a07Seric 		/* When the number of tasks is down to lowat, query some evp */
6626dc81a07Seric 		if (relay->ntask == (size_t)relay->limits->task_lowat) {
6636dc81a07Seric 			if (relay->state & RELAY_ONHOLD) {
6646dc81a07Seric 				log_info("smtp-out: back to lowat on %s: releasing",
6656dc81a07Seric 				    mta_relay_to_text(relay));
6666dc81a07Seric 				relay->state &= ~RELAY_ONHOLD;
6676dc81a07Seric 			}
668704aa3edSeric 			if (relay->state & RELAY_HOLDQ) {
669aa1d5973Seric 				m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1);
6706dc81a07Seric 				m_add_id(p_queue, relay->id);
6716dc81a07Seric 				m_add_int(p_queue, relay->limits->task_release);
6726dc81a07Seric 				m_close(p_queue);
6736dc81a07Seric 			}
674704aa3edSeric 		}
675704aa3edSeric 		else if (relay->ntask == 0 && relay->state & RELAY_HOLDQ) {
676aa1d5973Seric 			m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1);
6776dc81a07Seric 			m_add_id(p_queue, relay->id);
6786dc81a07Seric 			m_add_int(p_queue, 0);
6796dc81a07Seric 			m_close(p_queue);
6806dc81a07Seric 		}
68165c4fdfbSgilles 	}
68265c4fdfbSgilles 
68365c4fdfbSgilles 	return (task);
684ac5aa4b9Seric }
685ac5aa4b9Seric 
686af241c42Seric static void
mta_handle_envelope(struct envelope * evp,const char * smarthost)687a8e22235Sgilles mta_handle_envelope(struct envelope *evp, const char *smarthost)
688ffad4ff8Seric {
689ffad4ff8Seric 	struct mta_relay	*relay;
690ffad4ff8Seric 	struct mta_task		*task;
691ffad4ff8Seric 	struct mta_envelope	*e;
692a8e22235Sgilles 	struct dispatcher	*dispatcher;
6938cfb7dbcSgilles 	struct mailaddr		 maddr;
694ead27be3Seric 	struct relayhost	 relayh;
6959c44f234Seric 	char			 buf[LINE_MAX];
696a8e22235Sgilles 
697a8e22235Sgilles 	dispatcher = dict_xget(env->sc_dispatchers, evp->dispatcher);
698a8e22235Sgilles 	if (dispatcher->u.remote.smarthost && smarthost == NULL) {
699a8e22235Sgilles 		mta_query_smarthost(evp);
700a8e22235Sgilles 		return;
701a8e22235Sgilles 	}
702a8e22235Sgilles 
703ead27be3Seric 	memset(&relayh, 0, sizeof(relayh));
7044c503616Seric 	relayh.tls = RELAY_TLS_OPPORTUNISTIC;
705ead27be3Seric 	if (smarthost && !text_to_relayhost(&relayh, smarthost)) {
706150c99b2Seric 		log_warnx("warn: Failed to parse smarthost %s", smarthost);
707a8e22235Sgilles 		m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1);
708a8e22235Sgilles 		m_add_evpid(p_queue, evp->id);
709a8e22235Sgilles 		m_add_string(p_queue, "Cannot parse smarthost");
710a8e22235Sgilles 		m_add_int(p_queue, ESC_OTHER_STATUS);
711a8e22235Sgilles 		m_close(p_queue);
712c06eb656Seric 		return;
713a8e22235Sgilles 	}
714ffad4ff8Seric 
7154b9d2e57Seric 	if (relayh.flags & RELAY_AUTH && dispatcher->u.remote.auth == NULL) {
7164b9d2e57Seric 		log_warnx("warn: No auth table on action \"%s\" for relay %s",
7174b9d2e57Seric 		    evp->dispatcher, smarthost);
7184b9d2e57Seric 		m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1);
7194b9d2e57Seric 		m_add_evpid(p_queue, evp->id);
7204b9d2e57Seric 		m_add_string(p_queue, "No auth table for relaying");
7214b9d2e57Seric 		m_add_int(p_queue, ESC_OTHER_STATUS);
7224b9d2e57Seric 		m_close(p_queue);
7234b9d2e57Seric 		return;
7244b9d2e57Seric 	}
7254b9d2e57Seric 
7261aa03b06Seric 	if (dispatcher->u.remote.tls_required) {
7271aa03b06Seric 		/* Reject relay if smtp+notls:// is requested */
7281aa03b06Seric 		if (relayh.tls == RELAY_TLS_NO) {
7291aa03b06Seric 			log_warnx("warn: TLS required for action \"%s\"",
7301aa03b06Seric 			    evp->dispatcher);
7311aa03b06Seric 			m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1);
7321aa03b06Seric 			m_add_evpid(p_queue, evp->id);
7331aa03b06Seric 			m_add_string(p_queue, "TLS required for relaying");
7341aa03b06Seric 			m_add_int(p_queue, ESC_OTHER_STATUS);
7351aa03b06Seric 			m_close(p_queue);
7361aa03b06Seric 			return;
7371aa03b06Seric 		}
7381aa03b06Seric 		/* Update smtp:// to smtp+tls:// */
7391aa03b06Seric 		if (relayh.tls == RELAY_TLS_OPPORTUNISTIC)
7401aa03b06Seric 			relayh.tls = RELAY_TLS_STARTTLS;
7411aa03b06Seric 	}
7421aa03b06Seric 
743ead27be3Seric 	relay = mta_relay(evp, &relayh);
744ffad4ff8Seric 	/* ignore if we don't know the limits yet */
745ffad4ff8Seric 	if (relay->limits &&
746ffad4ff8Seric 	    relay->ntask >= (size_t)relay->limits->task_hiwat) {
747ffad4ff8Seric 		if (!(relay->state & RELAY_ONHOLD)) {
748ffad4ff8Seric 			log_info("smtp-out: hiwat reached on %s: holding envelopes",
749ffad4ff8Seric 			    mta_relay_to_text(relay));
750ffad4ff8Seric 			relay->state |= RELAY_ONHOLD;
751ffad4ff8Seric 		}
752ffad4ff8Seric 	}
753ffad4ff8Seric 
754ffad4ff8Seric 	/*
755ffad4ff8Seric 	 * If the relay has too many pending tasks, tell the
756ffad4ff8Seric 	 * scheduler to hold it until further notice
757ffad4ff8Seric 	 */
758ffad4ff8Seric 	if (relay->state & RELAY_ONHOLD) {
759ffad4ff8Seric 		relay->state |= RELAY_HOLDQ;
760ffad4ff8Seric 		m_create(p_queue, IMSG_MTA_DELIVERY_HOLD, 0, 0, -1);
761ffad4ff8Seric 		m_add_evpid(p_queue, evp->id);
762ffad4ff8Seric 		m_add_id(p_queue, relay->id);
763ffad4ff8Seric 		m_close(p_queue);
764ffad4ff8Seric 		mta_relay_unref(relay); /* from here */
765ffad4ff8Seric 		return;
766ffad4ff8Seric 	}
767ffad4ff8Seric 
768ffad4ff8Seric 	task = NULL;
769ffad4ff8Seric 	TAILQ_FOREACH(task, &relay->tasks, entry)
770ffad4ff8Seric 		if (task->msgid == evpid_to_msgid(evp->id))
771ffad4ff8Seric 			break;
772ffad4ff8Seric 
773ffad4ff8Seric 	if (task == NULL) {
774118c16f3Sgilles 		task = xmalloc(sizeof *task);
775ffad4ff8Seric 		TAILQ_INIT(&task->envelopes);
776ffad4ff8Seric 		task->relay = relay;
777ffad4ff8Seric 		relay->ntask += 1;
778ffad4ff8Seric 		TAILQ_INSERT_TAIL(&relay->tasks, task, entry);
779ffad4ff8Seric 		task->msgid = evpid_to_msgid(evp->id);
780ffad4ff8Seric 		if (evp->sender.user[0] || evp->sender.domain[0])
781ffad4ff8Seric 			(void)snprintf(buf, sizeof buf, "%s@%s",
782ffad4ff8Seric 			    evp->sender.user, evp->sender.domain);
783ffad4ff8Seric 		else
784ffad4ff8Seric 			buf[0] = '\0';
7858cfb7dbcSgilles 
7868cfb7dbcSgilles 		if (dispatcher->u.remote.mail_from && evp->sender.user[0]) {
7878cfb7dbcSgilles 			memset(&maddr, 0, sizeof (maddr));
7888cfb7dbcSgilles 			if (text_to_mailaddr(&maddr,
7898cfb7dbcSgilles 				dispatcher->u.remote.mail_from)) {
7908cfb7dbcSgilles 				(void)snprintf(buf, sizeof buf, "%s@%s",
7918cfb7dbcSgilles 				    maddr.user[0] ? maddr.user : evp->sender.user,
7928cfb7dbcSgilles 				    maddr.domain[0] ? maddr.domain : evp->sender.domain);
7938cfb7dbcSgilles 			}
7948cfb7dbcSgilles 		}
7958cfb7dbcSgilles 
796118c16f3Sgilles 		task->sender = xstrdup(buf);
797ffad4ff8Seric 		stat_increment("mta.task", 1);
798ffad4ff8Seric 	}
799ffad4ff8Seric 
800118c16f3Sgilles 	e = xcalloc(1, sizeof *e);
801ffad4ff8Seric 	e->id = evp->id;
802ffad4ff8Seric 	e->creation = evp->creation;
803118c16f3Sgilles 	e->smtpname = xstrdup(evp->smtpname);
804ffad4ff8Seric 	(void)snprintf(buf, sizeof buf, "%s@%s",
805ffad4ff8Seric 	    evp->dest.user, evp->dest.domain);
806118c16f3Sgilles 	e->dest = xstrdup(buf);
807ffad4ff8Seric 	(void)snprintf(buf, sizeof buf, "%s@%s",
808ffad4ff8Seric 	    evp->rcpt.user, evp->rcpt.domain);
809ffad4ff8Seric 	if (strcmp(buf, e->dest))
810118c16f3Sgilles 		e->rcpt = xstrdup(buf);
811ffad4ff8Seric 	e->task = task;
812cd8603dbSop 	if (evp->dsn_orcpt[0] != '\0')
813cd8603dbSop 		e->dsn_orcpt = xstrdup(evp->dsn_orcpt);
814ffad4ff8Seric 	(void)strlcpy(e->dsn_envid, evp->dsn_envid,
815ffad4ff8Seric 	    sizeof e->dsn_envid);
816ffad4ff8Seric 	e->dsn_notify = evp->dsn_notify;
817ffad4ff8Seric 	e->dsn_ret = evp->dsn_ret;
818ffad4ff8Seric 
819ffad4ff8Seric 	TAILQ_INSERT_TAIL(&task->envelopes, e, entry);
820ffad4ff8Seric 	log_debug("debug: mta: received evp:%016" PRIx64
821ffad4ff8Seric 	    " for <%s>", e->id, e->dest);
822ffad4ff8Seric 
823ffad4ff8Seric 	stat_increment("mta.envelope", 1);
824ffad4ff8Seric 
825ffad4ff8Seric 	mta_drain(relay);
826ffad4ff8Seric 	mta_relay_unref(relay); /* from here */
827ffad4ff8Seric }
828ffad4ff8Seric 
829ffad4ff8Seric static void
mta_delivery_flush_event(int fd,short event,void * arg)830af241c42Seric mta_delivery_flush_event(int fd, short event, void *arg)
831af241c42Seric {
832af241c42Seric 	struct mta_envelope	*e;
833af241c42Seric 	struct timeval		 tv;
834af241c42Seric 
835af241c42Seric 	if (tree_poproot(&flush_evp, NULL, (void**)(&e))) {
836af241c42Seric 
837aa1d5973Seric 		if (e->delivery == IMSG_MTA_DELIVERY_OK) {
838aa1d5973Seric 			m_create(p_queue, IMSG_MTA_DELIVERY_OK, 0, 0, -1);
839fe95d8d0Seric 			m_add_evpid(p_queue, e->id);
840fe95d8d0Seric 			m_add_int(p_queue, e->ext);
841fe95d8d0Seric 			m_close(p_queue);
842aa1d5973Seric 		} else if (e->delivery == IMSG_MTA_DELIVERY_TEMPFAIL) {
843aa1d5973Seric 			m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1);
844aa1d5973Seric 			m_add_evpid(p_queue, e->id);
845aa1d5973Seric 			m_add_string(p_queue, e->status);
846aa1d5973Seric 			m_add_int(p_queue, ESC_OTHER_STATUS);
847aa1d5973Seric 			m_close(p_queue);
848aa1d5973Seric 		}
849aa1d5973Seric 		else if (e->delivery == IMSG_MTA_DELIVERY_PERMFAIL) {
850aa1d5973Seric 			m_create(p_queue, IMSG_MTA_DELIVERY_PERMFAIL, 0, 0, -1);
851aa1d5973Seric 			m_add_evpid(p_queue, e->id);
852aa1d5973Seric 			m_add_string(p_queue, e->status);
853aa1d5973Seric 			m_add_int(p_queue, ESC_OTHER_STATUS);
854aa1d5973Seric 			m_close(p_queue);
855aa1d5973Seric 		}
856aa1d5973Seric 		else if (e->delivery == IMSG_MTA_DELIVERY_LOOP) {
857aa1d5973Seric 			m_create(p_queue, IMSG_MTA_DELIVERY_LOOP, 0, 0, -1);
858aa1d5973Seric 			m_add_evpid(p_queue, e->id);
859aa1d5973Seric 			m_close(p_queue);
860aa1d5973Seric 		}
861af241c42Seric 		else {
8625c9fb78eSeric 			log_warnx("warn: bad delivery type %d for %016" PRIx64,
863af241c42Seric 			    e->delivery, e->id);
864af241c42Seric 			fatalx("aborting");
865af241c42Seric 		}
866af241c42Seric 
867af241c42Seric 		log_debug("debug: mta: flush for %016"PRIx64" (-> %s)", e->id, e->dest);
868af241c42Seric 
869a8e22235Sgilles 		free(e->smtpname);
870af241c42Seric 		free(e->dest);
871af241c42Seric 		free(e->rcpt);
872fe95d8d0Seric 		free(e->dsn_orcpt);
873af241c42Seric 		free(e);
874af241c42Seric 
875af241c42Seric 		tv.tv_sec = 0;
876af241c42Seric 		tv.tv_usec = 0;
877af241c42Seric 		evtimer_add(&ev_flush_evp, &tv);
878af241c42Seric 	}
879af241c42Seric }
880af241c42Seric 
881ac5aa4b9Seric void
mta_delivery_log(struct mta_envelope * e,const char * source,const char * relay,int delivery,const char * status)882c5acbec8Seric mta_delivery_log(struct mta_envelope *e, const char *source, const char *relay,
883299c4efeSeric     int delivery, const char *status)
884ac5aa4b9Seric {
885aa1d5973Seric 	if (delivery == IMSG_MTA_DELIVERY_OK)
886299c4efeSeric 		mta_log(e, "Ok", source, relay, status);
887aa1d5973Seric 	else if (delivery == IMSG_MTA_DELIVERY_TEMPFAIL)
888299c4efeSeric 		mta_log(e, "TempFail", source, relay, status);
889aa1d5973Seric 	else if (delivery == IMSG_MTA_DELIVERY_PERMFAIL)
890299c4efeSeric 		mta_log(e, "PermFail", source, relay, status);
891aa1d5973Seric 	else if (delivery == IMSG_MTA_DELIVERY_LOOP)
892299c4efeSeric 		mta_log(e, "PermFail", source, relay, "Loop detected");
893af241c42Seric 	else {
8945c9fb78eSeric 		log_warnx("warn: bad delivery type %d for %016" PRIx64,
895af241c42Seric 		    delivery, e->id);
896af241c42Seric 		fatalx("aborting");
897c5acbec8Seric 	}
898af241c42Seric 
899af241c42Seric 	e->delivery = delivery;
900af241c42Seric 	if (status)
901769fc316Sgilles 		(void)strlcpy(e->status, status, sizeof(e->status));
902c5acbec8Seric }
903c5acbec8Seric 
904c5acbec8Seric void
mta_delivery_notify(struct mta_envelope * e)905d6b3bcf4Seric mta_delivery_notify(struct mta_envelope *e)
906c5acbec8Seric {
907af241c42Seric 	struct timeval	tv;
908ac5aa4b9Seric 
909af241c42Seric 	tree_xset(&flush_evp, e->id, e);
910af241c42Seric 	if (tree_count(&flush_evp) == 1) {
911af241c42Seric 		tv.tv_sec = 0;
912af241c42Seric 		tv.tv_usec = 0;
913af241c42Seric 		evtimer_add(&ev_flush_evp, &tv);
914af241c42Seric 	}
915c5acbec8Seric }
916c5acbec8Seric 
91765c4fdfbSgilles static void
mta_query_mx(struct mta_relay * relay)91865c4fdfbSgilles mta_query_mx(struct mta_relay *relay)
91965c4fdfbSgilles {
92065c4fdfbSgilles 	uint64_t	id;
92165c4fdfbSgilles 
92265c4fdfbSgilles 	if (relay->status & RELAY_WAIT_MX)
92365c4fdfbSgilles 		return;
92465c4fdfbSgilles 
925c5acbec8Seric 	log_debug("debug: mta: querying MX for %s...",
926c5acbec8Seric 	    mta_relay_to_text(relay));
92765c4fdfbSgilles 
92865c4fdfbSgilles 	if (waitq_wait(&relay->domain->mxs, mta_on_mx, relay)) {
92965c4fdfbSgilles 		id = generate_uid();
93065c4fdfbSgilles 		tree_xset(&wait_mx, id, relay->domain);
9311a271186Seric 		if (relay->domain->as_host)
932aa1d5973Seric 			m_create(p_lka,  IMSG_MTA_DNS_HOST, 0, 0, -1);
93365c4fdfbSgilles 		else
934aa1d5973Seric 			m_create(p_lka,  IMSG_MTA_DNS_MX, 0, 0, -1);
935aa1d5973Seric 		m_add_id(p_lka, id);
936aa1d5973Seric 		m_add_string(p_lka, relay->domain->name);
937aa1d5973Seric 		m_close(p_lka);
93865c4fdfbSgilles 	}
93965c4fdfbSgilles 	relay->status |= RELAY_WAIT_MX;
94065c4fdfbSgilles 	mta_relay_ref(relay);
94165c4fdfbSgilles }
94265c4fdfbSgilles 
94365c4fdfbSgilles static void
mta_query_limits(struct mta_relay * relay)944c5acbec8Seric mta_query_limits(struct mta_relay *relay)
945c5acbec8Seric {
946c5acbec8Seric 	if (relay->status & RELAY_WAIT_LIMITS)
947c5acbec8Seric 		return;
948c5acbec8Seric 
949c5acbec8Seric 	relay->limits = dict_get(env->sc_limits_dict, relay->domain->name);
950c5acbec8Seric 	if (relay->limits == NULL)
951c5acbec8Seric 		relay->limits = dict_get(env->sc_limits_dict, "default");
952c5acbec8Seric 
953c5acbec8Seric 	if (max_seen_conndelay_route < relay->limits->conndelay_route)
954c5acbec8Seric 		max_seen_conndelay_route = relay->limits->conndelay_route;
955c5acbec8Seric 	if (max_seen_discdelay_route < relay->limits->discdelay_route)
956c5acbec8Seric 		max_seen_discdelay_route = relay->limits->discdelay_route;
957c5acbec8Seric }
958c5acbec8Seric 
959c5acbec8Seric static void
mta_query_secret(struct mta_relay * relay)96065c4fdfbSgilles mta_query_secret(struct mta_relay *relay)
96165c4fdfbSgilles {
96265c4fdfbSgilles 	if (relay->status & RELAY_WAIT_SECRET)
96365c4fdfbSgilles 		return;
96465c4fdfbSgilles 
965c5acbec8Seric 	log_debug("debug: mta: querying secret for %s...",
966c5acbec8Seric 	    mta_relay_to_text(relay));
96765c4fdfbSgilles 
96865c4fdfbSgilles 	tree_xset(&wait_secret, relay->id, relay);
96965c4fdfbSgilles 	relay->status |= RELAY_WAIT_SECRET;
97065c4fdfbSgilles 
971aa1d5973Seric 	m_create(p_lka, IMSG_MTA_LOOKUP_CREDENTIALS, 0, 0, -1);
97265c4fdfbSgilles 	m_add_id(p_lka, relay->id);
97365c4fdfbSgilles 	m_add_string(p_lka, relay->authtable);
97465c4fdfbSgilles 	m_add_string(p_lka, relay->authlabel);
97565c4fdfbSgilles 	m_close(p_lka);
97665c4fdfbSgilles 
97765c4fdfbSgilles 	mta_relay_ref(relay);
97865c4fdfbSgilles }
97965c4fdfbSgilles 
98065c4fdfbSgilles static void
mta_query_smarthost(struct envelope * evp0)981a8e22235Sgilles mta_query_smarthost(struct envelope *evp0)
982a8e22235Sgilles {
983a8e22235Sgilles 	struct dispatcher *dispatcher;
984a8e22235Sgilles 	struct envelope *evp;
985a8e22235Sgilles 
986a8e22235Sgilles 	evp = malloc(sizeof(*evp));
987a8e22235Sgilles 	memmove(evp, evp0, sizeof(*evp));
988a8e22235Sgilles 
989a8e22235Sgilles 	dispatcher = dict_xget(env->sc_dispatchers, evp->dispatcher);
990a8e22235Sgilles 
991a8e22235Sgilles 	log_debug("debug: mta: querying smarthost for %s:%s...",
992a8e22235Sgilles 	    evp->dispatcher, dispatcher->u.remote.smarthost);
993a8e22235Sgilles 
994a8e22235Sgilles 	tree_xset(&wait_smarthost, evp->id, evp);
995a8e22235Sgilles 
996a8e22235Sgilles 	m_create(p_lka, IMSG_MTA_LOOKUP_SMARTHOST, 0, 0, -1);
997a8e22235Sgilles 	m_add_id(p_lka, evp->id);
9989e7c83d3Sgilles 	if (dispatcher->u.remote.smarthost_domain)
9999e7c83d3Sgilles 		m_add_string(p_lka, evp->dest.domain);
10009e7c83d3Sgilles 	else
10019e7c83d3Sgilles 		m_add_string(p_lka, NULL);
1002a8e22235Sgilles 	m_add_string(p_lka, dispatcher->u.remote.smarthost);
1003a8e22235Sgilles 	m_close(p_lka);
1004a8e22235Sgilles 
1005a8e22235Sgilles 	log_debug("debug: mta: querying smarthost");
1006a8e22235Sgilles }
1007a8e22235Sgilles 
1008a8e22235Sgilles static void
mta_query_preference(struct mta_relay * relay)100965c4fdfbSgilles mta_query_preference(struct mta_relay *relay)
101065c4fdfbSgilles {
101165c4fdfbSgilles 	if (relay->status & RELAY_WAIT_PREFERENCE)
101265c4fdfbSgilles 		return;
101365c4fdfbSgilles 
1014c5acbec8Seric 	log_debug("debug: mta: querying preference for %s...",
1015c5acbec8Seric 	    mta_relay_to_text(relay));
101665c4fdfbSgilles 
101765c4fdfbSgilles 	tree_xset(&wait_preference, relay->id, relay);
101865c4fdfbSgilles 	relay->status |= RELAY_WAIT_PREFERENCE;
1019aa1d5973Seric 
1020aa1d5973Seric 	m_create(p_lka,  IMSG_MTA_DNS_MX_PREFERENCE, 0, 0, -1);
1021aa1d5973Seric 	m_add_id(p_lka, relay->id);
1022aa1d5973Seric 	m_add_string(p_lka, relay->domain->name);
1023aa1d5973Seric 	m_add_string(p_lka, relay->backupname);
1024aa1d5973Seric 	m_close(p_lka);
1025aa1d5973Seric 
102665c4fdfbSgilles 	mta_relay_ref(relay);
102765c4fdfbSgilles }
102865c4fdfbSgilles 
102965c4fdfbSgilles static void
mta_query_source(struct mta_relay * relay)103065c4fdfbSgilles mta_query_source(struct mta_relay *relay)
103165c4fdfbSgilles {
1032c5acbec8Seric 	log_debug("debug: mta: querying source for %s...",
1033c5acbec8Seric 	    mta_relay_to_text(relay));
1034c5acbec8Seric 
1035c5acbec8Seric 	relay->sourceloop += 1;
1036c5acbec8Seric 
1037c5acbec8Seric 	if (relay->sourcetable == NULL) {
1038c5acbec8Seric 		/*
1039c5acbec8Seric 		 * This is a recursive call, but it only happens once, since
1040af0dba2aSsobrado 		 * another source will not be queried immediately.
1041c5acbec8Seric 		 */
1042c5acbec8Seric 		mta_relay_ref(relay);
1043c5acbec8Seric 		mta_on_source(relay, mta_source(NULL));
1044c5acbec8Seric 		return;
1045c5acbec8Seric 	}
104665c4fdfbSgilles 
1047aa1d5973Seric 	m_create(p_lka, IMSG_MTA_LOOKUP_SOURCE, 0, 0, -1);
104865c4fdfbSgilles 	m_add_id(p_lka, relay->id);
104965c4fdfbSgilles 	m_add_string(p_lka, relay->sourcetable);
105065c4fdfbSgilles 	m_close(p_lka);
105165c4fdfbSgilles 
105265c4fdfbSgilles 	tree_xset(&wait_source, relay->id, relay);
105365c4fdfbSgilles 	relay->status |= RELAY_WAIT_SOURCE;
105465c4fdfbSgilles 	mta_relay_ref(relay);
105565c4fdfbSgilles }
105665c4fdfbSgilles 
105765c4fdfbSgilles static void
mta_on_mx(void * tag,void * arg,void * data)105865c4fdfbSgilles mta_on_mx(void *tag, void *arg, void *data)
105965c4fdfbSgilles {
106065c4fdfbSgilles 	struct mta_domain	*domain = data;
106165c4fdfbSgilles 	struct mta_relay	*relay = arg;
106265c4fdfbSgilles 
1063c5acbec8Seric 	log_debug("debug: mta: ... got mx (%p, %s, %s)",
106465c4fdfbSgilles 	    tag, domain->name, mta_relay_to_text(relay));
106565c4fdfbSgilles 
106665c4fdfbSgilles 	switch (domain->mxstatus) {
106765c4fdfbSgilles 	case DNS_OK:
106865c4fdfbSgilles 		break;
106965c4fdfbSgilles 	case DNS_RETRY:
1070aa1d5973Seric 		relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL;
107165c4fdfbSgilles 		relay->failstr = "Temporary failure in MX lookup";
107265c4fdfbSgilles 		break;
107365c4fdfbSgilles 	case DNS_EINVAL:
1074aa1d5973Seric 		relay->fail = IMSG_MTA_DELIVERY_PERMFAIL;
107565c4fdfbSgilles 		relay->failstr = "Invalid domain name";
107665c4fdfbSgilles 		break;
107765c4fdfbSgilles 	case DNS_ENONAME:
1078aa1d5973Seric 		relay->fail = IMSG_MTA_DELIVERY_PERMFAIL;
107965c4fdfbSgilles 		relay->failstr = "Domain does not exist";
108065c4fdfbSgilles 		break;
108165c4fdfbSgilles 	case DNS_ENOTFOUND:
1082aa1d5973Seric 		relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL;
108342d9b5d3Seric 		if (relay->domain->as_host)
108442d9b5d3Seric 			relay->failstr = "Host not found";
108542d9b5d3Seric 		else
108665c4fdfbSgilles 			relay->failstr = "No MX found for domain";
108765c4fdfbSgilles 		break;
10882e9bd56eSop 	case DNS_NULLMX:
10892e9bd56eSop 		relay->fail = IMSG_MTA_DELIVERY_PERMFAIL;
10902e9bd56eSop 		relay->failstr = "Domain does not accept mail";
10912e9bd56eSop 		break;
109265c4fdfbSgilles 	default:
109365c4fdfbSgilles 		fatalx("bad DNS lookup error code");
109465c4fdfbSgilles 		break;
109565c4fdfbSgilles 	}
109665c4fdfbSgilles 
109765c4fdfbSgilles 	if (domain->mxstatus)
109865c4fdfbSgilles 		log_info("smtp-out: Failed to resolve MX for %s: %s",
109965c4fdfbSgilles 		    mta_relay_to_text(relay), relay->failstr);
110065c4fdfbSgilles 
110165c4fdfbSgilles 	relay->status &= ~RELAY_WAIT_MX;
110265c4fdfbSgilles 	mta_drain(relay);
110365c4fdfbSgilles 	mta_relay_unref(relay); /* from mta_drain() */
110465c4fdfbSgilles }
110565c4fdfbSgilles 
110665c4fdfbSgilles static void
mta_on_secret(struct mta_relay * relay,const char * secret)1107c5acbec8Seric mta_on_secret(struct mta_relay *relay, const char *secret)
1108c5acbec8Seric {
1109c5acbec8Seric 	log_debug("debug: mta: ... got secret for %s: %s",
1110c5acbec8Seric 	    mta_relay_to_text(relay), secret);
1111c5acbec8Seric 
1112c5acbec8Seric 	if (secret)
1113c5acbec8Seric 		relay->secret = strdup(secret);
1114c5acbec8Seric 
1115c5acbec8Seric 	if (relay->secret == NULL) {
1116c5acbec8Seric 		log_warnx("warn: Failed to retrieve secret "
1117c5acbec8Seric 			    "for %s", mta_relay_to_text(relay));
1118aa1d5973Seric 		relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL;
1119c5acbec8Seric 		relay->failstr = "Could not retrieve credentials";
1120c5acbec8Seric 	}
1121c5acbec8Seric 
1122c5acbec8Seric 	relay->status &= ~RELAY_WAIT_SECRET;
1123c5acbec8Seric 	mta_drain(relay);
1124c5acbec8Seric 	mta_relay_unref(relay); /* from mta_query_secret() */
1125c5acbec8Seric }
1126c5acbec8Seric 
1127c5acbec8Seric static void
mta_on_smarthost(struct envelope * evp,const char * smarthost)1128a8e22235Sgilles mta_on_smarthost(struct envelope *evp, const char *smarthost)
1129a8e22235Sgilles {
1130a8e22235Sgilles 	if (smarthost == NULL) {
1131a8e22235Sgilles 		log_warnx("warn: Failed to retrieve smarthost "
1132a8e22235Sgilles 			    "for envelope %"PRIx64, evp->id);
1133a8e22235Sgilles 		m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1);
1134a8e22235Sgilles 		m_add_evpid(p_queue, evp->id);
1135a8e22235Sgilles 		m_add_string(p_queue, "Cannot retrieve smarthost");
1136a8e22235Sgilles 		m_add_int(p_queue, ESC_OTHER_STATUS);
1137d9480a72Seric 		m_close(p_queue);
1138a8e22235Sgilles 		return;
1139a8e22235Sgilles 	}
1140a8e22235Sgilles 
11418f342d13Seric 	log_debug("debug: mta: ... got smarthost for %016"PRIx64": %s",
11428f342d13Seric 	    evp->id, smarthost);
1143a8e22235Sgilles 	mta_handle_envelope(evp, smarthost);
1144a8e22235Sgilles 	free(evp);
1145a8e22235Sgilles }
1146a8e22235Sgilles 
1147a8e22235Sgilles static void
mta_on_preference(struct mta_relay * relay,int preference)1148bc504fceSeric mta_on_preference(struct mta_relay *relay, int preference)
1149c5acbec8Seric {
1150bc504fceSeric 	log_debug("debug: mta: ... got preference for %s: %d",
1151bc504fceSeric 	    mta_relay_to_text(relay), preference);
1152bc504fceSeric 
1153c5acbec8Seric 	relay->backuppref = preference;
1154c5acbec8Seric 
1155c5acbec8Seric 	relay->status &= ~RELAY_WAIT_PREFERENCE;
1156c5acbec8Seric 	mta_drain(relay);
1157c5acbec8Seric 	mta_relay_unref(relay); /* from mta_query_preference() */
1158c5acbec8Seric }
1159c5acbec8Seric 
1160c5acbec8Seric static void
mta_on_source(struct mta_relay * relay,struct mta_source * source)116165c4fdfbSgilles mta_on_source(struct mta_relay *relay, struct mta_source *source)
116265c4fdfbSgilles {
1163c5acbec8Seric 	struct mta_connector	*c;
1164c5acbec8Seric 	void			*iter;
1165c5acbec8Seric 	int			 delay, errmask;
1166c5acbec8Seric 
1167c5acbec8Seric 	log_debug("debug: mta: ... got source for %s: %s",
1168c5acbec8Seric 	    mta_relay_to_text(relay), source ? mta_source_to_text(source) : "NULL");
1169c5acbec8Seric 
1170c5acbec8Seric 	relay->lastsource = time(NULL);
1171c5acbec8Seric 	delay = DELAY_CHECK_SOURCE_SLOW;
1172c5acbec8Seric 
1173c5acbec8Seric 	if (source) {
1174c5acbec8Seric 		c = mta_connector(relay, source);
1175c5acbec8Seric 		if (c->flags & CONNECTOR_NEW) {
1176c5acbec8Seric 			c->flags &= ~CONNECTOR_NEW;
1177c5acbec8Seric 			delay = DELAY_CHECK_SOURCE;
1178c5acbec8Seric 		}
1179c5acbec8Seric 		mta_connect(c);
1180c5acbec8Seric 		if ((c->flags & CONNECTOR_ERROR) == 0)
1181c5acbec8Seric 			relay->sourceloop = 0;
1182c5acbec8Seric 		else
1183c5acbec8Seric 			delay = DELAY_CHECK_SOURCE_FAST;
1184c5acbec8Seric 		mta_source_unref(source); /* from constructor */
1185c5acbec8Seric 	}
1186c5acbec8Seric 	else {
1187f847569bStedu 		log_warnx("warn: Failed to get source address for %s",
1188f847569bStedu 		    mta_relay_to_text(relay));
1189c5acbec8Seric 	}
1190c5acbec8Seric 
1191c5acbec8Seric 	if (tree_count(&relay->connectors) == 0) {
1192aa1d5973Seric 		relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL;
1193c5acbec8Seric 		relay->failstr = "Could not retrieve source address";
1194c5acbec8Seric 	}
1195c5acbec8Seric 	if (tree_count(&relay->connectors) < relay->sourceloop) {
1196aa1d5973Seric 		relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL;
1197c5acbec8Seric 		relay->failstr = "No valid route to remote MX";
1198c5acbec8Seric 
1199c5acbec8Seric 		errmask = 0;
1200c5acbec8Seric 		iter = NULL;
1201c5acbec8Seric 		while (tree_iter(&relay->connectors, &iter, NULL, (void **)&c))
1202c5acbec8Seric 			errmask |= c->flags;
1203c5acbec8Seric 
1204c5acbec8Seric 		if (errmask & CONNECTOR_ERROR_ROUTE_SMTP)
1205c5acbec8Seric 			relay->failstr = "Destination seem to reject all mails";
1206c5acbec8Seric 		else if (errmask & CONNECTOR_ERROR_ROUTE_NET)
1207c5acbec8Seric 			relay->failstr = "Network error on destination MXs";
1208c5acbec8Seric 		else if (errmask & CONNECTOR_ERROR_MX)
1209c5acbec8Seric 			relay->failstr = "No MX found for destination";
1210c5acbec8Seric 		else if (errmask & CONNECTOR_ERROR_FAMILY)
1211c5acbec8Seric 			relay->failstr = "Address family mismatch on destination MXs";
12125b6a9ce9Seric 		else if (errmask & CONNECTOR_ERROR_BLOCKED)
12135b6a9ce9Seric 			relay->failstr = "All routes to destination blocked";
1214c5acbec8Seric 		else
1215c5acbec8Seric 			relay->failstr = "No valid route to destination";
1216c5acbec8Seric 	}
1217c5acbec8Seric 
1218c5acbec8Seric 	relay->nextsource = relay->lastsource + delay;
1219c5acbec8Seric 	relay->status &= ~RELAY_WAIT_SOURCE;
1220c5acbec8Seric 	mta_drain(relay);
1221c5acbec8Seric 	mta_relay_unref(relay); /* from mta_query_source() */
122265c4fdfbSgilles }
122365c4fdfbSgilles 
122465c4fdfbSgilles static void
mta_connect(struct mta_connector * c)122565c4fdfbSgilles mta_connect(struct mta_connector *c)
122665c4fdfbSgilles {
122765c4fdfbSgilles 	struct mta_route	*route;
1228d6ee62d2Seric 	struct mta_mx		*mx;
1229c5acbec8Seric 	struct mta_limits	*l = c->relay->limits;
1230c5acbec8Seric 	int			 limits;
1231c5acbec8Seric 	time_t			 nextconn, now;
123265c4fdfbSgilles 
12335b6a9ce9Seric 	/* toggle the block flag */
12345b6a9ce9Seric 	if (mta_is_blocked(c->source, c->relay->domain->name))
12355b6a9ce9Seric 		c->flags |= CONNECTOR_ERROR_BLOCKED;
12365b6a9ce9Seric 	else
12375b6a9ce9Seric 		c->flags &= ~CONNECTOR_ERROR_BLOCKED;
12385b6a9ce9Seric 
1239c5acbec8Seric     again:
124065c4fdfbSgilles 
1241c5acbec8Seric 	log_debug("debug: mta: connecting with %s", mta_connector_to_text(c));
1242c5acbec8Seric 
1243c5acbec8Seric 	/* Do not connect if this connector has an error. */
1244c5acbec8Seric 	if (c->flags & CONNECTOR_ERROR) {
1245c5acbec8Seric 		log_debug("debug: mta: connector error");
124665c4fdfbSgilles 		return;
124765c4fdfbSgilles 	}
124865c4fdfbSgilles 
1249c5acbec8Seric 	if (c->flags & CONNECTOR_WAIT) {
12505c9fb78eSeric 		log_debug("debug: mta: cancelling connector timeout");
125128efdb08Seric 		runq_cancel(runq_connector, c);
125241ac183fSeric 		c->flags &= ~CONNECTOR_WAIT;
1253c5acbec8Seric 	}
1254c5acbec8Seric 
1255c5acbec8Seric 	/* No job. */
1256c5acbec8Seric 	if (c->relay->ntask == 0) {
1257c5acbec8Seric 		log_debug("debug: mta: no task for connector");
1258c5acbec8Seric 		return;
1259c5acbec8Seric 	}
1260c5acbec8Seric 
1261c5acbec8Seric 	/* Do not create more connections than necessary */
1262c5acbec8Seric 	if ((c->relay->nconn_ready >= c->relay->ntask) ||
1263c5acbec8Seric 	    (c->relay->nconn > 2 && c->relay->nconn >= c->relay->ntask / 2)) {
1264c5acbec8Seric 		log_debug("debug: mta: enough connections already");
1265c5acbec8Seric 		return;
1266c5acbec8Seric 	}
1267c5acbec8Seric 
1268c5acbec8Seric 	limits = 0;
1269c5acbec8Seric 	nextconn = now = time(NULL);
1270c5acbec8Seric 
1271c5acbec8Seric 	if (c->relay->domain->lastconn + l->conndelay_domain > nextconn) {
1272c5acbec8Seric 		log_debug("debug: mta: cannot use domain %s before %llus",
1273c5acbec8Seric 		    c->relay->domain->name,
1274c5acbec8Seric 		    (unsigned long long) c->relay->domain->lastconn + l->conndelay_domain - now);
1275c5acbec8Seric 		nextconn = c->relay->domain->lastconn + l->conndelay_domain;
1276c5acbec8Seric 	}
1277c5acbec8Seric 	if (c->relay->domain->nconn >= l->maxconn_per_domain) {
1278c5acbec8Seric 		log_debug("debug: mta: hit domain limit");
1279c5acbec8Seric 		limits |= CONNECTOR_LIMIT_DOMAIN;
1280c5acbec8Seric 	}
1281c5acbec8Seric 
1282c5acbec8Seric 	if (c->source->lastconn + l->conndelay_source > nextconn) {
1283c5acbec8Seric 		log_debug("debug: mta: cannot use source %s before %llus",
1284c5acbec8Seric 		    mta_source_to_text(c->source),
1285c5acbec8Seric 		    (unsigned long long) c->source->lastconn + l->conndelay_source - now);
1286c5acbec8Seric 		nextconn = c->source->lastconn + l->conndelay_source;
1287c5acbec8Seric 	}
1288c5acbec8Seric 	if (c->source->nconn >= l->maxconn_per_source) {
1289c5acbec8Seric 		log_debug("debug: mta: hit source limit");
1290c5acbec8Seric 		limits |= CONNECTOR_LIMIT_SOURCE;
1291c5acbec8Seric 	}
1292c5acbec8Seric 
1293c5acbec8Seric 	if (c->lastconn + l->conndelay_connector > nextconn) {
1294c5acbec8Seric 		log_debug("debug: mta: cannot use %s before %llus",
1295c5acbec8Seric 		    mta_connector_to_text(c),
1296c5acbec8Seric 		    (unsigned long long) c->lastconn + l->conndelay_connector - now);
1297c5acbec8Seric 		nextconn = c->lastconn + l->conndelay_connector;
1298c5acbec8Seric 	}
1299c5acbec8Seric 	if (c->nconn >= l->maxconn_per_connector) {
1300c5acbec8Seric 		log_debug("debug: mta: hit connector limit");
1301c5acbec8Seric 		limits |= CONNECTOR_LIMIT_CONN;
1302c5acbec8Seric 	}
1303c5acbec8Seric 
1304c5acbec8Seric 	if (c->relay->lastconn + l->conndelay_relay > nextconn) {
1305c5acbec8Seric 		log_debug("debug: mta: cannot use %s before %llus",
1306c5acbec8Seric 		    mta_relay_to_text(c->relay),
1307c5acbec8Seric 		    (unsigned long long) c->relay->lastconn + l->conndelay_relay - now);
1308c5acbec8Seric 		nextconn = c->relay->lastconn + l->conndelay_relay;
1309c5acbec8Seric 	}
1310c5acbec8Seric 	if (c->relay->nconn >= l->maxconn_per_relay) {
1311c5acbec8Seric 		log_debug("debug: mta: hit relay limit");
1312c5acbec8Seric 		limits |= CONNECTOR_LIMIT_RELAY;
1313c5acbec8Seric 	}
1314c5acbec8Seric 
1315c5acbec8Seric 	/* We can connect now, find a route */
1316c5acbec8Seric 	if (!limits && nextconn <= now)
1317d6ee62d2Seric 		route = mta_find_route(c, now, &limits, &nextconn, &mx);
1318c5acbec8Seric 	else
1319c5acbec8Seric 		route = NULL;
1320c5acbec8Seric 
1321c5acbec8Seric 	/* No route */
1322c5acbec8Seric 	if (route == NULL) {
1323c5acbec8Seric 
1324c5acbec8Seric 		if (c->flags & CONNECTOR_ERROR) {
1325c5acbec8Seric 			/* XXX we might want to clear this flag later */
1326c5acbec8Seric 			log_debug("debug: mta-routing: no route available for %s: errors on connector",
1327c5acbec8Seric 			    mta_connector_to_text(c));
1328c5acbec8Seric 			return;
1329c5acbec8Seric 		}
1330c5acbec8Seric 		else if (limits) {
1331c5acbec8Seric 			log_debug("debug: mta-routing: no route available for %s: limits reached",
1332c5acbec8Seric 			    mta_connector_to_text(c));
1333c5acbec8Seric 			nextconn = now + DELAY_CHECK_LIMIT;
1334c5acbec8Seric 		}
1335c5acbec8Seric 		else {
1336c5acbec8Seric 			log_debug("debug: mta-routing: no route available for %s: must wait a bit",
1337c5acbec8Seric 			    mta_connector_to_text(c));
1338c5acbec8Seric 		}
1339c5acbec8Seric 		log_debug("debug: mta: retrying to connect on %s in %llus...",
1340c5acbec8Seric 		    mta_connector_to_text(c),
1341c5acbec8Seric 		    (unsigned long long) nextconn - time(NULL));
1342c5acbec8Seric 		c->flags |= CONNECTOR_WAIT;
134328efdb08Seric 		runq_schedule_at(runq_connector, nextconn, c);
1344c5acbec8Seric 		return;
1345c5acbec8Seric 	}
1346c5acbec8Seric 
1347c5acbec8Seric 	log_debug("debug: mta-routing: spawning new connection on %s",
1348c5acbec8Seric 		    mta_route_to_text(route));
1349c5acbec8Seric 
135065c4fdfbSgilles 	c->nconn += 1;
135165c4fdfbSgilles 	c->lastconn = time(NULL);
135265c4fdfbSgilles 
135365c4fdfbSgilles 	c->relay->nconn += 1;
135465c4fdfbSgilles 	c->relay->lastconn = c->lastconn;
1355c5acbec8Seric 	c->relay->domain->nconn += 1;
1356c5acbec8Seric 	c->relay->domain->lastconn = c->lastconn;
135765c4fdfbSgilles 	route->nconn += 1;
135865c4fdfbSgilles 	route->lastconn = c->lastconn;
135965c4fdfbSgilles 	route->src->nconn += 1;
136065c4fdfbSgilles 	route->src->lastconn = c->lastconn;
136165c4fdfbSgilles 	route->dst->nconn += 1;
136265c4fdfbSgilles 	route->dst->lastconn = c->lastconn;
136365c4fdfbSgilles 
1364d6ee62d2Seric 	mta_session(c->relay, route, mx->mxname);	/* this never fails synchronously */
1365c5acbec8Seric 	mta_relay_ref(c->relay);
1366c5acbec8Seric 
1367c5acbec8Seric 	goto again;
136865c4fdfbSgilles }
136965c4fdfbSgilles 
137065c4fdfbSgilles static void
mta_on_timeout(struct runq * runq,void * arg)1371c5acbec8Seric mta_on_timeout(struct runq *runq, void *arg)
137265c4fdfbSgilles {
1373c5acbec8Seric 	struct mta_connector	*connector = arg;
1374c5acbec8Seric 	struct mta_relay	*relay = arg;
1375c5acbec8Seric 	struct mta_route	*route = arg;
1376c5acbec8Seric 	struct hoststat		*hs = arg;
137765c4fdfbSgilles 
1378c5acbec8Seric 	if (runq == runq_relay) {
1379c5acbec8Seric 		log_debug("debug: mta: ... timeout for %s",
1380c5acbec8Seric 		    mta_relay_to_text(relay));
1381c5acbec8Seric 		relay->status &= ~RELAY_WAIT_CONNECTOR;
1382c5acbec8Seric 		mta_drain(relay);
1383c5acbec8Seric 		mta_relay_unref(relay); /* from mta_drain() */
138465c4fdfbSgilles 	}
1385c5acbec8Seric 	else if (runq == runq_connector) {
1386c5acbec8Seric 		log_debug("debug: mta: ... timeout for %s",
1387c5acbec8Seric 		    mta_connector_to_text(connector));
1388c5acbec8Seric 		connector->flags &= ~CONNECTOR_WAIT;
1389c5acbec8Seric 		mta_connect(connector);
139065c4fdfbSgilles 	}
1391c5acbec8Seric 	else if (runq == runq_route) {
1392c5acbec8Seric 		route->flags &= ~ROUTE_RUNQ;
1393c5acbec8Seric 		mta_route_enable(route);
1394c5acbec8Seric 		mta_route_unref(route);
139565c4fdfbSgilles 	}
1396c5acbec8Seric 	else if (runq == runq_hoststat) {
1397c5acbec8Seric 		log_debug("debug: mta: ... timeout for hoststat %s",
1398c5acbec8Seric 			hs->name);
1399c5acbec8Seric 		mta_hoststat_remove_entry(hs);
1400c5acbec8Seric 		free(hs);
140165c4fdfbSgilles 	}
140265c4fdfbSgilles }
140365c4fdfbSgilles 
140465c4fdfbSgilles static void
mta_route_disable(struct mta_route * route,int penalty,int reason)1405c5acbec8Seric mta_route_disable(struct mta_route *route, int penalty, int reason)
140665c4fdfbSgilles {
1407c5acbec8Seric 	unsigned long long	delay;
140865c4fdfbSgilles 
1409c5acbec8Seric 	route->penalty += penalty;
1410c5acbec8Seric 	route->lastpenalty = time(NULL);
1411c5acbec8Seric 	delay = (unsigned long long)DELAY_ROUTE_BASE * route->penalty * route->penalty;
1412c5acbec8Seric 	if (delay > DELAY_ROUTE_MAX)
1413c5acbec8Seric 		delay = DELAY_ROUTE_MAX;
1414c5acbec8Seric #if 0
1415c5acbec8Seric 	delay = 60;
1416c5acbec8Seric #endif
141765c4fdfbSgilles 
1418c5acbec8Seric 	log_info("smtp-out: Disabling route %s for %llus",
1419c5acbec8Seric 	    mta_route_to_text(route), delay);
142065c4fdfbSgilles 
1421775712a7Stim 	if (route->flags & ROUTE_DISABLED)
142228efdb08Seric 		runq_cancel(runq_route, route);
1423775712a7Stim 	else
1424775712a7Stim 		mta_route_ref(route);
1425775712a7Stim 
1426c5acbec8Seric 	route->flags |= reason & ROUTE_DISABLED;
142728efdb08Seric 	runq_schedule(runq_route, delay, route);
142865c4fdfbSgilles }
142965c4fdfbSgilles 
143065c4fdfbSgilles static void
mta_route_enable(struct mta_route * route)1431c5acbec8Seric mta_route_enable(struct mta_route *route)
143265c4fdfbSgilles {
1433c5acbec8Seric 	if (route->flags & ROUTE_DISABLED) {
1434c5acbec8Seric 		log_info("smtp-out: Enabling route %s",
1435c5acbec8Seric 		    mta_route_to_text(route));
1436c5acbec8Seric 		route->flags &= ~ROUTE_DISABLED;
1437c5acbec8Seric 		route->flags |= ROUTE_NEW;
14385d758dd0Seric 		route->nerror = 0;
1439c5acbec8Seric 	}
144065c4fdfbSgilles 
1441c5acbec8Seric 	if (route->penalty) {
1442c5acbec8Seric #if DELAY_QUADRATIC
1443c5acbec8Seric 		route->penalty -= 1;
1444c5acbec8Seric 		route->lastpenalty = time(NULL);
1445c5acbec8Seric #else
1446c5acbec8Seric 		route->penalty = 0;
1447c5acbec8Seric #endif
1448c5acbec8Seric 	}
144965c4fdfbSgilles }
145065c4fdfbSgilles 
145165c4fdfbSgilles static void
mta_drain(struct mta_relay * r)145265c4fdfbSgilles mta_drain(struct mta_relay *r)
145365c4fdfbSgilles {
145465c4fdfbSgilles 	char			 buf[64];
145565c4fdfbSgilles 
145665c4fdfbSgilles 	log_debug("debug: mta: draining %s "
1457d7bcae4dSeric 	    "refcount=%d, ntask=%zu, nconnector=%zu, nconn=%zu",
145865c4fdfbSgilles 	    mta_relay_to_text(r),
1459c5acbec8Seric 	    r->refcount, r->ntask, tree_count(&r->connectors), r->nconn);
146065c4fdfbSgilles 
146165c4fdfbSgilles 	/*
146265c4fdfbSgilles 	 * All done.
146365c4fdfbSgilles 	 */
146465c4fdfbSgilles 	if (r->ntask == 0) {
146565c4fdfbSgilles 		log_debug("debug: mta: all done for %s", mta_relay_to_text(r));
146665c4fdfbSgilles 		return;
146765c4fdfbSgilles 	}
146865c4fdfbSgilles 
146965c4fdfbSgilles 	/*
147065c4fdfbSgilles 	 * If we know that this relay is failing flush the tasks.
147165c4fdfbSgilles 	 */
147265c4fdfbSgilles 	if (r->fail) {
147365c4fdfbSgilles 		mta_flush(r, r->fail, r->failstr);
147465c4fdfbSgilles 		return;
147565c4fdfbSgilles 	}
147665c4fdfbSgilles 
147765c4fdfbSgilles 	/* Query secret if needed. */
147865c4fdfbSgilles 	if (r->flags & RELAY_AUTH && r->secret == NULL)
147965c4fdfbSgilles 		mta_query_secret(r);
148065c4fdfbSgilles 
148165c4fdfbSgilles 	/* Query our preference if needed. */
148265c4fdfbSgilles 	if (r->backupname && r->backuppref == -1)
148365c4fdfbSgilles 		mta_query_preference(r);
148465c4fdfbSgilles 
148565c4fdfbSgilles 	/* Query the domain MXs if needed. */
148665c4fdfbSgilles 	if (r->domain->lastmxquery == 0)
148765c4fdfbSgilles 		mta_query_mx(r);
148865c4fdfbSgilles 
1489c5acbec8Seric 	/* Query the limits if needed. */
1490c5acbec8Seric 	if (r->limits == NULL)
1491c5acbec8Seric 		mta_query_limits(r);
1492c5acbec8Seric 
149365c4fdfbSgilles 	/* Wait until we are ready to proceed. */
149465c4fdfbSgilles 	if (r->status & RELAY_WAITMASK) {
149565c4fdfbSgilles 		buf[0] = '\0';
149665c4fdfbSgilles 		if (r->status & RELAY_WAIT_MX)
1497769fc316Sgilles 			(void)strlcat(buf, " MX", sizeof buf);
149865c4fdfbSgilles 		if (r->status & RELAY_WAIT_PREFERENCE)
1499769fc316Sgilles 			(void)strlcat(buf, " preference", sizeof buf);
150065c4fdfbSgilles 		if (r->status & RELAY_WAIT_SECRET)
1501769fc316Sgilles 			(void)strlcat(buf, " secret", sizeof buf);
150265c4fdfbSgilles 		if (r->status & RELAY_WAIT_SOURCE)
1503769fc316Sgilles 			(void)strlcat(buf, " source", sizeof buf);
1504c5acbec8Seric 		if (r->status & RELAY_WAIT_CONNECTOR)
1505769fc316Sgilles 			(void)strlcat(buf, " connector", sizeof buf);
150665c4fdfbSgilles 		log_debug("debug: mta: %s waiting for%s",
150765c4fdfbSgilles 		    mta_relay_to_text(r), buf);
150865c4fdfbSgilles 		return;
150965c4fdfbSgilles 	}
151065c4fdfbSgilles 
151165c4fdfbSgilles 	/*
1512c5acbec8Seric 	 * We have pending task, and it's maybe time too try a new source.
151365c4fdfbSgilles 	 */
1514c5acbec8Seric 	if (r->nextsource <= time(NULL))
151565c4fdfbSgilles 		mta_query_source(r);
1516c5acbec8Seric 	else {
1517c5acbec8Seric 		log_debug("debug: mta: scheduling relay %s in %llus...",
1518c5acbec8Seric 		    mta_relay_to_text(r),
1519c5acbec8Seric 		    (unsigned long long) r->nextsource - time(NULL));
152028efdb08Seric 		runq_schedule_at(runq_relay, r->nextsource, r);
1521c5acbec8Seric 		r->status |= RELAY_WAIT_CONNECTOR;
1522c5acbec8Seric 		mta_relay_ref(r);
152365c4fdfbSgilles 	}
152465c4fdfbSgilles }
152565c4fdfbSgilles 
152665c4fdfbSgilles static void
mta_flush(struct mta_relay * relay,int fail,const char * error)152765c4fdfbSgilles mta_flush(struct mta_relay *relay, int fail, const char *error)
152865c4fdfbSgilles {
1529399c053eSeric 	struct mta_envelope	*e;
153065c4fdfbSgilles 	struct mta_task		*task;
1531c5acbec8Seric 	const char     		*domain;
1532c5acbec8Seric 	void			*iter;
1533c5acbec8Seric 	struct mta_connector	*c;
1534af241c42Seric 	size_t			 n, r;
153565c4fdfbSgilles 
1536d7bcae4dSeric 	log_debug("debug: mta_flush(%s, %d, \"%s\")",
153765c4fdfbSgilles 	    mta_relay_to_text(relay), fail, error);
153865c4fdfbSgilles 
1539aa1d5973Seric 	if (fail != IMSG_MTA_DELIVERY_TEMPFAIL && fail != IMSG_MTA_DELIVERY_PERMFAIL)
1540ff01b044Seric 		fatalx("unexpected delivery status %d", fail);
154165c4fdfbSgilles 
154265c4fdfbSgilles 	n = 0;
154365c4fdfbSgilles 	while ((task = TAILQ_FIRST(&relay->tasks))) {
154465c4fdfbSgilles 		TAILQ_REMOVE(&relay->tasks, task, entry);
154565c4fdfbSgilles 		while ((e = TAILQ_FIRST(&task->envelopes))) {
154665c4fdfbSgilles 			TAILQ_REMOVE(&task->envelopes, e, entry);
1547c5acbec8Seric 
1548c5acbec8Seric 			/*
1549c5acbec8Seric 			 * host was suspended, cache envelope id in hoststat tree
1550c5acbec8Seric 			 * so that it can be retried when a delivery succeeds for
1551c5acbec8Seric 			 * that domain.
1552c5acbec8Seric 			 */
1553c5acbec8Seric 			domain = strchr(e->dest, '@');
1554aa1d5973Seric 			if (fail == IMSG_MTA_DELIVERY_TEMPFAIL && domain) {
1555c5acbec8Seric 				r = 0;
1556c5acbec8Seric 				iter = NULL;
1557c5acbec8Seric 				while (tree_iter(&relay->connectors, &iter,
1558c5acbec8Seric 					NULL, (void **)&c)) {
1559c5acbec8Seric 					if (c->flags & CONNECTOR_ERROR_ROUTE)
1560c5acbec8Seric 						r++;
1561c5acbec8Seric 				}
1562c5acbec8Seric 				if (tree_count(&relay->connectors) == r)
1563c5acbec8Seric 					mta_hoststat_cache(domain+1, e->id);
1564c5acbec8Seric 			}
1565c5acbec8Seric 
1566af241c42Seric 			mta_delivery_log(e, NULL, relay->domain->name, fail, error);
1567d6b3bcf4Seric 			mta_delivery_notify(e);
1568af241c42Seric 
156965c4fdfbSgilles 			n++;
157065c4fdfbSgilles 		}
1571399c053eSeric 		free(task->sender);
157265c4fdfbSgilles 		free(task);
157365c4fdfbSgilles 	}
157465c4fdfbSgilles 
157565c4fdfbSgilles 	stat_decrement("mta.task", relay->ntask);
157665c4fdfbSgilles 	stat_decrement("mta.envelope", n);
157765c4fdfbSgilles 	relay->ntask = 0;
15786dc81a07Seric 
15796dc81a07Seric 	/* release all waiting envelopes for the relay */
1580704aa3edSeric 	if (relay->state & RELAY_HOLDQ) {
1581aa1d5973Seric 		m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1);
15826dc81a07Seric 		m_add_id(p_queue, relay->id);
158381ab3bccSeric 		m_add_int(p_queue, -1);
15846dc81a07Seric 		m_close(p_queue);
158565c4fdfbSgilles 	}
1586704aa3edSeric }
158765c4fdfbSgilles 
158865c4fdfbSgilles /*
158965c4fdfbSgilles  * Find a route to use for this connector
159065c4fdfbSgilles  */
159165c4fdfbSgilles static struct mta_route *
mta_find_route(struct mta_connector * c,time_t now,int * limits,time_t * nextconn,struct mta_mx ** pmx)1592c5acbec8Seric mta_find_route(struct mta_connector *c, time_t now, int *limits,
1593d6ee62d2Seric     time_t *nextconn, struct mta_mx **pmx)
159465c4fdfbSgilles {
159565c4fdfbSgilles 	struct mta_route	*route, *best;
1596c5acbec8Seric 	struct mta_limits	*l = c->relay->limits;
159765c4fdfbSgilles 	struct mta_mx		*mx;
159865c4fdfbSgilles 	int			 level, limit_host, limit_route;
1599c5acbec8Seric 	int			 family_mismatch, seen, suspended_route;
1600c5acbec8Seric 	time_t			 tm;
160165c4fdfbSgilles 
1602c5acbec8Seric 	log_debug("debug: mta-routing: searching new route for %s...",
1603c5acbec8Seric 	    mta_connector_to_text(c));
1604c5acbec8Seric 
1605c5acbec8Seric 	tm = 0;
160665c4fdfbSgilles 	limit_host = 0;
160765c4fdfbSgilles 	limit_route = 0;
1608c5acbec8Seric 	suspended_route = 0;
160965c4fdfbSgilles 	family_mismatch = 0;
161065c4fdfbSgilles 	level = -1;
161165c4fdfbSgilles 	best = NULL;
161265c4fdfbSgilles 	seen = 0;
161365c4fdfbSgilles 
161465c4fdfbSgilles 	TAILQ_FOREACH(mx, &c->relay->domain->mxs, entry) {
161565c4fdfbSgilles 		/*
161665c4fdfbSgilles 		 * New preference level
161765c4fdfbSgilles 		 */
161865c4fdfbSgilles 		if (mx->preference > level) {
161965c4fdfbSgilles #ifndef IGNORE_MX_PREFERENCE
162065c4fdfbSgilles 			/*
162165c4fdfbSgilles 			 * Use the current best MX if found.
162265c4fdfbSgilles 			 */
162365c4fdfbSgilles 			if (best)
162465c4fdfbSgilles 				break;
162565c4fdfbSgilles 
162665c4fdfbSgilles 			/*
162765c4fdfbSgilles 			 * No candidate found.  There are valid MXs at this
1628c5acbec8Seric 			 * preference level but they reached their limit, or
1629c5acbec8Seric 			 * we can't connect yet.
163065c4fdfbSgilles 			 */
1631c5acbec8Seric 			if (limit_host || limit_route || tm)
163265c4fdfbSgilles 				break;
163365c4fdfbSgilles 
163465c4fdfbSgilles 			/*
163565c4fdfbSgilles 			 *  If we are a backup MX, do not relay to MXs with
163665c4fdfbSgilles 			 *  a greater preference value.
163765c4fdfbSgilles 			 */
163865c4fdfbSgilles 			if (c->relay->backuppref >= 0 &&
163965c4fdfbSgilles 			    mx->preference >= c->relay->backuppref)
164065c4fdfbSgilles 				break;
164165c4fdfbSgilles 
164265c4fdfbSgilles 			/*
164365c4fdfbSgilles 			 * Start looking at MXs on this preference level.
164465c4fdfbSgilles 			 */
164565c4fdfbSgilles #endif
164665c4fdfbSgilles 			level = mx->preference;
164765c4fdfbSgilles 		}
164865c4fdfbSgilles 
164965c4fdfbSgilles 		if (mx->host->flags & HOST_IGNORE)
165065c4fdfbSgilles 			continue;
165165c4fdfbSgilles 
165265c4fdfbSgilles 		/* Found a possibly valid mx */
165365c4fdfbSgilles 		seen++;
165465c4fdfbSgilles 
1655c5acbec8Seric 		if ((c->source->sa &&
1656c5acbec8Seric 		     c->source->sa->sa_family != mx->host->sa->sa_family) ||
1657c5acbec8Seric 		    (l->family && l->family != mx->host->sa->sa_family)) {
1658c5acbec8Seric 			log_debug("debug: mta-routing: skipping host %s: AF mismatch",
1659c5acbec8Seric 			    mta_host_to_text(mx->host));
166065c4fdfbSgilles 			family_mismatch = 1;
166165c4fdfbSgilles 			continue;
166265c4fdfbSgilles 		}
166365c4fdfbSgilles 
1664c5acbec8Seric 		if (mx->host->nconn >= l->maxconn_per_host) {
1665c5acbec8Seric 			log_debug("debug: mta-routing: skipping host %s: too many connections",
1666c5acbec8Seric 			    mta_host_to_text(mx->host));
166765c4fdfbSgilles 			limit_host = 1;
166865c4fdfbSgilles 			continue;
166965c4fdfbSgilles 		}
167065c4fdfbSgilles 
1671c5acbec8Seric 		if (mx->host->lastconn + l->conndelay_host > now) {
1672c5acbec8Seric 			log_debug("debug: mta-routing: skipping host %s: cannot use before %llus",
1673c5acbec8Seric 			    mta_host_to_text(mx->host),
1674c5acbec8Seric 			    (unsigned long long) mx->host->lastconn + l->conndelay_host - now);
1675c5acbec8Seric 			if (tm == 0 || mx->host->lastconn + l->conndelay_host < tm)
1676c5acbec8Seric 				tm = mx->host->lastconn + l->conndelay_host;
1677c5acbec8Seric 			continue;
1678c5acbec8Seric 		}
1679c5acbec8Seric 
168065c4fdfbSgilles 		route = mta_route(c->source, mx->host);
168165c4fdfbSgilles 
1682c5acbec8Seric 		if (route->flags & ROUTE_DISABLED) {
1683c5acbec8Seric 			log_debug("debug: mta-routing: skipping route %s: suspend",
1684c5acbec8Seric 			    mta_route_to_text(route));
1685c5acbec8Seric 			suspended_route |= route->flags & ROUTE_DISABLED;
1686c5acbec8Seric 			mta_route_unref(route); /* from here */
1687c5acbec8Seric 			continue;
1688c5acbec8Seric 		}
1689c5acbec8Seric 
1690c5acbec8Seric 		if (route->nconn && (route->flags & ROUTE_NEW)) {
1691c5acbec8Seric 			log_debug("debug: mta-routing: skipping route %s: not validated yet",
1692c5acbec8Seric 			    mta_route_to_text(route));
169365c4fdfbSgilles 			limit_route = 1;
169465c4fdfbSgilles 			mta_route_unref(route); /* from here */
169565c4fdfbSgilles 			continue;
169665c4fdfbSgilles 		}
169765c4fdfbSgilles 
1698c5acbec8Seric 		if (route->nconn >= l->maxconn_per_route) {
1699c5acbec8Seric 			log_debug("debug: mta-routing: skipping route %s: too many connections",
1700c5acbec8Seric 			    mta_route_to_text(route));
1701c5acbec8Seric 			limit_route = 1;
1702c5acbec8Seric 			mta_route_unref(route); /* from here */
1703c5acbec8Seric 			continue;
1704c5acbec8Seric 		}
1705c5acbec8Seric 
1706c5acbec8Seric 		if (route->lastconn + l->conndelay_route > now) {
1707c5acbec8Seric 			log_debug("debug: mta-routing: skipping route %s: cannot use before %llus (delay after connect)",
1708c5acbec8Seric 			    mta_route_to_text(route),
1709c5acbec8Seric 			    (unsigned long long) route->lastconn + l->conndelay_route - now);
1710c5acbec8Seric 			if (tm == 0 || route->lastconn + l->conndelay_route < tm)
1711c5acbec8Seric 				tm = route->lastconn + l->conndelay_route;
1712c5acbec8Seric 			mta_route_unref(route); /* from here */
1713c5acbec8Seric 			continue;
1714c5acbec8Seric 		}
1715c5acbec8Seric 
1716c5acbec8Seric 		if (route->lastdisc + l->discdelay_route > now) {
1717c5acbec8Seric 			log_debug("debug: mta-routing: skipping route %s: cannot use before %llus (delay after disconnect)",
1718c5acbec8Seric 			    mta_route_to_text(route),
1719c5acbec8Seric 			    (unsigned long long) route->lastdisc + l->discdelay_route - now);
1720c5acbec8Seric 			if (tm == 0 || route->lastdisc + l->discdelay_route < tm)
1721c5acbec8Seric 				tm = route->lastdisc + l->discdelay_route;
1722c5acbec8Seric 			mta_route_unref(route); /* from here */
1723c5acbec8Seric 			continue;
1724c5acbec8Seric 		}
1725c5acbec8Seric 
172665c4fdfbSgilles 		/* Use the route with the lowest number of connections. */
172765c4fdfbSgilles 		if (best && route->nconn >= best->nconn) {
1728c5acbec8Seric 			log_debug("debug: mta-routing: skipping route %s: current one is better",
1729c5acbec8Seric 			    mta_route_to_text(route));
173065c4fdfbSgilles 			mta_route_unref(route); /* from here */
173165c4fdfbSgilles 			continue;
173265c4fdfbSgilles 		}
173365c4fdfbSgilles 
173465c4fdfbSgilles 		if (best)
173565c4fdfbSgilles 			mta_route_unref(best); /* from here */
173665c4fdfbSgilles 		best = route;
1737d6ee62d2Seric 		*pmx = mx;
1738c5acbec8Seric 		log_debug("debug: mta-routing: selecting candidate route %s",
1739c5acbec8Seric 		    mta_route_to_text(route));
174065c4fdfbSgilles 	}
174165c4fdfbSgilles 
174265c4fdfbSgilles 	if (best)
174365c4fdfbSgilles 		return (best);
174465c4fdfbSgilles 
1745c5acbec8Seric 	/* Order is important */
174665c4fdfbSgilles 	if (seen == 0) {
1747c5acbec8Seric 		log_info("smtp-out: No MX found for %s",
174865c4fdfbSgilles 		    mta_connector_to_text(c));
1749c5acbec8Seric 		c->flags |= CONNECTOR_ERROR_MX;
175065c4fdfbSgilles 	}
175165c4fdfbSgilles 	else if (limit_route) {
1752c5acbec8Seric 		log_debug("debug: mta: hit route limit");
1753c5acbec8Seric 		*limits |= CONNECTOR_LIMIT_ROUTE;
175465c4fdfbSgilles 	}
175565c4fdfbSgilles 	else if (limit_host) {
1756c5acbec8Seric 		log_debug("debug: mta: hit host limit");
1757c5acbec8Seric 		*limits |= CONNECTOR_LIMIT_HOST;
1758c5acbec8Seric 	}
1759c5acbec8Seric 	else if (tm) {
1760c5acbec8Seric 		if (tm > *nextconn)
1761c5acbec8Seric 			*nextconn = tm;
176265c4fdfbSgilles 	}
1763299c4efeSeric 	else if (family_mismatch) {
1764c5acbec8Seric 		log_info("smtp-out: Address family mismatch on %s",
1765299c4efeSeric 		    mta_connector_to_text(c));
1766c5acbec8Seric 		c->flags |= CONNECTOR_ERROR_FAMILY;
1767c5acbec8Seric 	}
1768c5acbec8Seric 	else if (suspended_route) {
1769c5acbec8Seric 		log_info("smtp-out: No valid route for %s",
1770c5acbec8Seric 		    mta_connector_to_text(c));
1771c5acbec8Seric 		if (suspended_route & ROUTE_DISABLED_NET)
1772c5acbec8Seric 			c->flags |= CONNECTOR_ERROR_ROUTE_NET;
1773c5acbec8Seric 		if (suspended_route & ROUTE_DISABLED_SMTP)
1774c5acbec8Seric 			c->flags |= CONNECTOR_ERROR_ROUTE_SMTP;
1775299c4efeSeric 	}
177665c4fdfbSgilles 
177765c4fdfbSgilles 	return (NULL);
177865c4fdfbSgilles }
177965c4fdfbSgilles 
178065c4fdfbSgilles static void
mta_log(const struct mta_envelope * evp,const char * prefix,const char * source,const char * relay,const char * status)1781299c4efeSeric mta_log(const struct mta_envelope *evp, const char *prefix, const char *source,
1782299c4efeSeric     const char *relay, const char *status)
178365c4fdfbSgilles {
1784cf540174Sgilles 	log_info("%016"PRIx64" mta delivery evpid=%016"PRIx64" "
1785a206140dSgiovanni 	    "from=<%s> to=<%s> rcpt=<%s> source=\"%s\" "
1786a206140dSgiovanni 	    "relay=\"%s\" delay=%s result=\"%s\" stat=\"%s\"",
1787c5acbec8Seric 	    evp->session,
17887a93bdaaSgilles 	    evp->id,
1789399c053eSeric 	    evp->task->sender,
1790399c053eSeric 	    evp->dest,
1791c5acbec8Seric 	    evp->rcpt ? evp->rcpt : "-",
1792c5acbec8Seric 	    source ? source : "-",
179365c4fdfbSgilles 	    relay,
179465c4fdfbSgilles 	    duration_to_text(time(NULL) - evp->creation),
17957a93bdaaSgilles 	    prefix,
179665c4fdfbSgilles 	    status);
179765c4fdfbSgilles }
179865c4fdfbSgilles 
179965c4fdfbSgilles static struct mta_relay *
mta_relay(struct envelope * e,struct relayhost * relayh)1800ead27be3Seric mta_relay(struct envelope *e, struct relayhost *relayh)
180165c4fdfbSgilles {
1802f0965f72Seric 	struct dispatcher	*dispatcher;
180365c4fdfbSgilles 	struct mta_relay	 key, *r;
180465c4fdfbSgilles 
1805f0965f72Seric 	dispatcher = dict_xget(env->sc_dispatchers, e->dispatcher);
1806f0965f72Seric 
1807c1392a69Seric 	memset(&key, 0, sizeof key);
180865c4fdfbSgilles 
1809f0965f72Seric 	key.pki_name = dispatcher->u.remote.pki;
1810f0965f72Seric 	key.ca_name = dispatcher->u.remote.ca;
1811f0965f72Seric 	key.authtable = dispatcher->u.remote.auth;
1812f0965f72Seric 	key.sourcetable = dispatcher->u.remote.source;
1813f0965f72Seric 	key.helotable = dispatcher->u.remote.helo_source;
1814f0965f72Seric 	key.heloname = dispatcher->u.remote.helo;
1815c78098c5Sgilles 	key.srs = dispatcher->u.remote.srs;
1816f0965f72Seric 
1817d0a6e81fSeric 	if (relayh->hostname[0]) {
1818d0a6e81fSeric 		key.domain = mta_domain(relayh->hostname, 1);
1819d0a6e81fSeric 	}
1820d0a6e81fSeric 	else {
1821d0a6e81fSeric 		key.domain = mta_domain(e->dest.domain, 0);
18229c44f234Seric 		if (dispatcher->u.remote.backup) {
18239c44f234Seric 			key.backupname = dispatcher->u.remote.backupmx;
18249c44f234Seric 			if (key.backupname == NULL)
18259c44f234Seric 				key.backupname = e->smtpname;
1826d0a6e81fSeric 		}
182765c4fdfbSgilles 	}
182865c4fdfbSgilles 
18294c503616Seric 	key.tls = relayh->tls;
1830ead27be3Seric 	key.flags |= relayh->flags;
1831ead27be3Seric 	key.port = relayh->port;
1832ead27be3Seric 	key.authlabel = relayh->authlabel;
183365c4fdfbSgilles 	if (!key.authlabel[0])
183465c4fdfbSgilles 		key.authlabel = NULL;
183565c4fdfbSgilles 
183665c4fdfbSgilles 	if ((r = SPLAY_FIND(mta_relay_tree, &relays, &key)) == NULL) {
1837118c16f3Sgilles 		r = xcalloc(1, sizeof *r);
183865c4fdfbSgilles 		TAILQ_INIT(&r->tasks);
183965c4fdfbSgilles 		r->id = generate_uid();
1840dd6d690aSgilles 		r->dispatcher = dispatcher;
18414c503616Seric 		r->tls = key.tls;
184265c4fdfbSgilles 		r->flags = key.flags;
184365c4fdfbSgilles 		r->domain = key.domain;
184465c4fdfbSgilles 		r->backupname = key.backupname ?
1845118c16f3Sgilles 		    xstrdup(key.backupname) : NULL;
184665c4fdfbSgilles 		r->backuppref = -1;
184765c4fdfbSgilles 		r->port = key.port;
1848118c16f3Sgilles 		r->pki_name = key.pki_name ? xstrdup(key.pki_name) : NULL;
1849118c16f3Sgilles 		r->ca_name = key.ca_name ? xstrdup(key.ca_name) : NULL;
185065c4fdfbSgilles 		if (key.authtable)
1851118c16f3Sgilles 			r->authtable = xstrdup(key.authtable);
185265c4fdfbSgilles 		if (key.authlabel)
1853118c16f3Sgilles 			r->authlabel = xstrdup(key.authlabel);
185465c4fdfbSgilles 		if (key.sourcetable)
1855118c16f3Sgilles 			r->sourcetable = xstrdup(key.sourcetable);
185665c4fdfbSgilles 		if (key.helotable)
1857118c16f3Sgilles 			r->helotable = xstrdup(key.helotable);
1858b61c9c75Seric 		if (key.heloname)
1859118c16f3Sgilles 			r->heloname = xstrdup(key.heloname);
1860c78098c5Sgilles 		r->srs = key.srs;
186165c4fdfbSgilles 		SPLAY_INSERT(mta_relay_tree, &relays, r);
186265c4fdfbSgilles 		stat_increment("mta.relay", 1);
186365c4fdfbSgilles 	} else {
186465c4fdfbSgilles 		mta_domain_unref(key.domain); /* from here */
186565c4fdfbSgilles 	}
186665c4fdfbSgilles 
186765c4fdfbSgilles 	r->refcount++;
186865c4fdfbSgilles 	return (r);
186965c4fdfbSgilles }
187065c4fdfbSgilles 
187165c4fdfbSgilles static void
mta_relay_ref(struct mta_relay * r)187265c4fdfbSgilles mta_relay_ref(struct mta_relay *r)
187365c4fdfbSgilles {
187465c4fdfbSgilles 	r->refcount++;
187565c4fdfbSgilles }
187665c4fdfbSgilles 
187765c4fdfbSgilles static void
mta_relay_unref(struct mta_relay * relay)187865c4fdfbSgilles mta_relay_unref(struct mta_relay *relay)
187965c4fdfbSgilles {
188065c4fdfbSgilles 	struct mta_connector	*c;
188165c4fdfbSgilles 
188265c4fdfbSgilles 	if (--relay->refcount)
188365c4fdfbSgilles 		return;
188465c4fdfbSgilles 
18856dc81a07Seric 	/* Make sure they are no envelopes held for this relay */
1886704aa3edSeric 	if (relay->state & RELAY_HOLDQ) {
1887aa1d5973Seric 		m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1);
18886dc81a07Seric 		m_add_id(p_queue, relay->id);
18896dc81a07Seric 		m_add_int(p_queue, 0);
18906dc81a07Seric 		m_close(p_queue);
1891704aa3edSeric 	}
18926dc81a07Seric 
189365c4fdfbSgilles 	log_debug("debug: mta: freeing %s", mta_relay_to_text(relay));
189465c4fdfbSgilles 	SPLAY_REMOVE(mta_relay_tree, &relays, relay);
1895edae97a5Seric 
18967d590283Seric 	while ((tree_poproot(&relay->connectors, NULL, (void**)&c)))
18977d590283Seric 		mta_connector_free(c);
18987d590283Seric 
189965c4fdfbSgilles 	free(relay->authlabel);
1900edae97a5Seric 	free(relay->authtable);
1901edae97a5Seric 	free(relay->backupname);
19021c3ac238Seric 	free(relay->pki_name);
1903f7aa1c30Sgilles 	free(relay->ca_name);
1904edae97a5Seric 	free(relay->helotable);
1905b61c9c75Seric 	free(relay->heloname);
1906edae97a5Seric 	free(relay->secret);
1907edae97a5Seric 	free(relay->sourcetable);
190865c4fdfbSgilles 
190965c4fdfbSgilles 	mta_domain_unref(relay->domain); /* from constructor */
191065c4fdfbSgilles 	free(relay);
191165c4fdfbSgilles 	stat_decrement("mta.relay", 1);
1912ac5aa4b9Seric }
1913ac5aa4b9Seric 
1914ac5aa4b9Seric const char *
mta_relay_to_text(struct mta_relay * relay)191565c4fdfbSgilles mta_relay_to_text(struct mta_relay *relay)
1916ac5aa4b9Seric {
1917ac5aa4b9Seric 	static char	 buf[1024];
1918e009e7d3Seric 	char		 tmp[32];
191965c4fdfbSgilles 	const char	*sep = ",";
1920ac5aa4b9Seric 
1921769fc316Sgilles 	(void)snprintf(buf, sizeof buf, "[relay:%s", relay->domain->name);
1922ac5aa4b9Seric 
192365c4fdfbSgilles 	if (relay->port) {
1924769fc316Sgilles 		(void)strlcat(buf, sep, sizeof buf);
1925769fc316Sgilles 		(void)snprintf(tmp, sizeof tmp, "port=%d", (int)relay->port);
1926769fc316Sgilles 		(void)strlcat(buf, tmp, sizeof buf);
1927e009e7d3Seric 	}
1928e009e7d3Seric 
1929769fc316Sgilles 	(void)strlcat(buf, sep, sizeof buf);
19304c503616Seric 	switch(relay->tls) {
19314c503616Seric 	case RELAY_TLS_OPPORTUNISTIC:
19324c503616Seric 		(void)strlcat(buf, "smtp", sizeof buf);
19334c503616Seric 		break;
19344c503616Seric 	case RELAY_TLS_STARTTLS:
19354c503616Seric 		(void)strlcat(buf, "smtp+tls", sizeof buf);
19364c503616Seric 		break;
19374c503616Seric 	case RELAY_TLS_SMTPS:
1938769fc316Sgilles 		(void)strlcat(buf, "smtps", sizeof buf);
19394c503616Seric 		break;
19404c503616Seric 	case RELAY_TLS_NO:
19414c503616Seric 		if (relay->flags & RELAY_LMTP)
19424c503616Seric 			(void)strlcat(buf, "lmtp", sizeof buf);
19434c503616Seric 		else
19444c503616Seric 			(void)strlcat(buf, "smtp+notls", sizeof buf);
19454c503616Seric 		break;
19464c503616Seric 	default:
19474c503616Seric 		(void)strlcat(buf, "???", sizeof buf);
1948ac5aa4b9Seric 	}
1949ac5aa4b9Seric 
195065c4fdfbSgilles 	if (relay->flags & RELAY_AUTH) {
1951769fc316Sgilles 		(void)strlcat(buf, sep, sizeof buf);
1952769fc316Sgilles 		(void)strlcat(buf, "auth=", sizeof buf);
1953769fc316Sgilles 		(void)strlcat(buf, relay->authtable, sizeof buf);
1954769fc316Sgilles 		(void)strlcat(buf, ":", sizeof buf);
1955769fc316Sgilles 		(void)strlcat(buf, relay->authlabel, sizeof buf);
1956ac5aa4b9Seric 	}
1957ac5aa4b9Seric 
19581c3ac238Seric 	if (relay->pki_name) {
1959769fc316Sgilles 		(void)strlcat(buf, sep, sizeof buf);
1960769fc316Sgilles 		(void)strlcat(buf, "pki_name=", sizeof buf);
1961769fc316Sgilles 		(void)strlcat(buf, relay->pki_name, sizeof buf);
1962ac5aa4b9Seric 	}
1963ac5aa4b9Seric 
19641a271186Seric 	if (relay->domain->as_host) {
1965769fc316Sgilles 		(void)strlcat(buf, sep, sizeof buf);
1966769fc316Sgilles 		(void)strlcat(buf, "mx", sizeof buf);
1967ac5aa4b9Seric 	}
1968a2389673Seric 
1969b61c9c75Seric 	if (relay->backupname) {
1970769fc316Sgilles 		(void)strlcat(buf, sep, sizeof buf);
1971769fc316Sgilles 		(void)strlcat(buf, "backup=", sizeof buf);
1972769fc316Sgilles 		(void)strlcat(buf, relay->backupname, sizeof buf);
197365c4fdfbSgilles 	}
197465c4fdfbSgilles 
197565c4fdfbSgilles 	if (relay->sourcetable) {
1976769fc316Sgilles 		(void)strlcat(buf, sep, sizeof buf);
1977769fc316Sgilles 		(void)strlcat(buf, "sourcetable=", sizeof buf);
1978769fc316Sgilles 		(void)strlcat(buf, relay->sourcetable, sizeof buf);
1979a2389673Seric 	}
1980a2389673Seric 
1981b61c9c75Seric 	if (relay->helotable) {
1982769fc316Sgilles 		(void)strlcat(buf, sep, sizeof buf);
1983769fc316Sgilles 		(void)strlcat(buf, "helotable=", sizeof buf);
1984769fc316Sgilles 		(void)strlcat(buf, relay->helotable, sizeof buf);
1985b61c9c75Seric 	}
1986b61c9c75Seric 
1987b61c9c75Seric 	if (relay->heloname) {
1988769fc316Sgilles 		(void)strlcat(buf, sep, sizeof buf);
1989769fc316Sgilles 		(void)strlcat(buf, "heloname=", sizeof buf);
1990769fc316Sgilles 		(void)strlcat(buf, relay->heloname, sizeof buf);
1991b61c9c75Seric 	}
1992b61c9c75Seric 
1993769fc316Sgilles 	(void)strlcat(buf, "]", sizeof buf);
1994ac5aa4b9Seric 
1995ac5aa4b9Seric 	return (buf);
1996ac5aa4b9Seric }
1997ac5aa4b9Seric 
1998f9a337f4Seric static void
mta_relay_show(struct mta_relay * r,struct mproc * p,uint32_t id,time_t t)1999f9a337f4Seric mta_relay_show(struct mta_relay *r, struct mproc *p, uint32_t id, time_t t)
2000f9a337f4Seric {
2001f9a337f4Seric 	struct mta_connector	*c;
2002f9a337f4Seric 	void			*iter;
2003f9a337f4Seric 	char			 buf[1024], flags[1024], dur[64];
2004f9a337f4Seric 	time_t			 to;
2005f9a337f4Seric 
2006f9a337f4Seric 	flags[0] = '\0';
2007f9a337f4Seric 
2008f9a337f4Seric #define SHOWSTATUS(f, n) do {							\
2009f9a337f4Seric 		if (r->status & (f)) {						\
2010f9a337f4Seric 			if (flags[0])						\
2011769fc316Sgilles 				(void)strlcat(flags, ",", sizeof(flags));	\
2012769fc316Sgilles 			(void)strlcat(flags, (n), sizeof(flags));		\
2013f9a337f4Seric 		}								\
2014f9a337f4Seric 	} while(0)
2015f9a337f4Seric 
2016f9a337f4Seric 	SHOWSTATUS(RELAY_WAIT_MX, "MX");
2017f9a337f4Seric 	SHOWSTATUS(RELAY_WAIT_PREFERENCE, "preference");
2018f9a337f4Seric 	SHOWSTATUS(RELAY_WAIT_SECRET, "secret");
2019f9a337f4Seric 	SHOWSTATUS(RELAY_WAIT_LIMITS, "limits");
2020f9a337f4Seric 	SHOWSTATUS(RELAY_WAIT_SOURCE, "source");
2021f9a337f4Seric 	SHOWSTATUS(RELAY_WAIT_CONNECTOR, "connector");
2022f9a337f4Seric #undef SHOWSTATUS
2023f9a337f4Seric 
202428efdb08Seric 	if (runq_pending(runq_relay, r, &to))
2025769fc316Sgilles 		(void)snprintf(dur, sizeof(dur), "%s", duration_to_text(to - t));
2026f9a337f4Seric 	else
2027769fc316Sgilles 		(void)strlcpy(dur, "-", sizeof(dur));
2028f9a337f4Seric 
2029769fc316Sgilles 	(void)snprintf(buf, sizeof(buf), "%s refcount=%d ntask=%zu nconn=%zu lastconn=%s timeout=%s wait=%s%s",
2030f9a337f4Seric 	    mta_relay_to_text(r),
2031f9a337f4Seric 	    r->refcount,
2032f9a337f4Seric 	    r->ntask,
2033f9a337f4Seric 	    r->nconn,
2034f9a337f4Seric 	    r->lastconn ? duration_to_text(t - r->lastconn) : "-",
2035f9a337f4Seric 	    dur,
2036f9a337f4Seric 	    flags,
2037f9a337f4Seric 	    (r->state & RELAY_ONHOLD) ? "ONHOLD" : "");
2038f9a337f4Seric 	m_compose(p, IMSG_CTL_MTA_SHOW_RELAYS, id, 0, -1, buf, strlen(buf) + 1);
2039f9a337f4Seric 
2040f9a337f4Seric 	iter = NULL;
2041f9a337f4Seric 	while (tree_iter(&r->connectors, &iter, NULL, (void **)&c)) {
2042f9a337f4Seric 
204328efdb08Seric 		if (runq_pending(runq_connector, c, &to))
2044769fc316Sgilles 			(void)snprintf(dur, sizeof(dur), "%s", duration_to_text(to - t));
2045f9a337f4Seric 		else
2046769fc316Sgilles 			(void)strlcpy(dur, "-", sizeof(dur));
2047f9a337f4Seric 
2048f9a337f4Seric 		flags[0] = '\0';
2049f9a337f4Seric 
2050f9a337f4Seric #define SHOWFLAG(f, n) do {							\
2051f9a337f4Seric 		if (c->flags & (f)) {						\
2052f9a337f4Seric 			if (flags[0])						\
2053769fc316Sgilles 				(void)strlcat(flags, ",", sizeof(flags));	\
2054769fc316Sgilles 			(void)strlcat(flags, (n), sizeof(flags));		\
2055f9a337f4Seric 		}								\
2056f9a337f4Seric 	} while(0)
2057f9a337f4Seric 
2058f9a337f4Seric 		SHOWFLAG(CONNECTOR_NEW,		"NEW");
2059f9a337f4Seric 		SHOWFLAG(CONNECTOR_WAIT,	"WAIT");
2060f9a337f4Seric 
2061f9a337f4Seric 		SHOWFLAG(CONNECTOR_ERROR_FAMILY,	"ERROR_FAMILY");
2062f9a337f4Seric 		SHOWFLAG(CONNECTOR_ERROR_SOURCE,	"ERROR_SOURCE");
2063f9a337f4Seric 		SHOWFLAG(CONNECTOR_ERROR_MX,		"ERROR_MX");
2064f9a337f4Seric 		SHOWFLAG(CONNECTOR_ERROR_ROUTE_NET,	"ERROR_ROUTE_NET");
2065f9a337f4Seric 		SHOWFLAG(CONNECTOR_ERROR_ROUTE_SMTP,	"ERROR_ROUTE_SMTP");
20665b6a9ce9Seric 		SHOWFLAG(CONNECTOR_ERROR_BLOCKED,	"ERROR_BLOCKED");
2067f9a337f4Seric 
2068f9a337f4Seric 		SHOWFLAG(CONNECTOR_LIMIT_HOST,		"LIMIT_HOST");
2069f9a337f4Seric 		SHOWFLAG(CONNECTOR_LIMIT_ROUTE,		"LIMIT_ROUTE");
2070f9a337f4Seric 		SHOWFLAG(CONNECTOR_LIMIT_SOURCE,	"LIMIT_SOURCE");
2071f9a337f4Seric 		SHOWFLAG(CONNECTOR_LIMIT_RELAY,		"LIMIT_RELAY");
2072f9a337f4Seric 		SHOWFLAG(CONNECTOR_LIMIT_CONN,		"LIMIT_CONN");
2073f9a337f4Seric 		SHOWFLAG(CONNECTOR_LIMIT_DOMAIN,	"LIMIT_DOMAIN");
2074f9a337f4Seric #undef SHOWFLAG
2075f9a337f4Seric 
2076769fc316Sgilles 		(void)snprintf(buf, sizeof(buf),
2077f9a337f4Seric 		    "  connector %s refcount=%d nconn=%zu lastconn=%s timeout=%s flags=%s",
2078f9a337f4Seric 		    mta_source_to_text(c->source),
2079f9a337f4Seric 		    c->refcount,
2080f9a337f4Seric 		    c->nconn,
2081f9a337f4Seric 		    c->lastconn ? duration_to_text(t - c->lastconn) : "-",
2082f9a337f4Seric 		    dur,
2083f9a337f4Seric 		    flags);
2084f9a337f4Seric 		m_compose(p, IMSG_CTL_MTA_SHOW_RELAYS, id, 0, -1, buf,
2085f9a337f4Seric 		    strlen(buf) + 1);
2086f9a337f4Seric 
2087f9a337f4Seric 
2088f9a337f4Seric 	}
2089f9a337f4Seric }
2090f9a337f4Seric 
2091ac5aa4b9Seric static int
mta_relay_cmp(const struct mta_relay * a,const struct mta_relay * b)209265c4fdfbSgilles mta_relay_cmp(const struct mta_relay *a, const struct mta_relay *b)
2093ac5aa4b9Seric {
2094ac5aa4b9Seric 	int	r;
2095ac5aa4b9Seric 
209665c4fdfbSgilles 	if (a->domain < b->domain)
209765c4fdfbSgilles 		return (-1);
209865c4fdfbSgilles 	if (a->domain > b->domain)
209965c4fdfbSgilles 		return (1);
210065c4fdfbSgilles 
21014c503616Seric 	if (a->tls < b->tls)
21024c503616Seric 		return (-1);
21034c503616Seric 	if (a->tls > b->tls)
21044c503616Seric 		return (1);
21054c503616Seric 
2106ac5aa4b9Seric 	if (a->flags < b->flags)
2107ac5aa4b9Seric 		return (-1);
2108ac5aa4b9Seric 	if (a->flags > b->flags)
2109ac5aa4b9Seric 		return (1);
2110ac5aa4b9Seric 
2111ac5aa4b9Seric 	if (a->port < b->port)
2112ac5aa4b9Seric 		return (-1);
2113ac5aa4b9Seric 	if (a->port > b->port)
2114ac5aa4b9Seric 		return (1);
2115ac5aa4b9Seric 
211665c4fdfbSgilles 	if (a->authtable == NULL && b->authtable)
2117ac5aa4b9Seric 		return (-1);
211865c4fdfbSgilles 	if (a->authtable && b->authtable == NULL)
2119ac5aa4b9Seric 		return (1);
212065c4fdfbSgilles 	if (a->authtable && ((r = strcmp(a->authtable, b->authtable))))
212165c4fdfbSgilles 		return (r);
212267608b6dSgilles 	if (a->authlabel == NULL && b->authlabel)
212367608b6dSgilles 		return (-1);
212467608b6dSgilles 	if (a->authlabel && b->authlabel == NULL)
212567608b6dSgilles 		return (1);
212665c4fdfbSgilles 	if (a->authlabel && ((r = strcmp(a->authlabel, b->authlabel))))
212765c4fdfbSgilles 		return (r);
212865c4fdfbSgilles 	if (a->sourcetable == NULL && b->sourcetable)
212965c4fdfbSgilles 		return (-1);
213065c4fdfbSgilles 	if (a->sourcetable && b->sourcetable == NULL)
213165c4fdfbSgilles 		return (1);
213265c4fdfbSgilles 	if (a->sourcetable && ((r = strcmp(a->sourcetable, b->sourcetable))))
2133ac5aa4b9Seric 		return (r);
2134b61c9c75Seric 	if (a->helotable == NULL && b->helotable)
2135b61c9c75Seric 		return (-1);
2136b61c9c75Seric 	if (a->helotable && b->helotable == NULL)
2137b61c9c75Seric 		return (1);
2138b61c9c75Seric 	if (a->helotable && ((r = strcmp(a->helotable, b->helotable))))
2139b61c9c75Seric 		return (r);
2140b61c9c75Seric 	if (a->heloname == NULL && b->heloname)
2141b61c9c75Seric 		return (-1);
2142b61c9c75Seric 	if (a->heloname && b->heloname == NULL)
2143b61c9c75Seric 		return (1);
2144b61c9c75Seric 	if (a->heloname && ((r = strcmp(a->heloname, b->heloname))))
2145b61c9c75Seric 		return (r);
2146ac5aa4b9Seric 
21471c3ac238Seric 	if (a->pki_name == NULL && b->pki_name)
2148ac5aa4b9Seric 		return (-1);
21491c3ac238Seric 	if (a->pki_name && b->pki_name == NULL)
2150ac5aa4b9Seric 		return (1);
21511c3ac238Seric 	if (a->pki_name && ((r = strcmp(a->pki_name, b->pki_name))))
2152ac5aa4b9Seric 		return (r);
2153ac5aa4b9Seric 
2154f7aa1c30Sgilles 	if (a->ca_name == NULL && b->ca_name)
2155f7aa1c30Sgilles 		return (-1);
2156f7aa1c30Sgilles 	if (a->ca_name && b->ca_name == NULL)
2157f7aa1c30Sgilles 		return (1);
2158f7aa1c30Sgilles 	if (a->ca_name && ((r = strcmp(a->ca_name, b->ca_name))))
2159f7aa1c30Sgilles 		return (r);
2160f7aa1c30Sgilles 
216167608b6dSgilles 	if (a->backupname == NULL && b->backupname)
216267608b6dSgilles 		return (-1);
216367608b6dSgilles 	if (a->backupname && b->backupname == NULL)
216467608b6dSgilles 		return (1);
21656916573aSeric 	if (a->backupname && ((r = strcmp(a->backupname, b->backupname))))
21666916573aSeric 		return (r);
21676916573aSeric 
2168c78098c5Sgilles 	if (a->srs < b->srs)
2169c78098c5Sgilles 		return (-1);
2170c78098c5Sgilles 	if (a->srs > b->srs)
2171c78098c5Sgilles 		return (1);
2172c78098c5Sgilles 
217365c4fdfbSgilles 	return (0);
217465c4fdfbSgilles }
217565c4fdfbSgilles 
217665c4fdfbSgilles SPLAY_GENERATE(mta_relay_tree, mta_relay, entry, mta_relay_cmp);
217765c4fdfbSgilles 
217865c4fdfbSgilles static struct mta_host *
mta_host(const struct sockaddr * sa)217965c4fdfbSgilles mta_host(const struct sockaddr *sa)
218065c4fdfbSgilles {
218165c4fdfbSgilles 	struct mta_host		key, *h;
218265c4fdfbSgilles 	struct sockaddr_storage	ss;
218365c4fdfbSgilles 
218465c4fdfbSgilles 	memmove(&ss, sa, sa->sa_len);
218565c4fdfbSgilles 	key.sa = (struct sockaddr*)&ss;
218665c4fdfbSgilles 	h = SPLAY_FIND(mta_host_tree, &hosts, &key);
218765c4fdfbSgilles 
218865c4fdfbSgilles 	if (h == NULL) {
2189118c16f3Sgilles 		h = xcalloc(1, sizeof(*h));
2190118c16f3Sgilles 		h->sa = xmemdup(sa, sa->sa_len);
219165c4fdfbSgilles 		SPLAY_INSERT(mta_host_tree, &hosts, h);
219265c4fdfbSgilles 		stat_increment("mta.host", 1);
219365c4fdfbSgilles 	}
219465c4fdfbSgilles 
219565c4fdfbSgilles 	h->refcount++;
219665c4fdfbSgilles 	return (h);
219765c4fdfbSgilles }
219865c4fdfbSgilles 
219965c4fdfbSgilles static void
mta_host_ref(struct mta_host * h)220065c4fdfbSgilles mta_host_ref(struct mta_host *h)
220165c4fdfbSgilles {
220265c4fdfbSgilles 	h->refcount++;
220365c4fdfbSgilles }
220465c4fdfbSgilles 
220565c4fdfbSgilles static void
mta_host_unref(struct mta_host * h)220665c4fdfbSgilles mta_host_unref(struct mta_host *h)
220765c4fdfbSgilles {
220865c4fdfbSgilles 	if (--h->refcount)
220965c4fdfbSgilles 		return;
221065c4fdfbSgilles 
221165c4fdfbSgilles 	SPLAY_REMOVE(mta_host_tree, &hosts, h);
221265c4fdfbSgilles 	free(h->sa);
221365c4fdfbSgilles 	free(h->ptrname);
2214edae97a5Seric 	free(h);
221565c4fdfbSgilles 	stat_decrement("mta.host", 1);
221665c4fdfbSgilles }
221765c4fdfbSgilles 
221865c4fdfbSgilles const char *
mta_host_to_text(struct mta_host * h)221965c4fdfbSgilles mta_host_to_text(struct mta_host *h)
222065c4fdfbSgilles {
222165c4fdfbSgilles 	static char buf[1024];
222265c4fdfbSgilles 
222365c4fdfbSgilles 	if (h->ptrname)
2224769fc316Sgilles 		(void)snprintf(buf, sizeof buf, "%s (%s)",
222565c4fdfbSgilles 		    sa_to_text(h->sa), h->ptrname);
222665c4fdfbSgilles 	else
2227769fc316Sgilles 		(void)snprintf(buf, sizeof buf, "%s", sa_to_text(h->sa));
222865c4fdfbSgilles 
222965c4fdfbSgilles 	return (buf);
223065c4fdfbSgilles }
223165c4fdfbSgilles 
223265c4fdfbSgilles static int
mta_host_cmp(const struct mta_host * a,const struct mta_host * b)223365c4fdfbSgilles mta_host_cmp(const struct mta_host *a, const struct mta_host *b)
223465c4fdfbSgilles {
223565c4fdfbSgilles 	if (a->sa->sa_len < b->sa->sa_len)
223665c4fdfbSgilles 		return (-1);
223765c4fdfbSgilles 	if (a->sa->sa_len > b->sa->sa_len)
223865c4fdfbSgilles 		return (1);
223965c4fdfbSgilles 	return (memcmp(a->sa, b->sa, a->sa->sa_len));
224065c4fdfbSgilles }
224165c4fdfbSgilles 
224265c4fdfbSgilles SPLAY_GENERATE(mta_host_tree, mta_host, entry, mta_host_cmp);
224365c4fdfbSgilles 
224465c4fdfbSgilles static struct mta_domain *
mta_domain(char * name,int as_host)22451a271186Seric mta_domain(char *name, int as_host)
224665c4fdfbSgilles {
224765c4fdfbSgilles 	struct mta_domain	key, *d;
224865c4fdfbSgilles 
224965c4fdfbSgilles 	key.name = name;
22501a271186Seric 	key.as_host = as_host;
225165c4fdfbSgilles 	d = SPLAY_FIND(mta_domain_tree, &domains, &key);
225265c4fdfbSgilles 
225365c4fdfbSgilles 	if (d == NULL) {
2254118c16f3Sgilles 		d = xcalloc(1, sizeof(*d));
2255118c16f3Sgilles 		d->name = xstrdup(name);
22561a271186Seric 		d->as_host = as_host;
225765c4fdfbSgilles 		TAILQ_INIT(&d->mxs);
225865c4fdfbSgilles 		SPLAY_INSERT(mta_domain_tree, &domains, d);
225965c4fdfbSgilles 		stat_increment("mta.domain", 1);
226065c4fdfbSgilles 	}
226165c4fdfbSgilles 
226265c4fdfbSgilles 	d->refcount++;
226365c4fdfbSgilles 	return (d);
226465c4fdfbSgilles }
226565c4fdfbSgilles 
226665c4fdfbSgilles #if 0
226765c4fdfbSgilles static void
226865c4fdfbSgilles mta_domain_ref(struct mta_domain *d)
226965c4fdfbSgilles {
227065c4fdfbSgilles 	d->refcount++;
227165c4fdfbSgilles }
227265c4fdfbSgilles #endif
227365c4fdfbSgilles 
227465c4fdfbSgilles static void
mta_domain_unref(struct mta_domain * d)227565c4fdfbSgilles mta_domain_unref(struct mta_domain *d)
227665c4fdfbSgilles {
227765c4fdfbSgilles 	struct mta_mx	*mx;
227865c4fdfbSgilles 
227965c4fdfbSgilles 	if (--d->refcount)
228065c4fdfbSgilles 		return;
228165c4fdfbSgilles 
228265c4fdfbSgilles 	while ((mx = TAILQ_FIRST(&d->mxs))) {
228365c4fdfbSgilles 		TAILQ_REMOVE(&d->mxs, mx, entry);
228465c4fdfbSgilles 		mta_host_unref(mx->host); /* from IMSG_DNS_HOST */
2285d6ee62d2Seric 		free(mx->mxname);
228665c4fdfbSgilles 		free(mx);
228765c4fdfbSgilles 	}
228865c4fdfbSgilles 
228965c4fdfbSgilles 	SPLAY_REMOVE(mta_domain_tree, &domains, d);
229065c4fdfbSgilles 	free(d->name);
2291edae97a5Seric 	free(d);
229265c4fdfbSgilles 	stat_decrement("mta.domain", 1);
229365c4fdfbSgilles }
229465c4fdfbSgilles 
229565c4fdfbSgilles static int
mta_domain_cmp(const struct mta_domain * a,const struct mta_domain * b)229665c4fdfbSgilles mta_domain_cmp(const struct mta_domain *a, const struct mta_domain *b)
229765c4fdfbSgilles {
22981a271186Seric 	if (a->as_host < b->as_host)
229965c4fdfbSgilles 		return (-1);
23001a271186Seric 	if (a->as_host > b->as_host)
230165c4fdfbSgilles 		return (1);
230265c4fdfbSgilles 	return (strcasecmp(a->name, b->name));
230365c4fdfbSgilles }
230465c4fdfbSgilles 
230565c4fdfbSgilles SPLAY_GENERATE(mta_domain_tree, mta_domain, entry, mta_domain_cmp);
230665c4fdfbSgilles 
230765c4fdfbSgilles static struct mta_source *
mta_source(const struct sockaddr * sa)230865c4fdfbSgilles mta_source(const struct sockaddr *sa)
230965c4fdfbSgilles {
231065c4fdfbSgilles 	struct mta_source	key, *s;
231165c4fdfbSgilles 	struct sockaddr_storage	ss;
231265c4fdfbSgilles 
231365c4fdfbSgilles 	if (sa) {
231465c4fdfbSgilles 		memmove(&ss, sa, sa->sa_len);
231565c4fdfbSgilles 		key.sa = (struct sockaddr*)&ss;
231665c4fdfbSgilles 	} else
231765c4fdfbSgilles 		key.sa = NULL;
231865c4fdfbSgilles 	s = SPLAY_FIND(mta_source_tree, &sources, &key);
231965c4fdfbSgilles 
232065c4fdfbSgilles 	if (s == NULL) {
2321118c16f3Sgilles 		s = xcalloc(1, sizeof(*s));
232265c4fdfbSgilles 		if (sa)
2323118c16f3Sgilles 			s->sa = xmemdup(sa, sa->sa_len);
232465c4fdfbSgilles 		SPLAY_INSERT(mta_source_tree, &sources, s);
232565c4fdfbSgilles 		stat_increment("mta.source", 1);
232665c4fdfbSgilles 	}
232765c4fdfbSgilles 
232865c4fdfbSgilles 	s->refcount++;
232965c4fdfbSgilles 	return (s);
233065c4fdfbSgilles }
233165c4fdfbSgilles 
233265c4fdfbSgilles static void
mta_source_ref(struct mta_source * s)233365c4fdfbSgilles mta_source_ref(struct mta_source *s)
233465c4fdfbSgilles {
233565c4fdfbSgilles 	s->refcount++;
233665c4fdfbSgilles }
233765c4fdfbSgilles 
233865c4fdfbSgilles static void
mta_source_unref(struct mta_source * s)233965c4fdfbSgilles mta_source_unref(struct mta_source *s)
234065c4fdfbSgilles {
234165c4fdfbSgilles 	if (--s->refcount)
234265c4fdfbSgilles 		return;
234365c4fdfbSgilles 
234465c4fdfbSgilles 	SPLAY_REMOVE(mta_source_tree, &sources, s);
234565c4fdfbSgilles 	free(s->sa);
2346edae97a5Seric 	free(s);
234765c4fdfbSgilles 	stat_decrement("mta.source", 1);
234865c4fdfbSgilles }
234965c4fdfbSgilles 
235065c4fdfbSgilles static const char *
mta_source_to_text(struct mta_source * s)235165c4fdfbSgilles mta_source_to_text(struct mta_source *s)
235265c4fdfbSgilles {
235365c4fdfbSgilles 	static char buf[1024];
235465c4fdfbSgilles 
235565c4fdfbSgilles 	if (s->sa == NULL)
235665c4fdfbSgilles 		return "[]";
2357769fc316Sgilles 	(void)snprintf(buf, sizeof buf, "%s", sa_to_text(s->sa));
235865c4fdfbSgilles 	return (buf);
235965c4fdfbSgilles }
236065c4fdfbSgilles 
236165c4fdfbSgilles static int
mta_source_cmp(const struct mta_source * a,const struct mta_source * b)236265c4fdfbSgilles mta_source_cmp(const struct mta_source *a, const struct mta_source *b)
236365c4fdfbSgilles {
236465c4fdfbSgilles 	if (a->sa == NULL)
236565c4fdfbSgilles 		return ((b->sa == NULL) ? 0 : -1);
236665c4fdfbSgilles 	if (b->sa == NULL)
236765c4fdfbSgilles 		return (1);
236865c4fdfbSgilles 	if (a->sa->sa_len < b->sa->sa_len)
236965c4fdfbSgilles 		return (-1);
237065c4fdfbSgilles 	if (a->sa->sa_len > b->sa->sa_len)
237165c4fdfbSgilles 		return (1);
237265c4fdfbSgilles 	return (memcmp(a->sa, b->sa, a->sa->sa_len));
237365c4fdfbSgilles }
237465c4fdfbSgilles 
237565c4fdfbSgilles SPLAY_GENERATE(mta_source_tree, mta_source, entry, mta_source_cmp);
237665c4fdfbSgilles 
237765c4fdfbSgilles static struct mta_connector *
mta_connector(struct mta_relay * relay,struct mta_source * source)237865c4fdfbSgilles mta_connector(struct mta_relay *relay, struct mta_source *source)
237965c4fdfbSgilles {
238065c4fdfbSgilles 	struct mta_connector	*c;
238165c4fdfbSgilles 
238265c4fdfbSgilles 	c = tree_get(&relay->connectors, (uintptr_t)(source));
238365c4fdfbSgilles 	if (c == NULL) {
2384118c16f3Sgilles 		c = xcalloc(1, sizeof(*c));
238565c4fdfbSgilles 		c->relay = relay;
238665c4fdfbSgilles 		c->source = source;
2387c5acbec8Seric 		c->flags |= CONNECTOR_NEW;
238865c4fdfbSgilles 		mta_source_ref(source);
238965c4fdfbSgilles 		tree_xset(&relay->connectors, (uintptr_t)(source), c);
239065c4fdfbSgilles 		stat_increment("mta.connector", 1);
2391c5acbec8Seric 		log_debug("debug: mta: new %s", mta_connector_to_text(c));
239265c4fdfbSgilles 	}
239365c4fdfbSgilles 
239465c4fdfbSgilles 	return (c);
239565c4fdfbSgilles }
239665c4fdfbSgilles 
239765c4fdfbSgilles static void
mta_connector_free(struct mta_connector * c)239865c4fdfbSgilles mta_connector_free(struct mta_connector *c)
239965c4fdfbSgilles {
2400c5acbec8Seric 	log_debug("debug: mta: freeing %s",
2401c5acbec8Seric 	    mta_connector_to_text(c));
2402c5acbec8Seric 
2403c5acbec8Seric 	if (c->flags & CONNECTOR_WAIT) {
24045c9fb78eSeric 		log_debug("debug: mta: cancelling timeout for %s",
2405c5acbec8Seric 		    mta_connector_to_text(c));
240628efdb08Seric 		runq_cancel(runq_connector, c);
2407c5acbec8Seric 	}
2408c5acbec8Seric 	mta_source_unref(c->source); /* from constructor */
2409edae97a5Seric 	free(c);
2410c5acbec8Seric 
241165c4fdfbSgilles 	stat_decrement("mta.connector", 1);
241265c4fdfbSgilles }
241365c4fdfbSgilles 
241465c4fdfbSgilles static const char *
mta_connector_to_text(struct mta_connector * c)241565c4fdfbSgilles mta_connector_to_text(struct mta_connector *c)
241665c4fdfbSgilles {
241765c4fdfbSgilles 	static char buf[1024];
241865c4fdfbSgilles 
2419769fc316Sgilles 	(void)snprintf(buf, sizeof buf, "[connector:%s->%s,0x%x]",
242065c4fdfbSgilles 	    mta_source_to_text(c->source),
2421c5acbec8Seric 	    mta_relay_to_text(c->relay),
2422c5acbec8Seric 	    c->flags);
242365c4fdfbSgilles 	return (buf);
242465c4fdfbSgilles }
242565c4fdfbSgilles 
242665c4fdfbSgilles static struct mta_route *
mta_route(struct mta_source * src,struct mta_host * dst)242765c4fdfbSgilles mta_route(struct mta_source *src, struct mta_host *dst)
242865c4fdfbSgilles {
242965c4fdfbSgilles 	struct mta_route	key, *r;
2430c5acbec8Seric 	static uint64_t		rid = 0;
243165c4fdfbSgilles 
243265c4fdfbSgilles 	key.src = src;
243365c4fdfbSgilles 	key.dst = dst;
243465c4fdfbSgilles 	r = SPLAY_FIND(mta_route_tree, &routes, &key);
243565c4fdfbSgilles 
243665c4fdfbSgilles 	if (r == NULL) {
2437118c16f3Sgilles 		r = xcalloc(1, sizeof(*r));
243865c4fdfbSgilles 		r->src = src;
243965c4fdfbSgilles 		r->dst = dst;
2440c5acbec8Seric 		r->flags |= ROUTE_NEW;
2441c5acbec8Seric 		r->id = ++rid;
244265c4fdfbSgilles 		SPLAY_INSERT(mta_route_tree, &routes, r);
244365c4fdfbSgilles 		mta_source_ref(src);
244465c4fdfbSgilles 		mta_host_ref(dst);
244565c4fdfbSgilles 		stat_increment("mta.route", 1);
244665c4fdfbSgilles 	}
2447c5acbec8Seric 	else if (r->flags & ROUTE_RUNQ) {
24485c9fb78eSeric 		log_debug("debug: mta: mta_route_ref(): cancelling runq for route %s",
2449c5acbec8Seric 		    mta_route_to_text(r));
2450c5acbec8Seric 		r->flags &= ~(ROUTE_RUNQ | ROUTE_KEEPALIVE);
245128efdb08Seric 		runq_cancel(runq_route, r);
2452c5acbec8Seric 		r->refcount--; /* from mta_route_unref() */
2453c5acbec8Seric 	}
245465c4fdfbSgilles 
245565c4fdfbSgilles 	r->refcount++;
2456ac5aa4b9Seric 	return (r);
245765c4fdfbSgilles }
245865c4fdfbSgilles 
245965c4fdfbSgilles static void
mta_route_ref(struct mta_route * r)246065c4fdfbSgilles mta_route_ref(struct mta_route *r)
246165c4fdfbSgilles {
246265c4fdfbSgilles 	r->refcount++;
246365c4fdfbSgilles }
246465c4fdfbSgilles 
246565c4fdfbSgilles static void
mta_route_unref(struct mta_route * r)246665c4fdfbSgilles mta_route_unref(struct mta_route *r)
246765c4fdfbSgilles {
2468c5acbec8Seric 	time_t	sched, now;
2469c5acbec8Seric 	int	delay;
2470c5acbec8Seric 
247165c4fdfbSgilles 	if (--r->refcount)
247265c4fdfbSgilles 		return;
247365c4fdfbSgilles 
2474c5acbec8Seric 	/*
2475c5acbec8Seric 	 * Nothing references this route, but we might want to keep it alive
2476c5acbec8Seric 	 * for a while.
2477c5acbec8Seric 	 */
2478c5acbec8Seric 	now = time(NULL);
2479c5acbec8Seric 	sched = 0;
2480c5acbec8Seric 
2481c5acbec8Seric 	if (r->penalty) {
2482c5acbec8Seric #if DELAY_QUADRATIC
2483c5acbec8Seric 		delay = DELAY_ROUTE_BASE * r->penalty * r->penalty;
2484c5acbec8Seric #else
2485c5acbec8Seric 		delay = 15 * 60;
2486c5acbec8Seric #endif
2487c5acbec8Seric 		if (delay > DELAY_ROUTE_MAX)
2488c5acbec8Seric 			delay = DELAY_ROUTE_MAX;
2489c5acbec8Seric 		sched = r->lastpenalty + delay;
2490d7bcae4dSeric 		log_debug("debug: mta: mta_route_unref(): keeping route %s alive for %llus (penalty %d)",
2491c5acbec8Seric 		    mta_route_to_text(r), (unsigned long long) sched - now, r->penalty);
2492c5acbec8Seric 	} else if (!(r->flags & ROUTE_KEEPALIVE)) {
2493c5acbec8Seric 		if (r->lastconn + max_seen_conndelay_route > now)
2494c5acbec8Seric 			sched = r->lastconn + max_seen_conndelay_route;
2495c5acbec8Seric 		if (r->lastdisc + max_seen_discdelay_route > now &&
2496c5acbec8Seric 		    r->lastdisc + max_seen_discdelay_route < sched)
2497c5acbec8Seric 			sched = r->lastdisc + max_seen_discdelay_route;
2498c5acbec8Seric 
2499c5acbec8Seric 		if (sched > now)
2500c5acbec8Seric 			log_debug("debug: mta: mta_route_unref(): keeping route %s alive for %llus (imposed delay)",
2501c5acbec8Seric 			    mta_route_to_text(r), (unsigned long long) sched - now);
2502c5acbec8Seric 	}
2503c5acbec8Seric 
2504c5acbec8Seric 	if (sched > now) {
2505c5acbec8Seric 		r->flags |= ROUTE_RUNQ;
250628efdb08Seric 		runq_schedule_at(runq_route, sched, r);
2507c5acbec8Seric 		r->refcount++;
2508c5acbec8Seric 		return;
2509c5acbec8Seric 	}
2510c5acbec8Seric 
25115c9fb78eSeric 	log_debug("debug: mta: mta_route_unref(): really discarding route %s",
2512c5acbec8Seric 	    mta_route_to_text(r));
2513c5acbec8Seric 
251465c4fdfbSgilles 	SPLAY_REMOVE(mta_route_tree, &routes, r);
251565c4fdfbSgilles 	mta_source_unref(r->src); /* from constructor */
251665c4fdfbSgilles 	mta_host_unref(r->dst); /* from constructor */
2517edae97a5Seric 	free(r);
251865c4fdfbSgilles 	stat_decrement("mta.route", 1);
251965c4fdfbSgilles }
252065c4fdfbSgilles 
252165c4fdfbSgilles static const char *
mta_route_to_text(struct mta_route * r)252265c4fdfbSgilles mta_route_to_text(struct mta_route *r)
252365c4fdfbSgilles {
252465c4fdfbSgilles 	static char	buf[1024];
252565c4fdfbSgilles 
2526769fc316Sgilles 	(void)snprintf(buf, sizeof buf, "%s <-> %s",
252765c4fdfbSgilles 	    mta_source_to_text(r->src),
252865c4fdfbSgilles 	    mta_host_to_text(r->dst));
252965c4fdfbSgilles 
253065c4fdfbSgilles 	return (buf);
253165c4fdfbSgilles }
253265c4fdfbSgilles 
253365c4fdfbSgilles static int
mta_route_cmp(const struct mta_route * a,const struct mta_route * b)253465c4fdfbSgilles mta_route_cmp(const struct mta_route *a, const struct mta_route *b)
253565c4fdfbSgilles {
253665c4fdfbSgilles 	if (a->src < b->src)
253765c4fdfbSgilles 		return (-1);
253865c4fdfbSgilles 	if (a->src > b->src)
253965c4fdfbSgilles 		return (1);
254065c4fdfbSgilles 
254165c4fdfbSgilles 	if (a->dst < b->dst)
254265c4fdfbSgilles 		return (-1);
254365c4fdfbSgilles 	if (a->dst > b->dst)
254465c4fdfbSgilles 		return (1);
2545ac5aa4b9Seric 
2546ac5aa4b9Seric 	return (0);
2547ac5aa4b9Seric }
2548ac5aa4b9Seric 
2549ac5aa4b9Seric SPLAY_GENERATE(mta_route_tree, mta_route, entry, mta_route_cmp);
2550c5acbec8Seric 
25515b6a9ce9Seric void
mta_block(struct mta_source * src,char * dom)25525b6a9ce9Seric mta_block(struct mta_source *src, char *dom)
25535b6a9ce9Seric {
25545b6a9ce9Seric 	struct mta_block key, *b;
25555b6a9ce9Seric 
25565b6a9ce9Seric 	key.source = src;
25575b6a9ce9Seric 	key.domain = dom;
25585b6a9ce9Seric 
25595b6a9ce9Seric 	b = SPLAY_FIND(mta_block_tree, &blocks, &key);
25605b6a9ce9Seric 	if (b != NULL)
25615b6a9ce9Seric 		return;
25625b6a9ce9Seric 
2563118c16f3Sgilles 	b = xcalloc(1, sizeof(*b));
25645b6a9ce9Seric 	if (dom)
2565118c16f3Sgilles 		b->domain = xstrdup(dom);
25665b6a9ce9Seric 	b->source = src;
25675b6a9ce9Seric 	mta_source_ref(src);
25685b6a9ce9Seric 	SPLAY_INSERT(mta_block_tree, &blocks, b);
25695b6a9ce9Seric }
25705b6a9ce9Seric 
25715b6a9ce9Seric void
mta_unblock(struct mta_source * src,char * dom)25725b6a9ce9Seric mta_unblock(struct mta_source *src, char *dom)
25735b6a9ce9Seric {
25745b6a9ce9Seric 	struct mta_block key, *b;
25755b6a9ce9Seric 
25765b6a9ce9Seric 	key.source = src;
25775b6a9ce9Seric 	key.domain = dom;
25785b6a9ce9Seric 
25795b6a9ce9Seric 	b = SPLAY_FIND(mta_block_tree, &blocks, &key);
25805b6a9ce9Seric 	if (b == NULL)
25815b6a9ce9Seric 		return;
25825b6a9ce9Seric 
25835b6a9ce9Seric 	SPLAY_REMOVE(mta_block_tree, &blocks, b);
25845b6a9ce9Seric 
25855b6a9ce9Seric 	mta_source_unref(b->source);
25865b6a9ce9Seric 	free(b->domain);
25875b6a9ce9Seric 	free(b);
25885b6a9ce9Seric }
25895b6a9ce9Seric 
25905b6a9ce9Seric int
mta_is_blocked(struct mta_source * src,char * dom)25915b6a9ce9Seric mta_is_blocked(struct mta_source *src, char *dom)
25925b6a9ce9Seric {
25935b6a9ce9Seric 	struct mta_block key;
25945b6a9ce9Seric 
25955b6a9ce9Seric 	key.source = src;
25965b6a9ce9Seric 	key.domain = dom;
25975b6a9ce9Seric 
25985b6a9ce9Seric 	if (SPLAY_FIND(mta_block_tree, &blocks, &key))
25995b6a9ce9Seric 		return (1);
26005b6a9ce9Seric 
26015b6a9ce9Seric 	return (0);
26025b6a9ce9Seric }
26035b6a9ce9Seric 
26045b6a9ce9Seric static
26055b6a9ce9Seric int
mta_block_cmp(const struct mta_block * a,const struct mta_block * b)26065b6a9ce9Seric mta_block_cmp(const struct mta_block *a, const struct mta_block *b)
26075b6a9ce9Seric {
26085b6a9ce9Seric 	if (a->source < b->source)
26095b6a9ce9Seric 		return (-1);
26105b6a9ce9Seric 	if (a->source > b->source)
26115b6a9ce9Seric 		return (1);
26125b6a9ce9Seric 	if (!a->domain && b->domain)
26135b6a9ce9Seric 		return (-1);
26145b6a9ce9Seric 	if (a->domain && !b->domain)
26155b6a9ce9Seric 		return (1);
26165b6a9ce9Seric 	if (a->domain == b->domain)
26175b6a9ce9Seric 		return (0);
26183d4aee85Seric 	return (strcasecmp(a->domain, b->domain));
26195b6a9ce9Seric }
26205b6a9ce9Seric 
26215b6a9ce9Seric SPLAY_GENERATE(mta_block_tree, mta_block, entry, mta_block_cmp);
26225b6a9ce9Seric 
26235b6a9ce9Seric 
2624c5acbec8Seric 
2625c5acbec8Seric /* hoststat errors are not critical, we do best effort */
2626c5acbec8Seric void
mta_hoststat_update(const char * host,const char * error)2627c5acbec8Seric mta_hoststat_update(const char *host, const char *error)
2628c5acbec8Seric {
2629c5acbec8Seric 	struct hoststat	*hs = NULL;
2630953aae25Sderaadt 	char		 buf[HOST_NAME_MAX+1];
2631c5acbec8Seric 
2632c5acbec8Seric 	if (!lowercase(buf, host, sizeof buf))
2633c5acbec8Seric 		return;
2634c5acbec8Seric 
2635c5acbec8Seric 	hs = dict_get(&hoststat, buf);
2636c5acbec8Seric 	if (hs == NULL) {
26375b6a9ce9Seric 		if ((hs = calloc(1, sizeof *hs)) == NULL)
2638c5acbec8Seric 			return;
2639c5acbec8Seric 		tree_init(&hs->deferred);
264028efdb08Seric 		runq_schedule(runq_hoststat, HOSTSTAT_EXPIRE_DELAY, hs);
2641c5acbec8Seric 	}
2642769fc316Sgilles 	(void)strlcpy(hs->name, buf, sizeof hs->name);
2643769fc316Sgilles 	(void)strlcpy(hs->error, error, sizeof hs->error);
2644c5acbec8Seric 	hs->tm = time(NULL);
2645c5acbec8Seric 	dict_set(&hoststat, buf, hs);
2646c5acbec8Seric 
264728efdb08Seric 	runq_cancel(runq_hoststat, hs);
264828efdb08Seric 	runq_schedule(runq_hoststat, HOSTSTAT_EXPIRE_DELAY, hs);
2649c5acbec8Seric }
2650c5acbec8Seric 
2651c5acbec8Seric void
mta_hoststat_cache(const char * host,uint64_t evpid)2652c5acbec8Seric mta_hoststat_cache(const char *host, uint64_t evpid)
2653c5acbec8Seric {
2654c5acbec8Seric 	struct hoststat	*hs = NULL;
2655953aae25Sderaadt 	char buf[HOST_NAME_MAX+1];
2656c5acbec8Seric 
2657c5acbec8Seric 	if (!lowercase(buf, host, sizeof buf))
2658c5acbec8Seric 		return;
2659c5acbec8Seric 
2660c5acbec8Seric 	hs = dict_get(&hoststat, buf);
2661c5acbec8Seric 	if (hs == NULL)
2662c5acbec8Seric 		return;
2663c5acbec8Seric 
2664fde6b8cdSeric 	if (tree_count(&hs->deferred) >= env->sc_mta_max_deferred)
2665fde6b8cdSeric 		return;
2666fde6b8cdSeric 
2667c5acbec8Seric 	tree_set(&hs->deferred, evpid, NULL);
2668c5acbec8Seric }
2669c5acbec8Seric 
2670c5acbec8Seric void
mta_hoststat_uncache(const char * host,uint64_t evpid)2671c5acbec8Seric mta_hoststat_uncache(const char *host, uint64_t evpid)
2672c5acbec8Seric {
2673c5acbec8Seric 	struct hoststat	*hs = NULL;
2674953aae25Sderaadt 	char buf[HOST_NAME_MAX+1];
2675c5acbec8Seric 
2676c5acbec8Seric 	if (!lowercase(buf, host, sizeof buf))
2677c5acbec8Seric 		return;
2678c5acbec8Seric 
2679c5acbec8Seric 	hs = dict_get(&hoststat, buf);
2680c5acbec8Seric 	if (hs == NULL)
2681c5acbec8Seric 		return;
2682c5acbec8Seric 
2683c5acbec8Seric 	tree_pop(&hs->deferred, evpid);
2684c5acbec8Seric }
2685c5acbec8Seric 
2686c5acbec8Seric void
mta_hoststat_reschedule(const char * host)2687c5acbec8Seric mta_hoststat_reschedule(const char *host)
2688c5acbec8Seric {
2689c5acbec8Seric 	struct hoststat	*hs = NULL;
2690953aae25Sderaadt 	char		 buf[HOST_NAME_MAX+1];
2691c5acbec8Seric 	uint64_t	 evpid;
2692c5acbec8Seric 
2693c5acbec8Seric 	if (!lowercase(buf, host, sizeof buf))
2694c5acbec8Seric 		return;
2695c5acbec8Seric 
2696c5acbec8Seric 	hs = dict_get(&hoststat, buf);
2697c5acbec8Seric 	if (hs == NULL)
2698c5acbec8Seric 		return;
2699c5acbec8Seric 
2700c5acbec8Seric 	while (tree_poproot(&hs->deferred, &evpid, NULL)) {
2701c5acbec8Seric 		m_compose(p_queue, IMSG_MTA_SCHEDULE, 0, 0, -1,
2702c5acbec8Seric 		    &evpid, sizeof evpid);
2703c5acbec8Seric 	}
2704c5acbec8Seric }
2705c5acbec8Seric 
2706c5acbec8Seric static void
mta_hoststat_remove_entry(struct hoststat * hs)2707c5acbec8Seric mta_hoststat_remove_entry(struct hoststat *hs)
2708c5acbec8Seric {
2709c5acbec8Seric 	while (tree_poproot(&hs->deferred, NULL, NULL))
2710c5acbec8Seric 		;
2711c5acbec8Seric 	dict_pop(&hoststat, hs->name);
271228efdb08Seric 	runq_cancel(runq_hoststat, hs);
2713c5acbec8Seric }
2714