xref: /openbsd/usr.sbin/smtpd/mta.c (revision afc7e0ec)
1*afc7e0ecSeric /*	$OpenBSD: mta.c,v 1.167 2013/10/28 09:40:07 eric 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 
223ef9cbf7Sgilles #include <sys/types.h>
233ef9cbf7Sgilles #include <sys/queue.h>
243ef9cbf7Sgilles #include <sys/tree.h>
253ef9cbf7Sgilles #include <sys/socket.h>
263ef9cbf7Sgilles 
27b04e6f27Seric #include <ctype.h>
286d095224Schl #include <err.h>
293ef9cbf7Sgilles #include <errno.h>
303ef9cbf7Sgilles #include <event.h>
315eb8dddaSgilles #include <imsg.h>
32c50073caSchl #include <inttypes.h>
33eb143ecfSjacekm #include <netdb.h>
343ef9cbf7Sgilles #include <pwd.h>
359623339fSchl #include <signal.h>
363ef9cbf7Sgilles #include <stdio.h>
373ef9cbf7Sgilles #include <stdlib.h>
383ef9cbf7Sgilles #include <string.h>
39a44f0265Schl #include <time.h>
403ef9cbf7Sgilles #include <unistd.h>
413ef9cbf7Sgilles 
423ef9cbf7Sgilles #include "smtpd.h"
435eb8dddaSgilles #include "log.h"
443ef9cbf7Sgilles 
4565c4fdfbSgilles #define MAXERROR_PER_HOST	4
46ac5aa4b9Seric 
47c5acbec8Seric #define DELAY_CHECK_SOURCE	1
48c5acbec8Seric #define DELAY_CHECK_SOURCE_SLOW	10
49c5acbec8Seric #define DELAY_CHECK_SOURCE_FAST 0
50c5acbec8Seric #define DELAY_CHECK_LIMIT	5
51ac5aa4b9Seric 
52c5acbec8Seric #define	DELAY_QUADRATIC		1
53c5acbec8Seric #define DELAY_ROUTE_BASE	200
54c5acbec8Seric #define DELAY_ROUTE_MAX		(3600 * 4)
55ac5aa4b9Seric 
566dc81a07Seric #define RELAY_ONHOLD		0x01
576dc81a07Seric 
5865c4fdfbSgilles static void mta_imsg(struct mproc *, struct imsg *);
59be925435Sgilles static void mta_shutdown(void);
60be925435Sgilles static void mta_sig_handler(int, short, void *);
61ed1929b6Sjacekm 
6265c4fdfbSgilles static void mta_query_mx(struct mta_relay *);
6365c4fdfbSgilles static void mta_query_secret(struct mta_relay *);
6465c4fdfbSgilles static void mta_query_preference(struct mta_relay *);
6565c4fdfbSgilles static void mta_query_source(struct mta_relay *);
6665c4fdfbSgilles static void mta_on_mx(void *, void *, void *);
67c5acbec8Seric static void mta_on_secret(struct mta_relay *, const char *);
68c5acbec8Seric static void mta_on_preference(struct mta_relay *, int, int);
6965c4fdfbSgilles static void mta_on_source(struct mta_relay *, struct mta_source *);
70c5acbec8Seric static void mta_on_timeout(struct runq *, void *);
7165c4fdfbSgilles static void mta_connect(struct mta_connector *);
72c5acbec8Seric static void mta_route_enable(struct mta_route *);
73c5acbec8Seric static void mta_route_disable(struct mta_route *, int, int);
7465c4fdfbSgilles static void mta_drain(struct mta_relay *);
7565c4fdfbSgilles static void mta_flush(struct mta_relay *, int, const char *);
76c5acbec8Seric static struct mta_route *mta_find_route(struct mta_connector *, time_t, int*,
77c5acbec8Seric     time_t*);
78399c053eSeric static void mta_log(const struct mta_envelope *, const char *, const char *,
79299c4efeSeric     const char *, const char *);
80ac5aa4b9Seric 
8165c4fdfbSgilles SPLAY_HEAD(mta_relay_tree, mta_relay);
8265c4fdfbSgilles static struct mta_relay *mta_relay(struct envelope *);
8365c4fdfbSgilles static void mta_relay_ref(struct mta_relay *);
8465c4fdfbSgilles static void mta_relay_unref(struct mta_relay *);
8565c4fdfbSgilles static int mta_relay_cmp(const struct mta_relay *, const struct mta_relay *);
8665c4fdfbSgilles SPLAY_PROTOTYPE(mta_relay_tree, mta_relay, entry, mta_relay_cmp);
8765c4fdfbSgilles 
8865c4fdfbSgilles SPLAY_HEAD(mta_host_tree, mta_host);
8965c4fdfbSgilles static struct mta_host *mta_host(const struct sockaddr *);
9065c4fdfbSgilles static void mta_host_ref(struct mta_host *);
9165c4fdfbSgilles static void mta_host_unref(struct mta_host *);
9265c4fdfbSgilles static int mta_host_cmp(const struct mta_host *, const struct mta_host *);
9365c4fdfbSgilles SPLAY_PROTOTYPE(mta_host_tree, mta_host, entry, mta_host_cmp);
9465c4fdfbSgilles 
9565c4fdfbSgilles SPLAY_HEAD(mta_domain_tree, mta_domain);
9665c4fdfbSgilles static struct mta_domain *mta_domain(char *, int);
9765c4fdfbSgilles #if 0
9865c4fdfbSgilles static void mta_domain_ref(struct mta_domain *);
9965c4fdfbSgilles #endif
10065c4fdfbSgilles static void mta_domain_unref(struct mta_domain *);
10165c4fdfbSgilles static int mta_domain_cmp(const struct mta_domain *, const struct mta_domain *);
10265c4fdfbSgilles SPLAY_PROTOTYPE(mta_domain_tree, mta_domain, entry, mta_domain_cmp);
10365c4fdfbSgilles 
10465c4fdfbSgilles SPLAY_HEAD(mta_source_tree, mta_source);
10565c4fdfbSgilles static struct mta_source *mta_source(const struct sockaddr *);
10665c4fdfbSgilles static void mta_source_ref(struct mta_source *);
10765c4fdfbSgilles static void mta_source_unref(struct mta_source *);
10865c4fdfbSgilles static const char *mta_source_to_text(struct mta_source *);
10965c4fdfbSgilles static int mta_source_cmp(const struct mta_source *, const struct mta_source *);
11065c4fdfbSgilles SPLAY_PROTOTYPE(mta_source_tree, mta_source, entry, mta_source_cmp);
11165c4fdfbSgilles 
11265c4fdfbSgilles static struct mta_connector *mta_connector(struct mta_relay *,
11365c4fdfbSgilles     struct mta_source *);
11465c4fdfbSgilles static void mta_connector_free(struct mta_connector *);
11565c4fdfbSgilles static const char *mta_connector_to_text(struct mta_connector *);
11665c4fdfbSgilles 
11765c4fdfbSgilles SPLAY_HEAD(mta_route_tree, mta_route);
11865c4fdfbSgilles static struct mta_route *mta_route(struct mta_source *, struct mta_host *);
11965c4fdfbSgilles static void mta_route_ref(struct mta_route *);
12065c4fdfbSgilles static void mta_route_unref(struct mta_route *);
12165c4fdfbSgilles static const char *mta_route_to_text(struct mta_route *);
12265c4fdfbSgilles static int mta_route_cmp(const struct mta_route *, const struct mta_route *);
123ac5aa4b9Seric SPLAY_PROTOTYPE(mta_route_tree, mta_route, entry, mta_route_cmp);
124ac5aa4b9Seric 
12565c4fdfbSgilles static struct mta_relay_tree		relays;
12665c4fdfbSgilles static struct mta_domain_tree		domains;
12765c4fdfbSgilles static struct mta_host_tree		hosts;
12865c4fdfbSgilles static struct mta_source_tree		sources;
12965c4fdfbSgilles static struct mta_route_tree		routes;
13065c4fdfbSgilles 
13165c4fdfbSgilles static struct tree wait_mx;
13265c4fdfbSgilles static struct tree wait_preference;
13365c4fdfbSgilles static struct tree wait_secret;
13465c4fdfbSgilles static struct tree wait_source;
135ac5aa4b9Seric 
136c5acbec8Seric static struct runq *runq_relay;
137c5acbec8Seric static struct runq *runq_connector;
138c5acbec8Seric static struct runq *runq_route;
139c5acbec8Seric static struct runq *runq_hoststat;
140c5acbec8Seric 
141c5acbec8Seric static time_t	max_seen_conndelay_route;
142c5acbec8Seric static time_t	max_seen_discdelay_route;
143c5acbec8Seric 
144c5acbec8Seric #define	HOSTSTAT_EXPIRE_DELAY	(4 * 3600)
145c5acbec8Seric struct hoststat {
146c5acbec8Seric 	char			 name[SMTPD_MAXHOSTNAMELEN];
147c5acbec8Seric 	time_t			 tm;
148c5acbec8Seric 	char			 error[SMTPD_MAXLINESIZE];
149c5acbec8Seric 	struct tree		 deferred;
150c5acbec8Seric };
151c5acbec8Seric static struct dict hoststat;
152c5acbec8Seric 
153c5acbec8Seric void mta_hoststat_update(const char *, const char *);
154c5acbec8Seric void mta_hoststat_cache(const char *, uint64_t);
155c5acbec8Seric void mta_hoststat_uncache(const char *, uint64_t);
156c5acbec8Seric void mta_hoststat_reschedule(const char *);
157c5acbec8Seric static void mta_hoststat_remove_entry(struct hoststat *);
158c5acbec8Seric 
159c5acbec8Seric 
160ac5aa4b9Seric void
16165c4fdfbSgilles mta_imsg(struct mproc *p, struct imsg *imsg)
1623ef9cbf7Sgilles {
16365c4fdfbSgilles 	struct mta_relay	*relay;
164ac5aa4b9Seric 	struct mta_task		*task;
16565c4fdfbSgilles 	struct mta_domain	*domain;
166c5acbec8Seric 	struct mta_route	*route;
16765c4fdfbSgilles 	struct mta_mx		*mx, *imx;
168c5acbec8Seric 	struct hoststat		*hs;
169399c053eSeric 	struct mta_envelope	*e;
17065c4fdfbSgilles 	struct sockaddr_storage	 ss;
171399c053eSeric 	struct envelope		 evp;
17265c4fdfbSgilles 	struct msg		 m;
17365c4fdfbSgilles 	const char		*secret;
174c5acbec8Seric 	const char		*hostname;
17565c4fdfbSgilles 	uint64_t		 reqid;
176c5acbec8Seric 	time_t			 t;
177964ab7edSeric 	char			 buf[SMTPD_MAXLINESIZE];
17865c4fdfbSgilles 	int			 dnserror, preference, v, status;
179c5acbec8Seric 	void			*iter;
180c5acbec8Seric 	uint64_t		 u64;
181eb143ecfSjacekm 
18265c4fdfbSgilles 	if (p->proc == PROC_QUEUE) {
183ed1929b6Sjacekm 		switch (imsg->hdr.type) {
184ac5aa4b9Seric 
185c5acbec8Seric 		case IMSG_MTA_TRANSFER:
18665c4fdfbSgilles 			m_msg(&m, imsg);
187399c053eSeric 			m_get_envelope(&m, &evp);
18865c4fdfbSgilles 			m_end(&m);
189ac5aa4b9Seric 
190399c053eSeric 			relay = mta_relay(&evp);
1916dc81a07Seric 			/* ignore if we don't know the limits yet */
1926dc81a07Seric 			if (relay->limits &&
1936dc81a07Seric 			    relay->ntask >= (size_t)relay->limits->task_hiwat) {
1946dc81a07Seric 				if (!(relay->state & RELAY_ONHOLD)) {
1956dc81a07Seric 					log_info("smtp-out: hiwat reached on %s: holding envelopes",
1966dc81a07Seric 					    mta_relay_to_text(relay));
1976dc81a07Seric 					relay->state |= RELAY_ONHOLD;
1986dc81a07Seric 				}
1996dc81a07Seric 			}
2006dc81a07Seric 
2016dc81a07Seric 			/*
2026dc81a07Seric 			 * If the relay has too many pending tasks, tell the
2036dc81a07Seric 			 * scheduler to hold it until further notice
2046dc81a07Seric 			 */
2056dc81a07Seric 			if (relay->state & RELAY_ONHOLD) {
2066dc81a07Seric 				m_create(p_queue, IMSG_DELIVERY_HOLD, 0, 0, -1);
2076dc81a07Seric 				m_add_evpid(p_queue, evp.id);
2086dc81a07Seric 				m_add_id(p_queue, relay->id);
2096dc81a07Seric 				m_close(p_queue);
2106dc81a07Seric 				mta_relay_unref(relay);
2116dc81a07Seric 				return;
2126dc81a07Seric 			}
21365c4fdfbSgilles 
214c5acbec8Seric 			TAILQ_FOREACH(task, &relay->tasks, entry)
215c5acbec8Seric 				if (task->msgid == evpid_to_msgid(evp.id))
216c5acbec8Seric 					break;
217c5acbec8Seric 
218c5acbec8Seric 			if (task == NULL) {
219ac5aa4b9Seric 				task = xmalloc(sizeof *task, "mta_task");
220ac5aa4b9Seric 				TAILQ_INIT(&task->envelopes);
22165c4fdfbSgilles 				task->relay = relay;
222c5acbec8Seric 				relay->ntask += 1;
223c5acbec8Seric 				TAILQ_INSERT_TAIL(&relay->tasks, task, entry);
224399c053eSeric 				task->msgid = evpid_to_msgid(evp.id);
225399c053eSeric 				if (evp.sender.user[0] || evp.sender.domain[0])
226399c053eSeric 					snprintf(buf, sizeof buf, "%s@%s",
227399c053eSeric 					    evp.sender.user, evp.sender.domain);
228399c053eSeric 				else
229399c053eSeric 					buf[0] = '\0';
230399c053eSeric 				task->sender = xstrdup(buf, "mta_task:sender");
231c5acbec8Seric 				stat_increment("mta.task", 1);
232c5acbec8Seric 			}
233ac5aa4b9Seric 
234399c053eSeric 			e = xcalloc(1, sizeof *e, "mta_envelope");
235399c053eSeric 			e->id = evp.id;
236399c053eSeric 			e->creation = evp.creation;
237399c053eSeric 			snprintf(buf, sizeof buf, "%s@%s",
238399c053eSeric 			    evp.dest.user, evp.dest.domain);
239399c053eSeric 			e->dest = xstrdup(buf, "mta_envelope:dest");
240399c053eSeric 			snprintf(buf, sizeof buf, "%s@%s",
241399c053eSeric 			    evp.rcpt.user, evp.rcpt.domain);
242399c053eSeric 			if (strcmp(buf, e->dest))
243399c053eSeric 				e->rcpt = xstrdup(buf, "mta_envelope:rcpt");
244399c053eSeric 			e->task = task;
245c5acbec8Seric 
246ac5aa4b9Seric 			TAILQ_INSERT_TAIL(&task->envelopes, e, entry);
24765c4fdfbSgilles 			log_debug("debug: mta: received evp:%016" PRIx64
248399c053eSeric 			    " for <%s>", e->id, e->dest);
249ac5aa4b9Seric 
250c5acbec8Seric 			stat_increment("mta.envelope", 1);
251c5acbec8Seric 
25265c4fdfbSgilles 			mta_drain(relay);
253c5acbec8Seric 			mta_relay_unref(relay); /* from here */
254ac5aa4b9Seric 			return;
255ac5aa4b9Seric 
256e5b07014Sgilles 		case IMSG_QUEUE_MESSAGE_FD:
25765c4fdfbSgilles 			mta_session_imsg(p, imsg);
258b28a97afSjacekm 			return;
259df982ec9Sgilles 		}
2601eb29730Sjacekm 	}
2611eb29730Sjacekm 
26265c4fdfbSgilles 	if (p->proc == PROC_LKA) {
263ed1929b6Sjacekm 		switch (imsg->hdr.type) {
26465c4fdfbSgilles 
265ed1929b6Sjacekm 		case IMSG_LKA_SECRET:
26665c4fdfbSgilles 			m_msg(&m, imsg);
26765c4fdfbSgilles 			m_get_id(&m, &reqid);
26865c4fdfbSgilles 			m_get_string(&m, &secret);
26965c4fdfbSgilles 			m_end(&m);
27065c4fdfbSgilles 			relay = tree_xpop(&wait_secret, reqid);
271c5acbec8Seric 			mta_on_secret(relay, secret[0] ? secret : NULL);
27265c4fdfbSgilles 			return;
27365c4fdfbSgilles 
27465c4fdfbSgilles 		case IMSG_LKA_SOURCE:
27565c4fdfbSgilles 			m_msg(&m, imsg);
27665c4fdfbSgilles 			m_get_id(&m, &reqid);
27765c4fdfbSgilles 			m_get_int(&m, &status);
278c5acbec8Seric 			if (status == LKA_OK)
27965c4fdfbSgilles 				m_get_sockaddr(&m, (struct sockaddr*)&ss);
28065c4fdfbSgilles 			m_end(&m);
28165c4fdfbSgilles 
282c5acbec8Seric 			relay = tree_xpop(&wait_source, reqid);
283c5acbec8Seric 			mta_on_source(relay, (status == LKA_OK) ?
284c5acbec8Seric 			    mta_source((struct sockaddr *)&ss) : NULL);
28565c4fdfbSgilles 			return;
28665c4fdfbSgilles 
28765c4fdfbSgilles 		case IMSG_LKA_HELO:
28865c4fdfbSgilles 			mta_session_imsg(p, imsg);
28965c4fdfbSgilles 			return;
29065c4fdfbSgilles 
29125080696Sgilles 		case IMSG_DNS_HOST:
29265c4fdfbSgilles 			m_msg(&m, imsg);
29365c4fdfbSgilles 			m_get_id(&m, &reqid);
29465c4fdfbSgilles 			m_get_sockaddr(&m, (struct sockaddr*)&ss);
29565c4fdfbSgilles 			m_get_int(&m, &preference);
29665c4fdfbSgilles 			m_end(&m);
29765c4fdfbSgilles 			domain = tree_xget(&wait_mx, reqid);
29865c4fdfbSgilles 			mx = xcalloc(1, sizeof *mx, "mta: mx");
29965c4fdfbSgilles 			mx->host = mta_host((struct sockaddr*)&ss);
30065c4fdfbSgilles 			mx->preference = preference;
30165c4fdfbSgilles 			TAILQ_FOREACH(imx, &domain->mxs, entry) {
302e06daefdSeric 				if (imx->preference > mx->preference) {
30365c4fdfbSgilles 					TAILQ_INSERT_BEFORE(imx, mx, entry);
30465c4fdfbSgilles 					return;
30565c4fdfbSgilles 				}
30665c4fdfbSgilles 			}
30765c4fdfbSgilles 			TAILQ_INSERT_TAIL(&domain->mxs, mx, entry);
30865c4fdfbSgilles 			return;
30965c4fdfbSgilles 
31025080696Sgilles 		case IMSG_DNS_HOST_END:
31165c4fdfbSgilles 			m_msg(&m, imsg);
31265c4fdfbSgilles 			m_get_id(&m, &reqid);
31365c4fdfbSgilles 			m_get_int(&m, &dnserror);
31465c4fdfbSgilles 			m_end(&m);
31565c4fdfbSgilles 			domain = tree_xpop(&wait_mx, reqid);
31665c4fdfbSgilles 			domain->mxstatus = dnserror;
31765c4fdfbSgilles 			if (domain->mxstatus == DNS_OK) {
31865c4fdfbSgilles 				log_debug("debug: MXs for domain %s:",
31965c4fdfbSgilles 				    domain->name);
32065c4fdfbSgilles 				TAILQ_FOREACH(mx, &domain->mxs, entry)
321d7bcae4dSeric 					log_debug("	%s preference %d",
32265c4fdfbSgilles 					    sa_to_text(mx->host->sa),
32365c4fdfbSgilles 					    mx->preference);
32465c4fdfbSgilles 			}
32565c4fdfbSgilles 			else {
32665c4fdfbSgilles 				log_debug("debug: Failed MX query for %s:",
32765c4fdfbSgilles 				    domain->name);
32865c4fdfbSgilles 			}
329*afc7e0ecSeric 			domain->lastmxquery = time(NULL);
33065c4fdfbSgilles 			waitq_run(&domain->mxs, domain);
33165c4fdfbSgilles 			return;
33265c4fdfbSgilles 
33365c4fdfbSgilles 		case IMSG_DNS_MX_PREFERENCE:
33465c4fdfbSgilles 			m_msg(&m, imsg);
33565c4fdfbSgilles 			m_get_id(&m, &reqid);
33665c4fdfbSgilles 			m_get_int(&m, &dnserror);
337c5acbec8Seric 			if (dnserror == 0)
338c5acbec8Seric 				m_get_int(&m, &preference);
33965c4fdfbSgilles 			m_end(&m);
340c5acbec8Seric 
341c5acbec8Seric 			relay = tree_xpop(&wait_preference, reqid);
342c5acbec8Seric 			mta_on_preference(relay, dnserror, preference);
34365c4fdfbSgilles 			return;
34465c4fdfbSgilles 
345ed1929b6Sjacekm 		case IMSG_DNS_PTR:
34665c4fdfbSgilles 			mta_session_imsg(p, imsg);
34765c4fdfbSgilles 			return;
34865c4fdfbSgilles 
34965c4fdfbSgilles 		case IMSG_LKA_SSL_INIT:
35065c4fdfbSgilles 			mta_session_imsg(p, imsg);
35165c4fdfbSgilles 			return;
35265c4fdfbSgilles 
35365c4fdfbSgilles 		case IMSG_LKA_SSL_VERIFY:
35465c4fdfbSgilles 			mta_session_imsg(p, imsg);
3553ef9cbf7Sgilles 			return;
3563ef9cbf7Sgilles 		}
3571eb29730Sjacekm 	}
3581eb29730Sjacekm 
35965c4fdfbSgilles 	if (p->proc == PROC_PARENT) {
360ed1929b6Sjacekm 		switch (imsg->hdr.type) {
361ed1929b6Sjacekm 		case IMSG_CTL_VERBOSE:
36265c4fdfbSgilles 			m_msg(&m, imsg);
36365c4fdfbSgilles 			m_get_int(&m, &v);
36465c4fdfbSgilles 			m_end(&m);
36565c4fdfbSgilles 			log_verbose(v);
36665c4fdfbSgilles 			return;
36765c4fdfbSgilles 
36865c4fdfbSgilles 		case IMSG_CTL_PROFILE:
36965c4fdfbSgilles 			m_msg(&m, imsg);
37065c4fdfbSgilles 			m_get_int(&m, &v);
37165c4fdfbSgilles 			m_end(&m);
37265c4fdfbSgilles 			profiling = v;
373ed1929b6Sjacekm 			return;
374ed1929b6Sjacekm 		}
375af8de4b6Sgilles 	}
376eb143ecfSjacekm 
377c5acbec8Seric 	if (p->proc == PROC_CONTROL) {
378c5acbec8Seric 		switch (imsg->hdr.type) {
379c5acbec8Seric 
380c5acbec8Seric 		case IMSG_CTL_RESUME_ROUTE:
381c5acbec8Seric 			u64 = *((uint64_t *)imsg->data);
382c5acbec8Seric 			if (u64)
383c5acbec8Seric 				log_debug("resuming route: %llu",
384c5acbec8Seric 				    (unsigned long long)u64);
385c5acbec8Seric 			else
386c5acbec8Seric 				log_debug("resuming all routes");
387c5acbec8Seric 			SPLAY_FOREACH(route, mta_route_tree, &routes) {
388c5acbec8Seric 				if (u64 && route->id != u64)
389c5acbec8Seric 					continue;
390c5acbec8Seric 				mta_route_enable(route);
391c5acbec8Seric 				if (u64)
392c5acbec8Seric 					break;
393c5acbec8Seric 			}
394c5acbec8Seric 			return;
395c5acbec8Seric 
396c5acbec8Seric 		case IMSG_CTL_MTA_SHOW_ROUTES:
397c5acbec8Seric 			SPLAY_FOREACH(route, mta_route_tree, &routes) {
398c5acbec8Seric 				v = runq_pending(runq_route, NULL, route, &t);
399c5acbec8Seric 				snprintf(buf, sizeof(buf),
400d7bcae4dSeric 				    "%llu. %s %c%c%c%c nconn=%zu penalty=%d timeout=%s",
401c5acbec8Seric 				    (unsigned long long)route->id,
402c5acbec8Seric 				    mta_route_to_text(route),
403c5acbec8Seric 				    route->flags & ROUTE_NEW ? 'N' : '-',
404c5acbec8Seric 				    route->flags & ROUTE_DISABLED ? 'D' : '-',
405c5acbec8Seric 				    route->flags & ROUTE_RUNQ ? 'Q' : '-',
406c5acbec8Seric 				    route->flags & ROUTE_KEEPALIVE ? 'K' : '-',
407c5acbec8Seric 				    route->nconn,
408c5acbec8Seric 				    route->penalty,
409c5acbec8Seric 				    v ? duration_to_text(t - time(NULL)) : "-");
410c5acbec8Seric 				m_compose(p, IMSG_CTL_MTA_SHOW_ROUTES,
411c5acbec8Seric 				    imsg->hdr.peerid, 0, -1,
412c5acbec8Seric 				    buf, strlen(buf) + 1);
413c5acbec8Seric 			}
414c5acbec8Seric 			m_compose(p, IMSG_CTL_MTA_SHOW_ROUTES, imsg->hdr.peerid,
415c5acbec8Seric 			    0, -1, NULL, 0);
416c5acbec8Seric 			return;
417c5acbec8Seric 		case IMSG_CTL_MTA_SHOW_HOSTSTATS:
418c5acbec8Seric 			iter = NULL;
419c5acbec8Seric 			while (dict_iter(&hoststat, &iter, &hostname,
420c5acbec8Seric 				(void **)&hs)) {
421c5acbec8Seric 				snprintf(buf, sizeof(buf),
422c5acbec8Seric 				    "%s|%llu|%s",
423c5acbec8Seric 				    hostname, (unsigned long long) hs->tm,
424c5acbec8Seric 				    hs->error);
425c5acbec8Seric 				m_compose(p, IMSG_CTL_MTA_SHOW_HOSTSTATS,
426c5acbec8Seric 				    imsg->hdr.peerid, 0, -1,
427c5acbec8Seric 				    buf, strlen(buf) + 1);
428c5acbec8Seric 			}
429c5acbec8Seric 			m_compose(p, IMSG_CTL_MTA_SHOW_HOSTSTATS,
430c5acbec8Seric 			    imsg->hdr.peerid,
431c5acbec8Seric 			    0, -1, NULL, 0);
432c5acbec8Seric 			return;
433c5acbec8Seric 		}
434c5acbec8Seric 	}
435c5acbec8Seric 
4366d095224Schl 	errx(1, "mta_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
437ed1929b6Sjacekm }
438ed1929b6Sjacekm 
439be925435Sgilles static void
440ed1929b6Sjacekm mta_sig_handler(int sig, short event, void *p)
441ed1929b6Sjacekm {
442ed1929b6Sjacekm 	switch (sig) {
443ed1929b6Sjacekm 	case SIGINT:
444ed1929b6Sjacekm 	case SIGTERM:
445ed1929b6Sjacekm 		mta_shutdown();
446ed1929b6Sjacekm 		break;
447af8de4b6Sgilles 	default:
448ed1929b6Sjacekm 		fatalx("mta_sig_handler: unexpected signal");
449af8de4b6Sgilles 	}
450af8de4b6Sgilles }
451af8de4b6Sgilles 
452be925435Sgilles static void
4533ef9cbf7Sgilles mta_shutdown(void)
4543ef9cbf7Sgilles {
45582614934Seric 	log_info("info: mail transfer agent exiting");
4563ef9cbf7Sgilles 	_exit(0);
4573ef9cbf7Sgilles }
4583ef9cbf7Sgilles 
4593ef9cbf7Sgilles pid_t
460e4d36f12Seric mta(void)
4613ef9cbf7Sgilles {
4623ef9cbf7Sgilles 	pid_t		 pid;
4633ef9cbf7Sgilles 	struct passwd	*pw;
4643ef9cbf7Sgilles 	struct event	 ev_sigint;
4653ef9cbf7Sgilles 	struct event	 ev_sigterm;
4663ef9cbf7Sgilles 
4673ef9cbf7Sgilles 	switch (pid = fork()) {
4683ef9cbf7Sgilles 	case -1:
4693ef9cbf7Sgilles 		fatal("mta: cannot fork");
4703ef9cbf7Sgilles 	case 0:
4715894db6eSeric 		post_fork(PROC_MTA);
4723ef9cbf7Sgilles 		break;
4733ef9cbf7Sgilles 	default:
4743ef9cbf7Sgilles 		return (pid);
4753ef9cbf7Sgilles 	}
4763ef9cbf7Sgilles 
477e4d36f12Seric 	purge_config(PURGE_EVERYTHING);
4783ef9cbf7Sgilles 
47911d04e02Seric 	if ((pw = getpwnam(SMTPD_USER)) == NULL)
48011d04e02Seric 		fatalx("unknown user " SMTPD_USER);
48111d04e02Seric 
48211d04e02Seric 	if (chroot(PATH_CHROOT) == -1)
4833ef9cbf7Sgilles 		fatal("mta: chroot");
4843ef9cbf7Sgilles 	if (chdir("/") == -1)
4853ef9cbf7Sgilles 		fatal("mta: chdir(\"/\")");
4863ef9cbf7Sgilles 
48765c4fdfbSgilles 	config_process(PROC_MTA);
4883ef9cbf7Sgilles 
4893ef9cbf7Sgilles 	if (setgroups(1, &pw->pw_gid) ||
4903ef9cbf7Sgilles 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
4913ef9cbf7Sgilles 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
4923ef9cbf7Sgilles 		fatal("mta: cannot drop privileges");
4933ef9cbf7Sgilles 
49465c4fdfbSgilles 	SPLAY_INIT(&relays);
49565c4fdfbSgilles 	SPLAY_INIT(&domains);
49665c4fdfbSgilles 	SPLAY_INIT(&hosts);
49765c4fdfbSgilles 	SPLAY_INIT(&sources);
49865c4fdfbSgilles 	SPLAY_INIT(&routes);
49965c4fdfbSgilles 
50065c4fdfbSgilles 	tree_init(&wait_secret);
50165c4fdfbSgilles 	tree_init(&wait_mx);
50265c4fdfbSgilles 	tree_init(&wait_preference);
50365c4fdfbSgilles 	tree_init(&wait_source);
504c5acbec8Seric 	dict_init(&hoststat);
50565c4fdfbSgilles 
506ed1929b6Sjacekm 	imsg_callback = mta_imsg;
5073ef9cbf7Sgilles 	event_init();
5083ef9cbf7Sgilles 
509c5acbec8Seric 	runq_init(&runq_relay, mta_on_timeout);
510c5acbec8Seric 	runq_init(&runq_connector, mta_on_timeout);
511c5acbec8Seric 	runq_init(&runq_route, mta_on_timeout);
512c5acbec8Seric 	runq_init(&runq_hoststat, mta_on_timeout);
513c5acbec8Seric 
514e4d36f12Seric 	signal_set(&ev_sigint, SIGINT, mta_sig_handler, NULL);
515e4d36f12Seric 	signal_set(&ev_sigterm, SIGTERM, mta_sig_handler, NULL);
5163ef9cbf7Sgilles 	signal_add(&ev_sigint, NULL);
5173ef9cbf7Sgilles 	signal_add(&ev_sigterm, NULL);
5183ef9cbf7Sgilles 	signal(SIGPIPE, SIG_IGN);
5193ef9cbf7Sgilles 	signal(SIGHUP, SIG_IGN);
5203ef9cbf7Sgilles 
52165c4fdfbSgilles 	config_peer(PROC_PARENT);
52265c4fdfbSgilles 	config_peer(PROC_QUEUE);
52365c4fdfbSgilles 	config_peer(PROC_LKA);
52465c4fdfbSgilles 	config_peer(PROC_CONTROL);
52565c4fdfbSgilles 	config_done();
5263ef9cbf7Sgilles 
5271c09daa9Schl 	if (event_dispatch() < 0)
5281c09daa9Schl 		fatal("event_dispatch");
5293ef9cbf7Sgilles 	mta_shutdown();
5303ef9cbf7Sgilles 
5313ef9cbf7Sgilles 	return (0);
5323ef9cbf7Sgilles }
533ac5aa4b9Seric 
53465c4fdfbSgilles /*
53565c4fdfbSgilles  * Local error on the given source.
53665c4fdfbSgilles  */
53765c4fdfbSgilles void
53865c4fdfbSgilles mta_source_error(struct mta_relay *relay, struct mta_route *route, const char *e)
539ac5aa4b9Seric {
54065c4fdfbSgilles 	struct mta_connector	*c;
541c5acbec8Seric 
54265c4fdfbSgilles 	/*
54365c4fdfbSgilles 	 * Remember the source as broken for this connector.
54465c4fdfbSgilles 	 */
54565c4fdfbSgilles 	c = mta_connector(relay, route->src);
546c5acbec8Seric 	if (!(c->flags & CONNECTOR_ERROR_SOURCE))
547c5acbec8Seric 		log_info("smtp-out: Error on %s: %s",
548c5acbec8Seric 		    mta_route_to_text(route), e);
549c5acbec8Seric 	c->flags |= CONNECTOR_ERROR_SOURCE;
550ac5aa4b9Seric }
551ac5aa4b9Seric 
55265c4fdfbSgilles /*
55365c4fdfbSgilles  * TODO:
55465c4fdfbSgilles  * Currently all errors are reported on the host itself.  Technically,
55565c4fdfbSgilles  * it should depend on the error, and it would be probably better to report
55665c4fdfbSgilles  * it at the connector level.  But we would need to have persistent routes
55765c4fdfbSgilles  * for that.  Hosts are "naturally" persisted, as they are referenced from
55865c4fdfbSgilles  * the MX list on the domain.
55965c4fdfbSgilles  * Also, we need a timeout on that.
56065c4fdfbSgilles  */
56165c4fdfbSgilles void
5620cf935dfSgilles mta_route_error(struct mta_relay *relay, struct mta_route *route)
563ac5aa4b9Seric {
56465c4fdfbSgilles 	route->dst->nerror++;
565ac5aa4b9Seric 
56665c4fdfbSgilles 	if (route->dst->flags & HOST_IGNORE)
56765c4fdfbSgilles 		return;
56882614934Seric 
56965c4fdfbSgilles 	if (route->dst->nerror > MAXERROR_PER_HOST) {
57065c4fdfbSgilles 		log_info("smtp-out: Too many errors on host %s: ignoring this MX",
57165c4fdfbSgilles 		    mta_host_to_text(route->dst));
57265c4fdfbSgilles 		route->dst->flags |= HOST_IGNORE;
57365c4fdfbSgilles 	}
574ac5aa4b9Seric }
575ac5aa4b9Seric 
576ac5aa4b9Seric void
57765c4fdfbSgilles mta_route_ok(struct mta_relay *relay, struct mta_route *route)
578ac5aa4b9Seric {
57965c4fdfbSgilles 	struct mta_connector	*c;
58065c4fdfbSgilles 
581c5acbec8Seric 	if (!(route->flags & ROUTE_NEW))
582c5acbec8Seric 		return;
58365c4fdfbSgilles 
584c5acbec8Seric 	log_debug("debug: mta-routing: route %s is now valid.",
585c5acbec8Seric 	    mta_route_to_text(route));
586c5acbec8Seric 
587c5acbec8Seric 	route->flags &= ~ROUTE_NEW;
58865c4fdfbSgilles 
58965c4fdfbSgilles 	c = mta_connector(relay, route->src);
590c5acbec8Seric 	mta_connect(c);
591c5acbec8Seric }
592c5acbec8Seric 
593c5acbec8Seric void
594c5acbec8Seric mta_route_down(struct mta_relay *relay, struct mta_route *route)
595c5acbec8Seric {
596c5acbec8Seric 	mta_route_disable(route, 2, ROUTE_DISABLED_SMTP);
597ac5aa4b9Seric }
598ac5aa4b9Seric 
599ac5aa4b9Seric void
60065c4fdfbSgilles mta_route_collect(struct mta_relay *relay, struct mta_route *route)
601ac5aa4b9Seric {
60265c4fdfbSgilles 	struct mta_connector	*c;
60365c4fdfbSgilles 
604c5acbec8Seric 	log_debug("debug: mta_route_collect(%s)",
605c5acbec8Seric 	    mta_route_to_text(route));
60665c4fdfbSgilles 
60765c4fdfbSgilles 	relay->nconn -= 1;
608c5acbec8Seric 	relay->domain->nconn -= 1;
60965c4fdfbSgilles 	route->nconn -= 1;
61065c4fdfbSgilles 	route->src->nconn -= 1;
61165c4fdfbSgilles 	route->dst->nconn -= 1;
612c5acbec8Seric 	route->lastdisc = time(NULL);
613c5acbec8Seric 
614c5acbec8Seric 	/* First connection failed */
615c5acbec8Seric 	if (route->flags & ROUTE_NEW)
616c5acbec8Seric 		mta_route_disable(route, 2, ROUTE_DISABLED_NET);
61765c4fdfbSgilles 
61865c4fdfbSgilles 	c = mta_connector(relay, route->src);
61965c4fdfbSgilles 	c->nconn -= 1;
620c5acbec8Seric 	mta_connect(c);
621c5acbec8Seric 	mta_route_unref(route); /* from mta_find_route() */
62265c4fdfbSgilles 	mta_relay_unref(relay); /* from mta_connect() */
62365c4fdfbSgilles }
62465c4fdfbSgilles 
62565c4fdfbSgilles struct mta_task *
62665c4fdfbSgilles mta_route_next_task(struct mta_relay *relay, struct mta_route *route)
62765c4fdfbSgilles {
62865c4fdfbSgilles 	struct mta_task	*task;
62965c4fdfbSgilles 
63065c4fdfbSgilles 	if ((task = TAILQ_FIRST(&relay->tasks))) {
63165c4fdfbSgilles 		TAILQ_REMOVE(&relay->tasks, task, entry);
63265c4fdfbSgilles 		relay->ntask -= 1;
63365c4fdfbSgilles 		task->relay = NULL;
6346dc81a07Seric 
6356dc81a07Seric 		/* When the number of tasks is down to lowat, query some evp */
6366dc81a07Seric 		if (relay->ntask == (size_t)relay->limits->task_lowat) {
6376dc81a07Seric 			if (relay->state & RELAY_ONHOLD) {
6386dc81a07Seric 				log_info("smtp-out: back to lowat on %s: releasing",
6396dc81a07Seric 				    mta_relay_to_text(relay));
6406dc81a07Seric 				relay->state &= ~RELAY_ONHOLD;
6416dc81a07Seric 			}
6426dc81a07Seric 			m_create(p_queue, IMSG_DELIVERY_RELEASE, 0, 0, -1);
6436dc81a07Seric 			m_add_id(p_queue, relay->id);
6446dc81a07Seric 			m_add_int(p_queue, relay->limits->task_release);
6456dc81a07Seric 			m_close(p_queue);
6466dc81a07Seric 		}
6476dc81a07Seric 		else if (relay->ntask == 0) {
6486dc81a07Seric 			m_create(p_queue, IMSG_DELIVERY_RELEASE, 0, 0, -1);
6496dc81a07Seric 			m_add_id(p_queue, relay->id);
6506dc81a07Seric 			m_add_int(p_queue, 0);
6516dc81a07Seric 			m_close(p_queue);
6526dc81a07Seric 		}
65365c4fdfbSgilles 	}
65465c4fdfbSgilles 
65565c4fdfbSgilles 	return (task);
656ac5aa4b9Seric }
657ac5aa4b9Seric 
658ac5aa4b9Seric void
659c5acbec8Seric mta_delivery_log(struct mta_envelope *e, const char *source, const char *relay,
660299c4efeSeric     int delivery, const char *status)
661ac5aa4b9Seric {
66265c4fdfbSgilles 	if (delivery == IMSG_DELIVERY_OK) {
663299c4efeSeric 		mta_log(e, "Ok", source, relay, status);
66465c4fdfbSgilles 	}
66565c4fdfbSgilles 	else if (delivery == IMSG_DELIVERY_TEMPFAIL) {
666299c4efeSeric 		mta_log(e, "TempFail", source, relay, status);
66765c4fdfbSgilles 	}
66865c4fdfbSgilles 	else if (delivery == IMSG_DELIVERY_PERMFAIL) {
669299c4efeSeric 		mta_log(e, "PermFail", source, relay, status);
67065c4fdfbSgilles 	}
67165c4fdfbSgilles 	else if (delivery == IMSG_DELIVERY_LOOP) {
672299c4efeSeric 		mta_log(e, "PermFail", source, relay, "Loop detected");
673c5acbec8Seric 	}
674c5acbec8Seric 	else
675c5acbec8Seric 		errx(1, "bad delivery");
676c5acbec8Seric }
677c5acbec8Seric 
678c5acbec8Seric void
679c5acbec8Seric mta_delivery_notify(struct mta_envelope *e, int delivery, const char *status,
680c5acbec8Seric     uint32_t penalty)
681c5acbec8Seric {
682c5acbec8Seric 	if (delivery == IMSG_DELIVERY_OK) {
683c5acbec8Seric 		queue_ok(e->id);
684c5acbec8Seric 	}
685c5acbec8Seric 	else if (delivery == IMSG_DELIVERY_TEMPFAIL) {
686c5acbec8Seric 		queue_tempfail(e->id, penalty, status);
687c5acbec8Seric 	}
688c5acbec8Seric 	else if (delivery == IMSG_DELIVERY_PERMFAIL) {
689c5acbec8Seric 		queue_permfail(e->id, status);
690c5acbec8Seric 	}
691c5acbec8Seric 	else if (delivery == IMSG_DELIVERY_LOOP) {
69265c4fdfbSgilles 		queue_loop(e->id);
69365c4fdfbSgilles 	}
69465c4fdfbSgilles 	else
69565c4fdfbSgilles 		errx(1, "bad delivery");
69665c4fdfbSgilles }
697ac5aa4b9Seric 
698c5acbec8Seric void
699c5acbec8Seric mta_delivery(struct mta_envelope *e, const char *source, const char *relay,
700c5acbec8Seric     int delivery, const char *status, uint32_t penalty)
701c5acbec8Seric {
702c5acbec8Seric 	mta_delivery_log(e, source, relay, delivery, status);
703c5acbec8Seric 	mta_delivery_notify(e, delivery, status, penalty);
704c5acbec8Seric }
705c5acbec8Seric 
70665c4fdfbSgilles static void
70765c4fdfbSgilles mta_query_mx(struct mta_relay *relay)
70865c4fdfbSgilles {
70965c4fdfbSgilles 	uint64_t	id;
71065c4fdfbSgilles 
71165c4fdfbSgilles 	if (relay->status & RELAY_WAIT_MX)
71265c4fdfbSgilles 		return;
71365c4fdfbSgilles 
714c5acbec8Seric 	log_debug("debug: mta: querying MX for %s...",
715c5acbec8Seric 	    mta_relay_to_text(relay));
71665c4fdfbSgilles 
71765c4fdfbSgilles 	if (waitq_wait(&relay->domain->mxs, mta_on_mx, relay)) {
71865c4fdfbSgilles 		id = generate_uid();
71965c4fdfbSgilles 		tree_xset(&wait_mx, id, relay->domain);
72065c4fdfbSgilles 		if (relay->domain->flags)
72165c4fdfbSgilles 			dns_query_host(id, relay->domain->name);
72265c4fdfbSgilles 		else
72365c4fdfbSgilles 			dns_query_mx(id, relay->domain->name);
72465c4fdfbSgilles 	}
72565c4fdfbSgilles 	relay->status |= RELAY_WAIT_MX;
72665c4fdfbSgilles 	mta_relay_ref(relay);
72765c4fdfbSgilles }
72865c4fdfbSgilles 
72965c4fdfbSgilles static void
730c5acbec8Seric mta_query_limits(struct mta_relay *relay)
731c5acbec8Seric {
732c5acbec8Seric 	if (relay->status & RELAY_WAIT_LIMITS)
733c5acbec8Seric 		return;
734c5acbec8Seric 
735c5acbec8Seric 	relay->limits = dict_get(env->sc_limits_dict, relay->domain->name);
736c5acbec8Seric 	if (relay->limits == NULL)
737c5acbec8Seric 		relay->limits = dict_get(env->sc_limits_dict, "default");
738c5acbec8Seric 
739c5acbec8Seric 	if (max_seen_conndelay_route < relay->limits->conndelay_route)
740c5acbec8Seric 		max_seen_conndelay_route = relay->limits->conndelay_route;
741c5acbec8Seric 	if (max_seen_discdelay_route < relay->limits->discdelay_route)
742c5acbec8Seric 		max_seen_discdelay_route = relay->limits->discdelay_route;
743c5acbec8Seric }
744c5acbec8Seric 
745c5acbec8Seric static void
74665c4fdfbSgilles mta_query_secret(struct mta_relay *relay)
74765c4fdfbSgilles {
74865c4fdfbSgilles 	if (relay->status & RELAY_WAIT_SECRET)
74965c4fdfbSgilles 		return;
75065c4fdfbSgilles 
751c5acbec8Seric 	log_debug("debug: mta: querying secret for %s...",
752c5acbec8Seric 	    mta_relay_to_text(relay));
75365c4fdfbSgilles 
75465c4fdfbSgilles 	tree_xset(&wait_secret, relay->id, relay);
75565c4fdfbSgilles 	relay->status |= RELAY_WAIT_SECRET;
75665c4fdfbSgilles 
757299c4efeSeric 	m_create(p_lka, IMSG_LKA_SECRET, 0, 0, -1);
75865c4fdfbSgilles 	m_add_id(p_lka, relay->id);
75965c4fdfbSgilles 	m_add_string(p_lka, relay->authtable);
76065c4fdfbSgilles 	m_add_string(p_lka, relay->authlabel);
76165c4fdfbSgilles 	m_close(p_lka);
76265c4fdfbSgilles 
76365c4fdfbSgilles 	mta_relay_ref(relay);
76465c4fdfbSgilles }
76565c4fdfbSgilles 
76665c4fdfbSgilles static void
76765c4fdfbSgilles mta_query_preference(struct mta_relay *relay)
76865c4fdfbSgilles {
76965c4fdfbSgilles 	if (relay->status & RELAY_WAIT_PREFERENCE)
77065c4fdfbSgilles 		return;
77165c4fdfbSgilles 
772c5acbec8Seric 	log_debug("debug: mta: querying preference for %s...",
773c5acbec8Seric 	    mta_relay_to_text(relay));
77465c4fdfbSgilles 
77565c4fdfbSgilles 	tree_xset(&wait_preference, relay->id, relay);
77665c4fdfbSgilles 	relay->status |= RELAY_WAIT_PREFERENCE;
77765c4fdfbSgilles 	dns_query_mx_preference(relay->id, relay->domain->name,
77865c4fdfbSgilles 		relay->backupname);
77965c4fdfbSgilles 	mta_relay_ref(relay);
78065c4fdfbSgilles }
78165c4fdfbSgilles 
78265c4fdfbSgilles static void
78365c4fdfbSgilles mta_query_source(struct mta_relay *relay)
78465c4fdfbSgilles {
785c5acbec8Seric 	log_debug("debug: mta: querying source for %s...",
786c5acbec8Seric 	    mta_relay_to_text(relay));
787c5acbec8Seric 
788c5acbec8Seric 	relay->sourceloop += 1;
789c5acbec8Seric 
790c5acbec8Seric 	if (relay->sourcetable == NULL) {
791c5acbec8Seric 		/*
792c5acbec8Seric 		 * This is a recursive call, but it only happens once, since
793c5acbec8Seric 		 * another source will not be queried immediatly.
794c5acbec8Seric 		 */
795c5acbec8Seric 		mta_relay_ref(relay);
796c5acbec8Seric 		mta_on_source(relay, mta_source(NULL));
797c5acbec8Seric 		return;
798c5acbec8Seric 	}
79965c4fdfbSgilles 
800299c4efeSeric 	m_create(p_lka, IMSG_LKA_SOURCE, 0, 0, -1);
80165c4fdfbSgilles 	m_add_id(p_lka, relay->id);
80265c4fdfbSgilles 	m_add_string(p_lka, relay->sourcetable);
80365c4fdfbSgilles 	m_close(p_lka);
80465c4fdfbSgilles 
80565c4fdfbSgilles 	tree_xset(&wait_source, relay->id, relay);
80665c4fdfbSgilles 	relay->status |= RELAY_WAIT_SOURCE;
80765c4fdfbSgilles 	mta_relay_ref(relay);
80865c4fdfbSgilles }
80965c4fdfbSgilles 
81065c4fdfbSgilles static void
81165c4fdfbSgilles mta_on_mx(void *tag, void *arg, void *data)
81265c4fdfbSgilles {
81365c4fdfbSgilles 	struct mta_domain	*domain = data;
81465c4fdfbSgilles 	struct mta_relay	*relay = arg;
81565c4fdfbSgilles 
816c5acbec8Seric 	log_debug("debug: mta: ... got mx (%p, %s, %s)",
81765c4fdfbSgilles 	    tag, domain->name, mta_relay_to_text(relay));
81865c4fdfbSgilles 
81965c4fdfbSgilles 	switch (domain->mxstatus) {
82065c4fdfbSgilles 	case DNS_OK:
82165c4fdfbSgilles 		break;
82265c4fdfbSgilles 	case DNS_RETRY:
82365c4fdfbSgilles 		relay->fail = IMSG_DELIVERY_TEMPFAIL;
82465c4fdfbSgilles 		relay->failstr = "Temporary failure in MX lookup";
82565c4fdfbSgilles 		break;
82665c4fdfbSgilles 	case DNS_EINVAL:
82765c4fdfbSgilles 		relay->fail = IMSG_DELIVERY_PERMFAIL;
82865c4fdfbSgilles 		relay->failstr = "Invalid domain name";
82965c4fdfbSgilles 		break;
83065c4fdfbSgilles 	case DNS_ENONAME:
83165c4fdfbSgilles 		relay->fail = IMSG_DELIVERY_PERMFAIL;
83265c4fdfbSgilles 		relay->failstr = "Domain does not exist";
83365c4fdfbSgilles 		break;
83465c4fdfbSgilles 	case DNS_ENOTFOUND:
83565c4fdfbSgilles 		relay->fail = IMSG_DELIVERY_TEMPFAIL;
83665c4fdfbSgilles 		relay->failstr = "No MX found for domain";
83765c4fdfbSgilles 		break;
83865c4fdfbSgilles 	default:
83965c4fdfbSgilles 		fatalx("bad DNS lookup error code");
84065c4fdfbSgilles 		break;
84165c4fdfbSgilles 	}
84265c4fdfbSgilles 
84365c4fdfbSgilles 	if (domain->mxstatus)
84465c4fdfbSgilles 		log_info("smtp-out: Failed to resolve MX for %s: %s",
84565c4fdfbSgilles 		    mta_relay_to_text(relay), relay->failstr);
84665c4fdfbSgilles 
84765c4fdfbSgilles 	relay->status &= ~RELAY_WAIT_MX;
84865c4fdfbSgilles 	mta_drain(relay);
84965c4fdfbSgilles 	mta_relay_unref(relay); /* from mta_drain() */
85065c4fdfbSgilles }
85165c4fdfbSgilles 
85265c4fdfbSgilles static void
853c5acbec8Seric mta_on_secret(struct mta_relay *relay, const char *secret)
854c5acbec8Seric {
855c5acbec8Seric 	log_debug("debug: mta: ... got secret for %s: %s",
856c5acbec8Seric 	    mta_relay_to_text(relay), secret);
857c5acbec8Seric 
858c5acbec8Seric 	if (secret)
859c5acbec8Seric 		relay->secret = strdup(secret);
860c5acbec8Seric 
861c5acbec8Seric 	if (relay->secret == NULL) {
862c5acbec8Seric 		log_warnx("warn: Failed to retrieve secret "
863c5acbec8Seric 			    "for %s", mta_relay_to_text(relay));
864c5acbec8Seric 		relay->fail = IMSG_DELIVERY_TEMPFAIL;
865c5acbec8Seric 		relay->failstr = "Could not retrieve credentials";
866c5acbec8Seric 	}
867c5acbec8Seric 
868c5acbec8Seric 	relay->status &= ~RELAY_WAIT_SECRET;
869c5acbec8Seric 	mta_drain(relay);
870c5acbec8Seric 	mta_relay_unref(relay); /* from mta_query_secret() */
871c5acbec8Seric }
872c5acbec8Seric 
873c5acbec8Seric static void
874c5acbec8Seric mta_on_preference(struct mta_relay *relay, int dnserror, int preference)
875c5acbec8Seric {
876c5acbec8Seric 	if (dnserror) {
877c5acbec8Seric 		log_warnx("warn: Couldn't find backup preference for %s",
878c5acbec8Seric 		    mta_relay_to_text(relay));
879c5acbec8Seric 		relay->backuppref = INT_MAX;
880c5acbec8Seric 	}
881c5acbec8Seric 	else {
882d7bcae4dSeric 		log_debug("debug: mta: ... got preference for %s: %d, %d",
883c5acbec8Seric 		    mta_relay_to_text(relay), dnserror, preference);
884c5acbec8Seric 		relay->backuppref = preference;
885c5acbec8Seric 	}
886c5acbec8Seric 
887c5acbec8Seric 	relay->status &= ~RELAY_WAIT_PREFERENCE;
888c5acbec8Seric 	mta_drain(relay);
889c5acbec8Seric 	mta_relay_unref(relay); /* from mta_query_preference() */
890c5acbec8Seric }
891c5acbec8Seric 
892c5acbec8Seric static void
89365c4fdfbSgilles mta_on_source(struct mta_relay *relay, struct mta_source *source)
89465c4fdfbSgilles {
895c5acbec8Seric 	struct mta_connector	*c;
896c5acbec8Seric 	void			*iter;
897c5acbec8Seric 	int			 delay, errmask;
898c5acbec8Seric 
899c5acbec8Seric 	log_debug("debug: mta: ... got source for %s: %s",
900c5acbec8Seric 	    mta_relay_to_text(relay), source ? mta_source_to_text(source) : "NULL");
901c5acbec8Seric 
902c5acbec8Seric 	relay->lastsource = time(NULL);
903c5acbec8Seric 	delay = DELAY_CHECK_SOURCE_SLOW;
904c5acbec8Seric 
905c5acbec8Seric 	if (source) {
906c5acbec8Seric 		c = mta_connector(relay, source);
907c5acbec8Seric 		if (c->flags & CONNECTOR_NEW) {
908c5acbec8Seric 			c->flags &= ~CONNECTOR_NEW;
909c5acbec8Seric 			delay = DELAY_CHECK_SOURCE;
910c5acbec8Seric 		}
911c5acbec8Seric 		mta_connect(c);
912c5acbec8Seric 		if ((c->flags & CONNECTOR_ERROR) == 0)
913c5acbec8Seric 			relay->sourceloop = 0;
914c5acbec8Seric 		else
915c5acbec8Seric 			delay = DELAY_CHECK_SOURCE_FAST;
916c5acbec8Seric 		mta_source_unref(source); /* from constructor */
917c5acbec8Seric 	}
918c5acbec8Seric 	else {
919c5acbec8Seric 		log_warnx("warn: Failed to get source address"
920c5acbec8Seric 			    "for %s", mta_relay_to_text(relay));
921c5acbec8Seric 	}
922c5acbec8Seric 
923c5acbec8Seric 	if (tree_count(&relay->connectors) == 0) {
924c5acbec8Seric 		relay->fail = IMSG_DELIVERY_TEMPFAIL;
925c5acbec8Seric 		relay->failstr = "Could not retrieve source address";
926c5acbec8Seric 	}
927c5acbec8Seric 	if (tree_count(&relay->connectors) < relay->sourceloop) {
928c5acbec8Seric 		relay->fail = IMSG_DELIVERY_TEMPFAIL;
929c5acbec8Seric 		relay->failstr = "No valid route to remote MX";
930c5acbec8Seric 
931c5acbec8Seric 		errmask = 0;
932c5acbec8Seric 		iter = NULL;
933c5acbec8Seric 		while (tree_iter(&relay->connectors, &iter, NULL, (void **)&c))
934c5acbec8Seric 			errmask |= c->flags;
935c5acbec8Seric 
936c5acbec8Seric 		if (errmask & CONNECTOR_ERROR_ROUTE_SMTP)
937c5acbec8Seric 			relay->failstr = "Destination seem to reject all mails";
938c5acbec8Seric 		else if (errmask & CONNECTOR_ERROR_ROUTE_NET)
939c5acbec8Seric 			relay->failstr = "Network error on destination MXs";
940c5acbec8Seric 		else if (errmask & CONNECTOR_ERROR_MX)
941c5acbec8Seric 			relay->failstr = "No MX found for destination";
942c5acbec8Seric 		else if (errmask & CONNECTOR_ERROR_FAMILY)
943c5acbec8Seric 			relay->failstr = "Address family mismatch on destination MXs";
944c5acbec8Seric 		else
945c5acbec8Seric 			relay->failstr = "No valid route to destination";
946c5acbec8Seric 	}
947c5acbec8Seric 
948c5acbec8Seric 	relay->nextsource = relay->lastsource + delay;
949c5acbec8Seric 	relay->status &= ~RELAY_WAIT_SOURCE;
950c5acbec8Seric 	mta_drain(relay);
951c5acbec8Seric 	mta_relay_unref(relay); /* from mta_query_source() */
95265c4fdfbSgilles }
95365c4fdfbSgilles 
95465c4fdfbSgilles static void
95565c4fdfbSgilles mta_connect(struct mta_connector *c)
95665c4fdfbSgilles {
95765c4fdfbSgilles 	struct mta_route	*route;
958c5acbec8Seric 	struct mta_limits	*l = c->relay->limits;
959c5acbec8Seric 	int			 limits;
960c5acbec8Seric 	time_t			 nextconn, now;
96165c4fdfbSgilles 
962c5acbec8Seric     again:
96365c4fdfbSgilles 
964c5acbec8Seric 	log_debug("debug: mta: connecting with %s", mta_connector_to_text(c));
965c5acbec8Seric 
966c5acbec8Seric 	/* Do not connect if this connector has an error. */
967c5acbec8Seric 	if (c->flags & CONNECTOR_ERROR) {
968c5acbec8Seric 		log_debug("debug: mta: connector error");
96965c4fdfbSgilles 		return;
97065c4fdfbSgilles 	}
97165c4fdfbSgilles 
972c5acbec8Seric 	if (c->flags & CONNECTOR_WAIT) {
973c5acbec8Seric 		log_debug("debug: mta: canceling connector timeout");
974c5acbec8Seric 		runq_cancel(runq_connector, NULL, c);
975c5acbec8Seric 	}
976c5acbec8Seric 
977c5acbec8Seric 	/* No job. */
978c5acbec8Seric 	if (c->relay->ntask == 0) {
979c5acbec8Seric 		log_debug("debug: mta: no task for connector");
980c5acbec8Seric 		return;
981c5acbec8Seric 	}
982c5acbec8Seric 
983c5acbec8Seric 	/* Do not create more connections than necessary */
984c5acbec8Seric 	if ((c->relay->nconn_ready >= c->relay->ntask) ||
985c5acbec8Seric 	    (c->relay->nconn > 2 && c->relay->nconn >= c->relay->ntask / 2)) {
986c5acbec8Seric 		log_debug("debug: mta: enough connections already");
987c5acbec8Seric 		return;
988c5acbec8Seric 	}
989c5acbec8Seric 
990c5acbec8Seric 	limits = 0;
991c5acbec8Seric 	nextconn = now = time(NULL);
992c5acbec8Seric 
993c5acbec8Seric 	if (c->relay->domain->lastconn + l->conndelay_domain > nextconn) {
994c5acbec8Seric 		log_debug("debug: mta: cannot use domain %s before %llus",
995c5acbec8Seric 		    c->relay->domain->name,
996c5acbec8Seric 		    (unsigned long long) c->relay->domain->lastconn + l->conndelay_domain - now);
997c5acbec8Seric 		nextconn = c->relay->domain->lastconn + l->conndelay_domain;
998c5acbec8Seric 	}
999c5acbec8Seric 	if (c->relay->domain->nconn >= l->maxconn_per_domain) {
1000c5acbec8Seric 		log_debug("debug: mta: hit domain limit");
1001c5acbec8Seric 		limits |= CONNECTOR_LIMIT_DOMAIN;
1002c5acbec8Seric 	}
1003c5acbec8Seric 
1004c5acbec8Seric 	if (c->source->lastconn + l->conndelay_source > nextconn) {
1005c5acbec8Seric 		log_debug("debug: mta: cannot use source %s before %llus",
1006c5acbec8Seric 		    mta_source_to_text(c->source),
1007c5acbec8Seric 		    (unsigned long long) c->source->lastconn + l->conndelay_source - now);
1008c5acbec8Seric 		nextconn = c->source->lastconn + l->conndelay_source;
1009c5acbec8Seric 	}
1010c5acbec8Seric 	if (c->source->nconn >= l->maxconn_per_source) {
1011c5acbec8Seric 		log_debug("debug: mta: hit source limit");
1012c5acbec8Seric 		limits |= CONNECTOR_LIMIT_SOURCE;
1013c5acbec8Seric 	}
1014c5acbec8Seric 
1015c5acbec8Seric 	if (c->lastconn + l->conndelay_connector > nextconn) {
1016c5acbec8Seric 		log_debug("debug: mta: cannot use %s before %llus",
1017c5acbec8Seric 		    mta_connector_to_text(c),
1018c5acbec8Seric 		    (unsigned long long) c->lastconn + l->conndelay_connector - now);
1019c5acbec8Seric 		nextconn = c->lastconn + l->conndelay_connector;
1020c5acbec8Seric 	}
1021c5acbec8Seric 	if (c->nconn >= l->maxconn_per_connector) {
1022c5acbec8Seric 		log_debug("debug: mta: hit connector limit");
1023c5acbec8Seric 		limits |= CONNECTOR_LIMIT_CONN;
1024c5acbec8Seric 	}
1025c5acbec8Seric 
1026c5acbec8Seric 	if (c->relay->lastconn + l->conndelay_relay > nextconn) {
1027c5acbec8Seric 		log_debug("debug: mta: cannot use %s before %llus",
1028c5acbec8Seric 		    mta_relay_to_text(c->relay),
1029c5acbec8Seric 		    (unsigned long long) c->relay->lastconn + l->conndelay_relay - now);
1030c5acbec8Seric 		nextconn = c->relay->lastconn + l->conndelay_relay;
1031c5acbec8Seric 	}
1032c5acbec8Seric 	if (c->relay->nconn >= l->maxconn_per_relay) {
1033c5acbec8Seric 		log_debug("debug: mta: hit relay limit");
1034c5acbec8Seric 		limits |= CONNECTOR_LIMIT_RELAY;
1035c5acbec8Seric 	}
1036c5acbec8Seric 
1037c5acbec8Seric 	/* We can connect now, find a route */
1038c5acbec8Seric 	if (!limits && nextconn <= now)
1039c5acbec8Seric 		route = mta_find_route(c, now, &limits, &nextconn);
1040c5acbec8Seric 	else
1041c5acbec8Seric 		route = NULL;
1042c5acbec8Seric 
1043c5acbec8Seric 	/* No route */
1044c5acbec8Seric 	if (route == NULL) {
1045c5acbec8Seric 
1046c5acbec8Seric 		if (c->flags & CONNECTOR_ERROR) {
1047c5acbec8Seric 			/* XXX we might want to clear this flag later */
1048c5acbec8Seric 			log_debug("debug: mta-routing: no route available for %s: errors on connector",
1049c5acbec8Seric 			    mta_connector_to_text(c));
1050c5acbec8Seric 			return;
1051c5acbec8Seric 		}
1052c5acbec8Seric 		else if (limits) {
1053c5acbec8Seric 			log_debug("debug: mta-routing: no route available for %s: limits reached",
1054c5acbec8Seric 			    mta_connector_to_text(c));
1055c5acbec8Seric 			nextconn = now + DELAY_CHECK_LIMIT;
1056c5acbec8Seric 		}
1057c5acbec8Seric 		else {
1058c5acbec8Seric 			log_debug("debug: mta-routing: no route available for %s: must wait a bit",
1059c5acbec8Seric 			    mta_connector_to_text(c));
1060c5acbec8Seric 		}
1061c5acbec8Seric 		log_debug("debug: mta: retrying to connect on %s in %llus...",
1062c5acbec8Seric 		    mta_connector_to_text(c),
1063c5acbec8Seric 		    (unsigned long long) nextconn - time(NULL));
1064c5acbec8Seric 		c->flags |= CONNECTOR_WAIT;
1065c5acbec8Seric 		runq_schedule(runq_connector, nextconn, NULL, c);
1066c5acbec8Seric 		return;
1067c5acbec8Seric 	}
1068c5acbec8Seric 
1069c5acbec8Seric 	log_debug("debug: mta-routing: spawning new connection on %s",
1070c5acbec8Seric 		    mta_route_to_text(route));
1071c5acbec8Seric 
107265c4fdfbSgilles 	c->nconn += 1;
107365c4fdfbSgilles 	c->lastconn = time(NULL);
107465c4fdfbSgilles 
107565c4fdfbSgilles 	c->relay->nconn += 1;
107665c4fdfbSgilles 	c->relay->lastconn = c->lastconn;
1077c5acbec8Seric 	c->relay->domain->nconn += 1;
1078c5acbec8Seric 	c->relay->domain->lastconn = c->lastconn;
107965c4fdfbSgilles 	route->nconn += 1;
108065c4fdfbSgilles 	route->lastconn = c->lastconn;
108165c4fdfbSgilles 	route->src->nconn += 1;
108265c4fdfbSgilles 	route->src->lastconn = c->lastconn;
108365c4fdfbSgilles 	route->dst->nconn += 1;
108465c4fdfbSgilles 	route->dst->lastconn = c->lastconn;
108565c4fdfbSgilles 
108665c4fdfbSgilles 	mta_session(c->relay, route);	/* this never fails synchronously */
1087c5acbec8Seric 	mta_relay_ref(c->relay);
1088c5acbec8Seric 
1089c5acbec8Seric     goto again;
109065c4fdfbSgilles }
109165c4fdfbSgilles 
109265c4fdfbSgilles static void
1093c5acbec8Seric mta_on_timeout(struct runq *runq, void *arg)
109465c4fdfbSgilles {
1095c5acbec8Seric 	struct mta_connector	*connector = arg;
1096c5acbec8Seric 	struct mta_relay	*relay = arg;
1097c5acbec8Seric 	struct mta_route	*route = arg;
1098c5acbec8Seric 	struct hoststat		*hs = arg;
109965c4fdfbSgilles 
1100c5acbec8Seric 	if (runq == runq_relay) {
1101c5acbec8Seric 		log_debug("debug: mta: ... timeout for %s",
1102c5acbec8Seric 		    mta_relay_to_text(relay));
1103c5acbec8Seric 		relay->status &= ~RELAY_WAIT_CONNECTOR;
1104c5acbec8Seric 		mta_drain(relay);
1105c5acbec8Seric 		mta_relay_unref(relay); /* from mta_drain() */
110665c4fdfbSgilles 	}
1107c5acbec8Seric 	else if (runq == runq_connector) {
1108c5acbec8Seric 		log_debug("debug: mta: ... timeout for %s",
1109c5acbec8Seric 		    mta_connector_to_text(connector));
1110c5acbec8Seric 		connector->flags &= ~CONNECTOR_WAIT;
1111c5acbec8Seric 		mta_connect(connector);
111265c4fdfbSgilles 	}
1113c5acbec8Seric 	else if (runq == runq_route) {
1114c5acbec8Seric 		route->flags &= ~ROUTE_RUNQ;
1115c5acbec8Seric 		mta_route_enable(route);
1116c5acbec8Seric 		mta_route_unref(route);
111765c4fdfbSgilles 	}
1118c5acbec8Seric 	else if (runq == runq_hoststat) {
1119c5acbec8Seric 		log_debug("debug: mta: ... timeout for hoststat %s",
1120c5acbec8Seric 			hs->name);
1121c5acbec8Seric 		mta_hoststat_remove_entry(hs);
1122c5acbec8Seric 		free(hs);
112365c4fdfbSgilles 	}
112465c4fdfbSgilles }
112565c4fdfbSgilles 
112665c4fdfbSgilles static void
1127c5acbec8Seric mta_route_disable(struct mta_route *route, int penalty, int reason)
112865c4fdfbSgilles {
1129c5acbec8Seric 	unsigned long long	delay;
113065c4fdfbSgilles 
1131c5acbec8Seric 	route->penalty += penalty;
1132c5acbec8Seric 	route->lastpenalty = time(NULL);
1133c5acbec8Seric 	delay = (unsigned long long)DELAY_ROUTE_BASE * route->penalty * route->penalty;
1134c5acbec8Seric 	if (delay > DELAY_ROUTE_MAX)
1135c5acbec8Seric 		delay = DELAY_ROUTE_MAX;
1136c5acbec8Seric #if 0
1137c5acbec8Seric 	delay = 60;
1138c5acbec8Seric #endif
113965c4fdfbSgilles 
1140c5acbec8Seric 	log_info("smtp-out: Disabling route %s for %llus",
1141c5acbec8Seric 	    mta_route_to_text(route), delay);
114265c4fdfbSgilles 
1143c5acbec8Seric 	if (route->flags & ROUTE_DISABLED) {
1144c5acbec8Seric 		runq_cancel(runq_route, NULL, route);
1145c5acbec8Seric 		mta_route_unref(route); /* from last call to here */
114665c4fdfbSgilles 	}
1147c5acbec8Seric 	route->flags |= reason & ROUTE_DISABLED;
1148c5acbec8Seric 	runq_schedule(runq_route, time(NULL) + delay, NULL, route);
1149c5acbec8Seric 	mta_route_ref(route);
115065c4fdfbSgilles }
115165c4fdfbSgilles 
115265c4fdfbSgilles static void
1153c5acbec8Seric mta_route_enable(struct mta_route *route)
115465c4fdfbSgilles {
1155c5acbec8Seric 	if (route->flags & ROUTE_DISABLED) {
1156c5acbec8Seric 		log_info("smtp-out: Enabling route %s",
1157c5acbec8Seric 		    mta_route_to_text(route));
1158c5acbec8Seric 		route->flags &= ~ROUTE_DISABLED;
1159c5acbec8Seric 		route->flags |= ROUTE_NEW;
1160c5acbec8Seric 	}
116165c4fdfbSgilles 
1162c5acbec8Seric 	if (route->penalty) {
1163c5acbec8Seric #if DELAY_QUADRATIC
1164c5acbec8Seric 		route->penalty -= 1;
1165c5acbec8Seric 		route->lastpenalty = time(NULL);
1166c5acbec8Seric #else
1167c5acbec8Seric 		route->penalty = 0;
1168c5acbec8Seric #endif
1169c5acbec8Seric 	}
117065c4fdfbSgilles }
117165c4fdfbSgilles 
117265c4fdfbSgilles static void
117365c4fdfbSgilles mta_drain(struct mta_relay *r)
117465c4fdfbSgilles {
117565c4fdfbSgilles 	char			 buf[64];
117665c4fdfbSgilles 
117765c4fdfbSgilles 	log_debug("debug: mta: draining %s "
1178d7bcae4dSeric 	    "refcount=%d, ntask=%zu, nconnector=%zu, nconn=%zu",
117965c4fdfbSgilles 	    mta_relay_to_text(r),
1180c5acbec8Seric 	    r->refcount, r->ntask, tree_count(&r->connectors), r->nconn);
118165c4fdfbSgilles 
118265c4fdfbSgilles 	/*
118365c4fdfbSgilles 	 * All done.
118465c4fdfbSgilles 	 */
118565c4fdfbSgilles 	if (r->ntask == 0) {
118665c4fdfbSgilles 		log_debug("debug: mta: all done for %s", mta_relay_to_text(r));
118765c4fdfbSgilles 		return;
118865c4fdfbSgilles 	}
118965c4fdfbSgilles 
119065c4fdfbSgilles 	/*
119165c4fdfbSgilles 	 * If we know that this relay is failing flush the tasks.
119265c4fdfbSgilles 	 */
119365c4fdfbSgilles 	if (r->fail) {
119465c4fdfbSgilles 		mta_flush(r, r->fail, r->failstr);
119565c4fdfbSgilles 		return;
119665c4fdfbSgilles 	}
119765c4fdfbSgilles 
119865c4fdfbSgilles 	/* Query secret if needed. */
119965c4fdfbSgilles 	if (r->flags & RELAY_AUTH && r->secret == NULL)
120065c4fdfbSgilles 		mta_query_secret(r);
120165c4fdfbSgilles 
120265c4fdfbSgilles 	/* Query our preference if needed. */
120365c4fdfbSgilles 	if (r->backupname && r->backuppref == -1)
120465c4fdfbSgilles 		mta_query_preference(r);
120565c4fdfbSgilles 
120665c4fdfbSgilles 	/* Query the domain MXs if needed. */
120765c4fdfbSgilles 	if (r->domain->lastmxquery == 0)
120865c4fdfbSgilles 		mta_query_mx(r);
120965c4fdfbSgilles 
1210c5acbec8Seric 	/* Query the limits if needed. */
1211c5acbec8Seric 	if (r->limits == NULL)
1212c5acbec8Seric 		mta_query_limits(r);
1213c5acbec8Seric 
121465c4fdfbSgilles 	/* Wait until we are ready to proceed. */
121565c4fdfbSgilles 	if (r->status & RELAY_WAITMASK) {
121665c4fdfbSgilles 		buf[0] = '\0';
121765c4fdfbSgilles 		if (r->status & RELAY_WAIT_MX)
121865c4fdfbSgilles 			strlcat(buf, " MX", sizeof buf);
121965c4fdfbSgilles 		if (r->status & RELAY_WAIT_PREFERENCE)
122065c4fdfbSgilles 			strlcat(buf, " preference", sizeof buf);
122165c4fdfbSgilles 		if (r->status & RELAY_WAIT_SECRET)
122265c4fdfbSgilles 			strlcat(buf, " secret", sizeof buf);
122365c4fdfbSgilles 		if (r->status & RELAY_WAIT_SOURCE)
122465c4fdfbSgilles 			strlcat(buf, " source", sizeof buf);
1225c5acbec8Seric 		if (r->status & RELAY_WAIT_CONNECTOR)
1226c5acbec8Seric 			strlcat(buf, " connector", sizeof buf);
122765c4fdfbSgilles 		log_debug("debug: mta: %s waiting for%s",
122865c4fdfbSgilles 		    mta_relay_to_text(r), buf);
122965c4fdfbSgilles 		return;
123065c4fdfbSgilles 	}
123165c4fdfbSgilles 
123265c4fdfbSgilles 	/*
1233c5acbec8Seric 	 * We have pending task, and it's maybe time too try a new source.
123465c4fdfbSgilles 	 */
1235c5acbec8Seric 	if (r->nextsource <= time(NULL))
123665c4fdfbSgilles 		mta_query_source(r);
1237c5acbec8Seric 	else {
1238c5acbec8Seric 		log_debug("debug: mta: scheduling relay %s in %llus...",
1239c5acbec8Seric 		    mta_relay_to_text(r),
1240c5acbec8Seric 		    (unsigned long long) r->nextsource - time(NULL));
1241c5acbec8Seric 		runq_schedule(runq_relay, r->nextsource, NULL, r);
1242c5acbec8Seric 		r->status |= RELAY_WAIT_CONNECTOR;
1243c5acbec8Seric 		mta_relay_ref(r);
124465c4fdfbSgilles 	}
124565c4fdfbSgilles }
124665c4fdfbSgilles 
124765c4fdfbSgilles static void
124865c4fdfbSgilles mta_flush(struct mta_relay *relay, int fail, const char *error)
124965c4fdfbSgilles {
1250399c053eSeric 	struct mta_envelope	*e;
125165c4fdfbSgilles 	struct mta_task		*task;
1252c5acbec8Seric 	const char     		*domain;
1253c5acbec8Seric 	void			*iter;
1254c5acbec8Seric 	struct mta_connector	*c;
125565c4fdfbSgilles 	size_t			 n;
1256c5acbec8Seric 	size_t			 r;
125765c4fdfbSgilles 
1258d7bcae4dSeric 	log_debug("debug: mta_flush(%s, %d, \"%s\")",
125965c4fdfbSgilles 	    mta_relay_to_text(relay), fail, error);
126065c4fdfbSgilles 
1261299c4efeSeric 	if (fail != IMSG_DELIVERY_TEMPFAIL && fail != IMSG_DELIVERY_PERMFAIL)
1262d7bcae4dSeric 		errx(1, "unexpected delivery status %d", fail);
126365c4fdfbSgilles 
126465c4fdfbSgilles 	n = 0;
126565c4fdfbSgilles 	while ((task = TAILQ_FIRST(&relay->tasks))) {
126665c4fdfbSgilles 		TAILQ_REMOVE(&relay->tasks, task, entry);
126765c4fdfbSgilles 		while ((e = TAILQ_FIRST(&task->envelopes))) {
126865c4fdfbSgilles 			TAILQ_REMOVE(&task->envelopes, e, entry);
1269c5acbec8Seric 			mta_delivery(e, NULL, relay->domain->name, fail, error, 0);
1270c5acbec8Seric 
1271c5acbec8Seric 			/*
1272c5acbec8Seric 			 * host was suspended, cache envelope id in hoststat tree
1273c5acbec8Seric 			 * so that it can be retried when a delivery succeeds for
1274c5acbec8Seric 			 * that domain.
1275c5acbec8Seric 			 */
1276c5acbec8Seric 			domain = strchr(e->dest, '@');
1277c5acbec8Seric 			if (fail == IMSG_DELIVERY_TEMPFAIL && domain) {
1278c5acbec8Seric 				r = 0;
1279c5acbec8Seric 				iter = NULL;
1280c5acbec8Seric 				while (tree_iter(&relay->connectors, &iter,
1281c5acbec8Seric 					NULL, (void **)&c)) {
1282c5acbec8Seric 					if (c->flags & CONNECTOR_ERROR_ROUTE)
1283c5acbec8Seric 						r++;
1284c5acbec8Seric 				}
1285c5acbec8Seric 				if (tree_count(&relay->connectors) == r)
1286c5acbec8Seric 					mta_hoststat_cache(domain+1, e->id);
1287c5acbec8Seric 			}
1288c5acbec8Seric 
1289399c053eSeric 			free(e->dest);
1290399c053eSeric 			free(e->rcpt);
129165c4fdfbSgilles 			free(e);
129265c4fdfbSgilles 			n++;
129365c4fdfbSgilles 		}
1294399c053eSeric 		free(task->sender);
129565c4fdfbSgilles 		free(task);
129665c4fdfbSgilles 	}
129765c4fdfbSgilles 
129865c4fdfbSgilles 	stat_decrement("mta.task", relay->ntask);
129965c4fdfbSgilles 	stat_decrement("mta.envelope", n);
130065c4fdfbSgilles 	relay->ntask = 0;
13016dc81a07Seric 
13026dc81a07Seric 	/* release all waiting envelopes for the relay */
13036dc81a07Seric 	m_create(p_queue, IMSG_DELIVERY_RELEASE, 0, 0, -1);
13046dc81a07Seric 	m_add_id(p_queue, relay->id);
13056dc81a07Seric 	m_add_int(p_queue, 0);
13066dc81a07Seric 	m_close(p_queue);
130765c4fdfbSgilles }
130865c4fdfbSgilles 
130965c4fdfbSgilles /*
131065c4fdfbSgilles  * Find a route to use for this connector
131165c4fdfbSgilles  */
131265c4fdfbSgilles static struct mta_route *
1313c5acbec8Seric mta_find_route(struct mta_connector *c, time_t now, int *limits,
1314c5acbec8Seric     time_t *nextconn)
131565c4fdfbSgilles {
131665c4fdfbSgilles 	struct mta_route	*route, *best;
1317c5acbec8Seric 	struct mta_limits	*l = c->relay->limits;
131865c4fdfbSgilles 	struct mta_mx		*mx;
131965c4fdfbSgilles 	int			 level, limit_host, limit_route;
1320c5acbec8Seric 	int			 family_mismatch, seen, suspended_route;
1321c5acbec8Seric 	time_t			 tm;
132265c4fdfbSgilles 
1323c5acbec8Seric 	log_debug("debug: mta-routing: searching new route for %s...",
1324c5acbec8Seric 	    mta_connector_to_text(c));
1325c5acbec8Seric 
1326c5acbec8Seric 	tm = 0;
132765c4fdfbSgilles 	limit_host = 0;
132865c4fdfbSgilles 	limit_route = 0;
1329c5acbec8Seric 	suspended_route = 0;
133065c4fdfbSgilles 	family_mismatch = 0;
133165c4fdfbSgilles 	level = -1;
133265c4fdfbSgilles 	best = NULL;
133365c4fdfbSgilles 	seen = 0;
133465c4fdfbSgilles 
133565c4fdfbSgilles 	TAILQ_FOREACH(mx, &c->relay->domain->mxs, entry) {
133665c4fdfbSgilles 		/*
133765c4fdfbSgilles 		 * New preference level
133865c4fdfbSgilles 		 */
133965c4fdfbSgilles 		if (mx->preference > level) {
134065c4fdfbSgilles #ifndef IGNORE_MX_PREFERENCE
134165c4fdfbSgilles 			/*
134265c4fdfbSgilles 			 * Use the current best MX if found.
134365c4fdfbSgilles 			 */
134465c4fdfbSgilles 			if (best)
134565c4fdfbSgilles 				break;
134665c4fdfbSgilles 
134765c4fdfbSgilles 			/*
134865c4fdfbSgilles 			 * No candidate found.  There are valid MXs at this
1349c5acbec8Seric 			 * preference level but they reached their limit, or
1350c5acbec8Seric 			 * we can't connect yet.
135165c4fdfbSgilles 			 */
1352c5acbec8Seric 			if (limit_host || limit_route || tm)
135365c4fdfbSgilles 				break;
135465c4fdfbSgilles 
135565c4fdfbSgilles 			/*
135665c4fdfbSgilles 			 *  If we are a backup MX, do not relay to MXs with
135765c4fdfbSgilles 			 *  a greater preference value.
135865c4fdfbSgilles 			 */
135965c4fdfbSgilles 			if (c->relay->backuppref >= 0 &&
136065c4fdfbSgilles 			    mx->preference >= c->relay->backuppref)
136165c4fdfbSgilles 				break;
136265c4fdfbSgilles 
136365c4fdfbSgilles 			/*
136465c4fdfbSgilles 			 * Start looking at MXs on this preference level.
136565c4fdfbSgilles 			 */
136665c4fdfbSgilles #endif
136765c4fdfbSgilles 			level = mx->preference;
136865c4fdfbSgilles 		}
136965c4fdfbSgilles 
137065c4fdfbSgilles 		if (mx->host->flags & HOST_IGNORE)
137165c4fdfbSgilles 			continue;
137265c4fdfbSgilles 
137365c4fdfbSgilles 		/* Found a possibly valid mx */
137465c4fdfbSgilles 		seen++;
137565c4fdfbSgilles 
1376c5acbec8Seric 		if ((c->source->sa &&
1377c5acbec8Seric 		     c->source->sa->sa_family != mx->host->sa->sa_family) ||
1378c5acbec8Seric 		    (l->family && l->family != mx->host->sa->sa_family)) {
1379c5acbec8Seric 			log_debug("debug: mta-routing: skipping host %s: AF mismatch",
1380c5acbec8Seric 			    mta_host_to_text(mx->host));
138165c4fdfbSgilles 			family_mismatch = 1;
138265c4fdfbSgilles 			continue;
138365c4fdfbSgilles 		}
138465c4fdfbSgilles 
1385c5acbec8Seric 		if (mx->host->nconn >= l->maxconn_per_host) {
1386c5acbec8Seric 			log_debug("debug: mta-routing: skipping host %s: too many connections",
1387c5acbec8Seric 			    mta_host_to_text(mx->host));
138865c4fdfbSgilles 			limit_host = 1;
138965c4fdfbSgilles 			continue;
139065c4fdfbSgilles 		}
139165c4fdfbSgilles 
1392c5acbec8Seric 		if (mx->host->lastconn + l->conndelay_host > now) {
1393c5acbec8Seric 			log_debug("debug: mta-routing: skipping host %s: cannot use before %llus",
1394c5acbec8Seric 			    mta_host_to_text(mx->host),
1395c5acbec8Seric 			    (unsigned long long) mx->host->lastconn + l->conndelay_host - now);
1396c5acbec8Seric 			if (tm == 0 || mx->host->lastconn + l->conndelay_host < tm)
1397c5acbec8Seric 				tm = mx->host->lastconn + l->conndelay_host;
1398c5acbec8Seric 			continue;
1399c5acbec8Seric 		}
1400c5acbec8Seric 
140165c4fdfbSgilles 		route = mta_route(c->source, mx->host);
140265c4fdfbSgilles 
1403c5acbec8Seric 		if (route->flags & ROUTE_DISABLED) {
1404c5acbec8Seric 			log_debug("debug: mta-routing: skipping route %s: suspend",
1405c5acbec8Seric 			    mta_route_to_text(route));
1406c5acbec8Seric 			suspended_route |= route->flags & ROUTE_DISABLED;
1407c5acbec8Seric 			mta_route_unref(route); /* from here */
1408c5acbec8Seric 			continue;
1409c5acbec8Seric 		}
1410c5acbec8Seric 
1411c5acbec8Seric 		if (route->nconn && (route->flags & ROUTE_NEW)) {
1412c5acbec8Seric 			log_debug("debug: mta-routing: skipping route %s: not validated yet",
1413c5acbec8Seric 			    mta_route_to_text(route));
141465c4fdfbSgilles 			limit_route = 1;
141565c4fdfbSgilles 			mta_route_unref(route); /* from here */
141665c4fdfbSgilles 			continue;
141765c4fdfbSgilles 		}
141865c4fdfbSgilles 
1419c5acbec8Seric 		if (route->nconn >= l->maxconn_per_route) {
1420c5acbec8Seric 			log_debug("debug: mta-routing: skipping route %s: too many connections",
1421c5acbec8Seric 			    mta_route_to_text(route));
1422c5acbec8Seric 			limit_route = 1;
1423c5acbec8Seric 			mta_route_unref(route); /* from here */
1424c5acbec8Seric 			continue;
1425c5acbec8Seric 		}
1426c5acbec8Seric 
1427c5acbec8Seric 		if (route->lastconn + l->conndelay_route > now) {
1428c5acbec8Seric 			log_debug("debug: mta-routing: skipping route %s: cannot use before %llus (delay after connect)",
1429c5acbec8Seric 			    mta_route_to_text(route),
1430c5acbec8Seric 			    (unsigned long long) route->lastconn + l->conndelay_route - now);
1431c5acbec8Seric 			if (tm == 0 || route->lastconn + l->conndelay_route < tm)
1432c5acbec8Seric 				tm = route->lastconn + l->conndelay_route;
1433c5acbec8Seric 			mta_route_unref(route); /* from here */
1434c5acbec8Seric 			continue;
1435c5acbec8Seric 		}
1436c5acbec8Seric 
1437c5acbec8Seric 		if (route->lastdisc + l->discdelay_route > now) {
1438c5acbec8Seric 			log_debug("debug: mta-routing: skipping route %s: cannot use before %llus (delay after disconnect)",
1439c5acbec8Seric 			    mta_route_to_text(route),
1440c5acbec8Seric 			    (unsigned long long) route->lastdisc + l->discdelay_route - now);
1441c5acbec8Seric 			if (tm == 0 || route->lastdisc + l->discdelay_route < tm)
1442c5acbec8Seric 				tm = route->lastdisc + l->discdelay_route;
1443c5acbec8Seric 			mta_route_unref(route); /* from here */
1444c5acbec8Seric 			continue;
1445c5acbec8Seric 		}
1446c5acbec8Seric 
144765c4fdfbSgilles 		/* Use the route with the lowest number of connections. */
144865c4fdfbSgilles 		if (best && route->nconn >= best->nconn) {
1449c5acbec8Seric 			log_debug("debug: mta-routing: skipping route %s: current one is better",
1450c5acbec8Seric 			    mta_route_to_text(route));
145165c4fdfbSgilles 			mta_route_unref(route); /* from here */
145265c4fdfbSgilles 			continue;
145365c4fdfbSgilles 		}
145465c4fdfbSgilles 
145565c4fdfbSgilles 		if (best)
145665c4fdfbSgilles 			mta_route_unref(best); /* from here */
145765c4fdfbSgilles 		best = route;
1458c5acbec8Seric 		log_debug("debug: mta-routing: selecting candidate route %s",
1459c5acbec8Seric 		    mta_route_to_text(route));
146065c4fdfbSgilles 	}
146165c4fdfbSgilles 
146265c4fdfbSgilles 	if (best)
146365c4fdfbSgilles 		return (best);
146465c4fdfbSgilles 
1465c5acbec8Seric 	/* Order is important */
146665c4fdfbSgilles 	if (seen == 0) {
1467c5acbec8Seric 		log_info("smtp-out: No MX found for %s",
146865c4fdfbSgilles 		    mta_connector_to_text(c));
1469c5acbec8Seric 		c->flags |= CONNECTOR_ERROR_MX;
147065c4fdfbSgilles 	}
147165c4fdfbSgilles 	else if (limit_route) {
1472c5acbec8Seric 		log_debug("debug: mta: hit route limit");
1473c5acbec8Seric 		*limits |= CONNECTOR_LIMIT_ROUTE;
147465c4fdfbSgilles 	}
147565c4fdfbSgilles 	else if (limit_host) {
1476c5acbec8Seric 		log_debug("debug: mta: hit host limit");
1477c5acbec8Seric 		*limits |= CONNECTOR_LIMIT_HOST;
1478c5acbec8Seric 	}
1479c5acbec8Seric 	else if (tm) {
1480c5acbec8Seric 		if (tm > *nextconn)
1481c5acbec8Seric 			*nextconn = tm;
148265c4fdfbSgilles 	}
1483299c4efeSeric 	else if (family_mismatch) {
1484c5acbec8Seric 		log_info("smtp-out: Address family mismatch on %s",
1485299c4efeSeric 		    mta_connector_to_text(c));
1486c5acbec8Seric 		c->flags |= CONNECTOR_ERROR_FAMILY;
1487c5acbec8Seric 	}
1488c5acbec8Seric 	else if (suspended_route) {
1489c5acbec8Seric 		log_info("smtp-out: No valid route for %s",
1490c5acbec8Seric 		    mta_connector_to_text(c));
1491c5acbec8Seric 		if (suspended_route & ROUTE_DISABLED_NET)
1492c5acbec8Seric 			c->flags |= CONNECTOR_ERROR_ROUTE_NET;
1493c5acbec8Seric 		if (suspended_route & ROUTE_DISABLED_SMTP)
1494c5acbec8Seric 			c->flags |= CONNECTOR_ERROR_ROUTE_SMTP;
1495299c4efeSeric 	}
149665c4fdfbSgilles 
149765c4fdfbSgilles 	return (NULL);
149865c4fdfbSgilles }
149965c4fdfbSgilles 
150065c4fdfbSgilles static void
1501299c4efeSeric mta_log(const struct mta_envelope *evp, const char *prefix, const char *source,
1502299c4efeSeric     const char *relay, const char *status)
150365c4fdfbSgilles {
1504c5acbec8Seric 	log_info("relay: %s for %016" PRIx64 ": session=%016"PRIx64", "
1505c5acbec8Seric 	    "from=<%s>, to=<%s>, rcpt=<%s>, source=%s, "
1506c5acbec8Seric 	    "relay=%s, delay=%s, stat=%s",
150765c4fdfbSgilles 	    prefix,
1508399c053eSeric 	    evp->id,
1509c5acbec8Seric 	    evp->session,
1510399c053eSeric 	    evp->task->sender,
1511399c053eSeric 	    evp->dest,
1512c5acbec8Seric 	    evp->rcpt ? evp->rcpt : "-",
1513c5acbec8Seric 	    source ? source : "-",
151465c4fdfbSgilles 	    relay,
151565c4fdfbSgilles 	    duration_to_text(time(NULL) - evp->creation),
151665c4fdfbSgilles 	    status);
151765c4fdfbSgilles }
151865c4fdfbSgilles 
151965c4fdfbSgilles static struct mta_relay *
152065c4fdfbSgilles mta_relay(struct envelope *e)
152165c4fdfbSgilles {
152265c4fdfbSgilles 	struct mta_relay	 key, *r;
152365c4fdfbSgilles 
152465c4fdfbSgilles 	bzero(&key, sizeof key);
152565c4fdfbSgilles 
152665c4fdfbSgilles 	if (e->agent.mta.relay.flags & RELAY_BACKUP) {
152765c4fdfbSgilles 		key.domain = mta_domain(e->dest.domain, 0);
152865c4fdfbSgilles 		key.backupname = e->agent.mta.relay.hostname;
152965c4fdfbSgilles 	} else if (e->agent.mta.relay.hostname[0]) {
153065c4fdfbSgilles 		key.domain = mta_domain(e->agent.mta.relay.hostname, 1);
153165c4fdfbSgilles 		key.flags |= RELAY_MX;
153265c4fdfbSgilles 	} else {
153365c4fdfbSgilles 		key.domain = mta_domain(e->dest.domain, 0);
153465c4fdfbSgilles 	}
153565c4fdfbSgilles 
153665c4fdfbSgilles 	key.flags = e->agent.mta.relay.flags;
153765c4fdfbSgilles 	key.port = e->agent.mta.relay.port;
153865c4fdfbSgilles 	key.cert = e->agent.mta.relay.cert;
153965c4fdfbSgilles 	if (!key.cert[0])
154065c4fdfbSgilles 		key.cert = NULL;
154165c4fdfbSgilles 	key.authtable = e->agent.mta.relay.authtable;
154265c4fdfbSgilles 	if (!key.authtable[0])
154365c4fdfbSgilles 		key.authtable = NULL;
154465c4fdfbSgilles 	key.authlabel = e->agent.mta.relay.authlabel;
154565c4fdfbSgilles 	if (!key.authlabel[0])
154665c4fdfbSgilles 		key.authlabel = NULL;
154765c4fdfbSgilles 	key.sourcetable = e->agent.mta.relay.sourcetable;
154865c4fdfbSgilles 	if (!key.sourcetable[0])
154965c4fdfbSgilles 		key.sourcetable = NULL;
155065c4fdfbSgilles 	key.helotable = e->agent.mta.relay.helotable;
155165c4fdfbSgilles 	if (!key.helotable[0])
155265c4fdfbSgilles 		key.helotable = NULL;
155365c4fdfbSgilles 
155465c4fdfbSgilles 	if ((r = SPLAY_FIND(mta_relay_tree, &relays, &key)) == NULL) {
155565c4fdfbSgilles 		r = xcalloc(1, sizeof *r, "mta_relay");
155665c4fdfbSgilles 		TAILQ_INIT(&r->tasks);
155765c4fdfbSgilles 		r->id = generate_uid();
155865c4fdfbSgilles 		r->flags = key.flags;
155965c4fdfbSgilles 		r->domain = key.domain;
156065c4fdfbSgilles 		r->backupname = key.backupname ?
156165c4fdfbSgilles 		    xstrdup(key.backupname, "mta: backupname") : NULL;
156265c4fdfbSgilles 		r->backuppref = -1;
156365c4fdfbSgilles 		r->port = key.port;
156465c4fdfbSgilles 		r->cert = key.cert ? xstrdup(key.cert, "mta: cert") : NULL;
156565c4fdfbSgilles 		if (key.authtable)
156665c4fdfbSgilles 			r->authtable = xstrdup(key.authtable, "mta: authtable");
156765c4fdfbSgilles 		if (key.authlabel)
156865c4fdfbSgilles 			r->authlabel = xstrdup(key.authlabel, "mta: authlabel");
156965c4fdfbSgilles 		if (key.sourcetable)
157065c4fdfbSgilles 			r->sourcetable = xstrdup(key.sourcetable,
157165c4fdfbSgilles 			    "mta: sourcetable");
157265c4fdfbSgilles 		if (key.helotable)
157365c4fdfbSgilles 			r->helotable = xstrdup(key.helotable,
157465c4fdfbSgilles 			    "mta: helotable");
157565c4fdfbSgilles 		SPLAY_INSERT(mta_relay_tree, &relays, r);
157665c4fdfbSgilles 		stat_increment("mta.relay", 1);
157765c4fdfbSgilles 	} else {
157865c4fdfbSgilles 		mta_domain_unref(key.domain); /* from here */
157965c4fdfbSgilles 	}
158065c4fdfbSgilles 
158165c4fdfbSgilles 	r->refcount++;
158265c4fdfbSgilles 	return (r);
158365c4fdfbSgilles }
158465c4fdfbSgilles 
158565c4fdfbSgilles static void
158665c4fdfbSgilles mta_relay_ref(struct mta_relay *r)
158765c4fdfbSgilles {
158865c4fdfbSgilles 	r->refcount++;
158965c4fdfbSgilles }
159065c4fdfbSgilles 
159165c4fdfbSgilles static void
159265c4fdfbSgilles mta_relay_unref(struct mta_relay *relay)
159365c4fdfbSgilles {
159465c4fdfbSgilles 	struct mta_connector	*c;
159565c4fdfbSgilles 
159665c4fdfbSgilles 	if (--relay->refcount)
159765c4fdfbSgilles 		return;
159865c4fdfbSgilles 
15996dc81a07Seric 	/* Make sure they are no envelopes held for this relay */
16006dc81a07Seric 	m_create(p_queue, IMSG_DELIVERY_RELEASE, 0, 0, -1);
16016dc81a07Seric 	m_add_id(p_queue, relay->id);
16026dc81a07Seric 	m_add_int(p_queue, 0);
16036dc81a07Seric 	m_close(p_queue);
16046dc81a07Seric 
160565c4fdfbSgilles 	log_debug("debug: mta: freeing %s", mta_relay_to_text(relay));
160665c4fdfbSgilles 	SPLAY_REMOVE(mta_relay_tree, &relays, relay);
1607edae97a5Seric 
16087d590283Seric 	while ((tree_poproot(&relay->connectors, NULL, (void**)&c)))
16097d590283Seric 		mta_connector_free(c);
16107d590283Seric 
161165c4fdfbSgilles 	free(relay->authlabel);
1612edae97a5Seric 	free(relay->authtable);
1613edae97a5Seric 	free(relay->backupname);
1614edae97a5Seric 	free(relay->cert);
1615edae97a5Seric 	free(relay->helotable);
1616edae97a5Seric 	free(relay->secret);
1617edae97a5Seric 	free(relay->sourcetable);
161865c4fdfbSgilles 
161965c4fdfbSgilles 	mta_domain_unref(relay->domain); /* from constructor */
162065c4fdfbSgilles 	free(relay);
162165c4fdfbSgilles 	stat_decrement("mta.relay", 1);
1622ac5aa4b9Seric }
1623ac5aa4b9Seric 
1624ac5aa4b9Seric const char *
162565c4fdfbSgilles mta_relay_to_text(struct mta_relay *relay)
1626ac5aa4b9Seric {
1627ac5aa4b9Seric 	static char	 buf[1024];
1628e009e7d3Seric 	char		 tmp[32];
162965c4fdfbSgilles 	const char	*sep = ",";
1630ac5aa4b9Seric 
163165c4fdfbSgilles 	snprintf(buf, sizeof buf, "[relay:%s", relay->domain->name);
1632ac5aa4b9Seric 
163365c4fdfbSgilles 	if (relay->port) {
163465c4fdfbSgilles 		strlcat(buf, sep, sizeof buf);
1635d7bcae4dSeric 		snprintf(tmp, sizeof tmp, "port=%d", (int)relay->port);
1636e009e7d3Seric 		strlcat(buf, tmp, sizeof buf);
1637e009e7d3Seric 	}
1638e009e7d3Seric 
163965c4fdfbSgilles 	if (relay->flags & RELAY_STARTTLS) {
1640e009e7d3Seric 		strlcat(buf, sep, sizeof buf);
1641ac5aa4b9Seric 		strlcat(buf, "starttls", sizeof buf);
1642ac5aa4b9Seric 	}
1643ac5aa4b9Seric 
164465c4fdfbSgilles 	if (relay->flags & RELAY_SMTPS) {
1645a2389673Seric 		strlcat(buf, sep, sizeof buf);
1646ac5aa4b9Seric 		strlcat(buf, "smtps", sizeof buf);
1647ac5aa4b9Seric 	}
1648ac5aa4b9Seric 
164965c4fdfbSgilles 	if (relay->flags & RELAY_AUTH) {
1650a2389673Seric 		strlcat(buf, sep, sizeof buf);
1651ac5aa4b9Seric 		strlcat(buf, "auth=", sizeof buf);
165265c4fdfbSgilles 		strlcat(buf, relay->authtable, sizeof buf);
165365c4fdfbSgilles 		strlcat(buf, ":", sizeof buf);
165465c4fdfbSgilles 		strlcat(buf, relay->authlabel, sizeof buf);
1655ac5aa4b9Seric 	}
1656ac5aa4b9Seric 
165765c4fdfbSgilles 	if (relay->cert) {
1658a2389673Seric 		strlcat(buf, sep, sizeof buf);
1659ac5aa4b9Seric 		strlcat(buf, "cert=", sizeof buf);
166065c4fdfbSgilles 		strlcat(buf, relay->cert, sizeof buf);
1661ac5aa4b9Seric 	}
1662ac5aa4b9Seric 
166365c4fdfbSgilles 	if (relay->flags & RELAY_MX) {
1664a2389673Seric 		strlcat(buf, sep, sizeof buf);
1665ac5aa4b9Seric 		strlcat(buf, "mx", sizeof buf);
1666ac5aa4b9Seric 	}
1667a2389673Seric 
166865c4fdfbSgilles 	if (relay->flags & RELAY_BACKUP) {
1669a2389673Seric 		strlcat(buf, sep, sizeof buf);
1670a2389673Seric 		strlcat(buf, "backup=", sizeof buf);
167165c4fdfbSgilles 		strlcat(buf, relay->backupname, sizeof buf);
167265c4fdfbSgilles 	}
167365c4fdfbSgilles 
167465c4fdfbSgilles 	if (relay->sourcetable) {
167565c4fdfbSgilles 		strlcat(buf, sep, sizeof buf);
167665c4fdfbSgilles 		strlcat(buf, "sourcetable=", sizeof buf);
167765c4fdfbSgilles 		strlcat(buf, relay->sourcetable, sizeof buf);
1678a2389673Seric 	}
1679a2389673Seric 
1680ac5aa4b9Seric 	strlcat(buf, "]", sizeof buf);
1681ac5aa4b9Seric 
1682ac5aa4b9Seric 	return (buf);
1683ac5aa4b9Seric }
1684ac5aa4b9Seric 
1685ac5aa4b9Seric static int
168665c4fdfbSgilles mta_relay_cmp(const struct mta_relay *a, const struct mta_relay *b)
1687ac5aa4b9Seric {
1688ac5aa4b9Seric 	int	r;
1689ac5aa4b9Seric 
169065c4fdfbSgilles 	if (a->domain < b->domain)
169165c4fdfbSgilles 		return (-1);
169265c4fdfbSgilles 	if (a->domain > b->domain)
169365c4fdfbSgilles 		return (1);
169465c4fdfbSgilles 
1695ac5aa4b9Seric 	if (a->flags < b->flags)
1696ac5aa4b9Seric 		return (-1);
1697ac5aa4b9Seric 	if (a->flags > b->flags)
1698ac5aa4b9Seric 		return (1);
1699ac5aa4b9Seric 
1700ac5aa4b9Seric 	if (a->port < b->port)
1701ac5aa4b9Seric 		return (-1);
1702ac5aa4b9Seric 	if (a->port > b->port)
1703ac5aa4b9Seric 		return (1);
1704ac5aa4b9Seric 
170565c4fdfbSgilles 	if (a->authtable == NULL && b->authtable)
1706ac5aa4b9Seric 		return (-1);
170765c4fdfbSgilles 	if (a->authtable && b->authtable == NULL)
1708ac5aa4b9Seric 		return (1);
170965c4fdfbSgilles 	if (a->authtable && ((r = strcmp(a->authtable, b->authtable))))
171065c4fdfbSgilles 		return (r);
171165c4fdfbSgilles 	if (a->authlabel && ((r = strcmp(a->authlabel, b->authlabel))))
171265c4fdfbSgilles 		return (r);
171365c4fdfbSgilles 	if (a->sourcetable == NULL && b->sourcetable)
171465c4fdfbSgilles 		return (-1);
171565c4fdfbSgilles 	if (a->sourcetable && b->sourcetable == NULL)
171665c4fdfbSgilles 		return (1);
171765c4fdfbSgilles 	if (a->sourcetable && ((r = strcmp(a->sourcetable, b->sourcetable))))
1718ac5aa4b9Seric 		return (r);
1719ac5aa4b9Seric 
1720ac5aa4b9Seric 	if (a->cert == NULL && b->cert)
1721ac5aa4b9Seric 		return (-1);
1722ac5aa4b9Seric 	if (a->cert && b->cert == NULL)
1723ac5aa4b9Seric 		return (1);
1724ac5aa4b9Seric 	if (a->cert && ((r = strcmp(a->cert, b->cert))))
1725ac5aa4b9Seric 		return (r);
1726ac5aa4b9Seric 
17276916573aSeric 	if (a->backupname && ((r = strcmp(a->backupname, b->backupname))))
17286916573aSeric 		return (r);
17296916573aSeric 
173065c4fdfbSgilles 	return (0);
173165c4fdfbSgilles }
173265c4fdfbSgilles 
173365c4fdfbSgilles SPLAY_GENERATE(mta_relay_tree, mta_relay, entry, mta_relay_cmp);
173465c4fdfbSgilles 
173565c4fdfbSgilles static struct mta_host *
173665c4fdfbSgilles mta_host(const struct sockaddr *sa)
173765c4fdfbSgilles {
173865c4fdfbSgilles 	struct mta_host		key, *h;
173965c4fdfbSgilles 	struct sockaddr_storage	ss;
174065c4fdfbSgilles 
174165c4fdfbSgilles 	memmove(&ss, sa, sa->sa_len);
174265c4fdfbSgilles 	key.sa = (struct sockaddr*)&ss;
174365c4fdfbSgilles 	h = SPLAY_FIND(mta_host_tree, &hosts, &key);
174465c4fdfbSgilles 
174565c4fdfbSgilles 	if (h == NULL) {
174665c4fdfbSgilles 		h = xcalloc(1, sizeof(*h), "mta_host");
174765c4fdfbSgilles 		h->sa = xmemdup(sa, sa->sa_len, "mta_host");
174865c4fdfbSgilles 		SPLAY_INSERT(mta_host_tree, &hosts, h);
174965c4fdfbSgilles 		stat_increment("mta.host", 1);
175065c4fdfbSgilles 	}
175165c4fdfbSgilles 
175265c4fdfbSgilles 	h->refcount++;
175365c4fdfbSgilles 	return (h);
175465c4fdfbSgilles }
175565c4fdfbSgilles 
175665c4fdfbSgilles static void
175765c4fdfbSgilles mta_host_ref(struct mta_host *h)
175865c4fdfbSgilles {
175965c4fdfbSgilles 	h->refcount++;
176065c4fdfbSgilles }
176165c4fdfbSgilles 
176265c4fdfbSgilles static void
176365c4fdfbSgilles mta_host_unref(struct mta_host *h)
176465c4fdfbSgilles {
176565c4fdfbSgilles 	if (--h->refcount)
176665c4fdfbSgilles 		return;
176765c4fdfbSgilles 
176865c4fdfbSgilles 	SPLAY_REMOVE(mta_host_tree, &hosts, h);
176965c4fdfbSgilles 	free(h->sa);
177065c4fdfbSgilles 	free(h->ptrname);
1771edae97a5Seric 	free(h);
177265c4fdfbSgilles 	stat_decrement("mta.host", 1);
177365c4fdfbSgilles }
177465c4fdfbSgilles 
177565c4fdfbSgilles const char *
177665c4fdfbSgilles mta_host_to_text(struct mta_host *h)
177765c4fdfbSgilles {
177865c4fdfbSgilles 	static char buf[1024];
177965c4fdfbSgilles 
178065c4fdfbSgilles 	if (h->ptrname)
178165c4fdfbSgilles 		snprintf(buf, sizeof buf, "%s (%s)",
178265c4fdfbSgilles 		    sa_to_text(h->sa), h->ptrname);
178365c4fdfbSgilles 	else
178465c4fdfbSgilles 		snprintf(buf, sizeof buf, "%s", sa_to_text(h->sa));
178565c4fdfbSgilles 
178665c4fdfbSgilles 	return (buf);
178765c4fdfbSgilles }
178865c4fdfbSgilles 
178965c4fdfbSgilles static int
179065c4fdfbSgilles mta_host_cmp(const struct mta_host *a, const struct mta_host *b)
179165c4fdfbSgilles {
179265c4fdfbSgilles 	if (a->sa->sa_len < b->sa->sa_len)
179365c4fdfbSgilles 		return (-1);
179465c4fdfbSgilles 	if (a->sa->sa_len > b->sa->sa_len)
179565c4fdfbSgilles 		return (1);
179665c4fdfbSgilles 	return (memcmp(a->sa, b->sa, a->sa->sa_len));
179765c4fdfbSgilles }
179865c4fdfbSgilles 
179965c4fdfbSgilles SPLAY_GENERATE(mta_host_tree, mta_host, entry, mta_host_cmp);
180065c4fdfbSgilles 
180165c4fdfbSgilles static struct mta_domain *
180265c4fdfbSgilles mta_domain(char *name, int flags)
180365c4fdfbSgilles {
180465c4fdfbSgilles 	struct mta_domain	key, *d;
180565c4fdfbSgilles 
180665c4fdfbSgilles 	key.name = name;
180765c4fdfbSgilles 	key.flags = flags;
180865c4fdfbSgilles 	d = SPLAY_FIND(mta_domain_tree, &domains, &key);
180965c4fdfbSgilles 
181065c4fdfbSgilles 	if (d == NULL) {
181165c4fdfbSgilles 		d = xcalloc(1, sizeof(*d), "mta_domain");
181265c4fdfbSgilles 		d->name = xstrdup(name, "mta_domain");
181365c4fdfbSgilles 		d->flags = flags;
181465c4fdfbSgilles 		TAILQ_INIT(&d->mxs);
181565c4fdfbSgilles 		SPLAY_INSERT(mta_domain_tree, &domains, d);
181665c4fdfbSgilles 		stat_increment("mta.domain", 1);
181765c4fdfbSgilles 	}
181865c4fdfbSgilles 
181965c4fdfbSgilles 	d->refcount++;
182065c4fdfbSgilles 	return (d);
182165c4fdfbSgilles }
182265c4fdfbSgilles 
182365c4fdfbSgilles #if 0
182465c4fdfbSgilles static void
182565c4fdfbSgilles mta_domain_ref(struct mta_domain *d)
182665c4fdfbSgilles {
182765c4fdfbSgilles 	d->refcount++;
182865c4fdfbSgilles }
182965c4fdfbSgilles #endif
183065c4fdfbSgilles 
183165c4fdfbSgilles static void
183265c4fdfbSgilles mta_domain_unref(struct mta_domain *d)
183365c4fdfbSgilles {
183465c4fdfbSgilles 	struct mta_mx	*mx;
183565c4fdfbSgilles 
183665c4fdfbSgilles 	if (--d->refcount)
183765c4fdfbSgilles 		return;
183865c4fdfbSgilles 
183965c4fdfbSgilles 	while ((mx = TAILQ_FIRST(&d->mxs))) {
184065c4fdfbSgilles 		TAILQ_REMOVE(&d->mxs, mx, entry);
184165c4fdfbSgilles 		mta_host_unref(mx->host); /* from IMSG_DNS_HOST */
184265c4fdfbSgilles 		free(mx);
184365c4fdfbSgilles 	}
184465c4fdfbSgilles 
184565c4fdfbSgilles 	SPLAY_REMOVE(mta_domain_tree, &domains, d);
184665c4fdfbSgilles 	free(d->name);
1847edae97a5Seric 	free(d);
184865c4fdfbSgilles 	stat_decrement("mta.domain", 1);
184965c4fdfbSgilles }
185065c4fdfbSgilles 
185165c4fdfbSgilles static int
185265c4fdfbSgilles mta_domain_cmp(const struct mta_domain *a, const struct mta_domain *b)
185365c4fdfbSgilles {
185465c4fdfbSgilles 	if (a->flags < b->flags)
185565c4fdfbSgilles 		return (-1);
185665c4fdfbSgilles 	if (a->flags > b->flags)
185765c4fdfbSgilles 		return (1);
185865c4fdfbSgilles 	return (strcasecmp(a->name, b->name));
185965c4fdfbSgilles }
186065c4fdfbSgilles 
186165c4fdfbSgilles SPLAY_GENERATE(mta_domain_tree, mta_domain, entry, mta_domain_cmp);
186265c4fdfbSgilles 
186365c4fdfbSgilles static struct mta_source *
186465c4fdfbSgilles mta_source(const struct sockaddr *sa)
186565c4fdfbSgilles {
186665c4fdfbSgilles 	struct mta_source	key, *s;
186765c4fdfbSgilles 	struct sockaddr_storage	ss;
186865c4fdfbSgilles 
186965c4fdfbSgilles 	if (sa) {
187065c4fdfbSgilles 		memmove(&ss, sa, sa->sa_len);
187165c4fdfbSgilles 		key.sa = (struct sockaddr*)&ss;
187265c4fdfbSgilles 	} else
187365c4fdfbSgilles 		key.sa = NULL;
187465c4fdfbSgilles 	s = SPLAY_FIND(mta_source_tree, &sources, &key);
187565c4fdfbSgilles 
187665c4fdfbSgilles 	if (s == NULL) {
187765c4fdfbSgilles 		s = xcalloc(1, sizeof(*s), "mta_source");
187865c4fdfbSgilles 		if (sa)
187965c4fdfbSgilles 			s->sa = xmemdup(sa, sa->sa_len, "mta_source");
188065c4fdfbSgilles 		SPLAY_INSERT(mta_source_tree, &sources, s);
188165c4fdfbSgilles 		stat_increment("mta.source", 1);
188265c4fdfbSgilles 	}
188365c4fdfbSgilles 
188465c4fdfbSgilles 	s->refcount++;
188565c4fdfbSgilles 	return (s);
188665c4fdfbSgilles }
188765c4fdfbSgilles 
188865c4fdfbSgilles static void
188965c4fdfbSgilles mta_source_ref(struct mta_source *s)
189065c4fdfbSgilles {
189165c4fdfbSgilles 	s->refcount++;
189265c4fdfbSgilles }
189365c4fdfbSgilles 
189465c4fdfbSgilles static void
189565c4fdfbSgilles mta_source_unref(struct mta_source *s)
189665c4fdfbSgilles {
189765c4fdfbSgilles 	if (--s->refcount)
189865c4fdfbSgilles 		return;
189965c4fdfbSgilles 
190065c4fdfbSgilles 	SPLAY_REMOVE(mta_source_tree, &sources, s);
190165c4fdfbSgilles 	free(s->sa);
1902edae97a5Seric 	free(s);
190365c4fdfbSgilles 	stat_decrement("mta.source", 1);
190465c4fdfbSgilles }
190565c4fdfbSgilles 
190665c4fdfbSgilles static const char *
190765c4fdfbSgilles mta_source_to_text(struct mta_source *s)
190865c4fdfbSgilles {
190965c4fdfbSgilles 	static char buf[1024];
191065c4fdfbSgilles 
191165c4fdfbSgilles 	if (s->sa == NULL)
191265c4fdfbSgilles 		return "[]";
191365c4fdfbSgilles 	snprintf(buf, sizeof buf, "%s", sa_to_text(s->sa));
191465c4fdfbSgilles 	return (buf);
191565c4fdfbSgilles }
191665c4fdfbSgilles 
191765c4fdfbSgilles static int
191865c4fdfbSgilles mta_source_cmp(const struct mta_source *a, const struct mta_source *b)
191965c4fdfbSgilles {
192065c4fdfbSgilles 	if (a->sa == NULL)
192165c4fdfbSgilles 		return ((b->sa == NULL) ? 0 : -1);
192265c4fdfbSgilles 	if (b->sa == NULL)
192365c4fdfbSgilles 		return (1);
192465c4fdfbSgilles 	if (a->sa->sa_len < b->sa->sa_len)
192565c4fdfbSgilles 		return (-1);
192665c4fdfbSgilles 	if (a->sa->sa_len > b->sa->sa_len)
192765c4fdfbSgilles 		return (1);
192865c4fdfbSgilles 	return (memcmp(a->sa, b->sa, a->sa->sa_len));
192965c4fdfbSgilles }
193065c4fdfbSgilles 
193165c4fdfbSgilles SPLAY_GENERATE(mta_source_tree, mta_source, entry, mta_source_cmp);
193265c4fdfbSgilles 
193365c4fdfbSgilles static struct mta_connector *
193465c4fdfbSgilles mta_connector(struct mta_relay *relay, struct mta_source *source)
193565c4fdfbSgilles {
193665c4fdfbSgilles 	struct mta_connector	*c;
193765c4fdfbSgilles 
193865c4fdfbSgilles 	c = tree_get(&relay->connectors, (uintptr_t)(source));
193965c4fdfbSgilles 	if (c == NULL) {
194065c4fdfbSgilles 		c = xcalloc(1, sizeof(*c), "mta_connector");
194165c4fdfbSgilles 		c->relay = relay;
194265c4fdfbSgilles 		c->source = source;
1943c5acbec8Seric 		c->flags |= CONNECTOR_NEW;
194465c4fdfbSgilles 		mta_source_ref(source);
194565c4fdfbSgilles 		tree_xset(&relay->connectors, (uintptr_t)(source), c);
194665c4fdfbSgilles 		stat_increment("mta.connector", 1);
1947c5acbec8Seric 		log_debug("debug: mta: new %s", mta_connector_to_text(c));
194865c4fdfbSgilles 	}
194965c4fdfbSgilles 
195065c4fdfbSgilles 	return (c);
195165c4fdfbSgilles }
195265c4fdfbSgilles 
195365c4fdfbSgilles static void
195465c4fdfbSgilles mta_connector_free(struct mta_connector *c)
195565c4fdfbSgilles {
1956c5acbec8Seric 	log_debug("debug: mta: freeing %s",
1957c5acbec8Seric 	    mta_connector_to_text(c));
1958c5acbec8Seric 
1959c5acbec8Seric 	if (c->flags & CONNECTOR_WAIT) {
1960c5acbec8Seric 		log_debug("debug: mta: canceling timeout for %s",
1961c5acbec8Seric 		    mta_connector_to_text(c));
1962c5acbec8Seric 		runq_cancel(runq_connector, NULL, c);
1963c5acbec8Seric 	}
1964c5acbec8Seric 	mta_source_unref(c->source); /* from constructor */
1965edae97a5Seric 	free(c);
1966c5acbec8Seric 
196765c4fdfbSgilles 	stat_decrement("mta.connector", 1);
196865c4fdfbSgilles }
196965c4fdfbSgilles 
197065c4fdfbSgilles static const char *
197165c4fdfbSgilles mta_connector_to_text(struct mta_connector *c)
197265c4fdfbSgilles {
197365c4fdfbSgilles 	static char buf[1024];
197465c4fdfbSgilles 
1975c5acbec8Seric 	snprintf(buf, sizeof buf, "[connector:%s->%s,0x%x]",
197665c4fdfbSgilles 	    mta_source_to_text(c->source),
1977c5acbec8Seric 	    mta_relay_to_text(c->relay),
1978c5acbec8Seric 	    c->flags);
197965c4fdfbSgilles 	return (buf);
198065c4fdfbSgilles }
198165c4fdfbSgilles 
198265c4fdfbSgilles static struct mta_route *
198365c4fdfbSgilles mta_route(struct mta_source *src, struct mta_host *dst)
198465c4fdfbSgilles {
198565c4fdfbSgilles 	struct mta_route	key, *r;
1986c5acbec8Seric 	static uint64_t		rid = 0;
198765c4fdfbSgilles 
198865c4fdfbSgilles 	key.src = src;
198965c4fdfbSgilles 	key.dst = dst;
199065c4fdfbSgilles 	r = SPLAY_FIND(mta_route_tree, &routes, &key);
199165c4fdfbSgilles 
199265c4fdfbSgilles 	if (r == NULL) {
199365c4fdfbSgilles 		r = xcalloc(1, sizeof(*r), "mta_route");
199465c4fdfbSgilles 		r->src = src;
199565c4fdfbSgilles 		r->dst = dst;
1996c5acbec8Seric 		r->flags |= ROUTE_NEW;
1997c5acbec8Seric 		r->id = ++rid;
199865c4fdfbSgilles 		SPLAY_INSERT(mta_route_tree, &routes, r);
199965c4fdfbSgilles 		mta_source_ref(src);
200065c4fdfbSgilles 		mta_host_ref(dst);
200165c4fdfbSgilles 		stat_increment("mta.route", 1);
200265c4fdfbSgilles 	}
2003c5acbec8Seric 	else if (r->flags & ROUTE_RUNQ) {
2004c5acbec8Seric 		log_debug("debug: mta: mta_route_ref(): canceling runq for route %s",
2005c5acbec8Seric 		    mta_route_to_text(r));
2006c5acbec8Seric 		r->flags &= ~(ROUTE_RUNQ | ROUTE_KEEPALIVE);
2007c5acbec8Seric 		runq_cancel(runq_route, NULL, r);
2008c5acbec8Seric 		r->refcount--; /* from mta_route_unref() */
2009c5acbec8Seric 	}
201065c4fdfbSgilles 
201165c4fdfbSgilles 	r->refcount++;
2012ac5aa4b9Seric 	return (r);
201365c4fdfbSgilles }
201465c4fdfbSgilles 
201565c4fdfbSgilles static void
201665c4fdfbSgilles mta_route_ref(struct mta_route *r)
201765c4fdfbSgilles {
201865c4fdfbSgilles 	r->refcount++;
201965c4fdfbSgilles }
202065c4fdfbSgilles 
202165c4fdfbSgilles static void
202265c4fdfbSgilles mta_route_unref(struct mta_route *r)
202365c4fdfbSgilles {
2024c5acbec8Seric 	time_t	sched, now;
2025c5acbec8Seric 	int	delay;
2026c5acbec8Seric 
202765c4fdfbSgilles 	if (--r->refcount)
202865c4fdfbSgilles 		return;
202965c4fdfbSgilles 
2030c5acbec8Seric 	/*
2031c5acbec8Seric 	 * Nothing references this route, but we might want to keep it alive
2032c5acbec8Seric 	 * for a while.
2033c5acbec8Seric 	 */
2034c5acbec8Seric 	now = time(NULL);
2035c5acbec8Seric 	sched = 0;
2036c5acbec8Seric 
2037c5acbec8Seric 	if (r->penalty) {
2038c5acbec8Seric #if DELAY_QUADRATIC
2039c5acbec8Seric 		delay = DELAY_ROUTE_BASE * r->penalty * r->penalty;
2040c5acbec8Seric #else
2041c5acbec8Seric 		delay = 15 * 60;
2042c5acbec8Seric #endif
2043c5acbec8Seric 		if (delay > DELAY_ROUTE_MAX)
2044c5acbec8Seric 			delay = DELAY_ROUTE_MAX;
2045c5acbec8Seric 		sched = r->lastpenalty + delay;
2046d7bcae4dSeric 		log_debug("debug: mta: mta_route_unref(): keeping route %s alive for %llus (penalty %d)",
2047c5acbec8Seric 		    mta_route_to_text(r), (unsigned long long) sched - now, r->penalty);
2048c5acbec8Seric 	} else if (!(r->flags & ROUTE_KEEPALIVE)) {
2049c5acbec8Seric 		if (r->lastconn + max_seen_conndelay_route > now)
2050c5acbec8Seric 			sched = r->lastconn + max_seen_conndelay_route;
2051c5acbec8Seric 		if (r->lastdisc + max_seen_discdelay_route > now &&
2052c5acbec8Seric 		    r->lastdisc + max_seen_discdelay_route < sched)
2053c5acbec8Seric 			sched = r->lastdisc + max_seen_discdelay_route;
2054c5acbec8Seric 
2055c5acbec8Seric 		if (sched > now)
2056c5acbec8Seric 			log_debug("debug: mta: mta_route_unref(): keeping route %s alive for %llus (imposed delay)",
2057c5acbec8Seric 			    mta_route_to_text(r), (unsigned long long) sched - now);
2058c5acbec8Seric 	}
2059c5acbec8Seric 
2060c5acbec8Seric 	if (sched > now) {
2061c5acbec8Seric 		r->flags |= ROUTE_RUNQ;
2062c5acbec8Seric 		runq_schedule(runq_route, sched, NULL, r);
2063c5acbec8Seric 		r->refcount++;
2064c5acbec8Seric 		return;
2065c5acbec8Seric 	}
2066c5acbec8Seric 
2067c5acbec8Seric 	log_debug("debug: mta: ma_route_unref(): really discarding route %s",
2068c5acbec8Seric 	    mta_route_to_text(r));
2069c5acbec8Seric 
207065c4fdfbSgilles 	SPLAY_REMOVE(mta_route_tree, &routes, r);
207165c4fdfbSgilles 	mta_source_unref(r->src); /* from constructor */
207265c4fdfbSgilles 	mta_host_unref(r->dst); /* from constructor */
2073edae97a5Seric 	free(r);
207465c4fdfbSgilles 	stat_decrement("mta.route", 1);
207565c4fdfbSgilles }
207665c4fdfbSgilles 
207765c4fdfbSgilles static const char *
207865c4fdfbSgilles mta_route_to_text(struct mta_route *r)
207965c4fdfbSgilles {
208065c4fdfbSgilles 	static char	buf[1024];
208165c4fdfbSgilles 
208265c4fdfbSgilles 	snprintf(buf, sizeof buf, "%s <-> %s",
208365c4fdfbSgilles 	    mta_source_to_text(r->src),
208465c4fdfbSgilles 	    mta_host_to_text(r->dst));
208565c4fdfbSgilles 
208665c4fdfbSgilles 	return (buf);
208765c4fdfbSgilles }
208865c4fdfbSgilles 
208965c4fdfbSgilles static int
209065c4fdfbSgilles mta_route_cmp(const struct mta_route *a, const struct mta_route *b)
209165c4fdfbSgilles {
209265c4fdfbSgilles 	if (a->src < b->src)
209365c4fdfbSgilles 		return (-1);
209465c4fdfbSgilles 	if (a->src > b->src)
209565c4fdfbSgilles 		return (1);
209665c4fdfbSgilles 
209765c4fdfbSgilles 	if (a->dst < b->dst)
209865c4fdfbSgilles 		return (-1);
209965c4fdfbSgilles 	if (a->dst > b->dst)
210065c4fdfbSgilles 		return (1);
2101ac5aa4b9Seric 
2102ac5aa4b9Seric 	return (0);
2103ac5aa4b9Seric }
2104ac5aa4b9Seric 
2105ac5aa4b9Seric SPLAY_GENERATE(mta_route_tree, mta_route, entry, mta_route_cmp);
2106c5acbec8Seric 
2107c5acbec8Seric 
2108c5acbec8Seric /* hoststat errors are not critical, we do best effort */
2109c5acbec8Seric void
2110c5acbec8Seric mta_hoststat_update(const char *host, const char *error)
2111c5acbec8Seric {
2112c5acbec8Seric 	struct hoststat	*hs = NULL;
2113c5acbec8Seric 	char		 buf[SMTPD_MAXHOSTNAMELEN];
2114c5acbec8Seric 	time_t		 tm;
2115c5acbec8Seric 
2116c5acbec8Seric 	if (! lowercase(buf, host, sizeof buf))
2117c5acbec8Seric 		return;
2118c5acbec8Seric 
2119c5acbec8Seric 	tm = time(NULL);
2120c5acbec8Seric 	hs = dict_get(&hoststat, buf);
2121c5acbec8Seric 	if (hs == NULL) {
2122c5acbec8Seric 		hs = calloc(1, sizeof *hs);
2123c5acbec8Seric 		if (hs == NULL)
2124c5acbec8Seric 			return;
2125c5acbec8Seric 		tree_init(&hs->deferred);
2126c5acbec8Seric 		runq_schedule(runq_hoststat, tm+HOSTSTAT_EXPIRE_DELAY, NULL, hs);
2127c5acbec8Seric 	}
2128c5acbec8Seric 	strlcpy(hs->name, buf, sizeof hs->name);
2129c5acbec8Seric 	strlcpy(hs->error, error, sizeof hs->error);
2130c5acbec8Seric 	hs->tm = time(NULL);
2131c5acbec8Seric 	dict_set(&hoststat, buf, hs);
2132c5acbec8Seric 
2133c5acbec8Seric 	runq_cancel(runq_hoststat, NULL, hs);
2134c5acbec8Seric 	runq_schedule(runq_hoststat, tm+HOSTSTAT_EXPIRE_DELAY, NULL, hs);
2135c5acbec8Seric }
2136c5acbec8Seric 
2137c5acbec8Seric void
2138c5acbec8Seric mta_hoststat_cache(const char *host, uint64_t evpid)
2139c5acbec8Seric {
2140c5acbec8Seric 	struct hoststat	*hs = NULL;
2141c5acbec8Seric 	char buf[SMTPD_MAXHOSTNAMELEN];
2142c5acbec8Seric 
2143c5acbec8Seric 	if (! lowercase(buf, host, sizeof buf))
2144c5acbec8Seric 		return;
2145c5acbec8Seric 
2146c5acbec8Seric 	hs = dict_get(&hoststat, buf);
2147c5acbec8Seric 	if (hs == NULL)
2148c5acbec8Seric 		return;
2149c5acbec8Seric 
2150c5acbec8Seric 	tree_set(&hs->deferred, evpid, NULL);
2151c5acbec8Seric }
2152c5acbec8Seric 
2153c5acbec8Seric void
2154c5acbec8Seric mta_hoststat_uncache(const char *host, uint64_t evpid)
2155c5acbec8Seric {
2156c5acbec8Seric 	struct hoststat	*hs = NULL;
2157c5acbec8Seric 	char buf[SMTPD_MAXHOSTNAMELEN];
2158c5acbec8Seric 
2159c5acbec8Seric 	if (! lowercase(buf, host, sizeof buf))
2160c5acbec8Seric 		return;
2161c5acbec8Seric 
2162c5acbec8Seric 	hs = dict_get(&hoststat, buf);
2163c5acbec8Seric 	if (hs == NULL)
2164c5acbec8Seric 		return;
2165c5acbec8Seric 
2166c5acbec8Seric 	tree_pop(&hs->deferred, evpid);
2167c5acbec8Seric }
2168c5acbec8Seric 
2169c5acbec8Seric void
2170c5acbec8Seric mta_hoststat_reschedule(const char *host)
2171c5acbec8Seric {
2172c5acbec8Seric 	struct hoststat	*hs = NULL;
2173c5acbec8Seric 	char		 buf[SMTPD_MAXHOSTNAMELEN];
2174c5acbec8Seric 	uint64_t	 evpid;
2175c5acbec8Seric 
2176c5acbec8Seric 	if (! lowercase(buf, host, sizeof buf))
2177c5acbec8Seric 		return;
2178c5acbec8Seric 
2179c5acbec8Seric 	hs = dict_get(&hoststat, buf);
2180c5acbec8Seric 	if (hs == NULL)
2181c5acbec8Seric 		return;
2182c5acbec8Seric 
2183c5acbec8Seric 	while (tree_poproot(&hs->deferred, &evpid, NULL)) {
2184c5acbec8Seric 		m_compose(p_queue, IMSG_MTA_SCHEDULE, 0, 0, -1,
2185c5acbec8Seric 		    &evpid, sizeof evpid);
2186c5acbec8Seric 	}
2187c5acbec8Seric }
2188c5acbec8Seric 
2189c5acbec8Seric static void
2190c5acbec8Seric mta_hoststat_remove_entry(struct hoststat *hs)
2191c5acbec8Seric {
2192c5acbec8Seric 	while (tree_poproot(&hs->deferred, NULL, NULL))
2193c5acbec8Seric 		;
2194c5acbec8Seric 	dict_pop(&hoststat, hs->name);
2195c5acbec8Seric 	runq_cancel(runq_hoststat, NULL, hs);
2196c5acbec8Seric }
2197