xref: /openbsd-src/usr.sbin/smtpd/mta_session.c (revision ad8d242dc7ca593f1fe13645fd97cf16ba39bc1c)
1*ad8d242dSop /*	$OpenBSD: mta_session.c,v 1.152 2024/09/03 18:27:04 op Exp $	*/
202a71407Seric 
302a71407Seric /*
402a71407Seric  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
565c4fdfbSgilles  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
602a71407Seric  * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
702a71407Seric  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
802a71407Seric  *
902a71407Seric  * Permission to use, copy, modify, and distribute this software for any
1002a71407Seric  * purpose with or without fee is hereby granted, provided that the above
1102a71407Seric  * copyright notice and this permission notice appear in all copies.
1202a71407Seric  *
1302a71407Seric  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1402a71407Seric  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1502a71407Seric  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1602a71407Seric  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1702a71407Seric  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1802a71407Seric  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1902a71407Seric  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2002a71407Seric  */
2102a71407Seric 
225c7993c7Sgilles #include <sys/stat.h>
2302a71407Seric 
2402a71407Seric #include <ctype.h>
2502a71407Seric #include <errno.h>
2602a71407Seric #include <inttypes.h>
2702a71407Seric #include <stdlib.h>
2802a71407Seric #include <string.h>
290dcffd0dSop #include <time.h>
30eed85469Seric #include <tls.h>
3102a71407Seric #include <unistd.h>
3202a71407Seric 
3302a71407Seric #include "smtpd.h"
3402a71407Seric #include "log.h"
3565c4fdfbSgilles 
36c5acbec8Seric #define MAX_TRYBEFOREDISABLE	10
3702a71407Seric 
38f4807aa6Seric #define MTA_HIWAT		65535
39f4807aa6Seric 
40f4807aa6Seric enum mta_state {
41f4807aa6Seric 	MTA_INIT,
4265c4fdfbSgilles 	MTA_BANNER,
4365c4fdfbSgilles 	MTA_EHLO,
4465c4fdfbSgilles 	MTA_HELO,
450faa9237Seric 	MTA_LHLO,
4665c4fdfbSgilles 	MTA_STARTTLS,
4765c4fdfbSgilles 	MTA_AUTH,
48d6d017c1Seric 	MTA_AUTH_PLAIN,
49d6d017c1Seric 	MTA_AUTH_LOGIN,
50d6d017c1Seric 	MTA_AUTH_LOGIN_USER,
51d6d017c1Seric 	MTA_AUTH_LOGIN_PASS,
5265c4fdfbSgilles 	MTA_READY,
5365c4fdfbSgilles 	MTA_MAIL,
5465c4fdfbSgilles 	MTA_RCPT,
55f4807aa6Seric 	MTA_DATA,
5665c4fdfbSgilles 	MTA_BODY,
5765c4fdfbSgilles 	MTA_EOM,
580faa9237Seric 	MTA_LMTP_EOM,
5965c4fdfbSgilles 	MTA_RSET,
6065c4fdfbSgilles 	MTA_QUIT,
61f4807aa6Seric };
62f4807aa6Seric 
6365c4fdfbSgilles #define MTA_FORCE_ANYSSL	0x0001
6465c4fdfbSgilles #define MTA_FORCE_SMTPS		0x0002
6565c4fdfbSgilles #define MTA_FORCE_TLS     	0x0004
6665c4fdfbSgilles #define MTA_FORCE_PLAIN		0x0008
6765c4fdfbSgilles #define MTA_WANT_SECURE		0x0010
6863dab8efSgilles #define MTA_DOWNGRADE_PLAIN    	0x0080
6965c4fdfbSgilles 
7065c4fdfbSgilles #define MTA_TLS			0x0100
712e0b0231Seric #define MTA_TLS_VERIFIED	0x0200
7265c4fdfbSgilles 
7365c4fdfbSgilles #define MTA_FREE		0x0400
740faa9237Seric #define MTA_LMTP		0x0800
7508843899Seric #define MTA_WAIT		0x1000
76c5acbec8Seric #define MTA_HANGON		0x2000
77987b6918Seric #define MTA_RECONN		0x4000
78f4807aa6Seric 
79f4807aa6Seric #define MTA_EXT_STARTTLS	0x01
80d6d017c1Seric #define MTA_EXT_PIPELINING	0x02
81d6d017c1Seric #define MTA_EXT_AUTH		0x04
82d6d017c1Seric #define MTA_EXT_AUTH_PLAIN     	0x08
83d6d017c1Seric #define MTA_EXT_AUTH_LOGIN     	0x10
84e645dcceSgilles #define MTA_EXT_SIZE     	0x20
85d6d017c1Seric 
86ee990baaSeric struct mta_session {
87ac5aa4b9Seric 	uint64_t		 id;
8865c4fdfbSgilles 	struct mta_relay	*relay;
89ac5aa4b9Seric 	struct mta_route	*route;
9065c4fdfbSgilles 	char			*helo;
91d6ee62d2Seric 	char			*mxname;
92ac5aa4b9Seric 
9302ec394cSgilles 	char			*username;
9402ec394cSgilles 
95ac5aa4b9Seric 	int			 flags;
9665c4fdfbSgilles 
9765c4fdfbSgilles 	int			 attempt;
9865c4fdfbSgilles 	int			 use_smtps;
9965c4fdfbSgilles 	int			 use_starttls;
10065c4fdfbSgilles 	int			 use_smtp_tls;
1018fb9a403Seric 	int			 ready;
102ac5aa4b9Seric 
103c73dd3e0Seric 	struct event		 ev;
1048d3f7f0dSeric 	struct io		*io;
105ac5aa4b9Seric 	int			 ext;
10665c4fdfbSgilles 
107e645dcceSgilles 	size_t			 ext_size;
108e645dcceSgilles 
109c5acbec8Seric 	size_t			 msgtried;
110c5acbec8Seric 	size_t			 msgcount;
111c5acbec8Seric 	size_t			 rcptcount;
112c5acbec8Seric 	int			 hangon;
11365c4fdfbSgilles 
11465c4fdfbSgilles 	enum mta_state		 state;
11565c4fdfbSgilles 	struct mta_task		*task;
116399c053eSeric 	struct mta_envelope	*currevp;
11765c4fdfbSgilles 	FILE			*datafp;
118c29b04ffSgilles 	size_t			 datalen;
119c5acbec8Seric 
120d6b3bcf4Seric 	size_t			 failures;
12180c6a60cSgilles 
12280c6a60cSgilles 	char			 replybuf[2048];
123ee990baaSeric };
124ee990baaSeric 
12565c4fdfbSgilles static void mta_session_init(void);
12665c4fdfbSgilles static void mta_start(int fd, short ev, void *arg);
127b556a8d3Seric static void mta_io(struct io *, int, void *);
12865c4fdfbSgilles static void mta_free(struct mta_session *);
12901eba458Seric static void mta_getnameinfo_cb(void *, int, const char *, const char *);
13065c4fdfbSgilles static void mta_on_ptr(void *, void *, void *);
131c5acbec8Seric static void mta_on_timeout(struct runq *, void *);
13265c4fdfbSgilles static void mta_connect(struct mta_session *);
13302a71407Seric static void mta_enter_state(struct mta_session *, int);
134c5acbec8Seric static void mta_flush_task(struct mta_session *, int, const char *, size_t, int);
135ceeebefeSbenno static void mta_error(struct mta_session *, const char *, ...)
136ceeebefeSbenno     __attribute__((__format__ (printf, 2, 3)));
137ceeebefeSbenno static void mta_send(struct mta_session *, char *, ...)
138ceeebefeSbenno     __attribute__((__format__ (printf, 2, 3)));
13902a71407Seric static ssize_t mta_queue_data(struct mta_session *);
14002a71407Seric static void mta_response(struct mta_session *, char *);
14102a71407Seric static const char * mta_strstate(int);
142eed85469Seric static void mta_tls_init(struct mta_session *);
143eed85469Seric static void mta_tls_started(struct mta_session *);
14408843899Seric static struct mta_session *mta_tree_pop(struct tree *, uint64_t);
145fe95d8d0Seric static const char * dsn_strret(enum dsn_ret);
146fe95d8d0Seric static const char * dsn_strnotify(uint8_t);
147d6b3bcf4Seric 
148c5acbec8Seric void mta_hoststat_update(const char *, const char *);
149c5acbec8Seric void mta_hoststat_reschedule(const char *);
150c5acbec8Seric void mta_hoststat_cache(const char *, uint64_t);
151c5acbec8Seric void mta_hoststat_uncache(const char *, uint64_t);
15202a71407Seric 
153a7061c79Sgilles 
154a7061c79Sgilles static void mta_filter_begin(struct mta_session *);
155a7061c79Sgilles static void mta_filter_end(struct mta_session *);
156a7061c79Sgilles static void mta_connected(struct mta_session *);
157a7061c79Sgilles static void mta_disconnected(struct mta_session *);
158a7061c79Sgilles 
159a7061c79Sgilles static void mta_report_link_connect(struct mta_session *, const char *, int,
160a7061c79Sgilles     const struct sockaddr_storage *,
161a7061c79Sgilles     const struct sockaddr_storage *);
162a7061c79Sgilles static void mta_report_link_greeting(struct mta_session *, const char *);
163a7061c79Sgilles static void mta_report_link_identify(struct mta_session *, const char *, const char *);
164a7061c79Sgilles static void mta_report_link_tls(struct mta_session *, const char *);
165a7061c79Sgilles static void mta_report_link_disconnect(struct mta_session *);
166a7061c79Sgilles static void mta_report_link_auth(struct mta_session *, const char *, const char *);
167a7061c79Sgilles static void mta_report_tx_reset(struct mta_session *, uint32_t);
168a7061c79Sgilles static void mta_report_tx_begin(struct mta_session *, uint32_t);
169a7061c79Sgilles static void mta_report_tx_mail(struct mta_session *, uint32_t, const char *, int);
170a7061c79Sgilles static void mta_report_tx_rcpt(struct mta_session *, uint32_t, const char *, int);
171a7061c79Sgilles static void mta_report_tx_envelope(struct mta_session *, uint32_t, uint64_t);
172a7061c79Sgilles static void mta_report_tx_data(struct mta_session *, uint32_t, int);
173a7061c79Sgilles static void mta_report_tx_commit(struct mta_session *, uint32_t, size_t);
174a7061c79Sgilles static void mta_report_tx_rollback(struct mta_session *, uint32_t);
175a7061c79Sgilles static void mta_report_protocol_client(struct mta_session *, const char *);
176a7061c79Sgilles static void mta_report_protocol_server(struct mta_session *, const char *);
177a7061c79Sgilles #if 0
178a7061c79Sgilles static void mta_report_filter_response(struct mta_session *, int, int, const char *);
179a7061c79Sgilles #endif
180a7061c79Sgilles static void mta_report_timeout(struct mta_session *);
181a7061c79Sgilles 
182a7061c79Sgilles 
18365c4fdfbSgilles static struct tree wait_helo;
18465c4fdfbSgilles static struct tree wait_ptr;
18565c4fdfbSgilles static struct tree wait_fd;
186346108feSgilles static struct tree wait_tls_init;
187346108feSgilles static struct tree wait_tls_verify;
18865c4fdfbSgilles 
189c5acbec8Seric static struct runq *hangon;
190c5acbec8Seric 
191a7061c79Sgilles #define	SESSION_FILTERED(s) \
192a7061c79Sgilles 	((s)->relay->dispatcher->u.remote.filtername)
193a7061c79Sgilles 
19465c4fdfbSgilles static void
19565c4fdfbSgilles mta_session_init(void)
19665c4fdfbSgilles {
19765c4fdfbSgilles 	static int init = 0;
19865c4fdfbSgilles 
19965c4fdfbSgilles 	if (!init) {
20065c4fdfbSgilles 		tree_init(&wait_helo);
20165c4fdfbSgilles 		tree_init(&wait_ptr);
20265c4fdfbSgilles 		tree_init(&wait_fd);
203346108feSgilles 		tree_init(&wait_tls_init);
204346108feSgilles 		tree_init(&wait_tls_verify);
205c5acbec8Seric 		runq_init(&hangon, mta_on_timeout);
20665c4fdfbSgilles 		init = 1;
20765c4fdfbSgilles 	}
20865c4fdfbSgilles }
209ac5aa4b9Seric 
210ac5aa4b9Seric void
211d6ee62d2Seric mta_session(struct mta_relay *relay, struct mta_route *route, const char *mxname)
212ac5aa4b9Seric {
21365c4fdfbSgilles 	struct mta_session	*s;
21465c4fdfbSgilles 	struct timeval		 tv;
215ac5aa4b9Seric 
21665c4fdfbSgilles 	mta_session_init();
217ac5aa4b9Seric 
218118c16f3Sgilles 	s = xcalloc(1, sizeof *s);
21965c4fdfbSgilles 	s->id = generate_uid();
22065c4fdfbSgilles 	s->relay = relay;
22165c4fdfbSgilles 	s->route = route;
222d6ee62d2Seric 	s->mxname = xstrdup(mxname);
22365c4fdfbSgilles 
224a7061c79Sgilles 	mta_filter_begin(s);
225a7061c79Sgilles 
2260faa9237Seric 	if (relay->flags & RELAY_LMTP)
2270faa9237Seric 		s->flags |= MTA_LMTP;
2284c503616Seric 	switch (relay->tls) {
2294c503616Seric 	case RELAY_TLS_SMTPS:
23065c4fdfbSgilles 		s->flags |= MTA_FORCE_SMTPS;
23165c4fdfbSgilles 		s->flags |= MTA_WANT_SECURE;
232ac5aa4b9Seric 		break;
2334c503616Seric 	case RELAY_TLS_STARTTLS:
23465c4fdfbSgilles 		s->flags |= MTA_FORCE_TLS;
23565c4fdfbSgilles 		s->flags |= MTA_WANT_SECURE;
23665c4fdfbSgilles 		break;
2374c503616Seric 	case RELAY_TLS_OPPORTUNISTIC:
23865c4fdfbSgilles 		/* do not force anything, try tls then smtp */
239ac5aa4b9Seric 		break;
2404c503616Seric 	case RELAY_TLS_NO:
24165c4fdfbSgilles 		s->flags |= MTA_FORCE_PLAIN;
2424c503616Seric 		break;
2434c503616Seric 	default:
2444c503616Seric 		fatalx("bad value for relay->tls: %d", relay->tls);
245ac5aa4b9Seric 	}
246ac5aa4b9Seric 
24765c4fdfbSgilles 	log_debug("debug: mta: %p: spawned for relay %s", s,
24865c4fdfbSgilles 	    mta_relay_to_text(relay));
24930f45e1aSeric 	stat_increment("mta.session", 1);
25065c4fdfbSgilles 
25165c4fdfbSgilles 	if (route->dst->ptrname || route->dst->lastptrquery) {
25265c4fdfbSgilles 		/* We want to delay the connection since to always notify
25365c4fdfbSgilles 		 * the relay asynchronously.
25465c4fdfbSgilles 		 */
25565c4fdfbSgilles 		tv.tv_sec = 0;
25665c4fdfbSgilles 		tv.tv_usec = 0;
257c73dd3e0Seric 		evtimer_set(&s->ev, mta_start, s);
258c73dd3e0Seric 		evtimer_add(&s->ev, &tv);
25965c4fdfbSgilles 	} else if (waitq_wait(&route->dst->ptrname, mta_on_ptr, s)) {
26097556c3cSmartijn 		resolver_getnameinfo(s->route->dst->sa, NI_NUMERICSERV,
26197556c3cSmartijn 		    mta_getnameinfo_cb, s);
26265c4fdfbSgilles 	}
263ac5aa4b9Seric }
26482cdf8beSeric 
26502a71407Seric void
26665c4fdfbSgilles mta_session_imsg(struct mproc *p, struct imsg *imsg)
26702a71407Seric {
26802a71407Seric 	struct mta_session	*s;
26965c4fdfbSgilles 	struct msg		 m;
27065c4fdfbSgilles 	uint64_t		 reqid;
27165c4fdfbSgilles 	const char		*name;
272510586acSclaudio 	int			 status, fd;
2735c7993c7Sgilles 	struct stat		 sb;
27402a71407Seric 
27502a71407Seric 	switch (imsg->hdr.type) {
27602a71407Seric 
277aa1d5973Seric 	case IMSG_MTA_OPEN_MESSAGE:
27865c4fdfbSgilles 		m_msg(&m, imsg);
27965c4fdfbSgilles 		m_get_id(&m, &reqid);
28065c4fdfbSgilles 		m_end(&m);
28165c4fdfbSgilles 
282510586acSclaudio 		fd = imsg_get_fd(imsg);
28308843899Seric 		s = mta_tree_pop(&wait_fd, reqid);
28408843899Seric 		if (s == NULL) {
285510586acSclaudio 			if (fd != -1)
286510586acSclaudio 				close(fd);
28708843899Seric 			return;
28808843899Seric 		}
28908843899Seric 
290510586acSclaudio 		if (fd == -1) {
291299c4efeSeric 			log_debug("debug: mta: failed to obtain msg fd");
292aa1d5973Seric 			mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL,
293c5acbec8Seric 			    "Could not get message fd", 0, 0);
294299c4efeSeric 			mta_enter_state(s, MTA_READY);
295299c4efeSeric 			return;
296299c4efeSeric 		}
297299c4efeSeric 
298a51f45e3Sgilles 		if ((s->ext & MTA_EXT_SIZE) && s->ext_size != 0) {
299510586acSclaudio 			if (fstat(fd, &sb) == -1) {
3005c7993c7Sgilles 				log_debug("debug: mta: failed to stat msg fd");
3015c7993c7Sgilles 				mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL,
3025c7993c7Sgilles 				    "Could not stat message fd", 0, 0);
3035c7993c7Sgilles 				mta_enter_state(s, MTA_READY);
304510586acSclaudio 				close(fd);
3055c7993c7Sgilles 				return;
3065c7993c7Sgilles 			}
3075c7993c7Sgilles 			if (sb.st_size > (off_t)s->ext_size) {
3085c7993c7Sgilles 				log_debug("debug: mta: message too large for peer");
3095c7993c7Sgilles 				mta_flush_task(s, IMSG_MTA_DELIVERY_PERMFAIL,
3105c7993c7Sgilles 				    "message too large for peer", 0, 0);
3115c7993c7Sgilles 				mta_enter_state(s, MTA_READY);
312510586acSclaudio 				close(fd);
3135c7993c7Sgilles 				return;
3145c7993c7Sgilles 			}
3155c7993c7Sgilles 		}
3165c7993c7Sgilles 
317510586acSclaudio 		s->datafp = fdopen(fd, "r");
31802a71407Seric 		if (s->datafp == NULL)
31902a71407Seric 			fatal("mta: fdopen");
320a7b2e833Seric 
32165c4fdfbSgilles 		mta_enter_state(s, MTA_MAIL);
32202a71407Seric 		return;
32302a71407Seric 
324aa1d5973Seric 	case IMSG_MTA_LOOKUP_HELO:
32565c4fdfbSgilles 		m_msg(&m, imsg);
32665c4fdfbSgilles 		m_get_id(&m, &reqid);
32765c4fdfbSgilles 		m_get_int(&m, &status);
32865c4fdfbSgilles 		if (status == LKA_OK)
32965c4fdfbSgilles 			m_get_string(&m, &name);
33065c4fdfbSgilles 		m_end(&m);
33165c4fdfbSgilles 
33208843899Seric 		s = mta_tree_pop(&wait_helo, reqid);
33308843899Seric 		if (s == NULL)
33408843899Seric 			return;
33565c4fdfbSgilles 
33665c4fdfbSgilles 		if (status == LKA_OK) {
337118c16f3Sgilles 			s->helo = xstrdup(name);
33865c4fdfbSgilles 			mta_connect(s);
33965c4fdfbSgilles 		} else {
34065c4fdfbSgilles 			mta_source_error(s->relay, s->route,
341b317af72Snicm 			    "Failed to retrieve helo string");
34265c4fdfbSgilles 			mta_free(s);
34365c4fdfbSgilles 		}
34465c4fdfbSgilles 		return;
34565c4fdfbSgilles 
34602a71407Seric 	default:
347ff01b044Seric 		fatalx("mta_session_imsg: unexpected %s imsg",
34802a71407Seric 		    imsg_to_str(imsg->hdr.type));
34902a71407Seric 	}
35002a71407Seric }
35102a71407Seric 
35208843899Seric static struct mta_session *
35308843899Seric mta_tree_pop(struct tree *wait, uint64_t reqid)
35408843899Seric {
35508843899Seric 	struct mta_session *s;
35608843899Seric 
35708843899Seric 	s = tree_xpop(wait, reqid);
35808843899Seric 	if (s->flags & MTA_FREE) {
35908843899Seric 		log_debug("debug: mta: %p: zombie session", s);
36008843899Seric 		mta_free(s);
36108843899Seric 		return (NULL);
36208843899Seric 	}
36308843899Seric 	s->flags &= ~MTA_WAIT;
36408843899Seric 
36508843899Seric 	return (s);
36608843899Seric }
36708843899Seric 
36802a71407Seric static void
36965c4fdfbSgilles mta_free(struct mta_session *s)
37065c4fdfbSgilles {
37165c4fdfbSgilles 	struct mta_relay *relay;
37265c4fdfbSgilles 	struct mta_route *route;
37365c4fdfbSgilles 
37465c4fdfbSgilles 	log_debug("debug: mta: %p: session done", s);
37565c4fdfbSgilles 
376a7061c79Sgilles 	mta_disconnected(s);
377a7061c79Sgilles 
378c5acbec8Seric 	if (s->ready)
379c5acbec8Seric 		s->relay->nconn_ready -= 1;
380c5acbec8Seric 
381c5acbec8Seric 	if (s->flags & MTA_HANGON) {
382c5acbec8Seric 		log_debug("debug: mta: %p: cancelling hangon timer", s);
38328efdb08Seric 		runq_cancel(hangon, s);
384c5acbec8Seric 	}
385c5acbec8Seric 
3868d3f7f0dSeric 	if (s->io)
3878d3f7f0dSeric 		io_free(s->io);
38865c4fdfbSgilles 
38965c4fdfbSgilles 	if (s->task)
39065c4fdfbSgilles 		fatalx("current task should have been deleted already");
391c29b04ffSgilles 	if (s->datafp) {
39265c4fdfbSgilles 		fclose(s->datafp);
393c29b04ffSgilles 		s->datalen = 0;
394c29b04ffSgilles 	}
39565c4fdfbSgilles 	free(s->helo);
39665c4fdfbSgilles 
39765c4fdfbSgilles 	relay = s->relay;
39865c4fdfbSgilles 	route = s->route;
39902ec394cSgilles 	free(s->username);
400d6ee62d2Seric 	free(s->mxname);
40165c4fdfbSgilles 	free(s);
40265c4fdfbSgilles 	stat_decrement("mta.session", 1);
40365c4fdfbSgilles 	mta_route_collect(relay, route);
40465c4fdfbSgilles }
40565c4fdfbSgilles 
40665c4fdfbSgilles static void
40701eba458Seric mta_getnameinfo_cb(void *arg, int gaierrno, const char *host, const char *serv)
40801eba458Seric {
40901eba458Seric 	struct mta_session *s = arg;
41001eba458Seric 	struct mta_host *h;
41101eba458Seric 
41201eba458Seric 	h = s->route->dst;
41301eba458Seric 	h->lastptrquery = time(NULL);
41401eba458Seric 	if (host)
41501eba458Seric 		h->ptrname = xstrdup(host);
41601eba458Seric 	waitq_run(&h->ptrname, h->ptrname);
41701eba458Seric }
41801eba458Seric 
41901eba458Seric static void
420c5acbec8Seric mta_on_timeout(struct runq *runq, void *arg)
421c5acbec8Seric {
422c5acbec8Seric 	struct mta_session *s = arg;
423c5acbec8Seric 
424c5acbec8Seric 	log_debug("mta: timeout for session hangon");
425c5acbec8Seric 
426c5acbec8Seric 	s->flags &= ~MTA_HANGON;
427c5acbec8Seric 	s->hangon++;
428c5acbec8Seric 
429c5acbec8Seric 	mta_enter_state(s, MTA_READY);
430c5acbec8Seric }
431c5acbec8Seric 
432c5acbec8Seric static void
43365c4fdfbSgilles mta_on_ptr(void *tag, void *arg, void *data)
43465c4fdfbSgilles {
43565c4fdfbSgilles 	struct mta_session *s = arg;
43665c4fdfbSgilles 
43765c4fdfbSgilles 	mta_connect(s);
43865c4fdfbSgilles }
43965c4fdfbSgilles 
44065c4fdfbSgilles static void
44165c4fdfbSgilles mta_start(int fd, short ev, void *arg)
44265c4fdfbSgilles {
44365c4fdfbSgilles 	struct mta_session *s = arg;
44465c4fdfbSgilles 
44565c4fdfbSgilles 	mta_connect(s);
44665c4fdfbSgilles }
44765c4fdfbSgilles 
44865c4fdfbSgilles static void
44965c4fdfbSgilles mta_connect(struct mta_session *s)
45065c4fdfbSgilles {
45165c4fdfbSgilles 	struct sockaddr_storage	 ss;
45265c4fdfbSgilles 	struct sockaddr		*sa;
45365c4fdfbSgilles 	int			 portno;
4545bc3c73eSeric 	const char		*schema;
45565c4fdfbSgilles 
45665c4fdfbSgilles 	if (s->helo == NULL) {
45765c4fdfbSgilles 		if (s->relay->helotable && s->route->src->sa) {
458aa1d5973Seric 			m_create(p_lka, IMSG_MTA_LOOKUP_HELO, 0, 0, -1);
45965c4fdfbSgilles 			m_add_id(p_lka, s->id);
46065c4fdfbSgilles 			m_add_string(p_lka, s->relay->helotable);
46165c4fdfbSgilles 			m_add_sockaddr(p_lka, s->route->src->sa);
46265c4fdfbSgilles 			m_close(p_lka);
46365c4fdfbSgilles 			tree_xset(&wait_helo, s->id, s);
46408843899Seric 			s->flags |= MTA_WAIT;
46565c4fdfbSgilles 			return;
46665c4fdfbSgilles 		}
467b61c9c75Seric 		else if (s->relay->heloname)
468118c16f3Sgilles 			s->helo = xstrdup(s->relay->heloname);
46965c4fdfbSgilles 		else
470118c16f3Sgilles 			s->helo = xstrdup(env->sc_hostname);
47165c4fdfbSgilles 	}
47265c4fdfbSgilles 
4738d3f7f0dSeric 	if (s->io) {
4748d3f7f0dSeric 		io_free(s->io);
4758d3f7f0dSeric 		s->io = NULL;
4768d3f7f0dSeric 	}
47765c4fdfbSgilles 
47865c4fdfbSgilles 	s->use_smtps = s->use_starttls = s->use_smtp_tls = 0;
47965c4fdfbSgilles 
48065c4fdfbSgilles 	switch (s->attempt) {
48165c4fdfbSgilles 	case 0:
48265c4fdfbSgilles 		if (s->flags & MTA_FORCE_SMTPS)
48365c4fdfbSgilles 			s->use_smtps = 1;	/* smtps */
48465c4fdfbSgilles 		else if (s->flags & (MTA_FORCE_TLS|MTA_FORCE_ANYSSL))
48565c4fdfbSgilles 			s->use_starttls = 1;	/* tls, tls+smtps */
48665c4fdfbSgilles 		else if (!(s->flags & MTA_FORCE_PLAIN))
48765c4fdfbSgilles 			s->use_smtp_tls = 1;
48865c4fdfbSgilles 		break;
48965c4fdfbSgilles 	case 1:
49065c4fdfbSgilles 		if (s->flags & MTA_FORCE_ANYSSL) {
49165c4fdfbSgilles 			s->use_smtps = 1;	/* tls+smtps */
49265c4fdfbSgilles 			break;
49365c4fdfbSgilles 		}
49463dab8efSgilles 		else if (s->flags & MTA_DOWNGRADE_PLAIN) {
4955bc3c73eSeric 			/* smtp, with tls failure */
49663dab8efSgilles 			break;
49763dab8efSgilles 		}
49865c4fdfbSgilles 	default:
499c5acbec8Seric 		mta_free(s);
500c5acbec8Seric 		return;
50165c4fdfbSgilles 	}
50265c4fdfbSgilles 	portno = s->use_smtps ? 465 : 25;
50365c4fdfbSgilles 
50465c4fdfbSgilles 	/* Override with relay-specified port */
50565c4fdfbSgilles 	if (s->relay->port)
50665c4fdfbSgilles 		portno = s->relay->port;
50765c4fdfbSgilles 
50865c4fdfbSgilles 	memmove(&ss, s->route->dst->sa, s->route->dst->sa->sa_len);
50965c4fdfbSgilles 	sa = (struct sockaddr *)&ss;
510ec74b5c6Seric 
511ec74b5c6Seric 	if (sa->sa_family == AF_INET)
512ec74b5c6Seric 		((struct sockaddr_in *)sa)->sin_port = htons(portno);
513ec74b5c6Seric 	else if (sa->sa_family == AF_INET6)
514ec74b5c6Seric 		((struct sockaddr_in6 *)sa)->sin6_port = htons(portno);
51565c4fdfbSgilles 
51665c4fdfbSgilles 	s->attempt += 1;
51765c4fdfbSgilles 	if (s->use_smtp_tls)
5185bc3c73eSeric 		schema = "smtp://";
51965c4fdfbSgilles 	else if (s->use_starttls)
5205bc3c73eSeric 		schema = "smtp+tls://";
52165c4fdfbSgilles 	else if (s->use_smtps)
52265c4fdfbSgilles 		schema = "smtps://";
5230faa9237Seric 	else if (s->flags & MTA_LMTP)
5240faa9237Seric 		schema = "lmtp://";
52565c4fdfbSgilles 	else
5265bc3c73eSeric 		schema = "smtp+notls://";
5270cf935dfSgilles 
5287a93bdaaSgilles 	log_info("%016"PRIx64" mta "
529cf540174Sgilles 	    "connecting address=%s%s:%d host=%s",
5307a93bdaaSgilles 	    s->id, schema, sa_to_text(s->route->dst->sa),
5317a93bdaaSgilles 	    portno, s->route->dst->ptrname);
53265c4fdfbSgilles 
53365c4fdfbSgilles 	mta_enter_state(s, MTA_INIT);
5348d3f7f0dSeric 	s->io = io_new();
5358d3f7f0dSeric 	io_set_callback(s->io, mta_io, s);
5368d3f7f0dSeric 	io_set_timeout(s->io, 300000);
5378d3f7f0dSeric 	if (io_connect(s->io, sa, s->route->src->sa) == -1) {
53865c4fdfbSgilles 		/*
53965c4fdfbSgilles 		 * This error is most likely a "no route",
54065c4fdfbSgilles 		 * so there is no need to try again.
54165c4fdfbSgilles 		 */
5428d3f7f0dSeric 		log_debug("debug: mta: io_connect failed: %s", io_error(s->io));
54365c4fdfbSgilles 		if (errno == EADDRNOTAVAIL)
5448d3f7f0dSeric 			mta_source_error(s->relay, s->route, io_error(s->io));
54565c4fdfbSgilles 		else
5468d3f7f0dSeric 			mta_error(s, "Connection failed: %s", io_error(s->io));
54765c4fdfbSgilles 		mta_free(s);
54865c4fdfbSgilles 	}
54965c4fdfbSgilles }
55065c4fdfbSgilles 
55165c4fdfbSgilles static void
55202a71407Seric mta_enter_state(struct mta_session *s, int newstate)
55302a71407Seric {
554fe95d8d0Seric 	struct mta_envelope	 *e;
555fe95d8d0Seric 	size_t			 envid_sz;
55602a71407Seric 	int			 oldstate;
55702a71407Seric 	ssize_t			 q;
558953aae25Sderaadt 	char			 ibuf[LINE_MAX];
559953aae25Sderaadt 	char			 obuf[LINE_MAX];
560d6d017c1Seric 	int			 offset;
561c78098c5Sgilles 	const char     		*srs_sender;
56202a71407Seric 
56302a71407Seric again:
56402a71407Seric 	oldstate = s->state;
56502a71407Seric 
56602a71407Seric 	log_trace(TRACE_MTA, "mta: %p: %s -> %s", s,
56702a71407Seric 	    mta_strstate(oldstate),
56802a71407Seric 	    mta_strstate(newstate));
56902a71407Seric 
57002a71407Seric 	s->state = newstate;
57102a71407Seric 
57280c6a60cSgilles 	memset(s->replybuf, 0, sizeof s->replybuf);
57380c6a60cSgilles 
57402a71407Seric 	/* don't try this at home! */
57502a71407Seric #define mta_enter_state(_s, _st) do { newstate = _st; goto again; } while (0)
57602a71407Seric 
57702a71407Seric 	switch (s->state) {
57802a71407Seric 	case MTA_INIT:
57965c4fdfbSgilles 	case MTA_BANNER:
58002a71407Seric 		break;
58102a71407Seric 
58265c4fdfbSgilles 	case MTA_EHLO:
58302a71407Seric 		s->ext = 0;
58465c4fdfbSgilles 		mta_send(s, "EHLO %s", s->helo);
585a7061c79Sgilles 		mta_report_link_identify(s, "EHLO", s->helo);
58602a71407Seric 		break;
58702a71407Seric 
58865c4fdfbSgilles 	case MTA_HELO:
58902a71407Seric 		s->ext = 0;
59065c4fdfbSgilles 		mta_send(s, "HELO %s", s->helo);
591a7061c79Sgilles 		mta_report_link_identify(s, "HELO", s->helo);
59202a71407Seric 		break;
59302a71407Seric 
5940faa9237Seric 	case MTA_LHLO:
5950faa9237Seric 		s->ext = 0;
5960faa9237Seric 		mta_send(s, "LHLO %s", s->helo);
597a7061c79Sgilles 		mta_report_link_identify(s, "LHLO", s->helo);
5980faa9237Seric 		break;
5990faa9237Seric 
60065c4fdfbSgilles 	case MTA_STARTTLS:
6013605276eSgilles 		if (s->flags & MTA_DOWNGRADE_PLAIN)
6023605276eSgilles 			mta_enter_state(s, MTA_AUTH);
60302a71407Seric 		if (s->flags & MTA_TLS) /* already started */
60465c4fdfbSgilles 			mta_enter_state(s, MTA_AUTH);
60565c4fdfbSgilles 		else if ((s->ext & MTA_EXT_STARTTLS) == 0) {
60665c4fdfbSgilles 			if (s->flags & MTA_FORCE_TLS || s->flags & MTA_WANT_SECURE) {
60765c4fdfbSgilles 				mta_error(s, "TLS required but not supported by remote host");
608987b6918Seric 				s->flags |= MTA_RECONN;
60965c4fdfbSgilles 			}
61065c4fdfbSgilles 			else
61102a71407Seric 				/* server doesn't support starttls, do not use it */
61265c4fdfbSgilles 				mta_enter_state(s, MTA_AUTH);
61365c4fdfbSgilles 		}
61402a71407Seric 		else
61502a71407Seric 			mta_send(s, "STARTTLS");
61602a71407Seric 		break;
61702a71407Seric 
61865c4fdfbSgilles 	case MTA_AUTH:
619d6d017c1Seric 		if (s->relay->secret && s->flags & MTA_TLS) {
620d6d017c1Seric 			if (s->ext & MTA_EXT_AUTH) {
621d6d017c1Seric 				if (s->ext & MTA_EXT_AUTH_PLAIN) {
622d6d017c1Seric 					mta_enter_state(s, MTA_AUTH_PLAIN);
623d6d017c1Seric 					break;
624d6d017c1Seric 				}
625d6d017c1Seric 				if (s->ext & MTA_EXT_AUTH_LOGIN) {
626d6d017c1Seric 					mta_enter_state(s, MTA_AUTH_LOGIN);
627d6d017c1Seric 					break;
628d6d017c1Seric 				}
629d6d017c1Seric 				log_debug("debug: mta: %p: no supported AUTH method on session", s);
630d6d017c1Seric 				mta_error(s, "no supported AUTH method");
631d6d017c1Seric 			}
632d6d017c1Seric 			else {
633d6d017c1Seric 				log_debug("debug: mta: %p: AUTH not advertised on session", s);
634d6d017c1Seric 				mta_error(s, "AUTH not advertised");
635d6d017c1Seric 			}
636d6d017c1Seric 		}
63765c4fdfbSgilles 		else if (s->relay->secret) {
63865c4fdfbSgilles 			log_debug("debug: mta: %p: not using AUTH on non-TLS "
63965c4fdfbSgilles 			    "session", s);
640*ad8d242dSop 			mta_error(s, "Refuse to AUTH over insecure channel");
64165c4fdfbSgilles 			mta_connect(s);
642ac5aa4b9Seric 		} else {
64365c4fdfbSgilles 			mta_enter_state(s, MTA_READY);
644ac5aa4b9Seric 		}
64502a71407Seric 		break;
64602a71407Seric 
647d6d017c1Seric 	case MTA_AUTH_PLAIN:
64802ec394cSgilles 		memset(ibuf, 0, sizeof ibuf);
64902ec394cSgilles 		if (base64_decode(s->relay->secret, (unsigned char *)ibuf,
65002ec394cSgilles 				  sizeof(ibuf)-1) == -1) {
65102ec394cSgilles 			log_debug("debug: mta: %p: credentials too large on session", s);
65202ec394cSgilles 			mta_error(s, "Credentials too large");
65302ec394cSgilles 			break;
65402ec394cSgilles 		}
65502ec394cSgilles 		s->username = xstrdup(ibuf+1);
656d6d017c1Seric 		mta_send(s, "AUTH PLAIN %s", s->relay->secret);
657d6d017c1Seric 		break;
658d6d017c1Seric 
659d6d017c1Seric 	case MTA_AUTH_LOGIN:
660d6d017c1Seric 		mta_send(s, "AUTH LOGIN");
661d6d017c1Seric 		break;
662d6d017c1Seric 
663d6d017c1Seric 	case MTA_AUTH_LOGIN_USER:
664c1392a69Seric 		memset(ibuf, 0, sizeof ibuf);
665254aed36Seric 		if (base64_decode(s->relay->secret, (unsigned char *)ibuf,
666254aed36Seric 				  sizeof(ibuf)-1) == -1) {
667d6d017c1Seric 			log_debug("debug: mta: %p: credentials too large on session", s);
668d6d017c1Seric 			mta_error(s, "Credentials too large");
669d6d017c1Seric 			break;
670d6d017c1Seric 		}
67102ec394cSgilles 		s->username = xstrdup(ibuf+1);
672d6d017c1Seric 
673c1392a69Seric 		memset(obuf, 0, sizeof obuf);
674254aed36Seric 		base64_encode((unsigned char *)ibuf + 1, strlen(ibuf + 1), obuf, sizeof obuf);
675d6d017c1Seric 		mta_send(s, "%s", obuf);
676d6d017c1Seric 
677c1392a69Seric 		memset(ibuf, 0, sizeof ibuf);
678c1392a69Seric 		memset(obuf, 0, sizeof obuf);
679d6d017c1Seric 		break;
680d6d017c1Seric 
681d6d017c1Seric 	case MTA_AUTH_LOGIN_PASS:
682c1392a69Seric 		memset(ibuf, 0, sizeof ibuf);
683254aed36Seric 		if (base64_decode(s->relay->secret, (unsigned char *)ibuf,
684254aed36Seric 				  sizeof(ibuf)-1) == -1) {
685d6d017c1Seric 			log_debug("debug: mta: %p: credentials too large on session", s);
686d6d017c1Seric 			mta_error(s, "Credentials too large");
687d6d017c1Seric 			break;
688d6d017c1Seric 		}
689d6d017c1Seric 
690d6d017c1Seric 		offset = strlen(ibuf+1)+2;
691c1392a69Seric 		memset(obuf, 0, sizeof obuf);
692254aed36Seric 		base64_encode((unsigned char *)ibuf + offset, strlen(ibuf + offset), obuf, sizeof obuf);
693d6d017c1Seric 		mta_send(s, "%s", obuf);
694d6d017c1Seric 
695c1392a69Seric 		memset(ibuf, 0, sizeof ibuf);
696c1392a69Seric 		memset(obuf, 0, sizeof obuf);
697d6d017c1Seric 		break;
698d6d017c1Seric 
69965c4fdfbSgilles 	case MTA_READY:
70065c4fdfbSgilles 		/* Ready to send a new mail */
7018fb9a403Seric 		if (s->ready == 0) {
7028fb9a403Seric 			s->ready = 1;
703c5acbec8Seric 			s->relay->nconn_ready += 1;
70465c4fdfbSgilles 			mta_route_ok(s->relay, s->route);
7058fb9a403Seric 		}
70665c4fdfbSgilles 
707c5acbec8Seric 		if (s->msgtried >= MAX_TRYBEFOREDISABLE) {
708cf540174Sgilles 			log_info("%016"PRIx64" mta host-rejects-all-mails",
709c5acbec8Seric 			    s->id);
710c5acbec8Seric 			mta_route_down(s->relay, s->route);
711c5acbec8Seric 			mta_enter_state(s, MTA_QUIT);
712c5acbec8Seric 			break;
713c5acbec8Seric 		}
714c5acbec8Seric 
715c5acbec8Seric 		if (s->msgcount >= s->relay->limits->max_mail_per_session) {
71665c4fdfbSgilles 			log_debug("debug: mta: "
71765c4fdfbSgilles 			    "%p: cannot send more message to relay %s", s,
71865c4fdfbSgilles 			    mta_relay_to_text(s->relay));
71965c4fdfbSgilles 			mta_enter_state(s, MTA_QUIT);
72065c4fdfbSgilles 			break;
72165c4fdfbSgilles 		}
72265c4fdfbSgilles 
7233605276eSgilles 		/*
7243605276eSgilles 		 * When downgrading from opportunistic TLS, clear flag and
7253605276eSgilles 		 * possibly reuse the same task (forbidden in other cases).
7263605276eSgilles 		 */
7273605276eSgilles 		if (s->flags & MTA_DOWNGRADE_PLAIN)
7283605276eSgilles 			s->flags &= ~MTA_DOWNGRADE_PLAIN;
7293605276eSgilles 		else if (s->task)
7303605276eSgilles 			fatalx("task should be NULL at this point");
7313605276eSgilles 
7323605276eSgilles 		if (s->task == NULL)
73365c4fdfbSgilles 			s->task = mta_route_next_task(s->relay, s->route);
73465c4fdfbSgilles 		if (s->task == NULL) {
73565c4fdfbSgilles 			log_debug("debug: mta: %p: no task for relay %s",
73665c4fdfbSgilles 			    s, mta_relay_to_text(s->relay));
737c5acbec8Seric 
738c5acbec8Seric 			if (s->relay->nconn > 1 ||
739c5acbec8Seric 			    s->hangon >= s->relay->limits->sessdelay_keepalive) {
74065c4fdfbSgilles 				mta_enter_state(s, MTA_QUIT);
74165c4fdfbSgilles 				break;
74265c4fdfbSgilles 			}
74365c4fdfbSgilles 
74466c3250dSderaadt 			log_debug("mta: debug: last connection: hanging on for %llds",
74566c3250dSderaadt 			    (long long)(s->relay->limits->sessdelay_keepalive -
74666c3250dSderaadt 			    s->hangon));
747c5acbec8Seric 			s->flags |= MTA_HANGON;
74828efdb08Seric 			runq_schedule(hangon, 1, s);
749c5acbec8Seric 			break;
750c5acbec8Seric 		}
751c5acbec8Seric 
75265c4fdfbSgilles 		log_debug("debug: mta: %p: handling next task for relay %s", s,
75365c4fdfbSgilles 			    mta_relay_to_text(s->relay));
75465c4fdfbSgilles 
75530f45e1aSeric 		stat_increment("mta.task.running", 1);
75665c4fdfbSgilles 
757aa1d5973Seric 		m_create(p_queue, IMSG_MTA_OPEN_MESSAGE, 0, 0, -1);
75865c4fdfbSgilles 		m_add_id(p_queue, s->id);
75965c4fdfbSgilles 		m_add_msgid(p_queue, s->task->msgid);
76065c4fdfbSgilles 		m_close(p_queue);
76165c4fdfbSgilles 
76265c4fdfbSgilles 		tree_xset(&wait_fd, s->id, s);
76308843899Seric 		s->flags |= MTA_WAIT;
76402a71407Seric 		break;
76502a71407Seric 
76665c4fdfbSgilles 	case MTA_MAIL:
767fe95d8d0Seric 		s->currevp = TAILQ_FIRST(&s->task->envelopes);
768fe95d8d0Seric 
769fe95d8d0Seric 		e = s->currevp;
770c5acbec8Seric 		s->hangon = 0;
771c5acbec8Seric 		s->msgtried++;
772fe95d8d0Seric 		envid_sz = strlen(e->dsn_envid);
773c78098c5Sgilles 
774c78098c5Sgilles 		/* SRS-encode if requested for the relay action, AND we're not
775c78098c5Sgilles 		 * bouncing, AND we have an RCPT which means we are forwarded,
776c78098c5Sgilles 		 * AND the RCPT has a '@' just for sanity check (will always).
777c78098c5Sgilles 		 */
778c78098c5Sgilles 		if (env->sc_srs_key != NULL &&
779c78098c5Sgilles 		    s->relay->srs &&
780c78098c5Sgilles 		    strchr(s->task->sender, '@') &&
781c78098c5Sgilles 		    e->rcpt &&
782c78098c5Sgilles 		    strchr(e->rcpt, '@')) {
783c78098c5Sgilles 			/* encode and replace task sender with new SRS-sender */
784c78098c5Sgilles 			srs_sender = srs_encode(s->task->sender,
785c78098c5Sgilles 			    strchr(e->rcpt, '@') + 1);
786c78098c5Sgilles 			if (srs_sender) {
787c78098c5Sgilles 				free(s->task->sender);
788c78098c5Sgilles 				s->task->sender = xstrdup(srs_sender);
789c78098c5Sgilles 			}
790c78098c5Sgilles 		}
791c78098c5Sgilles 
792fe95d8d0Seric 		if (s->ext & MTA_EXT_DSN) {
793fe95d8d0Seric 			mta_send(s, "MAIL FROM:<%s>%s%s%s%s",
794fe95d8d0Seric 			    s->task->sender,
795fe95d8d0Seric 			    e->dsn_ret ? " RET=" : "",
796fe95d8d0Seric 			    e->dsn_ret ? dsn_strret(e->dsn_ret) : "",
797fe95d8d0Seric 			    envid_sz ? " ENVID=" : "",
798fe95d8d0Seric 			    envid_sz ? e->dsn_envid : "");
799fe95d8d0Seric 		} else
800399c053eSeric 			mta_send(s, "MAIL FROM:<%s>", s->task->sender);
80102a71407Seric 		break;
80202a71407Seric 
80365c4fdfbSgilles 	case MTA_RCPT:
80482cdf8beSeric 		if (s->currevp == NULL)
805ac5aa4b9Seric 			s->currevp = TAILQ_FIRST(&s->task->envelopes);
806fe95d8d0Seric 
807fe95d8d0Seric 		e = s->currevp;
808fe95d8d0Seric 		if (s->ext & MTA_EXT_DSN) {
809fe95d8d0Seric 			mta_send(s, "RCPT TO:<%s>%s%s%s%s",
810fe95d8d0Seric 			    e->dest,
811fe95d8d0Seric 			    e->dsn_notify ? " NOTIFY=" : "",
812fe95d8d0Seric 			    e->dsn_notify ? dsn_strnotify(e->dsn_notify) : "",
813cd8603dbSop 			    e->dsn_orcpt ? " ORCPT=" : "",
814fe95d8d0Seric 			    e->dsn_orcpt ? e->dsn_orcpt : "");
815fe95d8d0Seric 		} else
816fe95d8d0Seric 			mta_send(s, "RCPT TO:<%s>", e->dest);
817fe95d8d0Seric 
818aebb80a1Sgilles 		mta_report_tx_envelope(s, s->task->msgid, e->id);
8190faa9237Seric 		s->rcptcount++;
82002a71407Seric 		break;
82102a71407Seric 
82265c4fdfbSgilles 	case MTA_DATA:
82302a71407Seric 		fseek(s->datafp, 0, SEEK_SET);
82402a71407Seric 		mta_send(s, "DATA");
82502a71407Seric 		break;
82602a71407Seric 
82765c4fdfbSgilles 	case MTA_BODY:
82802a71407Seric 		if (s->datafp == NULL) {
829ac5aa4b9Seric 			log_trace(TRACE_MTA, "mta: %p: end-of-file", s);
83065c4fdfbSgilles 			mta_enter_state(s, MTA_EOM);
83102a71407Seric 			break;
83202a71407Seric 		}
83302a71407Seric 
83402a71407Seric 		if ((q = mta_queue_data(s)) == -1) {
83565c4fdfbSgilles 			s->flags |= MTA_FREE;
83602a71407Seric 			break;
83702a71407Seric 		}
838299c4efeSeric 		if (q == 0) {
839299c4efeSeric 			mta_enter_state(s, MTA_BODY);
840299c4efeSeric 			break;
841299c4efeSeric 		}
84202a71407Seric 
8435c9fb78eSeric 		log_trace(TRACE_MTA, "mta: %p: >>> [...%zd bytes...]", s, q);
84402a71407Seric 		break;
84502a71407Seric 
84665c4fdfbSgilles 	case MTA_EOM:
84702a71407Seric 		mta_send(s, ".");
84802a71407Seric 		break;
84902a71407Seric 
8500faa9237Seric 	case MTA_LMTP_EOM:
8510faa9237Seric 		/* LMTP reports status of each delivery, so enable read */
8528d3f7f0dSeric 		io_set_read(s->io);
8530faa9237Seric 		break;
8540faa9237Seric 
85565c4fdfbSgilles 	case MTA_RSET:
85665c4fdfbSgilles 		if (s->datafp) {
85765c4fdfbSgilles 			fclose(s->datafp);
85865c4fdfbSgilles 			s->datafp = NULL;
859c29b04ffSgilles 			s->datalen = 0;
86065c4fdfbSgilles 		}
86165c4fdfbSgilles 		mta_send(s, "RSET");
86202a71407Seric 		break;
86302a71407Seric 
86465c4fdfbSgilles 	case MTA_QUIT:
86565c4fdfbSgilles 		mta_send(s, "QUIT");
86682cdf8beSeric 		break;
86782cdf8beSeric 
86802a71407Seric 	default:
8695a60eab8Seric 		fatalx("mta_enter_state: unknown state");
87002a71407Seric 	}
87102a71407Seric #undef mta_enter_state
87202a71407Seric }
87302a71407Seric 
87402a71407Seric /*
87502a71407Seric  * Handle a response to an SMTP command
87602a71407Seric  */
87702a71407Seric static void
87802a71407Seric mta_response(struct mta_session *s, char *line)
87902a71407Seric {
880399c053eSeric 	struct mta_envelope	*e;
88180f61805Seric 	struct sockaddr_storage	 ss;
88280f61805Seric 	struct sockaddr		*sa;
883c5acbec8Seric 	const char		*domain;
8842fed5853Sgilles 	char			*pbuf;
885c5acbec8Seric 	socklen_t		 sa_len;
886953aae25Sderaadt 	char			 buf[LINE_MAX];
88765c4fdfbSgilles 	int			 delivery;
88802a71407Seric 
88902a71407Seric 	switch (s->state) {
89002a71407Seric 
89165c4fdfbSgilles 	case MTA_BANNER:
89263dab8efSgilles 		if (line[0] != '2') {
89363dab8efSgilles 			mta_error(s, "BANNER rejected: %s", line);
89463dab8efSgilles 			s->flags |= MTA_FREE;
89563dab8efSgilles 			return;
89663dab8efSgilles 		}
8972fed5853Sgilles 
8982fed5853Sgilles 		pbuf = "";
8992fed5853Sgilles 		if (strlen(line) > 4) {
9002fed5853Sgilles 			(void)strlcpy(buf, line + 4, sizeof buf);
9012fed5853Sgilles 			if ((pbuf = strchr(buf, ' ')))
9022fed5853Sgilles 				*pbuf = '\0';
9032fed5853Sgilles 			pbuf = valid_domainpart(buf) ? buf : "";
9042fed5853Sgilles 		}
9052fed5853Sgilles 		mta_report_link_greeting(s, pbuf);
9062fed5853Sgilles 
9070faa9237Seric 		if (s->flags & MTA_LMTP)
9080faa9237Seric 			mta_enter_state(s, MTA_LHLO);
9090faa9237Seric 		else
91065c4fdfbSgilles 			mta_enter_state(s, MTA_EHLO);
91102a71407Seric 		break;
91202a71407Seric 
91365c4fdfbSgilles 	case MTA_EHLO:
91402a71407Seric 		if (line[0] != '2') {
91565c4fdfbSgilles 			/* rejected at ehlo state */
916ee68d859Seric 			if ((s->relay->flags & RELAY_AUTH) ||
91765c4fdfbSgilles 			    (s->flags & MTA_WANT_SECURE)) {
91865c4fdfbSgilles 				mta_error(s, "EHLO rejected: %s", line);
91965c4fdfbSgilles 				s->flags |= MTA_FREE;
92002a71407Seric 				return;
92102a71407Seric 			}
92265c4fdfbSgilles 			mta_enter_state(s, MTA_HELO);
92302a71407Seric 			return;
92402a71407Seric 		}
92565c4fdfbSgilles 		if (!(s->flags & MTA_FORCE_PLAIN))
92665c4fdfbSgilles 			mta_enter_state(s, MTA_STARTTLS);
92765c4fdfbSgilles 		else
92865c4fdfbSgilles 			mta_enter_state(s, MTA_READY);
92902a71407Seric 		break;
93002a71407Seric 
93165c4fdfbSgilles 	case MTA_HELO:
93202a71407Seric 		if (line[0] != '2') {
93365c4fdfbSgilles 			mta_error(s, "HELO rejected: %s", line);
93465c4fdfbSgilles 			s->flags |= MTA_FREE;
93502a71407Seric 			return;
93602a71407Seric 		}
93765c4fdfbSgilles 		mta_enter_state(s, MTA_READY);
93802a71407Seric 		break;
93902a71407Seric 
9400faa9237Seric 	case MTA_LHLO:
9410faa9237Seric 		if (line[0] != '2') {
9420faa9237Seric 			mta_error(s, "LHLO rejected: %s", line);
9430faa9237Seric 			s->flags |= MTA_FREE;
9440faa9237Seric 			return;
9450faa9237Seric 		}
9460faa9237Seric 		mta_enter_state(s, MTA_READY);
9470faa9237Seric 		break;
9480faa9237Seric 
94965c4fdfbSgilles 	case MTA_STARTTLS:
95002a71407Seric 		if (line[0] != '2') {
95165c4fdfbSgilles 			if (!(s->flags & MTA_WANT_SECURE)) {
95265c4fdfbSgilles 				mta_enter_state(s, MTA_AUTH);
95302a71407Seric 				return;
95402a71407Seric 			}
95565c4fdfbSgilles 			/* XXX mark that the MX doesn't support STARTTLS */
95665c4fdfbSgilles 			mta_error(s, "STARTTLS rejected: %s", line);
95765c4fdfbSgilles 			s->flags |= MTA_FREE;
95802a71407Seric 			return;
95902a71407Seric 		}
96065c4fdfbSgilles 
961eed85469Seric 		mta_tls_init(s);
96202a71407Seric 		break;
96302a71407Seric 
964d6d017c1Seric 	case MTA_AUTH_PLAIN:
965d6d017c1Seric 		if (line[0] != '2') {
966d6d017c1Seric 			mta_error(s, "AUTH rejected: %s", line);
96702ec394cSgilles 			mta_report_link_auth(s, s->username, "fail");
968d6d017c1Seric 			s->flags |= MTA_FREE;
969d6d017c1Seric 			return;
970d6d017c1Seric 		}
97102ec394cSgilles 		mta_report_link_auth(s, s->username, "pass");
972d6d017c1Seric 		mta_enter_state(s, MTA_READY);
973d6d017c1Seric 		break;
974d6d017c1Seric 
975d6d017c1Seric 	case MTA_AUTH_LOGIN:
976d6d017c1Seric 		if (strncmp(line, "334 ", 4) != 0) {
977d6d017c1Seric 			mta_error(s, "AUTH rejected: %s", line);
97802ec394cSgilles 			mta_report_link_auth(s, s->username, "fail");
979d6d017c1Seric 			s->flags |= MTA_FREE;
980d6d017c1Seric 			return;
981d6d017c1Seric 		}
982d6d017c1Seric 		mta_enter_state(s, MTA_AUTH_LOGIN_USER);
983d6d017c1Seric 		break;
984d6d017c1Seric 
985d6d017c1Seric 	case MTA_AUTH_LOGIN_USER:
986d6d017c1Seric 		if (strncmp(line, "334 ", 4) != 0) {
987d6d017c1Seric 			mta_error(s, "AUTH rejected: %s", line);
98802ec394cSgilles 			mta_report_link_auth(s, s->username, "fail");
989d6d017c1Seric 			s->flags |= MTA_FREE;
990d6d017c1Seric 			return;
991d6d017c1Seric 		}
992d6d017c1Seric 		mta_enter_state(s, MTA_AUTH_LOGIN_PASS);
993d6d017c1Seric 		break;
994d6d017c1Seric 
995d6d017c1Seric 	case MTA_AUTH_LOGIN_PASS:
99602a71407Seric 		if (line[0] != '2') {
99765c4fdfbSgilles 			mta_error(s, "AUTH rejected: %s", line);
99802ec394cSgilles 			mta_report_link_auth(s, s->username, "fail");
99965c4fdfbSgilles 			s->flags |= MTA_FREE;
100002a71407Seric 			return;
100102a71407Seric 		}
100202ec394cSgilles 		mta_report_link_auth(s, s->username, "pass");
100365c4fdfbSgilles 		mta_enter_state(s, MTA_READY);
100402a71407Seric 		break;
100502a71407Seric 
100665c4fdfbSgilles 	case MTA_MAIL:
100702a71407Seric 		if (line[0] != '2') {
100865c4fdfbSgilles 			if (line[0] == '5')
1009aa1d5973Seric 				delivery = IMSG_MTA_DELIVERY_PERMFAIL;
101065c4fdfbSgilles 			else
1011aa1d5973Seric 				delivery = IMSG_MTA_DELIVERY_TEMPFAIL;
1012a7061c79Sgilles 
1013c5acbec8Seric 			mta_flush_task(s, delivery, line, 0, 0);
101465c4fdfbSgilles 			mta_enter_state(s, MTA_RSET);
101502a71407Seric 			return;
101602a71407Seric 		}
1017aebb80a1Sgilles 		mta_report_tx_begin(s, s->task->msgid);
1018a7061c79Sgilles 		mta_report_tx_mail(s, s->task->msgid, s->task->sender, 1);
101965c4fdfbSgilles 		mta_enter_state(s, MTA_RCPT);
102002a71407Seric 		break;
102102a71407Seric 
102265c4fdfbSgilles 	case MTA_RCPT:
1023399c053eSeric 		e = s->currevp;
1024c5acbec8Seric 
1025c5acbec8Seric 		/* remove envelope from hosttat cache if there */
1026c5acbec8Seric 		if ((domain = strchr(e->dest, '@')) != NULL) {
1027c5acbec8Seric 			domain++;
1028c5acbec8Seric 			mta_hoststat_uncache(domain, e->id);
1029c5acbec8Seric 		}
1030c5acbec8Seric 
103102a71407Seric 		s->currevp = TAILQ_NEXT(s->currevp, entry);
1032c5acbec8Seric 		if (line[0] == '2') {
1033d6b3bcf4Seric 			s->failures = 0;
1034c5acbec8Seric 			/*
1035c5acbec8Seric 			 * this host is up, reschedule envelopes that
1036c5acbec8Seric 			 * were cached for reschedule.
1037c5acbec8Seric 			 */
1038c5acbec8Seric 			if (domain)
1039c5acbec8Seric 				mta_hoststat_reschedule(domain);
1040c5acbec8Seric 		}
1041c5acbec8Seric 		else {
1042a7061c79Sgilles 			mta_report_tx_rollback(s, s->task->msgid);
1043a7061c79Sgilles 			mta_report_tx_reset(s, s->task->msgid);
104465c4fdfbSgilles 			if (line[0] == '5')
1045aa1d5973Seric 				delivery = IMSG_MTA_DELIVERY_PERMFAIL;
104665c4fdfbSgilles 			else
1047aa1d5973Seric 				delivery = IMSG_MTA_DELIVERY_TEMPFAIL;
1048d6b3bcf4Seric 			s->failures++;
104965c4fdfbSgilles 
1050c5acbec8Seric 			/* remove failed envelope from task list */
1051399c053eSeric 			TAILQ_REMOVE(&s->task->envelopes, e, entry);
1052c5acbec8Seric 			stat_decrement("mta.envelope", 1);
1053c5acbec8Seric 
1054c5acbec8Seric 			/* log right away */
10559c7a0077Sgilles 			(void)snprintf(buf, sizeof(buf), "%s",
105665c4fdfbSgilles 			    mta_host_to_text(s->route->dst));
1057299c4efeSeric 
1058299c4efeSeric 			e->session = s->id;
1059299c4efeSeric 			/* XXX */
1060299c4efeSeric 			/*
1061299c4efeSeric 			 * getsockname() can only fail with ENOBUFS here
1062299c4efeSeric 			 * best effort, don't log source ...
1063299c4efeSeric 			 */
106480f61805Seric 			sa_len = sizeof(ss);
106580f61805Seric 			sa = (struct sockaddr *)&ss;
1066df69c215Sderaadt 			if (getsockname(io_fileno(s->io), sa, &sa_len) == -1)
1067c5acbec8Seric 				mta_delivery_log(e, NULL, buf, delivery, line);
1068299c4efeSeric 			else
106980f61805Seric 				mta_delivery_log(e, sa_to_text(sa),
1070299c4efeSeric 				    buf, delivery, line);
107165c4fdfbSgilles 
1072d6b3bcf4Seric 			if (domain)
10739ed05047Seric 				mta_hoststat_update(domain, e->status);
1074d6b3bcf4Seric 			mta_delivery_notify(e);
1075c5acbec8Seric 
1076d6b3bcf4Seric 			if (s->relay->limits->max_failures_per_session &&
1077d6b3bcf4Seric 			    s->failures == s->relay->limits->max_failures_per_session) {
1078aa1d5973Seric 					mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL,
1079d6b3bcf4Seric 					    "Too many consecutive errors, closing connection", 0, 1);
1080c5acbec8Seric 					mta_enter_state(s, MTA_QUIT);
1081c5acbec8Seric 					break;
1082c5acbec8Seric 				}
1083c5acbec8Seric 
1084c5acbec8Seric 			/*
1085c5acbec8Seric 			 * if no more envelopes, flush failed queue
1086c5acbec8Seric 			 */
1087ac5aa4b9Seric 			if (TAILQ_EMPTY(&s->task->envelopes)) {
1088aa1d5973Seric 				mta_flush_task(s, IMSG_MTA_DELIVERY_OK,
1089c5acbec8Seric 				    "No envelope", 0, 0);
109065c4fdfbSgilles 				mta_enter_state(s, MTA_RSET);
1091bca22c95Seric 				break;
1092bca22c95Seric 			}
1093ac5aa4b9Seric 		}
1094c5acbec8Seric 
1095a7061c79Sgilles 		switch (line[0]) {
1096a7061c79Sgilles 		case '2':
10972c7e0e88Sgilles 			mta_report_tx_rcpt(s,
10982c7e0e88Sgilles 			    s->task->msgid, e->dest, 1);
1099a7061c79Sgilles 			break;
1100a7061c79Sgilles 		case '4':
1101a7061c79Sgilles 			mta_report_tx_rcpt(s,
1102a7061c79Sgilles 			    s->task->msgid, e->dest, -1);
1103a7061c79Sgilles 			break;
1104a7061c79Sgilles 		case '5':
1105a7061c79Sgilles 			mta_report_tx_rcpt(s,
1106a7061c79Sgilles 			    s->task->msgid, e->dest, 0);
1107a7061c79Sgilles 			break;
1108a7061c79Sgilles 		}
1109a7061c79Sgilles 
111002a71407Seric 		if (s->currevp == NULL)
111165c4fdfbSgilles 			mta_enter_state(s, MTA_DATA);
111202a71407Seric 		else
111365c4fdfbSgilles 			mta_enter_state(s, MTA_RCPT);
111402a71407Seric 		break;
111502a71407Seric 
111665c4fdfbSgilles 	case MTA_DATA:
111765c4fdfbSgilles 		if (line[0] == '2' || line[0] == '3') {
1118a7061c79Sgilles 			mta_report_tx_data(s, s->task->msgid, 1);
111965c4fdfbSgilles 			mta_enter_state(s, MTA_BODY);
112065c4fdfbSgilles 			break;
112102a71407Seric 		}
1122a7061c79Sgilles 
112365c4fdfbSgilles 		if (line[0] == '5')
1124aa1d5973Seric 			delivery = IMSG_MTA_DELIVERY_PERMFAIL;
112565c4fdfbSgilles 		else
1126aa1d5973Seric 			delivery = IMSG_MTA_DELIVERY_TEMPFAIL;
1127a7061c79Sgilles 		mta_report_tx_data(s, s->task->msgid,
1128a7061c79Sgilles 		    delivery == IMSG_MTA_DELIVERY_TEMPFAIL ? -1 : 0);
1129a7061c79Sgilles 		mta_report_tx_rollback(s, s->task->msgid);
1130a7061c79Sgilles 		mta_report_tx_reset(s, s->task->msgid);
1131c5acbec8Seric 		mta_flush_task(s, delivery, line, 0, 0);
113265c4fdfbSgilles 		mta_enter_state(s, MTA_RSET);
113302a71407Seric 		break;
113402a71407Seric 
11350faa9237Seric 	case MTA_LMTP_EOM:
113665c4fdfbSgilles 	case MTA_EOM:
113765c4fdfbSgilles 		if (line[0] == '2') {
1138aa1d5973Seric 			delivery = IMSG_MTA_DELIVERY_OK;
1139c5acbec8Seric 			s->msgtried = 0;
1140ac5aa4b9Seric 			s->msgcount++;
114165c4fdfbSgilles 		}
114265c4fdfbSgilles 		else if (line[0] == '5')
1143aa1d5973Seric 			delivery = IMSG_MTA_DELIVERY_PERMFAIL;
114465c4fdfbSgilles 		else
1145aa1d5973Seric 			delivery = IMSG_MTA_DELIVERY_TEMPFAIL;
1146a7061c79Sgilles 		if (delivery != IMSG_MTA_DELIVERY_OK) {
1147a7061c79Sgilles 			mta_report_tx_rollback(s, s->task->msgid);
1148a7061c79Sgilles 			mta_report_tx_reset(s, s->task->msgid);
1149a7061c79Sgilles 		}
1150a7061c79Sgilles 		else {
1151a7061c79Sgilles 			mta_report_tx_commit(s, s->task->msgid, s->datalen);
1152a7061c79Sgilles 			mta_report_tx_reset(s, s->task->msgid);
1153a7061c79Sgilles 		}
1154c5acbec8Seric 		mta_flush_task(s, delivery, line, (s->flags & MTA_LMTP) ? 1 : 0, 0);
11550faa9237Seric 		if (s->task) {
11560faa9237Seric 			s->rcptcount--;
11570faa9237Seric 			mta_enter_state(s, MTA_LMTP_EOM);
11580faa9237Seric 		} else {
11590faa9237Seric 			s->rcptcount = 0;
1160c5acbec8Seric 			if (s->relay->limits->sessdelay_transaction) {
1161d7bcae4dSeric 				log_debug("debug: mta: waiting for %llds before next transaction",
1162e7954c7dSop 				    (long long)s->relay->limits->sessdelay_transaction);
1163c5acbec8Seric 				s->hangon = s->relay->limits->sessdelay_transaction -1;
1164c5acbec8Seric 				s->flags |= MTA_HANGON;
116528efdb08Seric 				runq_schedule(hangon,
116628efdb08Seric 				    s->relay->limits->sessdelay_transaction, s);
1167c5acbec8Seric 			}
1168c5acbec8Seric 			else
116965c4fdfbSgilles 				mta_enter_state(s, MTA_READY);
11700faa9237Seric 		}
117182cdf8beSeric 		break;
117282cdf8beSeric 
117365c4fdfbSgilles 	case MTA_RSET:
1174c5acbec8Seric 		s->rcptcount = 0;
1175a7061c79Sgilles 
1176a7061c79Sgilles 		if (s->task) {
1177a7061c79Sgilles 			mta_report_tx_rollback(s, s->task->msgid);
1178a7061c79Sgilles 			mta_report_tx_reset(s, s->task->msgid);
1179a7061c79Sgilles 		}
1180c5acbec8Seric 		if (s->relay->limits->sessdelay_transaction) {
1181d7bcae4dSeric 			log_debug("debug: mta: waiting for %llds after reset",
1182e7954c7dSop 			    (long long)s->relay->limits->sessdelay_transaction);
1183c5acbec8Seric 			s->hangon = s->relay->limits->sessdelay_transaction -1;
1184c5acbec8Seric 			s->flags |= MTA_HANGON;
118528efdb08Seric 			runq_schedule(hangon,
118628efdb08Seric 			    s->relay->limits->sessdelay_transaction, s);
1187c5acbec8Seric 		}
1188c5acbec8Seric 		else
118965c4fdfbSgilles 			mta_enter_state(s, MTA_READY);
119002a71407Seric 		break;
119102a71407Seric 
119202a71407Seric 	default:
11935a60eab8Seric 		fatalx("mta_response() bad state");
119402a71407Seric 	}
119502a71407Seric }
119602a71407Seric 
119702a71407Seric static void
1198b556a8d3Seric mta_io(struct io *io, int evt, void *arg)
119902a71407Seric {
1200b556a8d3Seric 	struct mta_session	*s = arg;
1201d6d017c1Seric 	char			*line, *msg, *p;
12028728f65bSgilles 	size_t			 len;
120302a71407Seric 	const char		*error;
120402a71407Seric 	int			 cont;
120502a71407Seric 
120665c4fdfbSgilles 	log_trace(TRACE_IO, "mta: %p: %s %s", s, io_strevent(evt),
120765c4fdfbSgilles 	    io_strio(io));
120802a71407Seric 
120902a71407Seric 	switch (evt) {
121002a71407Seric 
121102a71407Seric 	case IO_CONNECTED:
1212a7061c79Sgilles 		mta_connected(s);
121365c4fdfbSgilles 
121465c4fdfbSgilles 		if (s->use_smtps) {
121502a71407Seric 			io_set_write(io);
1216eed85469Seric 			mta_tls_init(s);
121796592628Smartijn 			if (s->flags & MTA_FREE)
121896592628Smartijn 				mta_free(s);
121965c4fdfbSgilles 		}
122065c4fdfbSgilles 		else {
122165c4fdfbSgilles 			mta_enter_state(s, MTA_BANNER);
122265c4fdfbSgilles 			io_set_read(io);
122365c4fdfbSgilles 		}
122402a71407Seric 		break;
122502a71407Seric 
122602a71407Seric 	case IO_TLSREADY:
12274412ef37Sgilles 		log_info("%016"PRIx64" mta tls ciphers=%s",
1228eed85469Seric 		    s->id, tls_to_text(io_tls(s->io)));
122902a71407Seric 		s->flags |= MTA_TLS;
123089818320Smillert 		if (s->relay->dispatcher->u.remote.tls_verify)
1231eed85469Seric 			s->flags |= MTA_TLS_VERIFIED;
123265c4fdfbSgilles 
1233eed85469Seric 		mta_tls_started(s);
1234a7061c79Sgilles 		mta_report_link_tls(s,
1235eed85469Seric 		    tls_to_text(io_tls(s->io)));
12361ddc3febSeric 		break;
12371ddc3febSeric 
123802a71407Seric 	case IO_DATAIN:
123902a71407Seric 	    nextline:
12408d3f7f0dSeric 		line = io_getline(s->io, &len);
124102a71407Seric 		if (line == NULL) {
12428d3f7f0dSeric 			if (io_datalen(s->io) >= LINE_MAX) {
124365c4fdfbSgilles 				mta_error(s, "Input too long");
124465c4fdfbSgilles 				mta_free(s);
124502a71407Seric 			}
12460e198904Seric 			return;
124702a71407Seric 		}
124802a71407Seric 
12490124f38dSeric 		/* Strip trailing '\r' */
12500124f38dSeric 		if (len && line[len - 1] == '\r')
12510124f38dSeric 			line[--len] = '\0';
12520124f38dSeric 
125302a71407Seric 		log_trace(TRACE_MTA, "mta: %p: <<< %s", s, line);
1254a7061c79Sgilles 		mta_report_protocol_server(s, line);
125502a71407Seric 
125602a71407Seric 		if ((error = parse_smtp_response(line, len, &msg, &cont))) {
125765c4fdfbSgilles 			mta_error(s, "Bad response: %s", error);
125865c4fdfbSgilles 			mta_free(s);
125902a71407Seric 			return;
126002a71407Seric 		}
126102a71407Seric 
126202a71407Seric 		/* read extensions */
126365c4fdfbSgilles 		if (s->state == MTA_EHLO) {
126402a71407Seric 			if (strcmp(msg, "STARTTLS") == 0)
126502a71407Seric 				s->ext |= MTA_EXT_STARTTLS;
1266d6d017c1Seric 			else if (strncmp(msg, "AUTH ", 5) == 0) {
126702a71407Seric                                 s->ext |= MTA_EXT_AUTH;
1268d6d017c1Seric                                 if ((p = strstr(msg, " PLAIN")) &&
1269d6d017c1Seric 				    (*(p+6) == '\0' || *(p+6) == ' '))
1270d6d017c1Seric                                         s->ext |= MTA_EXT_AUTH_PLAIN;
1271d6d017c1Seric                                 if ((p = strstr(msg, " LOGIN")) &&
1272d6d017c1Seric 				    (*(p+6) == '\0' || *(p+6) == ' '))
1273d6d017c1Seric                                         s->ext |= MTA_EXT_AUTH_LOGIN;
1274d6d017c1Seric 			}
127502a71407Seric 			else if (strcmp(msg, "PIPELINING") == 0)
127602a71407Seric 				s->ext |= MTA_EXT_PIPELINING;
1277fe95d8d0Seric 			else if (strcmp(msg, "DSN") == 0)
1278fe95d8d0Seric 				s->ext |= MTA_EXT_DSN;
1279e645dcceSgilles 			else if (strncmp(msg, "SIZE ", 5) == 0) {
1280e645dcceSgilles 				s->ext_size = strtonum(msg+5, 0, UINT32_MAX, &error);
1281e645dcceSgilles 				if (error == NULL)
1282e645dcceSgilles 					s->ext |= MTA_EXT_SIZE;
1283e645dcceSgilles 			}
128402a71407Seric 		}
128502a71407Seric 
128680c6a60cSgilles 		/* continuation reply, we parse out the repeating statuses and ESC */
128780c6a60cSgilles 		if (cont) {
128880c6a60cSgilles 			if (s->replybuf[0] == '\0')
128980c6a60cSgilles 				(void)strlcat(s->replybuf, line, sizeof s->replybuf);
12900228dab0Smillert 			else if (len > 4) {
1291000eaaf0Smillert 				p = line + 4;
1292000eaaf0Smillert 				if (isdigit((unsigned char)p[0]) && p[1] == '.' &&
1293000eaaf0Smillert 				    isdigit((unsigned char)p[2]) && p[3] == '.' &&
1294000eaaf0Smillert 				    isdigit((unsigned char)p[4]) && isspace((unsigned char)p[5]))
1295000eaaf0Smillert 					p += 5;
1296000eaaf0Smillert 				(void)strlcat(s->replybuf, p, sizeof s->replybuf);
129780c6a60cSgilles 			}
129802a71407Seric 			goto nextline;
129980c6a60cSgilles 		}
130080c6a60cSgilles 
130180c6a60cSgilles 		/* last line of a reply, check if we're on a continuation to parse out status and ESC.
130280c6a60cSgilles 		 * if we overflow reply buffer or are not on continuation, log entire last line.
130380c6a60cSgilles 		 */
13040228dab0Smillert 		if (s->replybuf[0] == '\0')
13050228dab0Smillert 			(void)strlcat(s->replybuf, line, sizeof s->replybuf);
13060228dab0Smillert 		else if (len > 4) {
130780c6a60cSgilles 			p = line + 4;
1308000eaaf0Smillert 			if (isdigit((unsigned char)p[0]) && p[1] == '.' &&
1309000eaaf0Smillert 			    isdigit((unsigned char)p[2]) && p[3] == '.' &&
1310000eaaf0Smillert 			    isdigit((unsigned char)p[4]) && isspace((unsigned char)p[5]))
131180c6a60cSgilles 				p += 5;
131280c6a60cSgilles 			if (strlcat(s->replybuf, p, sizeof s->replybuf) >= sizeof s->replybuf)
131380c6a60cSgilles 				(void)strlcpy(s->replybuf, line, sizeof s->replybuf);
131480c6a60cSgilles 		}
131502a71407Seric 
131665c4fdfbSgilles 		if (s->state == MTA_QUIT) {
1317cf540174Sgilles 			log_info("%016"PRIx64" mta disconnected reason=quit messages=%zu",
13187a93bdaaSgilles 			    s->id, s->msgcount);
131965c4fdfbSgilles 			mta_free(s);
132002a71407Seric 			return;
132102a71407Seric 		}
1322a0d490c4Seric 		io_set_write(io);
132380c6a60cSgilles 		mta_response(s, s->replybuf);
132465c4fdfbSgilles 		if (s->flags & MTA_FREE) {
132565c4fdfbSgilles 			mta_free(s);
132665c4fdfbSgilles 			return;
132765c4fdfbSgilles 		}
1328987b6918Seric 		if (s->flags & MTA_RECONN) {
1329987b6918Seric 			s->flags &= ~MTA_RECONN;
1330987b6918Seric 			mta_connect(s);
1331987b6918Seric 			return;
1332987b6918Seric 		}
133365c4fdfbSgilles 
13348d3f7f0dSeric 		if (io_datalen(s->io)) {
133582614934Seric 			log_debug("debug: mta: remaining data in input buffer");
133665c4fdfbSgilles 			mta_error(s, "Remote host sent too much data");
133708843899Seric 			if (s->flags & MTA_WAIT)
133808843899Seric 				s->flags |= MTA_FREE;
133908843899Seric 			else
134065c4fdfbSgilles 				mta_free(s);
1341a0d490c4Seric 		}
134202a71407Seric 		break;
134302a71407Seric 
134402a71407Seric 	case IO_LOWAT:
134565c4fdfbSgilles 		if (s->state == MTA_BODY) {
134665c4fdfbSgilles 			mta_enter_state(s, MTA_BODY);
134765c4fdfbSgilles 			if (s->flags & MTA_FREE) {
134865c4fdfbSgilles 				mta_free(s);
134965c4fdfbSgilles 				return;
135065c4fdfbSgilles 			}
135165c4fdfbSgilles 		}
135202a71407Seric 
13538d3f7f0dSeric 		if (io_queued(s->io) == 0)
135402a71407Seric 			io_set_read(io);
135502a71407Seric 		break;
135602a71407Seric 
135702a71407Seric 	case IO_TIMEOUT:
135882614934Seric 		log_debug("debug: mta: %p: connection timeout", s);
135965c4fdfbSgilles 		mta_error(s, "Connection timeout");
1360a7061c79Sgilles 		mta_report_timeout(s);
136165c4fdfbSgilles 		if (!s->ready)
136265c4fdfbSgilles 			mta_connect(s);
136365c4fdfbSgilles 		else
136465c4fdfbSgilles 			mta_free(s);
136502a71407Seric 		break;
136602a71407Seric 
136702a71407Seric 	case IO_ERROR:
1368219e2fd6Seric 		log_debug("debug: mta: %p: IO error: %s", s, io_error(io));
1369c81d3581Sgilles 
1370c81d3581Sgilles 		if (s->state == MTA_STARTTLS && s->use_smtp_tls) {
137163dab8efSgilles 			/* error in non-strict SSL negotiation, downgrade to plain */
13723605276eSgilles 			log_info("smtp-out: Error on session %016"PRIx64
13733605276eSgilles 			    ": opportunistic TLS failed, "
13743605276eSgilles 			    "downgrading to plain", s->id);
13753605276eSgilles 			s->flags &= ~MTA_TLS;
137663dab8efSgilles 			s->flags |= MTA_DOWNGRADE_PLAIN;
137763dab8efSgilles 			mta_connect(s);
13783605276eSgilles 			break;
137963dab8efSgilles 		}
138002a71407Seric 
1381219e2fd6Seric 		mta_error(s, "IO Error: %s", io_error(io));
138273f9c94dSeric 		mta_free(s);
138373f9c94dSeric 		break;
138473f9c94dSeric 
138502a71407Seric 	case IO_DISCONNECTED:
138665c4fdfbSgilles 		log_debug("debug: mta: %p: disconnected in state %s",
138765c4fdfbSgilles 		    s, mta_strstate(s->state));
138865c4fdfbSgilles 		mta_error(s, "Connection closed unexpectedly");
138965c4fdfbSgilles 		if (!s->ready)
139065c4fdfbSgilles 			mta_connect(s);
139165c4fdfbSgilles 		else
139265c4fdfbSgilles 			mta_free(s);
139302a71407Seric 		break;
139402a71407Seric 
139502a71407Seric 	default:
13965a60eab8Seric 		fatalx("mta_io() bad event");
139702a71407Seric 	}
139802a71407Seric }
139902a71407Seric 
140002a71407Seric static void
140102a71407Seric mta_send(struct mta_session *s, char *fmt, ...)
140202a71407Seric {
140302a71407Seric 	va_list  ap;
140402a71407Seric 	char	*p;
140502a71407Seric 	int	 len;
140602a71407Seric 
140702a71407Seric 	va_start(ap, fmt);
140802a71407Seric 	if ((len = vasprintf(&p, fmt, ap)) == -1)
140902a71407Seric 		fatal("mta: vasprintf");
141002a71407Seric 	va_end(ap);
141102a71407Seric 
141202a71407Seric 	log_trace(TRACE_MTA, "mta: %p: >>> %s", s, p);
141302a71407Seric 
1414a7061c79Sgilles 	if (strncasecmp(p, "AUTH PLAIN ", 11) == 0)
1415a7061c79Sgilles 		mta_report_protocol_client(s, "AUTH PLAIN ********");
1416a7061c79Sgilles 	else if (s->state == MTA_AUTH_LOGIN_USER || s->state == MTA_AUTH_LOGIN_PASS)
1417a7061c79Sgilles 		mta_report_protocol_client(s, "********");
1418a7061c79Sgilles 	else
1419a7061c79Sgilles 		mta_report_protocol_client(s, p);
1420a7061c79Sgilles 
14218d3f7f0dSeric 	io_xprintf(s->io, "%s\r\n", p);
142202a71407Seric 
142302a71407Seric 	free(p);
142402a71407Seric }
142502a71407Seric 
142602a71407Seric /*
142702a71407Seric  * Queue some data into the input buffer
142802a71407Seric  */
142902a71407Seric static ssize_t
143002a71407Seric mta_queue_data(struct mta_session *s)
143102a71407Seric {
1432178ba319Ssunil 	char	*ln = NULL;
1433178ba319Ssunil 	size_t	 sz = 0, q;
1434178ba319Ssunil 	ssize_t	 len;
143502a71407Seric 
14368d3f7f0dSeric 	q = io_queued(s->io);
143702a71407Seric 
14388d3f7f0dSeric 	while (io_queued(s->io) < MTA_HIWAT) {
1439178ba319Ssunil 		if ((len = getline(&ln, &sz, s->datafp)) == -1)
144002a71407Seric 			break;
144102a71407Seric 		if (ln[len - 1] == '\n')
144293f98431Schl 			ln[len - 1] = '\0';
1443c29b04ffSgilles 		s->datalen += io_xprintf(s->io, "%s%s\r\n", *ln == '.' ? "." : "", ln);
144402a71407Seric 	}
144502a71407Seric 
1446178ba319Ssunil 	free(ln);
144702a71407Seric 	if (ferror(s->datafp)) {
1448aa1d5973Seric 		mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL,
1449c5acbec8Seric 		    "Error reading content file", 0, 0);
145002a71407Seric 		return (-1);
145102a71407Seric 	}
145202a71407Seric 
145302a71407Seric 	if (feof(s->datafp)) {
145402a71407Seric 		fclose(s->datafp);
145502a71407Seric 		s->datafp = NULL;
145602a71407Seric 	}
145702a71407Seric 
14588d3f7f0dSeric 	return (io_queued(s->io) - q);
145902a71407Seric }
146002a71407Seric 
146102a71407Seric static void
1462c5acbec8Seric mta_flush_task(struct mta_session *s, int delivery, const char *error, size_t count,
1463c5acbec8Seric 	int cache)
146402a71407Seric {
1465399c053eSeric 	struct mta_envelope	*e;
1466953aae25Sderaadt 	char			 relay[LINE_MAX];
146765c4fdfbSgilles 	size_t			 n;
146880f61805Seric 	struct sockaddr_storage	 ss;
146980f61805Seric 	struct sockaddr		*sa;
1470299c4efeSeric 	socklen_t		 sa_len;
1471c5acbec8Seric 	const char		*domain;
147202a71407Seric 
14739c7a0077Sgilles 	(void)snprintf(relay, sizeof relay, "%s", mta_host_to_text(s->route->dst));
147465c4fdfbSgilles 	n = 0;
147565c4fdfbSgilles 	while ((e = TAILQ_FIRST(&s->task->envelopes))) {
14760faa9237Seric 
14770faa9237Seric 		if (count && n == count) {
14780faa9237Seric 			stat_decrement("mta.envelope", n);
14790faa9237Seric 			return;
14800faa9237Seric 		}
14810faa9237Seric 
148265c4fdfbSgilles 		TAILQ_REMOVE(&s->task->envelopes, e, entry);
1483299c4efeSeric 
1484299c4efeSeric 		/* we're about to log, associate session to envelope */
1485299c4efeSeric 		e->session = s->id;
1486fe95d8d0Seric 		e->ext = s->ext;
1487299c4efeSeric 
1488299c4efeSeric 		/* XXX */
1489299c4efeSeric 		/*
1490299c4efeSeric 		 * getsockname() can only fail with ENOBUFS here
1491299c4efeSeric 		 * best effort, don't log source ...
1492299c4efeSeric 		 */
149380f61805Seric 		sa = (struct sockaddr *)&ss;
149480f61805Seric 		sa_len = sizeof(ss);
1495df69c215Sderaadt 		if (getsockname(io_fileno(s->io), sa, &sa_len) == -1)
1496af241c42Seric 			mta_delivery_log(e, NULL, relay, delivery, error);
1497299c4efeSeric 		else
1498af241c42Seric 			mta_delivery_log(e, sa_to_text(sa),
1499af241c42Seric 			    relay, delivery, error);
1500af241c42Seric 
1501d6b3bcf4Seric 		mta_delivery_notify(e);
1502c5acbec8Seric 
1503c5acbec8Seric 		domain = strchr(e->dest, '@');
1504c5acbec8Seric 		if (domain) {
15059ed05047Seric 			domain++;
15069ed05047Seric 			mta_hoststat_update(domain, error);
1507c5acbec8Seric 			if (cache)
15089ed05047Seric 				mta_hoststat_cache(domain, e->id);
1509c5acbec8Seric 		}
1510299c4efeSeric 
151165c4fdfbSgilles 		n++;
151282cdf8beSeric 	}
151302a71407Seric 
1514399c053eSeric 	free(s->task->sender);
151565c4fdfbSgilles 	free(s->task);
151665c4fdfbSgilles 	s->task = NULL;
1517ac5aa4b9Seric 
151888d51f11Sgilles 	if (s->datafp) {
151988d51f11Sgilles 		fclose(s->datafp);
152088d51f11Sgilles 		s->datafp = NULL;
152188d51f11Sgilles 	}
152288d51f11Sgilles 
152365c4fdfbSgilles 	stat_decrement("mta.envelope", n);
152465c4fdfbSgilles 	stat_decrement("mta.task.running", 1);
152565c4fdfbSgilles 	stat_decrement("mta.task", 1);
152602a71407Seric }
152702a71407Seric 
1528ac5aa4b9Seric static void
152965c4fdfbSgilles mta_error(struct mta_session *s, const char *fmt, ...)
153002a71407Seric {
153165c4fdfbSgilles 	va_list  ap;
153265c4fdfbSgilles 	char	*error;
153365c4fdfbSgilles 	int	 len;
153402a71407Seric 
153565c4fdfbSgilles 	va_start(ap, fmt);
153665c4fdfbSgilles 	if ((len = vasprintf(&error, fmt, ap)) == -1)
153765c4fdfbSgilles 		fatal("mta: vasprintf");
153865c4fdfbSgilles 	va_end(ap);
153982614934Seric 
15400cf935dfSgilles 	if (s->msgcount)
15410cf935dfSgilles 		log_info("smtp-out: Error on session %016"PRIx64
1542c5acbec8Seric 		    " after %zu message%s sent: %s", s->id, s->msgcount,
15430cf935dfSgilles 		    (s->msgcount > 1) ? "s" : "", error);
15440cf935dfSgilles 	else
1545cf540174Sgilles 		log_info("%016"PRIx64" mta error reason=%s",
15460cf935dfSgilles 		    s->id, error);
15477a93bdaaSgilles 
15481c6ac251Seric 	/*
15491c6ac251Seric 	 * If not connected yet, and the error is not local, just ignore it
15501c6ac251Seric 	 * and try to reconnect.
15511c6ac251Seric 	 */
15521c6ac251Seric 	if (s->state == MTA_INIT &&
15531c6ac251Seric 	    (errno == ETIMEDOUT || errno == ECONNREFUSED)) {
15541c6ac251Seric 		log_debug("debug: mta: not reporting route error yet");
15551c6ac251Seric 		free(error);
15561c6ac251Seric 		return;
15571c6ac251Seric 	}
15580cf935dfSgilles 
15590cf935dfSgilles 	mta_route_error(s->relay, s->route);
156002a71407Seric 
156165c4fdfbSgilles 	if (s->task)
1562aa1d5973Seric 		mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, error, 0, 0);
1563ac5aa4b9Seric 
156465c4fdfbSgilles 	free(error);
156502a71407Seric }
1566a7b2e833Seric 
156765c4fdfbSgilles static void
1568eed85469Seric mta_tls_init(struct mta_session *s)
15695da31eabSeric {
1570b6444922Smillert 	struct dispatcher_remote *remote;
1571eed85469Seric 	struct tls *tls;
15725da31eabSeric 
1573eed85469Seric 	if ((tls = tls_client()) == NULL) {
1574eed85469Seric 		log_info("%016"PRIx64" mta closing reason=tls-failure", s->id);
157596592628Smartijn 		s->flags |= MTA_FREE;
15765da31eabSeric 		return;
15775da31eabSeric 	}
15785da31eabSeric 
1579b6444922Smillert 	remote = &s->relay->dispatcher->u.remote;
1580b6444922Smillert 	if ((s->flags & MTA_WANT_SECURE) && !remote->tls_required) {
1581b6444922Smillert 		/* If TLS not explicitly configured, use implicit config. */
1582b6444922Smillert 		remote->tls_required = 1;
1583b6444922Smillert 		remote->tls_verify = 1;
1584b6444922Smillert 		tls_config_verify(remote->tls_config);
1585b6444922Smillert 	}
1586b6444922Smillert 	if (tls_configure(tls, remote->tls_config) == -1) {
1587eed85469Seric 		log_info("%016"PRIx64" mta closing reason=tls-failure", s->id);
1588eed85469Seric 		tls_free(tls);
158996592628Smartijn 		s->flags |= MTA_FREE;
15905da31eabSeric 		return;
15915da31eabSeric 	}
15925da31eabSeric 
1593c11a901bSeric 	if (io_connect_tls(s->io, tls, s->mxname) == -1) {
1594c11a901bSeric 		log_info("%016"PRIx64" mta closing reason=tls-connect-failed", s->id);
1595c11a901bSeric 		tls_free(tls);
159696592628Smartijn 		s->flags |= MTA_FREE;
1597c11a901bSeric 	}
15985da31eabSeric }
15995da31eabSeric 
16005da31eabSeric static void
1601eed85469Seric mta_tls_started(struct mta_session *s)
16021ddc3febSeric {
1603eed85469Seric 	if (tls_peer_cert_provided(io_tls(s->io))) {
1604c6df4dd6Sgilles 		log_info("%016"PRIx64" mta "
1605eed85469Seric 		    "cert-check result=\"%s\" fingerprint=\"%s\"",
1606c6df4dd6Sgilles 		    s->id,
1607eed85469Seric 		    (s->flags & MTA_TLS_VERIFIED) ? "valid" : "unverified",
1608eed85469Seric 		    tls_peer_cert_hash(io_tls(s->io)));
1609eed85469Seric 	}
1610eed85469Seric 	else {
1611eed85469Seric 		log_info("%016"PRIx64" smtp "
1612eed85469Seric 		    "cert-check result=\"no certificate presented\"",
1613eed85469Seric 		    s->id);
16141ddc3febSeric 	}
16151ddc3febSeric 
16161ddc3febSeric 	if (s->use_smtps) {
16171ddc3febSeric 		mta_enter_state(s, MTA_BANNER);
16188d3f7f0dSeric 		io_set_read(s->io);
16191ddc3febSeric 	}
16201ddc3febSeric 	else
16211ddc3febSeric 		mta_enter_state(s, MTA_EHLO);
16221ddc3febSeric }
16231ddc3febSeric 
1624fe95d8d0Seric static const char *
1625fe95d8d0Seric dsn_strret(enum dsn_ret ret)
1626fe95d8d0Seric {
1627fe95d8d0Seric 	if (ret == DSN_RETHDRS)
1628fe95d8d0Seric 		return "HDRS";
1629fe95d8d0Seric 	else if (ret == DSN_RETFULL)
1630fe95d8d0Seric 		return "FULL";
1631fe95d8d0Seric 	else {
1632fe95d8d0Seric 		log_debug("mta: invalid ret %d", ret);
1633fe95d8d0Seric 		return "???";
1634fe95d8d0Seric 	}
1635fe95d8d0Seric }
1636fe95d8d0Seric 
1637fe95d8d0Seric static const char *
1638fe95d8d0Seric dsn_strnotify(uint8_t arg)
1639fe95d8d0Seric {
1640fe95d8d0Seric 	static char	buf[32];
1641fe95d8d0Seric 	size_t		sz;
1642fe95d8d0Seric 
16432aa80fc8Sgilles 	buf[0] = '\0';
1644fe95d8d0Seric 	if (arg & DSN_SUCCESS)
16459c7a0077Sgilles 		(void)strlcat(buf, "SUCCESS,", sizeof(buf));
1646fe95d8d0Seric 
1647fe95d8d0Seric 	if (arg & DSN_FAILURE)
16489c7a0077Sgilles 		(void)strlcat(buf, "FAILURE,", sizeof(buf));
1649fe95d8d0Seric 
1650fe95d8d0Seric 	if (arg & DSN_DELAY)
16519c7a0077Sgilles 		(void)strlcat(buf, "DELAY,", sizeof(buf));
1652fe95d8d0Seric 
1653fe95d8d0Seric 	if (arg & DSN_NEVER)
16549c7a0077Sgilles 		(void)strlcat(buf, "NEVER,", sizeof(buf));
1655fe95d8d0Seric 
1656fe95d8d0Seric 	/* trim trailing comma */
1657fe95d8d0Seric 	sz = strlen(buf);
1658fe95d8d0Seric 	if (sz)
1659fe95d8d0Seric 		buf[sz - 1] = '\0';
1660fe95d8d0Seric 
1661fe95d8d0Seric 	return (buf);
1662fe95d8d0Seric }
1663fe95d8d0Seric 
166465c4fdfbSgilles #define CASE(x) case x : return #x
166565c4fdfbSgilles 
166665c4fdfbSgilles static const char *
166765c4fdfbSgilles mta_strstate(int state)
166865c4fdfbSgilles {
166965c4fdfbSgilles 	switch (state) {
167065c4fdfbSgilles 	CASE(MTA_INIT);
167165c4fdfbSgilles 	CASE(MTA_BANNER);
167265c4fdfbSgilles 	CASE(MTA_EHLO);
167365c4fdfbSgilles 	CASE(MTA_HELO);
167465c4fdfbSgilles 	CASE(MTA_STARTTLS);
167565c4fdfbSgilles 	CASE(MTA_AUTH);
1676d6d017c1Seric 	CASE(MTA_AUTH_PLAIN);
1677d6d017c1Seric 	CASE(MTA_AUTH_LOGIN);
1678d6d017c1Seric 	CASE(MTA_AUTH_LOGIN_USER);
1679d6d017c1Seric 	CASE(MTA_AUTH_LOGIN_PASS);
168065c4fdfbSgilles 	CASE(MTA_READY);
168165c4fdfbSgilles 	CASE(MTA_MAIL);
168265c4fdfbSgilles 	CASE(MTA_RCPT);
168365c4fdfbSgilles 	CASE(MTA_DATA);
168465c4fdfbSgilles 	CASE(MTA_BODY);
168565c4fdfbSgilles 	CASE(MTA_EOM);
16860faa9237Seric 	CASE(MTA_LMTP_EOM);
168765c4fdfbSgilles 	CASE(MTA_RSET);
168865c4fdfbSgilles 	CASE(MTA_QUIT);
168965c4fdfbSgilles 	default:
169065c4fdfbSgilles 		return "MTA_???";
169165c4fdfbSgilles 	}
169265c4fdfbSgilles }
1693a7061c79Sgilles 
1694a7061c79Sgilles static void
1695a7061c79Sgilles mta_filter_begin(struct mta_session *s)
1696a7061c79Sgilles {
1697a7061c79Sgilles 	if (!SESSION_FILTERED(s))
1698a7061c79Sgilles 		return;
1699a7061c79Sgilles 
1700a7061c79Sgilles 	m_create(p_lka, IMSG_FILTER_SMTP_BEGIN, 0, 0, -1);
1701a7061c79Sgilles 	m_add_id(p_lka, s->id);
1702a7061c79Sgilles 	m_add_string(p_lka, s->relay->dispatcher->u.remote.filtername);
1703a7061c79Sgilles 	m_close(p_lka);
1704a7061c79Sgilles }
1705a7061c79Sgilles 
1706a7061c79Sgilles static void
1707a7061c79Sgilles mta_filter_end(struct mta_session *s)
1708a7061c79Sgilles {
1709a7061c79Sgilles 	if (!SESSION_FILTERED(s))
1710a7061c79Sgilles 		return;
1711a7061c79Sgilles 
1712a7061c79Sgilles 	m_create(p_lka, IMSG_FILTER_SMTP_END, 0, 0, -1);
1713a7061c79Sgilles 	m_add_id(p_lka, s->id);
1714a7061c79Sgilles 	m_close(p_lka);
1715a7061c79Sgilles }
1716a7061c79Sgilles 
1717a7061c79Sgilles static void
1718a7061c79Sgilles mta_connected(struct mta_session *s)
1719a7061c79Sgilles {
1720671d2f92Smillert 	struct sockaddr_storage sa_src;
1721671d2f92Smillert 	struct sockaddr_storage sa_dest;
1722a7061c79Sgilles 	int sa_len;
1723a7061c79Sgilles 
1724a7061c79Sgilles 	log_info("%016"PRIx64" mta connected", s->id);
1725a7061c79Sgilles 
1726671d2f92Smillert 	sa_len = sizeof sa_src;
1727671d2f92Smillert 	if (getsockname(io_fileno(s->io),
1728671d2f92Smillert 	    (struct sockaddr *)&sa_src, &sa_len) == -1)
1729a7061c79Sgilles 		bzero(&sa_src, sizeof sa_src);
1730671d2f92Smillert 	sa_len = sizeof sa_dest;
1731671d2f92Smillert 	if (getpeername(io_fileno(s->io),
1732671d2f92Smillert 	    (struct sockaddr *)&sa_dest, &sa_len) == -1)
1733a7061c79Sgilles 		bzero(&sa_dest, sizeof sa_dest);
1734a7061c79Sgilles 
1735a7061c79Sgilles 	mta_report_link_connect(s,
1736a7061c79Sgilles 	    s->route->dst->ptrname, 1,
1737671d2f92Smillert 	    &sa_src,
1738671d2f92Smillert 	    &sa_dest);
1739a7061c79Sgilles }
1740a7061c79Sgilles 
1741a7061c79Sgilles static void
1742a7061c79Sgilles mta_disconnected(struct mta_session *s)
1743a7061c79Sgilles {
1744a7061c79Sgilles 	mta_report_link_disconnect(s);
1745a7061c79Sgilles 	mta_filter_end(s);
1746a7061c79Sgilles }
1747a7061c79Sgilles 
1748a7061c79Sgilles 
1749a7061c79Sgilles static void
1750a7061c79Sgilles mta_report_link_connect(struct mta_session *s, const char *rdns, int fcrdns,
1751a7061c79Sgilles     const struct sockaddr_storage *ss_src,
1752a7061c79Sgilles     const struct sockaddr_storage *ss_dest)
1753a7061c79Sgilles {
1754a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1755a7061c79Sgilles 		return;
1756a7061c79Sgilles 
1757a7061c79Sgilles 	report_smtp_link_connect("smtp-out", s->id, rdns, fcrdns, ss_src, ss_dest);
1758a7061c79Sgilles }
1759a7061c79Sgilles 
1760a7061c79Sgilles static void
1761a7061c79Sgilles mta_report_link_greeting(struct mta_session *s,
1762a7061c79Sgilles     const char *domain)
1763a7061c79Sgilles {
1764a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1765a7061c79Sgilles 		return;
1766a7061c79Sgilles 
1767a7061c79Sgilles 	report_smtp_link_greeting("smtp-out", s->id, domain);
1768a7061c79Sgilles }
1769a7061c79Sgilles 
1770a7061c79Sgilles static void
1771a7061c79Sgilles mta_report_link_identify(struct mta_session *s, const char *method, const char *identity)
1772a7061c79Sgilles {
1773a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1774a7061c79Sgilles 		return;
1775a7061c79Sgilles 
1776a7061c79Sgilles 	report_smtp_link_identify("smtp-out", s->id, method, identity);
1777a7061c79Sgilles }
1778a7061c79Sgilles 
1779a7061c79Sgilles static void
1780a7061c79Sgilles mta_report_link_tls(struct mta_session *s, const char *ssl)
1781a7061c79Sgilles {
1782a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1783a7061c79Sgilles 		return;
1784a7061c79Sgilles 
1785a7061c79Sgilles 	report_smtp_link_tls("smtp-out", s->id, ssl);
1786a7061c79Sgilles }
1787a7061c79Sgilles 
1788a7061c79Sgilles static void
1789a7061c79Sgilles mta_report_link_disconnect(struct mta_session *s)
1790a7061c79Sgilles {
1791a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1792a7061c79Sgilles 		return;
1793a7061c79Sgilles 
1794a7061c79Sgilles 	report_smtp_link_disconnect("smtp-out", s->id);
1795a7061c79Sgilles }
1796a7061c79Sgilles 
1797a7061c79Sgilles static void
1798a7061c79Sgilles mta_report_link_auth(struct mta_session *s, const char *user, const char *result)
1799a7061c79Sgilles {
1800a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1801a7061c79Sgilles 		return;
1802a7061c79Sgilles 
1803a7061c79Sgilles 	report_smtp_link_auth("smtp-out", s->id, user, result);
1804a7061c79Sgilles }
1805a7061c79Sgilles 
1806a7061c79Sgilles static void
1807a7061c79Sgilles mta_report_tx_reset(struct mta_session *s, uint32_t msgid)
1808a7061c79Sgilles {
1809a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1810a7061c79Sgilles 		return;
1811a7061c79Sgilles 
1812a7061c79Sgilles 	report_smtp_tx_reset("smtp-out", s->id, msgid);
1813a7061c79Sgilles }
1814a7061c79Sgilles 
1815a7061c79Sgilles static void
1816a7061c79Sgilles mta_report_tx_begin(struct mta_session *s, uint32_t msgid)
1817a7061c79Sgilles {
1818a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1819a7061c79Sgilles 		return;
1820a7061c79Sgilles 
1821a7061c79Sgilles 	report_smtp_tx_begin("smtp-out", s->id, msgid);
1822a7061c79Sgilles }
1823a7061c79Sgilles 
1824a7061c79Sgilles static void
1825a7061c79Sgilles mta_report_tx_mail(struct mta_session *s, uint32_t msgid, const char *address, int ok)
1826a7061c79Sgilles {
1827a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1828a7061c79Sgilles 		return;
1829a7061c79Sgilles 
1830a7061c79Sgilles 	report_smtp_tx_mail("smtp-out", s->id, msgid, address, ok);
1831a7061c79Sgilles }
1832a7061c79Sgilles 
1833a7061c79Sgilles static void
1834a7061c79Sgilles mta_report_tx_rcpt(struct mta_session *s, uint32_t msgid, const char *address, int ok)
1835a7061c79Sgilles {
1836a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1837a7061c79Sgilles 		return;
1838a7061c79Sgilles 
1839a7061c79Sgilles 	report_smtp_tx_rcpt("smtp-out", s->id, msgid, address, ok);
1840a7061c79Sgilles }
1841a7061c79Sgilles 
1842a7061c79Sgilles static void
1843a7061c79Sgilles mta_report_tx_envelope(struct mta_session *s, uint32_t msgid, uint64_t evpid)
1844a7061c79Sgilles {
1845a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1846a7061c79Sgilles 		return;
1847a7061c79Sgilles 
1848a7061c79Sgilles 	report_smtp_tx_envelope("smtp-out", s->id, msgid, evpid);
1849a7061c79Sgilles }
1850a7061c79Sgilles 
1851a7061c79Sgilles static void
1852a7061c79Sgilles mta_report_tx_data(struct mta_session *s, uint32_t msgid, int ok)
1853a7061c79Sgilles {
1854a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1855a7061c79Sgilles 		return;
1856a7061c79Sgilles 
1857a7061c79Sgilles 	report_smtp_tx_data("smtp-out", s->id, msgid, ok);
1858a7061c79Sgilles }
1859a7061c79Sgilles 
1860a7061c79Sgilles static void
1861a7061c79Sgilles mta_report_tx_commit(struct mta_session *s, uint32_t msgid, size_t msgsz)
1862a7061c79Sgilles {
1863a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1864a7061c79Sgilles 		return;
1865a7061c79Sgilles 
1866a7061c79Sgilles 	report_smtp_tx_commit("smtp-out", s->id, msgid, msgsz);
1867a7061c79Sgilles }
1868a7061c79Sgilles 
1869a7061c79Sgilles static void
1870a7061c79Sgilles mta_report_tx_rollback(struct mta_session *s, uint32_t msgid)
1871a7061c79Sgilles {
1872a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1873a7061c79Sgilles 		return;
1874a7061c79Sgilles 
1875a7061c79Sgilles 	report_smtp_tx_rollback("smtp-out", s->id, msgid);
1876a7061c79Sgilles }
1877a7061c79Sgilles 
1878a7061c79Sgilles static void
1879a7061c79Sgilles mta_report_protocol_client(struct mta_session *s, const char *command)
1880a7061c79Sgilles {
1881a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1882a7061c79Sgilles 		return;
1883a7061c79Sgilles 
1884a7061c79Sgilles 	report_smtp_protocol_client("smtp-out", s->id, command);
1885a7061c79Sgilles }
1886a7061c79Sgilles 
1887a7061c79Sgilles static void
1888a7061c79Sgilles mta_report_protocol_server(struct mta_session *s, const char *response)
1889a7061c79Sgilles {
1890a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1891a7061c79Sgilles 		return;
1892a7061c79Sgilles 
1893a7061c79Sgilles 	report_smtp_protocol_server("smtp-out", s->id, response);
1894a7061c79Sgilles }
1895a7061c79Sgilles 
1896a7061c79Sgilles #if 0
1897a7061c79Sgilles static void
1898a7061c79Sgilles mta_report_filter_response(struct mta_session *s, int phase, int response, const char *param)
1899a7061c79Sgilles {
1900a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1901a7061c79Sgilles 		return;
1902a7061c79Sgilles 
1903a7061c79Sgilles 	report_smtp_filter_response("smtp-out", s->id, phase, response, param);
1904a7061c79Sgilles }
1905a7061c79Sgilles #endif
1906a7061c79Sgilles 
1907a7061c79Sgilles static void
1908a7061c79Sgilles mta_report_timeout(struct mta_session *s)
1909a7061c79Sgilles {
1910a7061c79Sgilles 	if (! SESSION_FILTERED(s))
1911a7061c79Sgilles 		return;
1912a7061c79Sgilles 
1913a7061c79Sgilles 	report_smtp_timeout("smtp-out", s->id);
1914a7061c79Sgilles }
1915