xref: /openbsd-src/usr.sbin/smtpd/smtp_session.c (revision d49c07c786cfbc18357be57b78b5771525d1b8bb)
1*d49c07c7Sop /*	$OpenBSD: smtp_session.c,v 1.443 2024/08/12 09:32:44 op Exp $	*/
21f3c2bcfSsobrado 
33ef9cbf7Sgilles /*
465c4fdfbSgilles  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
53ef9cbf7Sgilles  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6b8989c65Sjacekm  * Copyright (c) 2008-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 <ctype.h>
2382614934Seric #include <errno.h>
2482614934Seric #include <inttypes.h>
253ef9cbf7Sgilles #include <stdlib.h>
26a2bd1240Schl #include <string.h>
270dcffd0dSop #include <time.h>
28eed85469Seric #include <tls.h>
293ef9cbf7Sgilles #include <unistd.h>
3082614934Seric #include <vis.h>
313ef9cbf7Sgilles 
323ef9cbf7Sgilles #include "smtpd.h"
335eb8dddaSgilles #include "log.h"
3444f4452eSeric #include "rfc5322.h"
353ef9cbf7Sgilles 
3695be189aSgilles #define	SMTP_LINE_MAX			65535
37b34f8648Sgilles #define	DATA_HIWAT			65535
386162865dSeric #define	APPEND_DOMAIN_BUFFER_SIZE	SMTP_LINE_MAX
3927b32957Sgilles 
4065c4fdfbSgilles enum smtp_state {
4165c4fdfbSgilles 	STATE_NEW = 0,
4265c4fdfbSgilles 	STATE_CONNECTED,
4365c4fdfbSgilles 	STATE_TLS,
4465c4fdfbSgilles 	STATE_HELO,
4565c4fdfbSgilles 	STATE_AUTH_INIT,
4665c4fdfbSgilles 	STATE_AUTH_USERNAME,
4765c4fdfbSgilles 	STATE_AUTH_PASSWORD,
4865c4fdfbSgilles 	STATE_AUTH_FINALIZE,
4965c4fdfbSgilles 	STATE_BODY,
5065c4fdfbSgilles 	STATE_QUIT,
5165c4fdfbSgilles };
5265c4fdfbSgilles 
5365c4fdfbSgilles enum session_flags {
5465c4fdfbSgilles 	SF_EHLO			= 0x0001,
5565c4fdfbSgilles 	SF_8BITMIME		= 0x0002,
5665c4fdfbSgilles 	SF_SECURE		= 0x0004,
5765c4fdfbSgilles 	SF_AUTHENTICATED	= 0x0008,
5865c4fdfbSgilles 	SF_BOUNCE		= 0x0010,
59b78cbe77Sgilles 	SF_VERIFIED		= 0x0020,
60b78cbe77Sgilles 	SF_BADINPUT		= 0x0080,
6165c4fdfbSgilles };
6265c4fdfbSgilles 
63f299ff90Seric enum {
64f299ff90Seric 	TX_OK = 0,
65f299ff90Seric 	TX_ERROR_ENVELOPE,
66f299ff90Seric 	TX_ERROR_SIZE,
67f299ff90Seric 	TX_ERROR_IO,
68f299ff90Seric 	TX_ERROR_LOOP,
69f299ff90Seric 	TX_ERROR_MALFORMED,
7044f4452eSeric 	TX_ERROR_RESOURCES,
7144f4452eSeric 	TX_ERROR_INTERNAL,
7265c4fdfbSgilles };
7365c4fdfbSgilles 
7465c4fdfbSgilles enum smtp_command {
7565c4fdfbSgilles 	CMD_HELO = 0,
7665c4fdfbSgilles 	CMD_EHLO,
7765c4fdfbSgilles 	CMD_STARTTLS,
7865c4fdfbSgilles 	CMD_AUTH,
7965c4fdfbSgilles 	CMD_MAIL_FROM,
8065c4fdfbSgilles 	CMD_RCPT_TO,
8165c4fdfbSgilles 	CMD_DATA,
8265c4fdfbSgilles 	CMD_RSET,
8365c4fdfbSgilles 	CMD_QUIT,
8465c4fdfbSgilles 	CMD_HELP,
85fe95d8d0Seric 	CMD_WIZ,
8665c4fdfbSgilles 	CMD_NOOP,
87c8094670Sgilles 	CMD_COMMIT,
8865c4fdfbSgilles };
8965c4fdfbSgilles 
90ee3e6bebSeric struct smtp_rcpt {
91ee3e6bebSeric 	TAILQ_ENTRY(smtp_rcpt)	 entry;
92c6b5e508Sgilles 	uint64_t		 evpid;
93ee3e6bebSeric  	struct mailaddr		 maddr;
94ee3e6bebSeric 	size_t			 destcount;
95ee3e6bebSeric };
96ee3e6bebSeric 
970a80c58aSeric struct smtp_tx {
980a80c58aSeric 	struct smtp_session	*session;
99763ff2e3Seric 	uint32_t		 msgid;
1000a80c58aSeric 
1010a80c58aSeric 	struct envelope		 evp;
1020a80c58aSeric 	size_t			 rcptcount;
1030a80c58aSeric 	size_t			 destcount;
1040a80c58aSeric 	TAILQ_HEAD(, smtp_rcpt)	 rcpts;
1050a80c58aSeric 
106d7b42ce8Seric 	time_t			 time;
107f299ff90Seric 	int			 error;
108f71caa29Seric 	size_t			 datain;
1090a80c58aSeric 	size_t			 odatalen;
11084639b21Seric 	FILE			*ofile;
111590a6142Sgilles 	struct io		*filter;
11244f4452eSeric 	struct rfc5322_parser	*parser;
1130a80c58aSeric 	int			 rcvcount;
11444f4452eSeric 	int			 has_date;
11544f4452eSeric 	int			 has_message_id;
116f6b0bf9aSgilles 
117f6b0bf9aSgilles 	uint8_t			 junk;
1180a80c58aSeric };
1190a80c58aSeric 
12065c4fdfbSgilles struct smtp_session {
12165c4fdfbSgilles 	uint64_t		 id;
1228d3f7f0dSeric 	struct io		*io;
12365c4fdfbSgilles 	struct listener		*listener;
1241c3ac238Seric 	void			*ssl_ctx;
12565c4fdfbSgilles 	struct sockaddr_storage	 ss;
126744e25e4Sgilles 	char			 rdns[HOST_NAME_MAX+1];
127953aae25Sderaadt 	char			 smtpname[HOST_NAME_MAX+1];
128bcbdde81Seric 	int			 fcrdns;
12965c4fdfbSgilles 
13065c4fdfbSgilles 	int			 flags;
13165c4fdfbSgilles 	enum smtp_state		 state;
13265c4fdfbSgilles 
1330d4adc27Sgilles 	uint8_t			 banner_sent;
134953aae25Sderaadt 	char			 helo[LINE_MAX];
135953aae25Sderaadt 	char			 cmd[LINE_MAX];
1366dfe9135Sgilles 	char			 username[SMTPD_MAXMAILADDRSIZE];
13765c4fdfbSgilles 
13865c4fdfbSgilles 	size_t			 mailcount;
139299c4efeSeric 	struct event		 pause;
1406bc0eb6bSgilles 
1410a80c58aSeric 	struct smtp_tx		*tx;
142522448a1Sgilles 
1437929fb13Sgilles 	enum smtp_command	 last_cmd;
144522448a1Sgilles 	enum filter_phase	 filter_phase;
145522448a1Sgilles 	const char		*filter_param;
146f6b0bf9aSgilles 
147f6b0bf9aSgilles 	uint8_t			 junk;
14865c4fdfbSgilles };
1492a4d7b1bSeric 
150f4807aa6Seric #define ADVERTISE_TLS(s) \
15165c4fdfbSgilles 	((s)->listener->flags & F_STARTTLS && !((s)->flags & SF_SECURE))
152f4807aa6Seric 
153f4807aa6Seric #define ADVERTISE_AUTH(s) \
15465c4fdfbSgilles 	((s)->listener->flags & F_AUTH && (s)->flags & SF_SECURE && \
15565c4fdfbSgilles 	 !((s)->flags & SF_AUTHENTICATED))
156f4807aa6Seric 
157ad286364Seric #define ADVERTISE_EXT_DSN(s) \
158ad286364Seric 	((s)->listener->flags & F_EXT_DSN)
159ad286364Seric 
1604faa8b13Sgilles #define	SESSION_FILTERED(s) \
1614faa8b13Sgilles 	((s)->listener->flags & F_FILTERED)
1624faa8b13Sgilles 
1634faa8b13Sgilles #define	SESSION_DATA_FILTERED(s) \
164ec69ed85Sgilles 	((s)->listener->flags & F_FILTERED)
1654faa8b13Sgilles 
1664faa8b13Sgilles 
167cc81b7c6Seric static int smtp_mailaddr(struct mailaddr *, char *, int, char **, const char *);
16865c4fdfbSgilles static void smtp_session_init(void);
16914b654a8Seric static void smtp_lookup_servername(struct smtp_session *);
17001eba458Seric static void smtp_getnameinfo_cb(void *, int, const char *, const char *);
171bcbdde81Seric static void smtp_getaddrinfo_cb(void *, int, struct addrinfo *);
17265c4fdfbSgilles static void smtp_connected(struct smtp_session *);
173cc81b7c6Seric static void smtp_send_banner(struct smtp_session *);
174eed85469Seric static void smtp_tls_init(struct smtp_session *);
175eed85469Seric static void smtp_tls_started(struct smtp_session *);
176b556a8d3Seric static void smtp_io(struct io *, int, void *);
17765c4fdfbSgilles static void smtp_enter_state(struct smtp_session *, int);
17865c4fdfbSgilles static void smtp_reply(struct smtp_session *, char *, ...);
17965c4fdfbSgilles static void smtp_command(struct smtp_session *, char *);
18065c4fdfbSgilles static void smtp_rfc4954_auth_plain(struct smtp_session *, char *);
18165c4fdfbSgilles static void smtp_rfc4954_auth_login(struct smtp_session *, char *);
18265c4fdfbSgilles static void smtp_free(struct smtp_session *, const char *);
18365c4fdfbSgilles static const char *smtp_strstate(int);
184299c4efeSeric static void smtp_auth_failure_pause(struct smtp_session *);
185299c4efeSeric static void smtp_auth_failure_resume(int, short, void *);
186b04e6f27Seric 
1870a80c58aSeric static int  smtp_tx(struct smtp_session *);
1880a80c58aSeric static void smtp_tx_free(struct smtp_tx *);
189008f3f72Seric static void smtp_tx_create_message(struct smtp_tx *);
190aaf790e0Sgilles static void smtp_tx_mail_from(struct smtp_tx *, const char *);
191aaf790e0Sgilles static void smtp_tx_rcpt_to(struct smtp_tx *, const char *);
192008f3f72Seric static void smtp_tx_open_message(struct smtp_tx *);
193008f3f72Seric static void smtp_tx_commit(struct smtp_tx *);
194008f3f72Seric static void smtp_tx_rollback(struct smtp_tx *);
195008f3f72Seric static int  smtp_tx_dataline(struct smtp_tx *, const char *);
196590a6142Sgilles static int  smtp_tx_filtered_dataline(struct smtp_tx *, const char *);
197590a6142Sgilles static void smtp_tx_eom(struct smtp_tx *);
198590a6142Sgilles static void smtp_filter_fd(struct smtp_tx *, int);
199590a6142Sgilles static int  smtp_message_fd(struct smtp_tx *, int);
200590a6142Sgilles static void smtp_message_begin(struct smtp_tx *);
20155042ccbSeric static void smtp_message_end(struct smtp_tx *);
202f56ea996Smartijn static int  smtp_filter_printf(struct smtp_tx *, const char *, ...)
203f56ea996Smartijn     __attribute__((__format__ (printf, 2, 3)));
204f56ea996Smartijn static int  smtp_message_printf(struct smtp_tx *, const char *, ...)
205f56ea996Smartijn     __attribute__((__format__ (printf, 2, 3)));
20608becfa3Seric 
207d7b0dc3bSgilles static int  smtp_check_rset(struct smtp_session *, const char *);
208d7b0dc3bSgilles static int  smtp_check_helo(struct smtp_session *, const char *);
209d7b0dc3bSgilles static int  smtp_check_ehlo(struct smtp_session *, const char *);
210197a4a5eSgilles static int  smtp_check_auth(struct smtp_session *s, const char *);
211197a4a5eSgilles static int  smtp_check_starttls(struct smtp_session *, const char *);
212d7b0dc3bSgilles static int  smtp_check_mail_from(struct smtp_session *, const char *);
213d7b0dc3bSgilles static int  smtp_check_rcpt_to(struct smtp_session *, const char *);
214d7b0dc3bSgilles static int  smtp_check_data(struct smtp_session *, const char *);
215fbe04ea1Sop static int  smtp_check_noop(struct smtp_session *, const char *);
216d7b0dc3bSgilles static int  smtp_check_noparam(struct smtp_session *, const char *);
217197a4a5eSgilles 
218d7b0dc3bSgilles static void smtp_filter_phase(enum filter_phase, struct smtp_session *, const char *);
219197a4a5eSgilles 
220d7b0dc3bSgilles static void smtp_proceed_connected(struct smtp_session *);
221d7b0dc3bSgilles static void smtp_proceed_rset(struct smtp_session *, const char *);
222d7b0dc3bSgilles static void smtp_proceed_helo(struct smtp_session *, const char *);
223d7b0dc3bSgilles static void smtp_proceed_ehlo(struct smtp_session *, const char *);
224d7b0dc3bSgilles static void smtp_proceed_auth(struct smtp_session *, const char *);
225d7b0dc3bSgilles static void smtp_proceed_starttls(struct smtp_session *, const char *);
226d7b0dc3bSgilles static void smtp_proceed_mail_from(struct smtp_session *, const char *);
227d7b0dc3bSgilles static void smtp_proceed_rcpt_to(struct smtp_session *, const char *);
228d7b0dc3bSgilles static void smtp_proceed_data(struct smtp_session *, const char *);
229d7b0dc3bSgilles static void smtp_proceed_noop(struct smtp_session *, const char *);
230d7b0dc3bSgilles static void smtp_proceed_help(struct smtp_session *, const char *);
231d7b0dc3bSgilles static void smtp_proceed_wiz(struct smtp_session *, const char *);
232d7b0dc3bSgilles static void smtp_proceed_quit(struct smtp_session *, const char *);
233c8094670Sgilles static void smtp_proceed_commit(struct smtp_session *, const char *);
234c8094670Sgilles static void smtp_proceed_rollback(struct smtp_session *, const char *);
235c8094670Sgilles 
236590a6142Sgilles static void smtp_filter_begin(struct smtp_session *);
237590a6142Sgilles static void smtp_filter_end(struct smtp_session *);
238590a6142Sgilles static void smtp_filter_data_begin(struct smtp_session *);
239590a6142Sgilles static void smtp_filter_data_end(struct smtp_session *);
240d7b0dc3bSgilles 
2416771f06dSgilles static void smtp_report_link_connect(struct smtp_session *, const char *, int,
2426771f06dSgilles     const struct sockaddr_storage *,
2436771f06dSgilles     const struct sockaddr_storage *);
2446771f06dSgilles static void smtp_report_link_greeting(struct smtp_session *, const char *);
2456771f06dSgilles static void smtp_report_link_identify(struct smtp_session *, const char *, const char *);
2466771f06dSgilles static void smtp_report_link_tls(struct smtp_session *, const char *);
2476771f06dSgilles static void smtp_report_link_disconnect(struct smtp_session *);
2486771f06dSgilles static void smtp_report_link_auth(struct smtp_session *, const char *, const char *);
2496771f06dSgilles static void smtp_report_tx_reset(struct smtp_session *, uint32_t);
2506771f06dSgilles static void smtp_report_tx_begin(struct smtp_session *, uint32_t);
2516771f06dSgilles static void smtp_report_tx_mail(struct smtp_session *, uint32_t, const char *, int);
2526771f06dSgilles static void smtp_report_tx_rcpt(struct smtp_session *, uint32_t, const char *, int);
2536771f06dSgilles static void smtp_report_tx_envelope(struct smtp_session *, uint32_t, uint64_t);
2546771f06dSgilles static void smtp_report_tx_data(struct smtp_session *, uint32_t, int);
2556771f06dSgilles static void smtp_report_tx_commit(struct smtp_session *, uint32_t, size_t);
2566771f06dSgilles static void smtp_report_tx_rollback(struct smtp_session *, uint32_t);
2576771f06dSgilles static void smtp_report_protocol_client(struct smtp_session *, const char *);
2586771f06dSgilles static void smtp_report_protocol_server(struct smtp_session *, const char *);
2596771f06dSgilles static void smtp_report_filter_response(struct smtp_session *, int, int, const char *);
2606771f06dSgilles static void smtp_report_timeout(struct smtp_session *);
2616771f06dSgilles 
2626771f06dSgilles 
263d7b0dc3bSgilles static struct {
264d7b0dc3bSgilles 	int code;
265d7b0dc3bSgilles 	enum filter_phase filter_phase;
266d7b0dc3bSgilles 	const char *cmd;
267d7b0dc3bSgilles 
268d7b0dc3bSgilles 	int (*check)(struct smtp_session *, const char *);
269d7b0dc3bSgilles 	void (*proceed)(struct smtp_session *, const char *);
270d7b0dc3bSgilles } commands[] = {
271d7b0dc3bSgilles 	{ CMD_HELO,             FILTER_HELO,            "HELO",         smtp_check_helo,        smtp_proceed_helo },
272d7b0dc3bSgilles 	{ CMD_EHLO,             FILTER_EHLO,            "EHLO",         smtp_check_ehlo,        smtp_proceed_ehlo },
273d7b0dc3bSgilles 	{ CMD_STARTTLS,         FILTER_STARTTLS,        "STARTTLS",     smtp_check_starttls,    smtp_proceed_starttls },
274d7b0dc3bSgilles 	{ CMD_AUTH,             FILTER_AUTH,            "AUTH",         smtp_check_auth,        smtp_proceed_auth },
275d7b0dc3bSgilles 	{ CMD_MAIL_FROM,        FILTER_MAIL_FROM,       "MAIL FROM",    smtp_check_mail_from,   smtp_proceed_mail_from },
276d7b0dc3bSgilles 	{ CMD_RCPT_TO,          FILTER_RCPT_TO,         "RCPT TO",      smtp_check_rcpt_to,     smtp_proceed_rcpt_to },
277d7b0dc3bSgilles 	{ CMD_DATA,             FILTER_DATA,            "DATA",         smtp_check_data,        smtp_proceed_data },
278d7b0dc3bSgilles 	{ CMD_RSET,             FILTER_RSET,            "RSET",         smtp_check_rset,        smtp_proceed_rset },
279d7b0dc3bSgilles 	{ CMD_QUIT,             FILTER_QUIT,            "QUIT",         smtp_check_noparam,     smtp_proceed_quit },
280fbe04ea1Sop 	{ CMD_NOOP,             FILTER_NOOP,            "NOOP",         smtp_check_noop,        smtp_proceed_noop },
281d7b0dc3bSgilles 	{ CMD_HELP,             FILTER_HELP,            "HELP",         smtp_check_noparam,     smtp_proceed_help },
282d7b0dc3bSgilles 	{ CMD_WIZ,              FILTER_WIZ,             "WIZ",          smtp_check_noparam,     smtp_proceed_wiz },
283c8094670Sgilles 	{ CMD_COMMIT,  		FILTER_COMMIT,		".",		smtp_check_noparam,	smtp_proceed_commit },
284d7b0dc3bSgilles 	{ -1,                   0,                      NULL,           NULL },
2853ef9cbf7Sgilles };
2863ef9cbf7Sgilles 
287cc81b7c6Seric static struct tree wait_lka_helo;
288a1bd4550Sgilles static struct tree wait_lka_mail;
28965c4fdfbSgilles static struct tree wait_lka_rcpt;
29065c4fdfbSgilles static struct tree wait_parent_auth;
29165c4fdfbSgilles static struct tree wait_queue_msg;
29265c4fdfbSgilles static struct tree wait_queue_fd;
29365c4fdfbSgilles static struct tree wait_queue_commit;
29465c4fdfbSgilles static struct tree wait_ssl_init;
29565c4fdfbSgilles static struct tree wait_ssl_verify;
296d7b0dc3bSgilles static struct tree wait_filters;
297590a6142Sgilles static struct tree wait_filter_fd;
2983ef9cbf7Sgilles 
29965c4fdfbSgilles static void
30027b32957Sgilles header_append_domain_buffer(char *buffer, char *domain, size_t len)
30127b32957Sgilles {
30227b32957Sgilles 	size_t	i;
30327b32957Sgilles 	int	escape, quote, comment, bracket;
30427b32957Sgilles 	int	has_domain, has_bracket, has_group;
30527b32957Sgilles 	int	pos_bracket, pos_component, pos_insert;
30627b32957Sgilles 	char	copy[APPEND_DOMAIN_BUFFER_SIZE];
30727b32957Sgilles 
30827b32957Sgilles 	escape = quote = comment = bracket = 0;
30927b32957Sgilles 	has_domain = has_bracket = has_group = 0;
31027b32957Sgilles 	pos_bracket = pos_insert = pos_component = 0;
31127b32957Sgilles 	for (i = 0; buffer[i]; ++i) {
31227b32957Sgilles 		if (buffer[i] == '(' && !escape && !quote)
31327b32957Sgilles 			comment++;
31427b32957Sgilles 		if (buffer[i] == '"' && !escape && !comment)
31527b32957Sgilles 			quote = !quote;
31627b32957Sgilles 		if (buffer[i] == ')' && !escape && !quote && comment)
31727b32957Sgilles 			comment--;
31827b32957Sgilles 		if (buffer[i] == '\\' && !escape && !comment && !quote)
31927b32957Sgilles 			escape = 1;
32027b32957Sgilles 		else
32127b32957Sgilles 			escape = 0;
32227b32957Sgilles 		if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) {
32327b32957Sgilles 			bracket++;
32427b32957Sgilles 			has_bracket = 1;
32527b32957Sgilles 		}
32627b32957Sgilles 		if (buffer[i] == '>' && !escape && !comment && !quote && bracket) {
32727b32957Sgilles 			bracket--;
32827b32957Sgilles 			pos_bracket = i;
32927b32957Sgilles 		}
33027b32957Sgilles 		if (buffer[i] == '@' && !escape && !comment && !quote)
33127b32957Sgilles 			has_domain = 1;
33227b32957Sgilles 		if (buffer[i] == ':' && !escape && !comment && !quote)
33327b32957Sgilles 			has_group = 1;
334cb2a19a6Sgilles 
335cb2a19a6Sgilles 		/* update insert point if not in comment and not on a whitespace */
336cb2a19a6Sgilles 		if (!comment && buffer[i] != ')' && !isspace((unsigned char)buffer[i]))
33727b32957Sgilles 			pos_component = i;
33827b32957Sgilles 	}
33927b32957Sgilles 
34027b32957Sgilles 	/* parse error, do not attempt to modify */
34127b32957Sgilles 	if (escape || quote || comment || bracket)
34227b32957Sgilles 		return;
34327b32957Sgilles 
34427b32957Sgilles 	/* domain already present, no need to modify */
34527b32957Sgilles 	if (has_domain)
34627b32957Sgilles 		return;
34727b32957Sgilles 
34827b32957Sgilles 	/* address is group, skip */
34927b32957Sgilles 	if (has_group)
35027b32957Sgilles 		return;
35127b32957Sgilles 
35227b32957Sgilles 	/* there's an address between brackets, just append domain */
35327b32957Sgilles 	if (has_bracket) {
35427b32957Sgilles 		pos_bracket--;
355cb2a19a6Sgilles 		while (isspace((unsigned char)buffer[pos_bracket]))
35627b32957Sgilles 			pos_bracket--;
35727b32957Sgilles 		if (buffer[pos_bracket] == '<')
35827b32957Sgilles 			return;
35927b32957Sgilles 		pos_insert = pos_bracket + 1;
36027b32957Sgilles 	}
36127b32957Sgilles 	else {
36227b32957Sgilles 		/* otherwise append address to last component */
36327b32957Sgilles 		pos_insert = pos_component + 1;
36427b32957Sgilles 
36527b32957Sgilles 		/* empty address */
366cb2a19a6Sgilles                 if (buffer[pos_component] == '\0' ||
367cb2a19a6Sgilles 		    isspace((unsigned char)buffer[pos_component]))
36827b32957Sgilles                         return;
36927b32957Sgilles 	}
37027b32957Sgilles 
37127b32957Sgilles 	if (snprintf(copy, sizeof copy, "%.*s@%s%s",
37227b32957Sgilles 		(int)pos_insert, buffer,
37327b32957Sgilles 		domain,
37427b32957Sgilles 		buffer+pos_insert) >= (int)sizeof copy)
37527b32957Sgilles 		return;
37627b32957Sgilles 
37727b32957Sgilles 	memcpy(buffer, copy, len);
37827b32957Sgilles }
37927b32957Sgilles 
38027b32957Sgilles static void
3814d531edbSgilles header_address_rewrite_buffer(char *buffer, const char *address, size_t len)
3824d531edbSgilles {
3834d531edbSgilles 	size_t	i;
3844d531edbSgilles 	int	address_len;
3854d531edbSgilles 	int	escape, quote, comment, bracket;
3864d531edbSgilles 	int	has_bracket, has_group;
3874d531edbSgilles 	int	pos_bracket_beg, pos_bracket_end, pos_component_beg, pos_component_end;
3884d531edbSgilles 	int	insert_beg, insert_end;
3894d531edbSgilles 	char	copy[APPEND_DOMAIN_BUFFER_SIZE];
3904d531edbSgilles 
3914d531edbSgilles 	escape = quote = comment = bracket = 0;
3924d531edbSgilles 	has_bracket = has_group = 0;
3934d531edbSgilles 	pos_bracket_beg = pos_bracket_end = pos_component_beg = pos_component_end = 0;
3944d531edbSgilles 	for (i = 0; buffer[i]; ++i) {
3954d531edbSgilles 		if (buffer[i] == '(' && !escape && !quote)
3964d531edbSgilles 			comment++;
3974d531edbSgilles 		if (buffer[i] == '"' && !escape && !comment)
3984d531edbSgilles 			quote = !quote;
3994d531edbSgilles 		if (buffer[i] == ')' && !escape && !quote && comment)
4004d531edbSgilles 			comment--;
4014d531edbSgilles 		if (buffer[i] == '\\' && !escape && !comment && !quote)
4024d531edbSgilles 			escape = 1;
4034d531edbSgilles 		else
4044d531edbSgilles 			escape = 0;
4054d531edbSgilles 		if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) {
4064d531edbSgilles 			bracket++;
4074d531edbSgilles 			has_bracket = 1;
4084d531edbSgilles 			pos_bracket_beg = i+1;
4094d531edbSgilles 		}
4104d531edbSgilles 		if (buffer[i] == '>' && !escape && !comment && !quote && bracket) {
4114d531edbSgilles 			bracket--;
4124d531edbSgilles 			pos_bracket_end = i;
4134d531edbSgilles 		}
4144d531edbSgilles 		if (buffer[i] == ':' && !escape && !comment && !quote)
4154d531edbSgilles 			has_group = 1;
4164d531edbSgilles 
4174d531edbSgilles 		/* update insert point if not in comment and not on a whitespace */
4184d531edbSgilles 		if (!comment && buffer[i] != ')' && !isspace((unsigned char)buffer[i]))
4194d531edbSgilles 			pos_component_end = i;
4204d531edbSgilles 	}
4214d531edbSgilles 
4224d531edbSgilles 	/* parse error, do not attempt to modify */
4234d531edbSgilles 	if (escape || quote || comment || bracket)
4244d531edbSgilles 		return;
4254d531edbSgilles 
4264d531edbSgilles 	/* address is group, skip */
4274d531edbSgilles 	if (has_group)
4284d531edbSgilles 		return;
4294d531edbSgilles 
4304d531edbSgilles 	/* there's an address between brackets, just replace everything brackets */
4314d531edbSgilles 	if (has_bracket) {
4324d531edbSgilles 		insert_beg = pos_bracket_beg;
4334d531edbSgilles 		insert_end = pos_bracket_end;
4344d531edbSgilles 	}
4354d531edbSgilles 	else {
4364d531edbSgilles 		if (pos_component_end == 0)
4374d531edbSgilles 			pos_component_beg = 0;
4384d531edbSgilles 		else {
4394d531edbSgilles 			for (pos_component_beg = pos_component_end; pos_component_beg >= 0; --pos_component_beg)
4403d02c75dSmillert 				if (buffer[pos_component_beg] == ')' || isspace((unsigned char)buffer[pos_component_beg]))
4414d531edbSgilles 					break;
4424d531edbSgilles 			pos_component_beg += 1;
4434d531edbSgilles 			pos_component_end += 1;
4444d531edbSgilles 		}
4454d531edbSgilles 		insert_beg = pos_component_beg;
4464d531edbSgilles 		insert_end = pos_component_end;
4474d531edbSgilles 	}
4484d531edbSgilles 
4494d531edbSgilles 	/* check that masquerade won' t overflow */
4504d531edbSgilles 	address_len = strlen(address);
4514d531edbSgilles 	if (strlen(buffer) - (insert_end - insert_beg) + address_len >= len)
4524d531edbSgilles 		return;
4534d531edbSgilles 
4544d531edbSgilles 	(void)strlcpy(copy, buffer, sizeof copy);
4554d531edbSgilles 	(void)strlcpy(copy+insert_beg, address, sizeof (copy) - insert_beg);
4564d531edbSgilles 	(void)strlcat(copy, buffer+insert_end, sizeof (copy));
4574d531edbSgilles 	memcpy(buffer, copy, len);
4584d531edbSgilles }
4594d531edbSgilles 
4604d531edbSgilles static void
46144f4452eSeric header_domain_append_callback(struct smtp_tx *tx, const char *hdr,
46244f4452eSeric     const char *val)
4634d531edbSgilles {
46444f4452eSeric 	size_t			i, j, linelen;
4654d531edbSgilles 	int			escape, quote, comment, skip;
4664d531edbSgilles 	char			buffer[APPEND_DOMAIN_BUFFER_SIZE];
46744f4452eSeric 	const char *line, *end;
4684d531edbSgilles 
46944f4452eSeric 	if (smtp_message_printf(tx, "%s:", hdr) == -1)
4704d531edbSgilles 		return;
4714d531edbSgilles 
4724d531edbSgilles 	j = 0;
4734d531edbSgilles 	escape = quote = comment = skip = 0;
4744d531edbSgilles 	memset(buffer, 0, sizeof buffer);
4754d531edbSgilles 
47644f4452eSeric 	for (line = val; line; line = end) {
47744f4452eSeric 		end = strchr(line, '\n');
47844f4452eSeric 		if (end) {
47944f4452eSeric 			linelen = end - line;
48044f4452eSeric 			end++;
48144f4452eSeric 		}
48244f4452eSeric 		else
48344f4452eSeric 			linelen = strlen(line);
48444f4452eSeric 
48544f4452eSeric 		for (i = 0; i < linelen; ++i) {
48644f4452eSeric 			if (line[i] == '(' && !escape && !quote)
4874d531edbSgilles 				comment++;
48844f4452eSeric 			if (line[i] == '"' && !escape && !comment)
4894d531edbSgilles 				quote = !quote;
49044f4452eSeric 			if (line[i] == ')' && !escape && !quote && comment)
4914d531edbSgilles 				comment--;
4923b3439c3Sop 			if (line[i] == '\\' && !escape && !comment)
4934d531edbSgilles 				escape = 1;
4944d531edbSgilles 			else
4954d531edbSgilles 				escape = 0;
4964d531edbSgilles 
4974d531edbSgilles 			/* found a separator, buffer contains a full address */
49844f4452eSeric 			if (line[i] == ',' && !escape && !quote && !comment) {
49944f4452eSeric 				if (!skip && j + strlen(tx->session->listener->hostname) + 1 < sizeof buffer) {
50044f4452eSeric 					header_append_domain_buffer(buffer, tx->session->listener->hostname, sizeof buffer);
50144f4452eSeric 					if (tx->session->flags & SF_AUTHENTICATED &&
50244f4452eSeric 					    tx->session->listener->sendertable[0] &&
50344f4452eSeric 					    tx->session->listener->flags & F_MASQUERADE &&
50444f4452eSeric 					    !(strcasecmp(hdr, "From")))
50555042ccbSeric 						header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender),
5064d531edbSgilles 						    sizeof buffer);
5074d531edbSgilles 				}
50855042ccbSeric 				if (smtp_message_printf(tx, "%s,", buffer) == -1)
5094d531edbSgilles 					return;
5104d531edbSgilles 				j = 0;
5114d531edbSgilles 				skip = 0;
5124d531edbSgilles 				memset(buffer, 0, sizeof buffer);
5134d531edbSgilles 			}
5144d531edbSgilles 			else {
5154d531edbSgilles 				if (skip) {
51644f4452eSeric 					if (smtp_message_printf(tx, "%c", line[i]) == -1)
5174d531edbSgilles 						return;
5184d531edbSgilles 				}
5194d531edbSgilles 				else {
52044f4452eSeric 					buffer[j++] = line[i];
5214d531edbSgilles 					if (j == sizeof (buffer) - 1) {
52255042ccbSeric 						if (smtp_message_printf(tx, "%s", buffer) == -1)
5234d531edbSgilles 							return;
5244d531edbSgilles 						skip = 1;
5254d531edbSgilles 						j = 0;
5264d531edbSgilles 						memset(buffer, 0, sizeof buffer);
5274d531edbSgilles 					}
5284d531edbSgilles 				}
5294d531edbSgilles 			}
5304d531edbSgilles 		}
5314d531edbSgilles 		if (skip) {
53255042ccbSeric 			if (smtp_message_printf(tx, "\n") == -1)
5334d531edbSgilles 				return;
5344d531edbSgilles 		}
5354d531edbSgilles 		else {
5364d531edbSgilles 			buffer[j++] = '\n';
5374d531edbSgilles 			if (j == sizeof (buffer) - 1) {
53855042ccbSeric 				if (smtp_message_printf(tx, "%s", buffer) == -1)
5394d531edbSgilles 					return;
5404d531edbSgilles 				skip = 1;
5414d531edbSgilles 				j = 0;
5424d531edbSgilles 				memset(buffer, 0, sizeof buffer);
5434d531edbSgilles 			}
5444d531edbSgilles 		}
5454d531edbSgilles 	}
5464d531edbSgilles 
5474d531edbSgilles 	/* end of header, if buffer is not empty we'll process it */
5484d531edbSgilles 	if (buffer[0]) {
54944f4452eSeric 		if (j + strlen(tx->session->listener->hostname) + 1 < sizeof buffer) {
55044f4452eSeric 			header_append_domain_buffer(buffer, tx->session->listener->hostname, sizeof buffer);
55144f4452eSeric 			if (tx->session->flags & SF_AUTHENTICATED &&
55244f4452eSeric 			    tx->session->listener->sendertable[0] &&
55344f4452eSeric 			    tx->session->listener->flags & F_MASQUERADE &&
55444f4452eSeric 			    !(strcasecmp(hdr, "From")))
55555042ccbSeric 				header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender),
5564d531edbSgilles 				    sizeof buffer);
5574d531edbSgilles 		}
55855042ccbSeric 		smtp_message_printf(tx, "%s", buffer);
5594d531edbSgilles 	}
5604d531edbSgilles }
5614d531edbSgilles 
5624d531edbSgilles static void
56365c4fdfbSgilles smtp_session_init(void)
5643ef9cbf7Sgilles {
56565c4fdfbSgilles 	static int	init = 0;
566ef9f3596Sjacekm 
56765c4fdfbSgilles 	if (!init) {
568cc81b7c6Seric 		tree_init(&wait_lka_helo);
569a1bd4550Sgilles 		tree_init(&wait_lka_mail);
57065c4fdfbSgilles 		tree_init(&wait_lka_rcpt);
57165c4fdfbSgilles 		tree_init(&wait_parent_auth);
57265c4fdfbSgilles 		tree_init(&wait_queue_msg);
57365c4fdfbSgilles 		tree_init(&wait_queue_fd);
57465c4fdfbSgilles 		tree_init(&wait_queue_commit);
57565c4fdfbSgilles 		tree_init(&wait_ssl_init);
57665c4fdfbSgilles 		tree_init(&wait_ssl_verify);
577d7b0dc3bSgilles 		tree_init(&wait_filters);
578590a6142Sgilles 		tree_init(&wait_filter_fd);
57965c4fdfbSgilles 		init = 1;
58065c4fdfbSgilles 	}
5813ef9cbf7Sgilles }
5823ef9cbf7Sgilles 
58365c4fdfbSgilles int
58465c4fdfbSgilles smtp_session(struct listener *listener, int sock,
5853685495aSgilles     const struct sockaddr_storage *ss, const char *hostname, struct io *io)
5863ef9cbf7Sgilles {
58765c4fdfbSgilles 	struct smtp_session	*s;
5883ef9cbf7Sgilles 
58965c4fdfbSgilles 	smtp_session_init();
59065c4fdfbSgilles 
59165c4fdfbSgilles 	if ((s = calloc(1, sizeof(*s))) == NULL)
59265c4fdfbSgilles 		return (-1);
5930a80c58aSeric 
59465c4fdfbSgilles 	s->id = generate_uid();
59565c4fdfbSgilles 	s->listener = listener;
59665c4fdfbSgilles 	memmove(&s->ss, ss, sizeof(*ss));
5973685495aSgilles 
5983685495aSgilles 	if (io != NULL)
5993685495aSgilles 		s->io = io;
6003685495aSgilles 	else
6018d3f7f0dSeric 		s->io = io_new();
6023685495aSgilles 
6038d3f7f0dSeric 	io_set_callback(s->io, smtp_io, s);
6048d3f7f0dSeric 	io_set_fd(s->io, sock);
6058d3f7f0dSeric 	io_set_timeout(s->io, SMTPD_SESSION_TIMEOUT * 1000);
6068d3f7f0dSeric 	io_set_write(s->io);
60765c4fdfbSgilles 	s->state = STATE_NEW;
60865c4fdfbSgilles 
609c51a7fb7Sgilles 	(void)strlcpy(s->smtpname, listener->hostname, sizeof(s->smtpname));
610cc81b7c6Seric 
61165195c54Sgilles 	log_trace(TRACE_SMTP, "smtp: %p: connected to listener %p "
61265195c54Sgilles 	    "[hostname=%s, port=%d, tag=%s]", s, listener,
61365195c54Sgilles 	    listener->hostname, ntohs(listener->port), listener->tag);
61465195c54Sgilles 
61565c4fdfbSgilles 	/* For local enqueueing, the hostname is already set */
61665c4fdfbSgilles 	if (hostname) {
61765c4fdfbSgilles 		s->flags |= SF_AUTHENTICATED;
61865c4fdfbSgilles 		/* A bit of a hack */
61965c4fdfbSgilles 		if (!strcmp(hostname, "localhost"))
62065c4fdfbSgilles 			s->flags |= SF_BOUNCE;
621744e25e4Sgilles 		(void)strlcpy(s->rdns, hostname, sizeof(s->rdns));
622bcbdde81Seric 		s->fcrdns = 1;
62314b654a8Seric 		smtp_lookup_servername(s);
62465c4fdfbSgilles 	} else {
62597556c3cSmartijn 		resolver_getnameinfo((struct sockaddr *)&s->ss,
62697556c3cSmartijn 		    NI_NAMEREQD | NI_NUMERICSERV, smtp_getnameinfo_cb, s);
6277219d653Sjacekm 	}
628ef9f3596Sjacekm 
629cb2a19a6Sgilles 	/* session may have been freed by now */
6306bc0eb6bSgilles 
63165c4fdfbSgilles 	return (0);
6323ef9cbf7Sgilles }
6333ef9cbf7Sgilles 
63401eba458Seric static void
63501eba458Seric smtp_getnameinfo_cb(void *arg, int gaierrno, const char *host, const char *serv)
63601eba458Seric {
63701eba458Seric 	struct smtp_session *s = arg;
638bcbdde81Seric 	struct addrinfo hints;
63901eba458Seric 
640bcbdde81Seric 	if (gaierrno) {
641744e25e4Sgilles 		(void)strlcpy(s->rdns, "<unknown>", sizeof(s->rdns));
6428020a2a3Sgilles 
6438020a2a3Sgilles 		if (gaierrno == EAI_NODATA || gaierrno == EAI_NONAME)
6448020a2a3Sgilles 			s->fcrdns = 0;
6457b3b6902Sgilles 		else {
6467b3b6902Sgilles 			log_warnx("getnameinfo: %s: %s", ss_to_text(&s->ss),
6477b3b6902Sgilles 			    gai_strerror(gaierrno));
648bcbdde81Seric 			s->fcrdns = -1;
6497b3b6902Sgilles 		}
6508020a2a3Sgilles 
651bcbdde81Seric 		smtp_lookup_servername(s);
652bcbdde81Seric 		return;
653bcbdde81Seric 	}
65401eba458Seric 
655744e25e4Sgilles 	(void)strlcpy(s->rdns, host, sizeof(s->rdns));
65601eba458Seric 
657bcbdde81Seric 	memset(&hints, 0, sizeof(hints));
658bcbdde81Seric 	hints.ai_family = s->ss.ss_family;
659bcbdde81Seric 	hints.ai_socktype = SOCK_STREAM;
660744e25e4Sgilles 	resolver_getaddrinfo(s->rdns, NULL, &hints, smtp_getaddrinfo_cb, s);
661bcbdde81Seric }
662bcbdde81Seric 
663bcbdde81Seric static void
664bcbdde81Seric smtp_getaddrinfo_cb(void *arg, int gaierrno, struct addrinfo *ai0)
665bcbdde81Seric {
666bcbdde81Seric 	struct smtp_session *s = arg;
667bcbdde81Seric 	struct addrinfo *ai;
668bcbdde81Seric 	char fwd[64], rev[64];
669bcbdde81Seric 
670bcbdde81Seric 	if (gaierrno) {
6718020a2a3Sgilles 		if (gaierrno == EAI_NODATA || gaierrno == EAI_NONAME)
6728020a2a3Sgilles 			s->fcrdns = 0;
6737b3b6902Sgilles 		else {
6747b3b6902Sgilles 			log_warnx("getaddrinfo: %s: %s", s->rdns,
6757b3b6902Sgilles 			    gai_strerror(gaierrno));
676bcbdde81Seric 			s->fcrdns = -1;
677bcbdde81Seric 		}
6787b3b6902Sgilles 	}
679bcbdde81Seric 	else {
680bcbdde81Seric 		strlcpy(rev, ss_to_text(&s->ss), sizeof(rev));
681bcbdde81Seric 		for (ai = ai0; ai; ai = ai->ai_next) {
682bcbdde81Seric 			strlcpy(fwd, sa_to_text(ai->ai_addr), sizeof(fwd));
683bcbdde81Seric 			if (!strcmp(fwd, rev)) {
684bcbdde81Seric 				s->fcrdns = 1;
685bcbdde81Seric 				break;
686bcbdde81Seric 			}
687bcbdde81Seric 		}
688bcbdde81Seric 		freeaddrinfo(ai0);
689bcbdde81Seric 	}
690bcbdde81Seric 
69114b654a8Seric 	smtp_lookup_servername(s);
69201eba458Seric }
69301eba458Seric 
69465c4fdfbSgilles void
69565c4fdfbSgilles smtp_session_imsg(struct mproc *p, struct imsg *imsg)
69665c4fdfbSgilles {
69765c4fdfbSgilles 	struct smtp_session		*s;
698ee3e6bebSeric 	struct smtp_rcpt		*rcpt;
699db893743Sgilles 	char				 user[SMTPD_MAXMAILADDRSIZE];
7008bef9f7cSgilles 	char				 tmp[SMTP_LINE_MAX];
70165c4fdfbSgilles 	struct msg			 m;
702cc81b7c6Seric 	const char			*line, *helo;
70365c4fdfbSgilles 	uint64_t			 reqid, evpid;
70408becfa3Seric 	uint32_t			 msgid;
705510586acSclaudio 	int				 status, success, fd;
706d7b0dc3bSgilles 	int                              filter_response;
707d7b0dc3bSgilles 	const char                      *filter_param;
708d7b0dc3bSgilles 	uint8_t                          i;
70965c4fdfbSgilles 
71065c4fdfbSgilles 	switch (imsg->hdr.type) {
71165c4fdfbSgilles 
71206405042Ssunil 	case IMSG_SMTP_CHECK_SENDER:
71306405042Ssunil 		m_msg(&m, imsg);
71406405042Ssunil 		m_get_id(&m, &reqid);
71506405042Ssunil 		m_get_int(&m, &status);
71606405042Ssunil 		m_end(&m);
71706405042Ssunil 		s = tree_xpop(&wait_lka_mail, reqid);
71806405042Ssunil 		switch (status) {
71906405042Ssunil 		case LKA_OK:
720008f3f72Seric 			smtp_tx_create_message(s->tx);
72106405042Ssunil 			break;
72206405042Ssunil 
72306405042Ssunil 		case LKA_PERMFAIL:
7240490754bSeric 			smtp_tx_free(s->tx);
72506405042Ssunil 			smtp_reply(s, "%d %s", 530, "Sender rejected");
72606405042Ssunil 			break;
72706405042Ssunil 		case LKA_TEMPFAIL:
7280490754bSeric 			smtp_tx_free(s->tx);
7295fe3cdcbSgilles 			smtp_reply(s, "421 %s Temporary Error",
73006405042Ssunil 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
73106405042Ssunil 			break;
73206405042Ssunil 		}
73306405042Ssunil 		return;
73406405042Ssunil 
735aa1d5973Seric 	case IMSG_SMTP_EXPAND_RCPT:
73665c4fdfbSgilles 		m_msg(&m, imsg);
73765c4fdfbSgilles 		m_get_id(&m, &reqid);
73865c4fdfbSgilles 		m_get_int(&m, &status);
739299c4efeSeric 		m_get_string(&m, &line);
74065c4fdfbSgilles 		m_end(&m);
74165c4fdfbSgilles 		s = tree_xpop(&wait_lka_rcpt, reqid);
7428bef9f7cSgilles 
7438bef9f7cSgilles 		tmp[0] = '\0';
7448bef9f7cSgilles 		if (s->tx->evp.rcpt.user[0]) {
7458bef9f7cSgilles 			(void)strlcpy(tmp, s->tx->evp.rcpt.user, sizeof tmp);
7468bef9f7cSgilles 			if (s->tx->evp.rcpt.domain[0]) {
7478bef9f7cSgilles 				(void)strlcat(tmp, "@", sizeof tmp);
7488bef9f7cSgilles 				(void)strlcat(tmp, s->tx->evp.rcpt.domain,
7498bef9f7cSgilles 				    sizeof tmp);
7508bef9f7cSgilles 			}
7518bef9f7cSgilles 		}
7528bef9f7cSgilles 
75365c4fdfbSgilles 		switch (status) {
75465c4fdfbSgilles 		case LKA_OK:
75565c4fdfbSgilles 			fatalx("unexpected ok");
75665c4fdfbSgilles 		case LKA_PERMFAIL:
7578bef9f7cSgilles 			smtp_reply(s, "%s: <%s>", line, tmp);
75865c4fdfbSgilles 			break;
75965c4fdfbSgilles 		case LKA_TEMPFAIL:
7608bef9f7cSgilles 			smtp_reply(s, "%s: <%s>", line, tmp);
7618bef9f7cSgilles 			break;
76265c4fdfbSgilles 		}
76365c4fdfbSgilles 		return;
76465c4fdfbSgilles 
765aa1d5973Seric 	case IMSG_SMTP_LOOKUP_HELO:
766cc81b7c6Seric 		m_msg(&m, imsg);
767cc81b7c6Seric 		m_get_id(&m, &reqid);
768cc81b7c6Seric 		s = tree_xpop(&wait_lka_helo, reqid);
769cc81b7c6Seric 		m_get_int(&m, &status);
770cc81b7c6Seric 		if (status == LKA_OK) {
771cc81b7c6Seric 			m_get_string(&m, &helo);
772d429e01dSgilles 			(void)strlcpy(s->smtpname, helo, sizeof(s->smtpname));
773cc81b7c6Seric 		}
774cc81b7c6Seric 		m_end(&m);
7751c3ac238Seric 		smtp_connected(s);
776cc81b7c6Seric 		return;
777cc81b7c6Seric 
778aa1d5973Seric 	case IMSG_SMTP_MESSAGE_CREATE:
77965c4fdfbSgilles 		m_msg(&m, imsg);
78065c4fdfbSgilles 		m_get_id(&m, &reqid);
78165c4fdfbSgilles 		m_get_int(&m, &success);
78265c4fdfbSgilles 		s = tree_xpop(&wait_queue_msg, reqid);
78365c4fdfbSgilles 		if (success) {
78465c4fdfbSgilles 			m_get_msgid(&m, &msgid);
785763ff2e3Seric 			s->tx->msgid = msgid;
7860a80c58aSeric 			s->tx->evp.id = msgid_to_evpid(msgid);
7870a80c58aSeric 			s->tx->rcptcount = 0;
7885fe3cdcbSgilles 			smtp_reply(s, "250 %s Ok",
789fe95d8d0Seric 			    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
79065c4fdfbSgilles 		} else {
7915fe3cdcbSgilles 			smtp_reply(s, "421 %s Temporary Error",
792fe95d8d0Seric 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
793a06bfb0dSgilles 			smtp_tx_free(s->tx);
794299c4efeSeric 			smtp_enter_state(s, STATE_QUIT);
79565c4fdfbSgilles 		}
79665c4fdfbSgilles 		m_end(&m);
79765c4fdfbSgilles 		return;
79865c4fdfbSgilles 
799aa1d5973Seric 	case IMSG_SMTP_MESSAGE_OPEN:
80065c4fdfbSgilles 		m_msg(&m, imsg);
80165c4fdfbSgilles 		m_get_id(&m, &reqid);
80265c4fdfbSgilles 		m_get_int(&m, &success);
80365c4fdfbSgilles 		m_end(&m);
80465c4fdfbSgilles 
805510586acSclaudio 		fd = imsg_get_fd(imsg);
80665c4fdfbSgilles 		s = tree_xpop(&wait_queue_fd, reqid);
807510586acSclaudio 		if (!success || fd == -1) {
808510586acSclaudio 			if (fd != -1)
809510586acSclaudio 				close(fd);
8105fe3cdcbSgilles 			smtp_reply(s, "421 %s Temporary Error",
811fe95d8d0Seric 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
81265c4fdfbSgilles 			smtp_enter_state(s, STATE_QUIT);
81365c4fdfbSgilles 			return;
81465c4fdfbSgilles 		}
81565c4fdfbSgilles 
816510586acSclaudio 		log_debug("smtp: %p: fd %d from queue", s, fd);
81765c4fdfbSgilles 
818510586acSclaudio 		if (smtp_message_fd(s->tx, fd)) {
8194faa8b13Sgilles 			if (!SESSION_DATA_FILTERED(s))
820590a6142Sgilles 				smtp_message_begin(s->tx);
821590a6142Sgilles 			else
822590a6142Sgilles 				smtp_filter_data_begin(s);
823590a6142Sgilles 		}
824590a6142Sgilles 		return;
825590a6142Sgilles 
8262b1a70ccSgilles 	case IMSG_FILTER_SMTP_DATA_BEGIN:
827590a6142Sgilles 		m_msg(&m, imsg);
828590a6142Sgilles 		m_get_id(&m, &reqid);
829590a6142Sgilles 		m_get_int(&m, &success);
830590a6142Sgilles 		m_end(&m);
831590a6142Sgilles 
832510586acSclaudio 		fd = imsg_get_fd(imsg);
833590a6142Sgilles 		s = tree_xpop(&wait_filter_fd, reqid);
834510586acSclaudio 		if (!success || fd == -1) {
835510586acSclaudio 			if (fd != -1)
836510586acSclaudio 				close(fd);
8375fe3cdcbSgilles 			smtp_reply(s, "421 %s Temporary Error",
838590a6142Sgilles 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
839590a6142Sgilles 			smtp_enter_state(s, STATE_QUIT);
840590a6142Sgilles 			return;
841590a6142Sgilles 		}
842590a6142Sgilles 
843510586acSclaudio 		log_debug("smtp: %p: fd %d from lka", s, fd);
844590a6142Sgilles 
845510586acSclaudio 		smtp_filter_fd(s->tx, fd);
846590a6142Sgilles 		smtp_message_begin(s->tx);
84765c4fdfbSgilles 		return;
84865c4fdfbSgilles 
849aa1d5973Seric 	case IMSG_QUEUE_ENVELOPE_SUBMIT:
85065c4fdfbSgilles 		m_msg(&m, imsg);
85165c4fdfbSgilles 		m_get_id(&m, &reqid);
85265c4fdfbSgilles 		m_get_int(&m, &success);
85365c4fdfbSgilles 		s = tree_xget(&wait_lka_rcpt, reqid);
85465c4fdfbSgilles 		if (success) {
85565c4fdfbSgilles 			m_get_evpid(&m, &evpid);
856c6b5e508Sgilles 			s->tx->evp.id = evpid;
8570a80c58aSeric 			s->tx->destcount++;
8586771f06dSgilles 			smtp_report_tx_envelope(s, s->tx->msgid, evpid);
85965c4fdfbSgilles 		}
86065c4fdfbSgilles 		else
861f299ff90Seric 			s->tx->error = TX_ERROR_ENVELOPE;
86265c4fdfbSgilles 		m_end(&m);
86365c4fdfbSgilles 		return;
86465c4fdfbSgilles 
865aa1d5973Seric 	case IMSG_QUEUE_ENVELOPE_COMMIT:
86665c4fdfbSgilles 		m_msg(&m, imsg);
86765c4fdfbSgilles 		m_get_id(&m, &reqid);
86865c4fdfbSgilles 		m_get_int(&m, &success);
86965c4fdfbSgilles 		m_end(&m);
87065c4fdfbSgilles 		if (!success)
87165c4fdfbSgilles 			fatalx("commit evp failed: not supposed to happen");
87265c4fdfbSgilles 		s = tree_xpop(&wait_lka_rcpt, reqid);
873f299ff90Seric 		if (s->tx->error) {
87465c4fdfbSgilles 			/*
87565c4fdfbSgilles 			 * If an envelope failed, we can't cancel the last
87665c4fdfbSgilles 			 * RCPT only so we must cancel the whole transaction
87765c4fdfbSgilles 			 * and close the connection.
87865c4fdfbSgilles 			 */
8795fe3cdcbSgilles 			smtp_reply(s, "421 %s Temporary failure",
880fe95d8d0Seric 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
88165c4fdfbSgilles 			smtp_enter_state(s, STATE_QUIT);
88265c4fdfbSgilles 		}
88365c4fdfbSgilles 		else {
884118c16f3Sgilles 			rcpt = xcalloc(1, sizeof(*rcpt));
885c6b5e508Sgilles 			rcpt->evpid = s->tx->evp.id;
8860a80c58aSeric 			rcpt->destcount = s->tx->destcount;
8870a80c58aSeric 			rcpt->maddr = s->tx->evp.rcpt;
8880a80c58aSeric 			TAILQ_INSERT_TAIL(&s->tx->rcpts, rcpt, entry);
889ee3e6bebSeric 
8900a80c58aSeric 			s->tx->destcount = 0;
8910a80c58aSeric 			s->tx->rcptcount++;
892fe95d8d0Seric 			smtp_reply(s, "250 %s %s: Recipient ok",
893fe95d8d0Seric 			    esc_code(ESC_STATUS_OK, ESC_DESTINATION_ADDRESS_VALID),
894fe95d8d0Seric 			    esc_description(ESC_DESTINATION_ADDRESS_VALID));
89565c4fdfbSgilles 		}
89665c4fdfbSgilles 		return;
89765c4fdfbSgilles 
898aa1d5973Seric 	case IMSG_SMTP_MESSAGE_COMMIT:
89965c4fdfbSgilles 		m_msg(&m, imsg);
90065c4fdfbSgilles 		m_get_id(&m, &reqid);
90165c4fdfbSgilles 		m_get_int(&m, &success);
90265c4fdfbSgilles 		m_end(&m);
90365c4fdfbSgilles 		s = tree_xpop(&wait_queue_commit, reqid);
90465c4fdfbSgilles 		if (!success) {
9055fe3cdcbSgilles 			smtp_reply(s, "421 %s Temporary failure",
906fe95d8d0Seric 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
907aebb80a1Sgilles 			smtp_tx_free(s->tx);
90865c4fdfbSgilles 			smtp_enter_state(s, STATE_QUIT);
90965c4fdfbSgilles 			return;
91065c4fdfbSgilles 		}
91165c4fdfbSgilles 
9125fe3cdcbSgilles 		smtp_reply(s, "250 %s %08x Message accepted for delivery",
913fe95d8d0Seric 		    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS),
914763ff2e3Seric 		    s->tx->msgid);
915aebb80a1Sgilles 		smtp_report_tx_commit(s, s->tx->msgid, s->tx->odatalen);
916aebb80a1Sgilles 		smtp_report_tx_reset(s, s->tx->msgid);
91765c4fdfbSgilles 
918c6b5e508Sgilles 		log_info("%016"PRIx64" smtp message "
919c6b5e508Sgilles 		    "msgid=%08x size=%zu nrcpt=%zu proto=%s",
92065c4fdfbSgilles 		    s->id,
921763ff2e3Seric 		    s->tx->msgid,
922c6b5e508Sgilles 		    s->tx->odatalen,
923c6b5e508Sgilles 		    s->tx->rcptcount,
924c6b5e508Sgilles 		    s->flags & SF_EHLO ? "ESMTP" : "SMTP");
925c6b5e508Sgilles 		TAILQ_FOREACH(rcpt, &s->tx->rcpts, entry) {
926c6b5e508Sgilles 			log_info("%016"PRIx64" smtp envelope "
927c6b5e508Sgilles 			    "evpid=%016"PRIx64" from=<%s%s%s> to=<%s%s%s>",
928c6b5e508Sgilles 			    s->id,
929c6b5e508Sgilles 			    rcpt->evpid,
9300a80c58aSeric 			    s->tx->evp.sender.user,
9310a80c58aSeric 			    s->tx->evp.sender.user[0] == '\0' ? "" : "@",
9320a80c58aSeric 			    s->tx->evp.sender.domain,
933ee3e6bebSeric 			    rcpt->maddr.user,
934ee3e6bebSeric 			    rcpt->maddr.user[0] == '\0' ? "" : "@",
935c6b5e508Sgilles 			    rcpt->maddr.domain);
936ee3e6bebSeric 		}
9370490754bSeric 		smtp_tx_free(s->tx);
93865c4fdfbSgilles 		s->mailcount++;
93965c4fdfbSgilles 		smtp_enter_state(s, STATE_HELO);
94065c4fdfbSgilles 		return;
94165c4fdfbSgilles 
942aa1d5973Seric 	case IMSG_SMTP_AUTHENTICATE:
94365c4fdfbSgilles 		m_msg(&m, imsg);
94465c4fdfbSgilles 		m_get_id(&m, &reqid);
94565c4fdfbSgilles 		m_get_int(&m, &success);
94665c4fdfbSgilles 		m_end(&m);
94765c4fdfbSgilles 
94865c4fdfbSgilles 		s = tree_xpop(&wait_parent_auth, reqid);
94965c4fdfbSgilles 		strnvis(user, s->username, sizeof user, VIS_WHITE | VIS_SAFE);
95065c4fdfbSgilles 		if (success == LKA_OK) {
9517a93bdaaSgilles 			log_info("%016"PRIx64" smtp "
952c6b5e508Sgilles 			    "authentication user=%s "
953c6b5e508Sgilles 			    "result=ok",
954c6b5e508Sgilles 			    s->id, user);
95565c4fdfbSgilles 			s->flags |= SF_AUTHENTICATED;
9566771f06dSgilles 			smtp_report_link_auth(s, user, "pass");
9575fe3cdcbSgilles 			smtp_reply(s, "235 %s Authentication succeeded",
958fe95d8d0Seric 			    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
95965c4fdfbSgilles 		}
96065c4fdfbSgilles 		else if (success == LKA_PERMFAIL) {
9617a93bdaaSgilles 			log_info("%016"PRIx64" smtp "
962c6b5e508Sgilles 			    "authentication user=%s "
963c6b5e508Sgilles 			    "result=permfail",
964c6b5e508Sgilles 			    s->id, user);
9656771f06dSgilles 			smtp_report_link_auth(s, user, "fail");
966299c4efeSeric 			smtp_auth_failure_pause(s);
967299c4efeSeric 			return;
96865c4fdfbSgilles 		}
96965c4fdfbSgilles 		else if (success == LKA_TEMPFAIL) {
9707a93bdaaSgilles 			log_info("%016"PRIx64" smtp "
971c6b5e508Sgilles 			    "authentication user=%s "
972c6b5e508Sgilles 			    "result=tempfail",
973c6b5e508Sgilles 			    s->id, user);
9746771f06dSgilles 			smtp_report_link_auth(s, user, "error");
9755fe3cdcbSgilles 			smtp_reply(s, "421 %s Temporary failure",
976fe95d8d0Seric 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
97765c4fdfbSgilles 		}
97865c4fdfbSgilles 		else
97965c4fdfbSgilles 			fatalx("bad lka response");
980299c4efeSeric 
98165c4fdfbSgilles 		smtp_enter_state(s, STATE_HELO);
98265c4fdfbSgilles 		return;
98365c4fdfbSgilles 
9842b1a70ccSgilles 	case IMSG_FILTER_SMTP_PROTOCOL:
985d7b0dc3bSgilles 		m_msg(&m, imsg);
986d7b0dc3bSgilles 		m_get_id(&m, &reqid);
987d7b0dc3bSgilles 		m_get_int(&m, &filter_response);
988f6b0bf9aSgilles 		if (filter_response != FILTER_PROCEED &&
989f6b0bf9aSgilles 		    filter_response != FILTER_JUNK)
990d7b0dc3bSgilles 			m_get_string(&m, &filter_param);
991b0d3076dSgilles 		else
992b0d3076dSgilles 			filter_param = NULL;
993d7b0dc3bSgilles 		m_end(&m);
994d7b0dc3bSgilles 
995d7b0dc3bSgilles 		s = tree_xpop(&wait_filters, reqid);
996d7b0dc3bSgilles 
997d7b0dc3bSgilles 		switch (filter_response) {
998d7b0dc3bSgilles 		case FILTER_REJECT:
999d7b0dc3bSgilles 		case FILTER_DISCONNECT:
1000d7b0dc3bSgilles 			if (!valid_smtp_response(filter_param) ||
1001d7b0dc3bSgilles 			    (filter_param[0] != '4' && filter_param[0] != '5'))
1002d7b0dc3bSgilles 				filter_param = "421 Internal server error";
1003d7b0dc3bSgilles 			if (!strncmp(filter_param, "421", 3))
1004d7b0dc3bSgilles 				filter_response = FILTER_DISCONNECT;
1005d7b0dc3bSgilles 
10066771f06dSgilles 			smtp_report_filter_response(s, s->filter_phase,
1007b0d3076dSgilles 			    filter_response, filter_param);
1008b0d3076dSgilles 
1009d7b0dc3bSgilles 			smtp_reply(s, "%s", filter_param);
1010d7b0dc3bSgilles 
1011d7b0dc3bSgilles 			if (filter_response == FILTER_DISCONNECT)
1012d7b0dc3bSgilles 				smtp_enter_state(s, STATE_QUIT);
1013c8094670Sgilles 			else if (s->filter_phase == FILTER_COMMIT)
1014c8094670Sgilles 				smtp_proceed_rollback(s, NULL);
1015d7b0dc3bSgilles 			break;
1016d7b0dc3bSgilles 
1017f6b0bf9aSgilles 
1018f6b0bf9aSgilles 		case FILTER_JUNK:
1019f6b0bf9aSgilles 			if (s->tx)
1020f6b0bf9aSgilles 				s->tx->junk = 1;
1021f6b0bf9aSgilles 			else
1022f6b0bf9aSgilles 				s->junk = 1;
1023f6b0bf9aSgilles 			/* fallthrough */
1024f6b0bf9aSgilles 
1025d7b0dc3bSgilles 		case FILTER_PROCEED:
1026522448a1Sgilles 			filter_param = s->filter_param;
1027522448a1Sgilles 			/* fallthrough */
1028522448a1Sgilles 
1029*d49c07c7Sop 		case FILTER_REPORT:
1030d7b0dc3bSgilles 		case FILTER_REWRITE:
10316771f06dSgilles 			smtp_report_filter_response(s, s->filter_phase,
1032b0d3076dSgilles 			    filter_response,
1033b0d3076dSgilles 			    filter_param == s->filter_param ? NULL : filter_param);
1034ec69ed85Sgilles 			if (s->filter_phase == FILTER_CONNECT) {
1035d7b0dc3bSgilles 				smtp_proceed_connected(s);
1036d7b0dc3bSgilles 				return;
1037d7b0dc3bSgilles 			}
1038d7b0dc3bSgilles 			for (i = 0; i < nitems(commands); ++i)
1039522448a1Sgilles 				if (commands[i].filter_phase == s->filter_phase) {
1040d7b0dc3bSgilles 					if (filter_response == FILTER_REWRITE)
1041d7b0dc3bSgilles 						if (!commands[i].check(s, filter_param))
1042d7b0dc3bSgilles 							break;
1043d7b0dc3bSgilles 					commands[i].proceed(s, filter_param);
1044d7b0dc3bSgilles 					break;
1045d7b0dc3bSgilles 				}
1046d7b0dc3bSgilles 			break;
1047d7b0dc3bSgilles 		}
1048d7b0dc3bSgilles 		return;
10491ddc3febSeric 	}
10501ddc3febSeric 
10511ddc3febSeric 	log_warnx("smtp_session_imsg: unexpected %s imsg",
10521ddc3febSeric 	    imsg_to_str(imsg->hdr.type));
10531ddc3febSeric 	fatalx(NULL);
10541ddc3febSeric }
10551ddc3febSeric 
10561ddc3febSeric static void
1057eed85469Seric smtp_tls_init(struct smtp_session *s)
10581ddc3febSeric {
1059eed85469Seric 	io_set_read(s->io);
1060c11a901bSeric 	if (io_accept_tls(s->io, s->listener->tls) == -1) {
1061c11a901bSeric 		log_info("%016"PRIx64" smtp disconnected "
1062c11a901bSeric 		    "reason=tls-accept-failed",
1063c11a901bSeric 		    s->id);
1064c11a901bSeric 		smtp_free(s, "accept failed");
1065c11a901bSeric 	}
1066eed85469Seric }
10674b9ec75fSeric 
1068eed85469Seric static void
1069eed85469Seric smtp_tls_started(struct smtp_session *s)
1070eed85469Seric {
1071eed85469Seric 	if (tls_peer_cert_provided(io_tls(s->io))) {
10724b9ec75fSeric 		log_info("%016"PRIx64" smtp "
1073eed85469Seric 		    "cert-check result=\"%s\" fingerprint=\"%s\"",
1074c6b5e508Sgilles 		    s->id,
1075eed85469Seric 		    (s->flags & SF_VERIFIED) ? "verified" : "unchecked",
1076eed85469Seric 		    tls_peer_cert_hash(io_tls(s->io)));
1077eed85469Seric 	}
10784b9ec75fSeric 
10794b9ec75fSeric 	if (s->listener->flags & F_SMTPS) {
10804b9ec75fSeric 		stat_increment("smtp.smtps", 1);
10818d3f7f0dSeric 		io_set_write(s->io);
10824b9ec75fSeric 		smtp_send_banner(s);
10834b9ec75fSeric 	}
10844b9ec75fSeric 	else {
10854b9ec75fSeric 		stat_increment("smtp.tls", 1);
10864b9ec75fSeric 		smtp_enter_state(s, STATE_HELO);
10874b9ec75fSeric 	}
108865c4fdfbSgilles }
108965c4fdfbSgilles 
109065c4fdfbSgilles static void
1091b556a8d3Seric smtp_io(struct io *io, int evt, void *arg)
109265c4fdfbSgilles {
1093b556a8d3Seric 	struct smtp_session    *s = arg;
109465c4fdfbSgilles 	char		       *line;
1095b251880aSgilles 	size_t			len;
10965bfc3fefSeric 	int			eom;
109765c4fdfbSgilles 
109865c4fdfbSgilles 	log_trace(TRACE_IO, "smtp: %p: %s %s", s, io_strevent(evt),
109965c4fdfbSgilles 	    io_strio(io));
110065c4fdfbSgilles 
110165c4fdfbSgilles 	switch (evt) {
110265c4fdfbSgilles 
110365c4fdfbSgilles 	case IO_TLSREADY:
1104c6b5e508Sgilles 		log_info("%016"PRIx64" smtp tls ciphers=%s",
1105eed85469Seric 		    s->id, tls_to_text(io_tls(s->io)));
110665c4fdfbSgilles 
1107eed85469Seric 		smtp_report_link_tls(s, tls_to_text(io_tls(s->io)));
1108ff05ba49Sgilles 
110965c4fdfbSgilles 		s->flags |= SF_SECURE;
1110eed85469Seric 		if (s->listener->flags & F_TLS_VERIFY)
1111eed85469Seric 			s->flags |= SF_VERIFIED;
1112b937aab1Seric 		s->helo[0] = '\0';
111365c4fdfbSgilles 
1114eed85469Seric 		smtp_tls_started(s);
11151ddc3febSeric 		break;
111665c4fdfbSgilles 
111765c4fdfbSgilles 	case IO_DATAIN:
111865c4fdfbSgilles 	    nextline:
11198d3f7f0dSeric 		line = io_getline(s->io, &len);
11206162865dSeric 		if ((line == NULL && io_datalen(s->io) >= SMTP_LINE_MAX) ||
11216162865dSeric 		    (line && len >= SMTP_LINE_MAX)) {
1122299c4efeSeric 			s->flags |= SF_BADINPUT;
11235fe3cdcbSgilles 			smtp_reply(s, "500 %s Line too long",
1124fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS));
112565c4fdfbSgilles 			smtp_enter_state(s, STATE_QUIT);
112665c4fdfbSgilles 			io_set_write(io);
112765c4fdfbSgilles 			return;
112865c4fdfbSgilles 		}
112965c4fdfbSgilles 
1130df8e24afSkili 		/* No complete line received */
1131df8e24afSkili 		if (line == NULL)
1132df8e24afSkili 			return;
1133df8e24afSkili 
11340124f38dSeric 		/* Strip trailing '\r' */
11350124f38dSeric 		if (len && line[len - 1] == '\r')
11360124f38dSeric 			line[--len] = '\0';
11370124f38dSeric 
113865c4fdfbSgilles 		/* Message body */
11395bfc3fefSeric 		eom = 0;
11405bfc3fefSeric 		if (s->state == STATE_BODY) {
11413cf20826Sgilles 			if (strcmp(line, ".")) {
11423cf20826Sgilles 				s->tx->datain += strlen(line) + 1;
11433cf20826Sgilles 				if (s->tx->datain > env->sc_maxsize)
11443cf20826Sgilles 					s->tx->error = TX_ERROR_SIZE;
11453cf20826Sgilles 			}
1146590a6142Sgilles 			eom = (s->tx->filter == NULL) ?
1147590a6142Sgilles 			    smtp_tx_dataline(s->tx, line) :
1148590a6142Sgilles 			    smtp_tx_filtered_dataline(s->tx, line);
11495bfc3fefSeric 			if (eom == 0)
115065c4fdfbSgilles 				goto nextline;
115165c4fdfbSgilles 		}
115265c4fdfbSgilles 
115365c4fdfbSgilles 		/* Pipelining not supported */
11548d3f7f0dSeric 		if (io_datalen(s->io)) {
1155299c4efeSeric 			s->flags |= SF_BADINPUT;
1156fe95d8d0Seric 			smtp_reply(s, "500 %s %s: Pipelining not supported",
1157fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1158fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
115965c4fdfbSgilles 			smtp_enter_state(s, STATE_QUIT);
116065c4fdfbSgilles 			io_set_write(io);
116165c4fdfbSgilles 			return;
116265c4fdfbSgilles 		}
116365c4fdfbSgilles 
11645bfc3fefSeric 		if (eom) {
11655bfc3fefSeric 			io_set_write(io);
1166590a6142Sgilles 			if (s->tx->filter == NULL)
1167590a6142Sgilles 				smtp_tx_eom(s->tx);
116865c4fdfbSgilles 			return;
116965c4fdfbSgilles 		}
117065c4fdfbSgilles 
117165c4fdfbSgilles 		/* Must be a command */
11726162865dSeric 		if (strlcpy(s->cmd, line, sizeof(s->cmd)) >= sizeof(s->cmd)) {
11736162865dSeric 			s->flags |= SF_BADINPUT;
11745fe3cdcbSgilles 			smtp_reply(s, "500 %s Command line too long",
11756162865dSeric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS));
11766162865dSeric 			smtp_enter_state(s, STATE_QUIT);
11776162865dSeric 			io_set_write(io);
11786162865dSeric 			return;
11796162865dSeric 		}
118065c4fdfbSgilles 		io_set_write(io);
118165c4fdfbSgilles 		smtp_command(s, line);
118265c4fdfbSgilles 		break;
118365c4fdfbSgilles 
118465c4fdfbSgilles 	case IO_LOWAT:
118565c4fdfbSgilles 		if (s->state == STATE_QUIT) {
1186c6b5e508Sgilles 			log_info("%016"PRIx64" smtp disconnected "
1187a206140dSgiovanni 			    "reason=quit",
1188c6b5e508Sgilles 			    s->id);
118965c4fdfbSgilles 			smtp_free(s, "done");
119065c4fdfbSgilles 			break;
119165c4fdfbSgilles 		}
119265c4fdfbSgilles 
119365c4fdfbSgilles 		/* Wait for the client to start tls */
119465c4fdfbSgilles 		if (s->state == STATE_TLS) {
1195eed85469Seric 			smtp_tls_init(s);
119665c4fdfbSgilles 			break;
119765c4fdfbSgilles 		}
119865c4fdfbSgilles 
119965c4fdfbSgilles 		io_set_read(io);
120065c4fdfbSgilles 		break;
120165c4fdfbSgilles 
120265c4fdfbSgilles 	case IO_TIMEOUT:
1203c6b5e508Sgilles 		log_info("%016"PRIx64" smtp disconnected "
1204a206140dSgiovanni 		    "reason=timeout",
1205c6b5e508Sgilles 		    s->id);
12066771f06dSgilles 		smtp_report_timeout(s);
120765c4fdfbSgilles 		smtp_free(s, "timeout");
120865c4fdfbSgilles 		break;
120965c4fdfbSgilles 
121065c4fdfbSgilles 	case IO_DISCONNECTED:
1211c6b5e508Sgilles 		log_info("%016"PRIx64" smtp disconnected "
1212a206140dSgiovanni 		    "reason=disconnect",
1213c6b5e508Sgilles 		    s->id);
121465c4fdfbSgilles 		smtp_free(s, "disconnected");
121565c4fdfbSgilles 		break;
121665c4fdfbSgilles 
121765c4fdfbSgilles 	case IO_ERROR:
1218c6b5e508Sgilles 		log_info("%016"PRIx64" smtp disconnected "
1219a206140dSgiovanni 		    "reason=\"io-error: %s\"",
1220c6b5e508Sgilles 		    s->id, io_error(io));
122165c4fdfbSgilles 		smtp_free(s, "IO error");
122265c4fdfbSgilles 		break;
122365c4fdfbSgilles 
122465c4fdfbSgilles 	default:
122565c4fdfbSgilles 		fatalx("smtp_io()");
122665c4fdfbSgilles 	}
122765c4fdfbSgilles }
122865c4fdfbSgilles 
122965c4fdfbSgilles static void
123065c4fdfbSgilles smtp_command(struct smtp_session *s, char *line)
123165c4fdfbSgilles {
1232197a4a5eSgilles 	char			       *args;
123365c4fdfbSgilles 	int				cmd, i;
123465c4fdfbSgilles 
123565c4fdfbSgilles 	log_trace(TRACE_SMTP, "smtp: %p: <<< %s", s, line);
123665c4fdfbSgilles 
123765c4fdfbSgilles 	/*
123865c4fdfbSgilles 	 * These states are special.
123965c4fdfbSgilles 	 */
124065c4fdfbSgilles 	if (s->state == STATE_AUTH_INIT) {
12416771f06dSgilles 		smtp_report_protocol_client(s, "********");
124265c4fdfbSgilles 		smtp_rfc4954_auth_plain(s, line);
124365c4fdfbSgilles 		return;
124465c4fdfbSgilles 	}
124565c4fdfbSgilles 	if (s->state == STATE_AUTH_USERNAME || s->state == STATE_AUTH_PASSWORD) {
12466771f06dSgilles 		smtp_report_protocol_client(s, "********");
124765c4fdfbSgilles 		smtp_rfc4954_auth_login(s, line);
124865c4fdfbSgilles 		return;
124965c4fdfbSgilles 	}
125065c4fdfbSgilles 
12513917e8c7Sgilles 	if (s->state == STATE_HELO && strncasecmp(line, "AUTH PLAIN ", 11) == 0)
12526771f06dSgilles 		smtp_report_protocol_client(s, "AUTH PLAIN ********");
12533917e8c7Sgilles 	else
12546771f06dSgilles 		smtp_report_protocol_client(s, line);
12553f8dc4feSgilles 
12563917e8c7Sgilles 
125765c4fdfbSgilles 	/*
125865c4fdfbSgilles 	 * Unlike other commands, "mail from" and "rcpt to" contain a
125965c4fdfbSgilles 	 * space in the command name.
126065c4fdfbSgilles 	 */
126165c4fdfbSgilles 	if (strncasecmp("mail from:", line, 10) == 0 ||
126265c4fdfbSgilles 	    strncasecmp("rcpt to:", line, 8) == 0)
126365c4fdfbSgilles 		args = strchr(line, ':');
126465c4fdfbSgilles 	else
126565c4fdfbSgilles 		args = strchr(line, ' ');
126665c4fdfbSgilles 
126765c4fdfbSgilles 	if (args) {
126865c4fdfbSgilles 		*args++ = '\0';
1269fc3a8311Seric 		while (isspace((unsigned char)*args))
127065c4fdfbSgilles 			args++;
127165c4fdfbSgilles 	}
127265c4fdfbSgilles 
127365c4fdfbSgilles 	cmd = -1;
127465c4fdfbSgilles 	for (i = 0; commands[i].code != -1; i++)
127565c4fdfbSgilles 		if (!strcasecmp(line, commands[i].cmd)) {
127665c4fdfbSgilles 			cmd = commands[i].code;
127765c4fdfbSgilles 			break;
127865c4fdfbSgilles 		}
127965c4fdfbSgilles 
12807929fb13Sgilles 	s->last_cmd = cmd;
128165c4fdfbSgilles 	switch (cmd) {
128265c4fdfbSgilles 	/*
128365c4fdfbSgilles 	 * INIT
128465c4fdfbSgilles 	 */
128565c4fdfbSgilles 	case CMD_HELO:
1286d7b0dc3bSgilles 		if (!smtp_check_helo(s, args))
1287d7b0dc3bSgilles 			break;
1288d7b0dc3bSgilles 		smtp_filter_phase(FILTER_HELO, s, args);
1289d7b0dc3bSgilles 		break;
1290d7b0dc3bSgilles 
129165c4fdfbSgilles 	case CMD_EHLO:
1292d7b0dc3bSgilles 		if (!smtp_check_ehlo(s, args))
1293197a4a5eSgilles 			break;
1294d7b0dc3bSgilles 		smtp_filter_phase(FILTER_EHLO, s, args);
1295197a4a5eSgilles 		break;
1296d7b0dc3bSgilles 
1297197a4a5eSgilles 	/*
1298197a4a5eSgilles 	 * SETUP
1299197a4a5eSgilles 	 */
1300197a4a5eSgilles 	case CMD_STARTTLS:
1301197a4a5eSgilles 		if (!smtp_check_starttls(s, args))
1302197a4a5eSgilles 			break;
1303d7b0dc3bSgilles 
1304d7b0dc3bSgilles 		smtp_filter_phase(FILTER_STARTTLS, s, NULL);
1305197a4a5eSgilles 		break;
1306197a4a5eSgilles 
1307197a4a5eSgilles 	case CMD_AUTH:
1308197a4a5eSgilles 		if (!smtp_check_auth(s, args))
1309197a4a5eSgilles 			break;
1310522448a1Sgilles 		smtp_filter_phase(FILTER_AUTH, s, args);
1311197a4a5eSgilles 		break;
1312197a4a5eSgilles 
1313197a4a5eSgilles 	case CMD_MAIL_FROM:
1314d7b0dc3bSgilles 		if (!smtp_check_mail_from(s, args))
1315197a4a5eSgilles 			break;
1316d7b0dc3bSgilles 		smtp_filter_phase(FILTER_MAIL_FROM, s, args);
1317197a4a5eSgilles 		break;
1318197a4a5eSgilles 
1319197a4a5eSgilles 	/*
1320197a4a5eSgilles 	 * TRANSACTION
1321197a4a5eSgilles 	 */
1322197a4a5eSgilles 	case CMD_RCPT_TO:
1323d7b0dc3bSgilles 		if (!smtp_check_rcpt_to(s, args))
1324197a4a5eSgilles 			break;
1325d7b0dc3bSgilles 		smtp_filter_phase(FILTER_RCPT_TO, s, args);
1326197a4a5eSgilles 		break;
1327197a4a5eSgilles 
1328197a4a5eSgilles 	case CMD_RSET:
1329d7b0dc3bSgilles 		if (!smtp_check_rset(s, args))
1330197a4a5eSgilles 			break;
1331d7b0dc3bSgilles 		smtp_filter_phase(FILTER_RSET, s, NULL);
1332197a4a5eSgilles 		break;
1333197a4a5eSgilles 
1334197a4a5eSgilles 	case CMD_DATA:
1335d7b0dc3bSgilles 		if (!smtp_check_data(s, args))
1336197a4a5eSgilles 			break;
1337d7b0dc3bSgilles 		smtp_filter_phase(FILTER_DATA, s, NULL);
1338197a4a5eSgilles 		break;
1339197a4a5eSgilles 
1340197a4a5eSgilles 	/*
1341197a4a5eSgilles 	 * ANY
1342197a4a5eSgilles 	 */
1343197a4a5eSgilles 	case CMD_QUIT:
1344a6b09462Sgilles 		if (!smtp_check_noparam(s, args))
1345a6b09462Sgilles 			break;
1346d7b0dc3bSgilles 		smtp_filter_phase(FILTER_QUIT, s, NULL);
1347197a4a5eSgilles 		break;
1348197a4a5eSgilles 
1349197a4a5eSgilles 	case CMD_NOOP:
1350fbe04ea1Sop 		if (!smtp_check_noop(s, args))
1351a6b09462Sgilles 			break;
1352d7b0dc3bSgilles 		smtp_filter_phase(FILTER_NOOP, s, NULL);
1353197a4a5eSgilles 		break;
1354197a4a5eSgilles 
1355197a4a5eSgilles 	case CMD_HELP:
1356a6b09462Sgilles 		if (!smtp_check_noparam(s, args))
1357a6b09462Sgilles 			break;
1358d7b0dc3bSgilles 		smtp_proceed_help(s, NULL);
1359197a4a5eSgilles 		break;
1360197a4a5eSgilles 
1361197a4a5eSgilles 	case CMD_WIZ:
1362a6b09462Sgilles 		if (!smtp_check_noparam(s, args))
1363a6b09462Sgilles 			break;
1364d7b0dc3bSgilles 		smtp_proceed_wiz(s, NULL);
1365197a4a5eSgilles 		break;
1366197a4a5eSgilles 
1367197a4a5eSgilles 	default:
1368197a4a5eSgilles 		smtp_reply(s, "500 %s %s: Command unrecognized",
1369197a4a5eSgilles 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1370197a4a5eSgilles 			    esc_description(ESC_INVALID_COMMAND));
1371197a4a5eSgilles 		break;
1372197a4a5eSgilles 	}
1373197a4a5eSgilles }
1374197a4a5eSgilles 
1375197a4a5eSgilles static int
1376d7b0dc3bSgilles smtp_check_rset(struct smtp_session *s, const char *args)
1377197a4a5eSgilles {
1378a6b09462Sgilles 	if (!smtp_check_noparam(s, args))
1379a6b09462Sgilles 		return 0;
1380a6b09462Sgilles 
1381197a4a5eSgilles 	if (s->helo[0] == '\0') {
1382197a4a5eSgilles 		smtp_reply(s, "503 %s %s: Command not allowed at this point.",
1383197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1384197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND));
1385197a4a5eSgilles 		return 0;
1386197a4a5eSgilles 	}
1387197a4a5eSgilles 	return 1;
1388197a4a5eSgilles }
1389197a4a5eSgilles 
1390197a4a5eSgilles static int
1391d7b0dc3bSgilles smtp_check_helo(struct smtp_session *s, const char *args)
1392197a4a5eSgilles {
1393d7b0dc3bSgilles 	if (!s->banner_sent) {
1394d7b0dc3bSgilles 		smtp_reply(s, "503 %s %s: Command not allowed at this point.",
1395d7b0dc3bSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1396d7b0dc3bSgilles 		    esc_description(ESC_INVALID_COMMAND));
1397d7b0dc3bSgilles 		return 0;
1398d7b0dc3bSgilles 	}
1399d7b0dc3bSgilles 
1400b937aab1Seric 	if (s->helo[0]) {
1401af0dba2aSsobrado 		smtp_reply(s, "503 %s %s: Already identified",
1402fe95d8d0Seric 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1403fe95d8d0Seric 		    esc_description(ESC_INVALID_COMMAND));
1404197a4a5eSgilles 		return 0;
1405b8ff4170Sgilles 	}
1406b8ff4170Sgilles 
14073ef9cbf7Sgilles 	if (args == NULL) {
1408d7b0dc3bSgilles 		smtp_reply(s, "501 %s %s: HELO requires domain name",
1409fe95d8d0Seric 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1410d7b0dc3bSgilles 		    esc_description(ESC_INVALID_COMMAND));
1411d7b0dc3bSgilles 		return 0;
1412d7b0dc3bSgilles 	}
1413d7b0dc3bSgilles 
1414d7b0dc3bSgilles 	if (!valid_domainpart(args)) {
1415d7b0dc3bSgilles 		smtp_reply(s, "501 %s %s: Invalid domain name",
1416d7b0dc3bSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
1417d7b0dc3bSgilles 		    esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
1418d7b0dc3bSgilles 		return 0;
1419d7b0dc3bSgilles 	}
1420d7b0dc3bSgilles 
1421d7b0dc3bSgilles 	return 1;
1422d7b0dc3bSgilles }
1423d7b0dc3bSgilles 
1424d7b0dc3bSgilles static int
1425d7b0dc3bSgilles smtp_check_ehlo(struct smtp_session *s, const char *args)
1426d7b0dc3bSgilles {
1427d7b0dc3bSgilles 	if (!s->banner_sent) {
1428d7b0dc3bSgilles 		smtp_reply(s, "503 %s %s: Command not allowed at this point.",
1429d7b0dc3bSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1430d7b0dc3bSgilles 		    esc_description(ESC_INVALID_COMMAND));
1431d7b0dc3bSgilles 		return 0;
1432d7b0dc3bSgilles 	}
1433d7b0dc3bSgilles 
1434d7b0dc3bSgilles 	if (s->helo[0]) {
1435d7b0dc3bSgilles 		smtp_reply(s, "503 %s %s: Already identified",
1436d7b0dc3bSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1437d7b0dc3bSgilles 		    esc_description(ESC_INVALID_COMMAND));
1438d7b0dc3bSgilles 		return 0;
1439d7b0dc3bSgilles 	}
1440d7b0dc3bSgilles 
1441d7b0dc3bSgilles 	if (args == NULL) {
1442d7b0dc3bSgilles 		smtp_reply(s, "501 %s %s: EHLO requires domain name",
1443d7b0dc3bSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1444d7b0dc3bSgilles 		    esc_description(ESC_INVALID_COMMAND));
1445197a4a5eSgilles 		return 0;
144665c4fdfbSgilles 	}
144765c4fdfbSgilles 
144865c4fdfbSgilles 	if (!valid_domainpart(args)) {
1449fe95d8d0Seric 		smtp_reply(s, "501 %s %s: Invalid domain name",
1450fe95d8d0Seric 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
1451fe95d8d0Seric 		    esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
1452197a4a5eSgilles 		return 0;
145365c4fdfbSgilles 	}
1454197a4a5eSgilles 
1455197a4a5eSgilles 	return 1;
1456197a4a5eSgilles }
1457197a4a5eSgilles 
1458197a4a5eSgilles static int
1459197a4a5eSgilles smtp_check_auth(struct smtp_session *s, const char *args)
1460197a4a5eSgilles {
1461197a4a5eSgilles 	if (s->helo[0] == '\0' || s->tx) {
1462197a4a5eSgilles 		smtp_reply(s, "503 %s %s: Command not allowed at this point.",
1463197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1464197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND));
1465197a4a5eSgilles 		return 0;
1466197a4a5eSgilles 	}
1467197a4a5eSgilles 
1468197a4a5eSgilles 	if (s->flags & SF_AUTHENTICATED) {
1469197a4a5eSgilles 		smtp_reply(s, "503 %s %s: Already authenticated",
1470197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1471197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND));
1472197a4a5eSgilles 		return 0;
1473197a4a5eSgilles 	}
1474197a4a5eSgilles 
1475197a4a5eSgilles 	if (!ADVERTISE_AUTH(s)) {
1476197a4a5eSgilles 		smtp_reply(s, "503 %s %s: Command not supported",
1477197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1478197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND));
1479197a4a5eSgilles 		return 0;
1480197a4a5eSgilles 	}
1481197a4a5eSgilles 
1482197a4a5eSgilles 	if (args == NULL) {
1483197a4a5eSgilles 		smtp_reply(s, "501 %s %s: No parameters given",
1484197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
1485197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
1486197a4a5eSgilles 		return 0;
1487197a4a5eSgilles 	}
1488197a4a5eSgilles 
1489197a4a5eSgilles 	return 1;
1490197a4a5eSgilles }
1491197a4a5eSgilles 
1492197a4a5eSgilles static int
1493197a4a5eSgilles smtp_check_starttls(struct smtp_session *s, const char *args)
1494197a4a5eSgilles {
1495197a4a5eSgilles 	if (s->helo[0] == '\0' || s->tx) {
1496197a4a5eSgilles 		smtp_reply(s, "503 %s %s: Command not allowed at this point.",
1497197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1498197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND));
1499197a4a5eSgilles 		return 0;
1500197a4a5eSgilles 	}
1501197a4a5eSgilles 
1502197a4a5eSgilles 	if (!(s->listener->flags & F_STARTTLS)) {
1503197a4a5eSgilles 		smtp_reply(s, "503 %s %s: Command not supported",
1504197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1505197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND));
1506197a4a5eSgilles 		return 0;
1507197a4a5eSgilles 	}
1508197a4a5eSgilles 
1509197a4a5eSgilles 	if (s->flags & SF_SECURE) {
1510197a4a5eSgilles 		smtp_reply(s, "503 %s %s: Channel already secured",
1511197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1512197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND));
1513197a4a5eSgilles 		return 0;
1514197a4a5eSgilles 	}
1515197a4a5eSgilles 
1516197a4a5eSgilles 	if (args != NULL) {
1517197a4a5eSgilles 		smtp_reply(s, "501 %s %s: No parameters allowed",
1518197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
1519197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
1520197a4a5eSgilles 		return 0;
1521197a4a5eSgilles 	}
1522197a4a5eSgilles 
1523197a4a5eSgilles 	return 1;
1524197a4a5eSgilles }
1525197a4a5eSgilles 
1526197a4a5eSgilles static int
1527d7b0dc3bSgilles smtp_check_mail_from(struct smtp_session *s, const char *args)
1528197a4a5eSgilles {
1529a9213e5fSgilles 	char *copy;
1530a9213e5fSgilles 	char tmp[SMTP_LINE_MAX];
153103ee6fe4Sgilles 	struct mailaddr	sender;
1532a9213e5fSgilles 
1533a9213e5fSgilles 	(void)strlcpy(tmp, args, sizeof tmp);
1534a9213e5fSgilles 	copy = tmp;
1535a9213e5fSgilles 
1536197a4a5eSgilles 	if (s->helo[0] == '\0' || s->tx) {
1537197a4a5eSgilles 		smtp_reply(s, "503 %s %s: Command not allowed at this point.",
1538197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1539197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND));
1540197a4a5eSgilles 		return 0;
1541197a4a5eSgilles 	}
1542197a4a5eSgilles 
1543197a4a5eSgilles 	if (s->listener->flags & F_STARTTLS_REQUIRE &&
1544197a4a5eSgilles 	    !(s->flags & SF_SECURE)) {
1545197a4a5eSgilles 		smtp_reply(s,
1546197a4a5eSgilles 		    "530 %s %s: Must issue a STARTTLS command first",
1547197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1548197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND));
1549197a4a5eSgilles 		return 0;
1550197a4a5eSgilles 	}
1551197a4a5eSgilles 
1552197a4a5eSgilles 	if (s->listener->flags & F_AUTH_REQUIRE &&
1553197a4a5eSgilles 	    !(s->flags & SF_AUTHENTICATED)) {
1554197a4a5eSgilles 		smtp_reply(s,
1555197a4a5eSgilles 		    "530 %s %s: Must issue an AUTH command first",
1556197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1557197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND));
1558197a4a5eSgilles 		return 0;
1559197a4a5eSgilles 	}
1560197a4a5eSgilles 
1561197a4a5eSgilles 	if (s->mailcount >= env->sc_session_max_mails) {
1562197a4a5eSgilles 		/* we can pretend we had too many recipients */
1563197a4a5eSgilles 		smtp_reply(s, "452 %s %s: Too many messages sent",
1564197a4a5eSgilles 		    esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS),
1565197a4a5eSgilles 		    esc_description(ESC_TOO_MANY_RECIPIENTS));
1566197a4a5eSgilles 		return 0;
1567197a4a5eSgilles 	}
1568197a4a5eSgilles 
156903ee6fe4Sgilles 	if (smtp_mailaddr(&sender, copy, 1, &copy,
157003ee6fe4Sgilles 		s->smtpname) == 0) {
15715fe3cdcbSgilles 		smtp_reply(s, "553 %s Sender address syntax error",
1572a9213e5fSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS));
1573a9213e5fSgilles 		return 0;
1574a9213e5fSgilles 	}
1575a9213e5fSgilles 
1576197a4a5eSgilles 	return 1;
1577197a4a5eSgilles }
1578197a4a5eSgilles 
1579197a4a5eSgilles static int
1580d7b0dc3bSgilles smtp_check_rcpt_to(struct smtp_session *s, const char *args)
1581197a4a5eSgilles {
1582a9213e5fSgilles 	char *copy;
1583a9213e5fSgilles 	char tmp[SMTP_LINE_MAX];
1584a9213e5fSgilles 
1585a9213e5fSgilles 	(void)strlcpy(tmp, args, sizeof tmp);
1586a9213e5fSgilles 	copy = tmp;
1587a9213e5fSgilles 
1588197a4a5eSgilles 	if (s->tx == NULL) {
1589197a4a5eSgilles 		smtp_reply(s, "503 %s %s: Command not allowed at this point.",
1590197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1591197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND));
1592197a4a5eSgilles 		return 0;
1593197a4a5eSgilles 	}
1594197a4a5eSgilles 
1595a9213e5fSgilles 	if (s->tx->rcptcount >= env->sc_session_max_rcpt) {
1596a9213e5fSgilles 		smtp_reply(s->tx->session, "451 %s %s: Too many recipients",
1597a9213e5fSgilles 		    esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS),
1598a9213e5fSgilles 		    esc_description(ESC_TOO_MANY_RECIPIENTS));
1599a9213e5fSgilles 		return 0;
1600a9213e5fSgilles 	}
1601a9213e5fSgilles 
1602a9213e5fSgilles 	if (smtp_mailaddr(&s->tx->evp.rcpt, copy, 0, &copy,
1603a9213e5fSgilles 		s->tx->session->smtpname) == 0) {
1604a9213e5fSgilles 		smtp_reply(s->tx->session,
16055fe3cdcbSgilles 		    "501 %s Recipient address syntax error",
1606a9213e5fSgilles 		    esc_code(ESC_STATUS_PERMFAIL,
1607a9213e5fSgilles 		        ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX));
1608a9213e5fSgilles 		return 0;
1609a9213e5fSgilles 	}
1610a9213e5fSgilles 
1611197a4a5eSgilles 	return 1;
1612197a4a5eSgilles }
1613197a4a5eSgilles 
1614197a4a5eSgilles static int
1615d7b0dc3bSgilles smtp_check_data(struct smtp_session *s, const char *args)
1616197a4a5eSgilles {
1617a6b09462Sgilles 	if (!smtp_check_noparam(s, args))
1618a6b09462Sgilles 		return 0;
1619a6b09462Sgilles 
1620197a4a5eSgilles 	if (s->tx == NULL) {
1621197a4a5eSgilles 		smtp_reply(s, "503 %s %s: Command not allowed at this point.",
1622197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1623197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND));
1624197a4a5eSgilles 		return 0;
1625197a4a5eSgilles 	}
1626197a4a5eSgilles 
1627197a4a5eSgilles 	if (s->tx->rcptcount == 0) {
1628197a4a5eSgilles 		smtp_reply(s, "503 %s %s: No recipient specified",
1629197a4a5eSgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
1630197a4a5eSgilles 		    esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
1631197a4a5eSgilles 		return 0;
1632197a4a5eSgilles 	}
1633197a4a5eSgilles 
1634197a4a5eSgilles 	return 1;
1635197a4a5eSgilles }
1636197a4a5eSgilles 
1637d7b0dc3bSgilles static int
1638fbe04ea1Sop smtp_check_noop(struct smtp_session *s, const char *args)
1639fbe04ea1Sop {
1640fbe04ea1Sop 	return 1;
1641fbe04ea1Sop }
1642fbe04ea1Sop 
1643fbe04ea1Sop static int
1644d7b0dc3bSgilles smtp_check_noparam(struct smtp_session *s, const char *args)
1645d7b0dc3bSgilles {
1646a6b09462Sgilles 	if (args != NULL) {
1647a6b09462Sgilles 		smtp_reply(s, "500 %s %s: command does not accept arguments.",
1648a6b09462Sgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
1649a6b09462Sgilles 		    esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
1650a6b09462Sgilles 		return 0;
1651a6b09462Sgilles 	}
1652d7b0dc3bSgilles 	return 1;
1653d7b0dc3bSgilles }
1654197a4a5eSgilles 
1655197a4a5eSgilles static void
1656d7b0dc3bSgilles smtp_query_filters(enum filter_phase phase, struct smtp_session *s, const char *args)
1657d7b0dc3bSgilles {
16582b1a70ccSgilles 	m_create(p_lka, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
1659d7b0dc3bSgilles 	m_add_id(p_lka, s->id);
1660d7b0dc3bSgilles 	m_add_int(p_lka, phase);
1661d7b0dc3bSgilles 	m_add_string(p_lka, args);
1662d7b0dc3bSgilles 	m_close(p_lka);
1663d7b0dc3bSgilles 	tree_xset(&wait_filters, s->id, s);
1664d7b0dc3bSgilles }
1665d7b0dc3bSgilles 
1666d7b0dc3bSgilles static void
1667590a6142Sgilles smtp_filter_begin(struct smtp_session *s)
1668590a6142Sgilles {
16694faa8b13Sgilles 	if (!SESSION_FILTERED(s))
1670590a6142Sgilles 		return;
1671590a6142Sgilles 
16722b1a70ccSgilles 	m_create(p_lka, IMSG_FILTER_SMTP_BEGIN, 0, 0, -1);
1673590a6142Sgilles 	m_add_id(p_lka, s->id);
1674ec69ed85Sgilles 	m_add_string(p_lka, s->listener->filter_name);
1675590a6142Sgilles 	m_close(p_lka);
1676590a6142Sgilles }
1677590a6142Sgilles 
1678590a6142Sgilles static void
1679590a6142Sgilles smtp_filter_end(struct smtp_session *s)
1680590a6142Sgilles {
16814faa8b13Sgilles 	if (!SESSION_FILTERED(s))
1682590a6142Sgilles 		return;
1683590a6142Sgilles 
16842b1a70ccSgilles 	m_create(p_lka, IMSG_FILTER_SMTP_END, 0, 0, -1);
1685590a6142Sgilles 	m_add_id(p_lka, s->id);
1686590a6142Sgilles 	m_close(p_lka);
1687590a6142Sgilles }
1688590a6142Sgilles 
1689590a6142Sgilles static void
1690590a6142Sgilles smtp_filter_data_begin(struct smtp_session *s)
1691590a6142Sgilles {
16924faa8b13Sgilles 	if (!SESSION_FILTERED(s))
1693590a6142Sgilles 		return;
1694590a6142Sgilles 
16952b1a70ccSgilles 	m_create(p_lka, IMSG_FILTER_SMTP_DATA_BEGIN, 0, 0, -1);
1696590a6142Sgilles 	m_add_id(p_lka, s->id);
1697590a6142Sgilles 	m_close(p_lka);
1698590a6142Sgilles 	tree_xset(&wait_filter_fd, s->id, s);
1699590a6142Sgilles }
1700590a6142Sgilles 
1701590a6142Sgilles static void
1702590a6142Sgilles smtp_filter_data_end(struct smtp_session *s)
1703590a6142Sgilles {
17044faa8b13Sgilles 	if (!SESSION_FILTERED(s))
1705590a6142Sgilles 		return;
1706590a6142Sgilles 
1707590a6142Sgilles 	if (s->tx->filter == NULL)
1708590a6142Sgilles 		return;
1709590a6142Sgilles 
1710590a6142Sgilles 	io_free(s->tx->filter);
1711590a6142Sgilles 	s->tx->filter = NULL;
1712590a6142Sgilles 
17132b1a70ccSgilles 	m_create(p_lka, IMSG_FILTER_SMTP_DATA_END, 0, 0, -1);
1714590a6142Sgilles 	m_add_id(p_lka, s->id);
1715590a6142Sgilles 	m_close(p_lka);
1716590a6142Sgilles }
1717590a6142Sgilles 
1718590a6142Sgilles static void
1719d7b0dc3bSgilles smtp_filter_phase(enum filter_phase phase, struct smtp_session *s, const char *param)
1720d7b0dc3bSgilles {
1721486285b1Sgilles 	uint8_t i;
1722486285b1Sgilles 
1723cfb718b6Sgilles 	s->filter_phase = phase;
1724cfb718b6Sgilles 	s->filter_param = param;
1725cfb718b6Sgilles 
1726373949c1Sgilles 	if (SESSION_FILTERED(s)) {
1727d7b0dc3bSgilles 		smtp_query_filters(phase, s, param ? param : "");
1728486285b1Sgilles 		return;
1729486285b1Sgilles 	}
1730486285b1Sgilles 
1731ec69ed85Sgilles 	if (s->filter_phase == FILTER_CONNECT) {
1732486285b1Sgilles 		smtp_proceed_connected(s);
1733486285b1Sgilles 		return;
1734486285b1Sgilles 	}
1735373949c1Sgilles 
1736486285b1Sgilles 	for (i = 0; i < nitems(commands); ++i)
1737486285b1Sgilles 		if (commands[i].filter_phase == s->filter_phase) {
1738486285b1Sgilles 			commands[i].proceed(s, param);
1739486285b1Sgilles 			break;
1740486285b1Sgilles 		}
1741d7b0dc3bSgilles }
1742d7b0dc3bSgilles 
1743d7b0dc3bSgilles static void
1744d7b0dc3bSgilles smtp_proceed_rset(struct smtp_session *s, const char *args)
1745197a4a5eSgilles {
1746aebb80a1Sgilles 	smtp_reply(s, "250 %s Reset state",
1747aebb80a1Sgilles 	    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
1748aebb80a1Sgilles 
1749197a4a5eSgilles 	if (s->tx) {
1750197a4a5eSgilles 		if (s->tx->msgid)
1751197a4a5eSgilles 			smtp_tx_rollback(s->tx);
1752197a4a5eSgilles 		smtp_tx_free(s->tx);
1753197a4a5eSgilles 	}
1754197a4a5eSgilles }
1755197a4a5eSgilles 
1756197a4a5eSgilles static void
1757d7b0dc3bSgilles smtp_proceed_helo(struct smtp_session *s, const char *args)
1758197a4a5eSgilles {
1759c51a7fb7Sgilles 	(void)strlcpy(s->helo, args, sizeof(s->helo));
176084639b21Seric 	s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED;
176165c4fdfbSgilles 
17626771f06dSgilles 	smtp_report_link_identify(s, "HELO", s->helo);
17637ec3fc38Sgilles 
1764c679e749Seric 	smtp_enter_state(s, STATE_HELO);
176591affd4dSgilles 
176691affd4dSgilles 	smtp_reply(s, "250 %s Hello %s %s%s%s, pleased to meet you",
1767d7b0dc3bSgilles 	    s->smtpname,
1768d7b0dc3bSgilles 	    s->helo,
176991affd4dSgilles 	    s->ss.ss_family == AF_INET6 ? "" : "[",
177091affd4dSgilles 	    ss_to_text(&s->ss),
177191affd4dSgilles 	    s->ss.ss_family == AF_INET6 ? "" : "]");
1772d7b0dc3bSgilles }
1773d7b0dc3bSgilles 
1774d7b0dc3bSgilles static void
1775d7b0dc3bSgilles smtp_proceed_ehlo(struct smtp_session *s, const char *args)
1776d7b0dc3bSgilles {
1777d7b0dc3bSgilles 	(void)strlcpy(s->helo, args, sizeof(s->helo));
1778d7b0dc3bSgilles 	s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED;
1779d7b0dc3bSgilles 	s->flags |= SF_EHLO;
1780d7b0dc3bSgilles 	s->flags |= SF_8BITMIME;
1781d7b0dc3bSgilles 
17826771f06dSgilles 	smtp_report_link_identify(s, "EHLO", s->helo);
17837ec3fc38Sgilles 
1784d7b0dc3bSgilles 	smtp_enter_state(s, STATE_HELO);
178591affd4dSgilles 	smtp_reply(s, "250-%s Hello %s %s%s%s, pleased to meet you",
1786c679e749Seric 	    s->smtpname,
1787c679e749Seric 	    s->helo,
178891affd4dSgilles 	    s->ss.ss_family == AF_INET6 ? "" : "[",
178991affd4dSgilles 	    ss_to_text(&s->ss),
179091affd4dSgilles 	    s->ss.ss_family == AF_INET6 ? "" : "]");
1791c679e749Seric 
1792c679e749Seric 	smtp_reply(s, "250-8BITMIME");
1793c679e749Seric 	smtp_reply(s, "250-ENHANCEDSTATUSCODES");
1794c679e749Seric 	smtp_reply(s, "250-SIZE %zu", env->sc_maxsize);
1795c679e749Seric 	if (ADVERTISE_EXT_DSN(s))
1796c679e749Seric 		smtp_reply(s, "250-DSN");
1797c679e749Seric 	if (ADVERTISE_TLS(s))
1798c679e749Seric 		smtp_reply(s, "250-STARTTLS");
1799c679e749Seric 	if (ADVERTISE_AUTH(s))
1800c679e749Seric 		smtp_reply(s, "250-AUTH PLAIN LOGIN");
1801c679e749Seric 	smtp_reply(s, "250 HELP");
1802c679e749Seric }
180365c4fdfbSgilles 
1804197a4a5eSgilles static void
1805d7b0dc3bSgilles smtp_proceed_auth(struct smtp_session *s, const char *args)
1806197a4a5eSgilles {
1807d7b0dc3bSgilles 	char tmp[SMTP_LINE_MAX];
1808197a4a5eSgilles 	char *eom, *method;
18093ef9cbf7Sgilles 
1810d7b0dc3bSgilles 	(void)strlcpy(tmp, args, sizeof tmp);
1811d7b0dc3bSgilles 
1812d7b0dc3bSgilles 	method = tmp;
1813d7b0dc3bSgilles 	eom = strchr(tmp, ' ');
18143ef9cbf7Sgilles 	if (eom == NULL)
1815d7b0dc3bSgilles 		eom = strchr(tmp, '\t');
18163ef9cbf7Sgilles 	if (eom != NULL)
18173ef9cbf7Sgilles 		*eom++ = '\0';
181885e1f4f6Sgilles 	if (strcasecmp(method, "PLAIN") == 0)
181965c4fdfbSgilles 		smtp_rfc4954_auth_plain(s, eom);
182085e1f4f6Sgilles 	else if (strcasecmp(method, "LOGIN") == 0)
182165c4fdfbSgilles 		smtp_rfc4954_auth_login(s, eom);
1822e8fa9864Sjacekm 	else
1823fe95d8d0Seric 		smtp_reply(s, "504 %s %s: AUTH method \"%s\" not supported",
1824fe95d8d0Seric 		    esc_code(ESC_STATUS_PERMFAIL, ESC_SECURITY_FEATURES_NOT_SUPPORTED),
1825fe95d8d0Seric 		    esc_description(ESC_SECURITY_FEATURES_NOT_SUPPORTED),
182665c4fdfbSgilles 		    method);
182765c4fdfbSgilles }
182865c4fdfbSgilles 
1829197a4a5eSgilles static void
1830d7b0dc3bSgilles smtp_proceed_starttls(struct smtp_session *s, const char *args)
1831197a4a5eSgilles {
18325fe3cdcbSgilles 	smtp_reply(s, "220 %s Ready to start TLS",
1833fe95d8d0Seric 	    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
1834197a4a5eSgilles 	smtp_enter_state(s, STATE_TLS);
183565c4fdfbSgilles }
183665c4fdfbSgilles 
1837197a4a5eSgilles static void
1838d7b0dc3bSgilles smtp_proceed_mail_from(struct smtp_session *s, const char *args)
1839197a4a5eSgilles {
184003ee6fe4Sgilles 	char *copy;
184103ee6fe4Sgilles 	char tmp[SMTP_LINE_MAX];
184203ee6fe4Sgilles 
184303ee6fe4Sgilles 	(void)strlcpy(tmp, args, sizeof tmp);
184403ee6fe4Sgilles 	copy = tmp;
184503ee6fe4Sgilles 
184603ee6fe4Sgilles        	if (!smtp_tx(s)) {
184703ee6fe4Sgilles 		smtp_reply(s, "421 %s Temporary Error",
184803ee6fe4Sgilles 		    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
184903ee6fe4Sgilles 		smtp_enter_state(s, STATE_QUIT);
185003ee6fe4Sgilles 		return;
185103ee6fe4Sgilles 	}
185203ee6fe4Sgilles 
185303ee6fe4Sgilles 	if (smtp_mailaddr(&s->tx->evp.sender, copy, 1, &copy,
185403ee6fe4Sgilles 		s->smtpname) == 0) {
185503ee6fe4Sgilles 		smtp_reply(s, "553 %s Sender address syntax error",
185603ee6fe4Sgilles 		    esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS));
185703ee6fe4Sgilles 		smtp_tx_free(s->tx);
185803ee6fe4Sgilles 		return;
185903ee6fe4Sgilles 	}
186003ee6fe4Sgilles 
1861197a4a5eSgilles 	smtp_tx_mail_from(s->tx, args);
1862197a4a5eSgilles }
1863197a4a5eSgilles 
1864197a4a5eSgilles static void
1865d7b0dc3bSgilles smtp_proceed_rcpt_to(struct smtp_session *s, const char *args)
1866197a4a5eSgilles {
1867197a4a5eSgilles 	smtp_tx_rcpt_to(s->tx, args);
1868197a4a5eSgilles }
1869197a4a5eSgilles 
1870197a4a5eSgilles static void
1871d7b0dc3bSgilles smtp_proceed_data(struct smtp_session *s, const char *args)
1872197a4a5eSgilles {
1873008f3f72Seric 	smtp_tx_open_message(s->tx);
1874197a4a5eSgilles }
1875197a4a5eSgilles 
1876197a4a5eSgilles static void
1877d7b0dc3bSgilles smtp_proceed_quit(struct smtp_session *s, const char *args)
1878197a4a5eSgilles {
18795fe3cdcbSgilles 	smtp_reply(s, "221 %s Bye",
1880fe95d8d0Seric 	    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
188165c4fdfbSgilles 	smtp_enter_state(s, STATE_QUIT);
1882197a4a5eSgilles }
188365c4fdfbSgilles 
1884197a4a5eSgilles static void
1885d7b0dc3bSgilles smtp_proceed_noop(struct smtp_session *s, const char *args)
1886197a4a5eSgilles {
18875fe3cdcbSgilles 	smtp_reply(s, "250 %s Ok",
1888fe95d8d0Seric 	    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
1889197a4a5eSgilles }
189065c4fdfbSgilles 
1891197a4a5eSgilles static void
1892d7b0dc3bSgilles smtp_proceed_help(struct smtp_session *s, const char *args)
1893197a4a5eSgilles {
18945fe3cdcbSgilles 	const char *code = esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS);
18955fe3cdcbSgilles 
18965fe3cdcbSgilles 	smtp_reply(s, "214-%s This is " SMTPD_NAME, code);
18975fe3cdcbSgilles 	smtp_reply(s, "214-%s To report bugs in the implementation, "
18985fe3cdcbSgilles 	    "please contact bugs@openbsd.org", code);
18995fe3cdcbSgilles 	smtp_reply(s, "214-%s with full details", code);
19005fe3cdcbSgilles 	smtp_reply(s, "214 %s End of HELP info", code);
1901197a4a5eSgilles }
1902fe95d8d0Seric 
1903197a4a5eSgilles static void
1904d7b0dc3bSgilles smtp_proceed_wiz(struct smtp_session *s, const char *args)
1905197a4a5eSgilles {
1906fe95d8d0Seric 	smtp_reply(s, "500 %s %s: this feature is not supported yet ;-)",
1907fe95d8d0Seric 	    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1908fe95d8d0Seric 	    esc_description(ESC_INVALID_COMMAND));
190985e1f4f6Sgilles }
191085e1f4f6Sgilles 
1911be925435Sgilles static void
1912c8094670Sgilles smtp_proceed_commit(struct smtp_session *s, const char *args)
1913c8094670Sgilles {
1914c8094670Sgilles 	smtp_message_end(s->tx);
1915c8094670Sgilles }
1916c8094670Sgilles 
1917c8094670Sgilles static void
1918c8094670Sgilles smtp_proceed_rollback(struct smtp_session *s, const char *args)
1919c8094670Sgilles {
1920c8094670Sgilles 	struct smtp_tx *tx;
1921c8094670Sgilles 
1922c8094670Sgilles 	tx = s->tx;
1923c8094670Sgilles 
1924c8094670Sgilles 	fclose(tx->ofile);
1925c8094670Sgilles 	tx->ofile = NULL;
1926c8094670Sgilles 
1927c8094670Sgilles 	smtp_tx_rollback(tx);
1928c8094670Sgilles 	smtp_tx_free(tx);
1929c8094670Sgilles 	smtp_enter_state(s, STATE_HELO);
1930c8094670Sgilles }
1931c8094670Sgilles 
1932c8094670Sgilles static void
193365c4fdfbSgilles smtp_rfc4954_auth_plain(struct smtp_session *s, char *arg)
193485e1f4f6Sgilles {
1935e8fa9864Sjacekm 	char		 buf[1024], *user, *pass;
1936e8fa9864Sjacekm 	int		 len;
1937e8fa9864Sjacekm 
193865c4fdfbSgilles 	switch (s->state) {
193965c4fdfbSgilles 	case STATE_HELO:
194085e1f4f6Sgilles 		if (arg == NULL) {
194165c4fdfbSgilles 			smtp_enter_state(s, STATE_AUTH_INIT);
194265c4fdfbSgilles 			smtp_reply(s, "334 ");
1943e8fa9864Sjacekm 			return;
194485e1f4f6Sgilles 		}
194565c4fdfbSgilles 		smtp_enter_state(s, STATE_AUTH_INIT);
1946e8fa9864Sjacekm 		/* FALLTHROUGH */
194785e1f4f6Sgilles 
194865c4fdfbSgilles 	case STATE_AUTH_INIT:
1949e8fa9864Sjacekm 		/* String is not NUL terminated, leave room. */
1950254aed36Seric 		if ((len = base64_decode(arg, (unsigned char *)buf,
19517bdbba2fSeric 			    sizeof(buf) - 1)) == -1)
1952e8fa9864Sjacekm 			goto abort;
1953e8fa9864Sjacekm 		/* buf is a byte string, NUL terminate. */
1954e8fa9864Sjacekm 		buf[len] = '\0';
1955e8fa9864Sjacekm 
1956e8fa9864Sjacekm 		/*
1957e8fa9864Sjacekm 		 * Skip "foo" in "foo\0user\0pass", if present.
1958e8fa9864Sjacekm 		 */
1959e8fa9864Sjacekm 		user = memchr(buf, '\0', len);
1960e8fa9864Sjacekm 		if (user == NULL || user >= buf + len - 2)
1961e8fa9864Sjacekm 			goto abort;
1962e8fa9864Sjacekm 		user++; /* skip NUL */
196365c4fdfbSgilles 		if (strlcpy(s->username, user, sizeof(s->username))
196465c4fdfbSgilles 		    >= sizeof(s->username))
1965e8fa9864Sjacekm 			goto abort;
1966e8fa9864Sjacekm 
1967e8fa9864Sjacekm 		pass = memchr(user, '\0', len - (user - buf));
1968e8fa9864Sjacekm 		if (pass == NULL || pass >= buf + len - 2)
1969e8fa9864Sjacekm 			goto abort;
1970e8fa9864Sjacekm 		pass++; /* skip NUL */
19713ef9cbf7Sgilles 
1972aa1d5973Seric 		m_create(p_lka,  IMSG_SMTP_AUTHENTICATE, 0, 0, -1);
197365c4fdfbSgilles 		m_add_id(p_lka, s->id);
197465c4fdfbSgilles 		m_add_string(p_lka, s->listener->authtable);
197565c4fdfbSgilles 		m_add_string(p_lka, user);
197665c4fdfbSgilles 		m_add_string(p_lka, pass);
197765c4fdfbSgilles 		m_close(p_lka);
197865c4fdfbSgilles 		tree_xset(&wait_parent_auth, s->id, s);
1979e8fa9864Sjacekm 		return;
1980e8fa9864Sjacekm 
1981e8fa9864Sjacekm 	default:
198265c4fdfbSgilles 		fatal("smtp_rfc4954_auth_plain: unknown state");
19833ef9cbf7Sgilles 	}
19843ef9cbf7Sgilles 
1985e8fa9864Sjacekm abort:
1986fe95d8d0Seric 	smtp_reply(s, "501 %s %s: Syntax error",
1987fe95d8d0Seric 	    esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR),
1988fe95d8d0Seric 	    esc_description(ESC_SYNTAX_ERROR));
198965c4fdfbSgilles 	smtp_enter_state(s, STATE_HELO);
1990e8fa9864Sjacekm }
1991e8fa9864Sjacekm 
1992be925435Sgilles static void
199365c4fdfbSgilles smtp_rfc4954_auth_login(struct smtp_session *s, char *arg)
199485e1f4f6Sgilles {
1995953aae25Sderaadt 	char		buf[LINE_MAX];
199685e1f4f6Sgilles 
199765c4fdfbSgilles 	switch (s->state) {
199865c4fdfbSgilles 	case STATE_HELO:
199965c4fdfbSgilles 		smtp_enter_state(s, STATE_AUTH_USERNAME);
2000da29cb6eSgilles 		if (arg != NULL && *arg != '\0') {
2001da29cb6eSgilles 			smtp_rfc4954_auth_login(s, arg);
2002da29cb6eSgilles 			return;
2003da29cb6eSgilles 		}
200465c4fdfbSgilles 		smtp_reply(s, "334 VXNlcm5hbWU6");
2005e8fa9864Sjacekm 		return;
200685e1f4f6Sgilles 
200765c4fdfbSgilles 	case STATE_AUTH_USERNAME:
2008c1392a69Seric 		memset(s->username, 0, sizeof(s->username));
2009254aed36Seric 		if (base64_decode(arg, (unsigned char *)s->username,
201065c4fdfbSgilles 				  sizeof(s->username) - 1) == -1)
2011e8fa9864Sjacekm 			goto abort;
201285e1f4f6Sgilles 
201365c4fdfbSgilles 		smtp_enter_state(s, STATE_AUTH_PASSWORD);
201465c4fdfbSgilles 		smtp_reply(s, "334 UGFzc3dvcmQ6");
2015e8fa9864Sjacekm 		return;
201685e1f4f6Sgilles 
201765c4fdfbSgilles 	case STATE_AUTH_PASSWORD:
2018c1392a69Seric 		memset(buf, 0, sizeof(buf));
2019254aed36Seric 		if (base64_decode(arg, (unsigned char *)buf,
2020254aed36Seric 				  sizeof(buf)-1) == -1)
2021e8fa9864Sjacekm 			goto abort;
202285e1f4f6Sgilles 
2023aa1d5973Seric 		m_create(p_lka,  IMSG_SMTP_AUTHENTICATE, 0, 0, -1);
202465c4fdfbSgilles 		m_add_id(p_lka, s->id);
202565c4fdfbSgilles 		m_add_string(p_lka, s->listener->authtable);
202665c4fdfbSgilles 		m_add_string(p_lka, s->username);
202765c4fdfbSgilles 		m_add_string(p_lka, buf);
202865c4fdfbSgilles 		m_close(p_lka);
202965c4fdfbSgilles 		tree_xset(&wait_parent_auth, s->id, s);
2030e8fa9864Sjacekm 		return;
2031e8fa9864Sjacekm 
203285e1f4f6Sgilles 	default:
203365c4fdfbSgilles 		fatal("smtp_rfc4954_auth_login: unknown state");
203485e1f4f6Sgilles 	}
203585e1f4f6Sgilles 
2036e8fa9864Sjacekm abort:
2037fe95d8d0Seric 	smtp_reply(s, "501 %s %s: Syntax error",
2038fe95d8d0Seric 	    esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR),
2039fe95d8d0Seric 	    esc_description(ESC_SYNTAX_ERROR));
204065c4fdfbSgilles 	smtp_enter_state(s, STATE_HELO);
204185e1f4f6Sgilles }
204285e1f4f6Sgilles 
204314b654a8Seric static void
20441c3ac238Seric smtp_lookup_servername(struct smtp_session *s)
20451c3ac238Seric {
20461c3ac238Seric 	if (s->listener->hostnametable[0]) {
2047aa1d5973Seric 		m_create(p_lka, IMSG_SMTP_LOOKUP_HELO, 0, 0, -1);
20481c3ac238Seric 		m_add_id(p_lka, s->id);
20491c3ac238Seric 		m_add_string(p_lka, s->listener->hostnametable);
2050f2006a45Sgilles 		m_add_sockaddr(p_lka, (struct sockaddr*)&s->listener->ss);
20511c3ac238Seric 		m_close(p_lka);
20521c3ac238Seric 		tree_xset(&wait_lka_helo, s->id, s);
205308ad1f30Seric 		return;
20541c3ac238Seric 	}
205514b654a8Seric 
205614b654a8Seric 	smtp_connected(s);
20571c3ac238Seric }
20581c3ac238Seric 
205965c4fdfbSgilles static void
206065c4fdfbSgilles smtp_connected(struct smtp_session *s)
20613ef9cbf7Sgilles {
206265c4fdfbSgilles 	smtp_enter_state(s, STATE_CONNECTED);
20633ef9cbf7Sgilles 
2064cf540174Sgilles 	log_info("%016"PRIx64" smtp connected address=%s host=%s",
2065744e25e4Sgilles 	    s->id, ss_to_text(&s->ss), s->rdns);
206665c4fdfbSgilles 
2067ec69ed85Sgilles 	smtp_filter_begin(s);
2068ec69ed85Sgilles 
20696771f06dSgilles 	smtp_report_link_connect(s, s->rdns, s->fcrdns, &s->ss,
207046aa809dSgilles 	    &s->listener->ss);
207146aa809dSgilles 
2072ec69ed85Sgilles 	smtp_filter_phase(FILTER_CONNECT, s, ss_to_text(&s->ss));
2073d7b0dc3bSgilles }
2074d7b0dc3bSgilles 
2075d7b0dc3bSgilles static void
2076d7b0dc3bSgilles smtp_proceed_connected(struct smtp_session *s)
2077d7b0dc3bSgilles {
20785d65917aSeric 	if (s->listener->flags & F_SMTPS)
2079eed85469Seric 		smtp_tls_init(s);
20805d65917aSeric 	else
2081c679e749Seric 		smtp_send_banner(s);
20823ef9cbf7Sgilles }
20833ef9cbf7Sgilles 
2084cc81b7c6Seric static void
2085cc81b7c6Seric smtp_send_banner(struct smtp_session *s)
2086cc81b7c6Seric {
208748b77194Seric 	smtp_reply(s, "220 %s ESMTP %s", s->smtpname, SMTPD_NAME);
20880d4adc27Sgilles 	s->banner_sent = 1;
20896771f06dSgilles 	smtp_report_link_greeting(s, s->smtpname);
2090cc81b7c6Seric }
2091cc81b7c6Seric 
20923ef9cbf7Sgilles void
209365c4fdfbSgilles smtp_enter_state(struct smtp_session *s, int newstate)
209465c4fdfbSgilles {
209565c4fdfbSgilles 	log_trace(TRACE_SMTP, "smtp: %p: %s -> %s", s,
209665c4fdfbSgilles 	    smtp_strstate(s->state),
209765c4fdfbSgilles 	    smtp_strstate(newstate));
209865c4fdfbSgilles 
209965c4fdfbSgilles 	s->state = newstate;
210065c4fdfbSgilles }
210165c4fdfbSgilles 
210265c4fdfbSgilles static void
210365c4fdfbSgilles smtp_reply(struct smtp_session *s, char *fmt, ...)
2104a6ca2cc5Sjacekm {
2105a6ca2cc5Sjacekm 	va_list	 ap;
210665c4fdfbSgilles 	int	 n;
21076e163f27Sgilles 	char	 buf[LINE_MAX*2], tmp[LINE_MAX*2];
2108a6ca2cc5Sjacekm 
2109a6ca2cc5Sjacekm 	va_start(ap, fmt);
2110b04e6f27Seric 	n = vsnprintf(buf, sizeof buf, fmt, ap);
2111a6ca2cc5Sjacekm 	va_end(ap);
21126e163f27Sgilles 	if (n < 0)
21136e163f27Sgilles 		fatalx("smtp_reply: response format error");
2114b04e6f27Seric 	if (n < 4)
211565c4fdfbSgilles 		fatalx("smtp_reply: response too short");
21166e163f27Sgilles 	if (n >= (int)sizeof buf) {
21176e163f27Sgilles 		/* only first three bytes are used by SMTP logic,
21186e163f27Sgilles 		 * so if _our_ reply does not fit entirely in the
21196e163f27Sgilles 		 * buffer, it's ok to truncate.
21206e163f27Sgilles 		 */
21216e163f27Sgilles 	}
2122a6ca2cc5Sjacekm 
2123b04e6f27Seric 	log_trace(TRACE_SMTP, "smtp: %p: >>> %s", s, buf);
2124aebb80a1Sgilles 	smtp_report_protocol_server(s, buf);
2125b04e6f27Seric 
2126b04e6f27Seric 	switch (buf[0]) {
21277929fb13Sgilles 	case '2':
21287b136650Sgilles 		if (s->tx) {
2129aebb80a1Sgilles 			if (s->last_cmd == CMD_MAIL_FROM) {
2130aebb80a1Sgilles 				smtp_report_tx_begin(s, s->tx->msgid);
21316771f06dSgilles 				smtp_report_tx_mail(s, s->tx->msgid, s->cmd + 10, 1);
2132aebb80a1Sgilles 			}
21337929fb13Sgilles 			else if (s->last_cmd == CMD_RCPT_TO)
21346771f06dSgilles 				smtp_report_tx_rcpt(s, s->tx->msgid, s->cmd + 8, 1);
21357b136650Sgilles 		}
21367929fb13Sgilles 		break;
2137e7d82dd0Sgilles 	case '3':
21387b136650Sgilles 		if (s->tx) {
2139e7d82dd0Sgilles 			if (s->last_cmd == CMD_DATA)
21406771f06dSgilles 				smtp_report_tx_data(s, s->tx->msgid, 1);
21417b136650Sgilles 		}
2142e7d82dd0Sgilles 		break;
21431ef63b12Sjacekm 	case '5':
21441ef63b12Sjacekm 	case '4':
2145b2f40906Sgilles 		/* do not report smtp_tx_mail/smtp_tx_rcpt errors
2146b2f40906Sgilles 		 * if they happened outside of a transaction.
2147b2f40906Sgilles 		 */
2148b2f40906Sgilles 		if (s->tx) {
21497929fb13Sgilles 			if (s->last_cmd == CMD_MAIL_FROM)
21506771f06dSgilles 				smtp_report_tx_mail(s, s->tx->msgid,
2151b2f40906Sgilles 				    s->cmd + 10, buf[0] == '4' ? -1 : 0);
21527929fb13Sgilles 			else if (s->last_cmd == CMD_RCPT_TO)
21536771f06dSgilles 				smtp_report_tx_rcpt(s,
2154b2f40906Sgilles 				    s->tx->msgid, s->cmd + 8, buf[0] == '4' ? -1 : 0);
2155e7d82dd0Sgilles 			else if (s->last_cmd == CMD_DATA && s->tx->rcptcount)
21566771f06dSgilles 				smtp_report_tx_data(s, s->tx->msgid,
2157e7d82dd0Sgilles 				    buf[0] == '4' ? -1 : 0);
2158b2f40906Sgilles 		}
21597929fb13Sgilles 
2160299c4efeSeric 		if (s->flags & SF_BADINPUT) {
21617a93bdaaSgilles 			log_info("%016"PRIx64" smtp "
2162c6b5e508Sgilles 			    "bad-input result=\"%.*s\"",
2163c6b5e508Sgilles 			    s->id, n, buf);
2164299c4efeSeric 		}
21656f197337Sgilles 		else if (s->state == STATE_AUTH_INIT) {
2166a206140dSgiovanni 			log_info("%016"PRIx64" smtp "
2167c6b5e508Sgilles 			    "failed-command "
2168a206140dSgiovanni 			    "command=\"AUTH PLAIN (...)\" result=\"%.*s\"",
2169c6b5e508Sgilles 			    s->id, n, buf);
21706f197337Sgilles 		}
21716f197337Sgilles 		else if (s->state == STATE_AUTH_USERNAME) {
2172a206140dSgiovanni 			log_info("%016"PRIx64" smtp "
2173c6b5e508Sgilles 			    "failed-command "
2174a206140dSgiovanni 			    "command=\"AUTH LOGIN (username)\" result=\"%.*s\"",
2175c6b5e508Sgilles 			    s->id, n, buf);
21766f197337Sgilles 		}
21776f197337Sgilles 		else if (s->state == STATE_AUTH_PASSWORD) {
2178a206140dSgiovanni 			log_info("%016"PRIx64" smtp "
2179c6b5e508Sgilles 			    "failed-command "
2180a206140dSgiovanni 			    "command=\"AUTH LOGIN (password)\" result=\"%.*s\"",
2181c6b5e508Sgilles 			    s->id, n, buf);
2182299c4efeSeric 		}
2183299c4efeSeric 		else {
218482614934Seric 			strnvis(tmp, s->cmd, sizeof tmp, VIS_SAFE | VIS_CSTYLE);
21857a93bdaaSgilles 			log_info("%016"PRIx64" smtp "
2186c6b5e508Sgilles 			    "failed-command command=\"%s\" "
2187a206140dSgiovanni 			    "result=\"%.*s\"",
2188c6b5e508Sgilles 			    s->id, tmp, n, buf);
2189299c4efeSeric 		}
21901ef63b12Sjacekm 		break;
21911ef63b12Sjacekm 	}
2192e7d82dd0Sgilles 
2193e7d82dd0Sgilles 	io_xprintf(s->io, "%s\r\n", buf);
21945711eb67Sjacekm }
21955711eb67Sjacekm 
2196be925435Sgilles static void
219765c4fdfbSgilles smtp_free(struct smtp_session *s, const char * reason)
21988107cc2fSjacekm {
21990490754bSeric 	if (s->tx) {
22008d3f7f0dSeric 		if (s->tx->msgid)
2201008f3f72Seric 			smtp_tx_rollback(s->tx);
22020490754bSeric 		smtp_tx_free(s->tx);
22030490754bSeric 	}
2204dcdb4581Seric 
22056771f06dSgilles 	smtp_report_link_disconnect(s);
2206590a6142Sgilles 	smtp_filter_end(s);
2207ff05ba49Sgilles 
220865c4fdfbSgilles 	if (s->flags & SF_SECURE && s->listener->flags & F_SMTPS)
220965c4fdfbSgilles 		stat_decrement("smtp.smtps", 1);
221065c4fdfbSgilles 	if (s->flags & SF_SECURE && s->listener->flags & F_STARTTLS)
221165c4fdfbSgilles 		stat_decrement("smtp.tls", 1);
221265c4fdfbSgilles 
22138d3f7f0dSeric 	io_free(s->io);
221465c4fdfbSgilles 	free(s);
221565c4fdfbSgilles 
221665c4fdfbSgilles 	smtp_collect();
221765c4fdfbSgilles }
221865c4fdfbSgilles 
221965c4fdfbSgilles static int
2220cc81b7c6Seric smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args,
2221cc81b7c6Seric     const char *domain)
222265c4fdfbSgilles {
222365c4fdfbSgilles 	char   *p, *e;
222465c4fdfbSgilles 
222565c4fdfbSgilles 	if (line == NULL)
222665c4fdfbSgilles 		return (0);
222765c4fdfbSgilles 
222865c4fdfbSgilles 	if (*line != '<')
222965c4fdfbSgilles 		return (0);
223065c4fdfbSgilles 
223165c4fdfbSgilles 	e = strchr(line, '>');
223265c4fdfbSgilles 	if (e == NULL)
223365c4fdfbSgilles 		return (0);
223465c4fdfbSgilles 	*e++ = '\0';
223565c4fdfbSgilles 	while (*e == ' ')
223665c4fdfbSgilles 		e++;
223765c4fdfbSgilles 	*args = e;
223865c4fdfbSgilles 
223965c4fdfbSgilles 	if (!text_to_mailaddr(maddr, line + 1))
224065c4fdfbSgilles 		return (0);
224165c4fdfbSgilles 
224265c4fdfbSgilles 	p = strchr(maddr->user, ':');
224365c4fdfbSgilles 	if (p != NULL) {
224465c4fdfbSgilles 		p++;
224565c4fdfbSgilles 		memmove(maddr->user, p, strlen(p) + 1);
224665c4fdfbSgilles 	}
224765c4fdfbSgilles 
2248f3fad8caSgilles 	/* accept empty return-path in MAIL FROM, required for bounces */
2249f3fad8caSgilles 	if (mailfrom && maddr->user[0] == '\0' && maddr->domain[0] == '\0')
225065c4fdfbSgilles 		return (1);
2251cc81b7c6Seric 
22529dcfda04Sgilles 	/* no or invalid user-part, reject */
22539dcfda04Sgilles 	if (maddr->user[0] == '\0' || !valid_localpart(maddr->user))
2254f3fad8caSgilles 		return (0);
2255f3fad8caSgilles 
22569dcfda04Sgilles 	/* no domain part, local user */
2257f3fad8caSgilles 	if (maddr->domain[0] == '\0') {
2258cc81b7c6Seric 		(void)strlcpy(maddr->domain, domain,
2259cc81b7c6Seric 			sizeof(maddr->domain));
2260cc81b7c6Seric 	}
22619dcfda04Sgilles 
22629dcfda04Sgilles 	if (!valid_domainpart(maddr->domain))
226365c4fdfbSgilles 		return (0);
226465c4fdfbSgilles 
226565c4fdfbSgilles 	return (1);
226665c4fdfbSgilles }
226765c4fdfbSgilles 
22687627c963Seric static void
2269299c4efeSeric smtp_auth_failure_resume(int fd, short event, void *p)
2270299c4efeSeric {
2271299c4efeSeric 	struct smtp_session *s = p;
2272299c4efeSeric 
2273299c4efeSeric 	smtp_reply(s, "535 Authentication failed");
2274299c4efeSeric 	smtp_enter_state(s, STATE_HELO);
2275299c4efeSeric }
2276299c4efeSeric 
2277299c4efeSeric static void
2278299c4efeSeric smtp_auth_failure_pause(struct smtp_session *s)
2279299c4efeSeric {
2280299c4efeSeric 	struct timeval	tv;
2281299c4efeSeric 
2282299c4efeSeric 	tv.tv_sec = 0;
2283299c4efeSeric 	tv.tv_usec = arc4random_uniform(1000000);
2284299c4efeSeric 	log_trace(TRACE_SMTP, "smtp: timing-attack protection triggered, "
22858c71974fSop 	    "will defer answer for %lu microseconds", (long)tv.tv_usec);
2286299c4efeSeric 	evtimer_set(&s->pause, smtp_auth_failure_resume, s);
2287299c4efeSeric 	evtimer_add(&s->pause, &tv);
2288299c4efeSeric }
2289299c4efeSeric 
22905e3ff182Seric static int
22915e3ff182Seric smtp_tx(struct smtp_session *s)
22925e3ff182Seric {
22935e3ff182Seric 	struct smtp_tx *tx;
22945e3ff182Seric 
22955e3ff182Seric 	tx = calloc(1, sizeof(*tx));
22965e3ff182Seric 	if (tx == NULL)
22975e3ff182Seric 		return 0;
22985e3ff182Seric 
22995e3ff182Seric 	TAILQ_INIT(&tx->rcpts);
23005e3ff182Seric 
23015e3ff182Seric 	s->tx = tx;
23025e3ff182Seric 	tx->session = s;
23035e3ff182Seric 
23045e3ff182Seric 	/* setup the envelope */
230522135240Seric 	tx->evp.ss = s->ss;
230622135240Seric 	(void)strlcpy(tx->evp.tag, s->listener->tag, sizeof(tx->evp.tag));
230722135240Seric 	(void)strlcpy(tx->evp.smtpname, s->smtpname, sizeof(tx->evp.smtpname));
2308744e25e4Sgilles 	(void)strlcpy(tx->evp.hostname, s->rdns, sizeof tx->evp.hostname);
230922135240Seric 	(void)strlcpy(tx->evp.helo, s->helo, sizeof(tx->evp.helo));
23107d9f71f9Sgilles 	(void)strlcpy(tx->evp.username, s->username, sizeof(tx->evp.username));
23115e3ff182Seric 
23125e3ff182Seric 	if (s->flags & SF_BOUNCE)
231322135240Seric 		tx->evp.flags |= EF_BOUNCE;
23145e3ff182Seric 	if (s->flags & SF_AUTHENTICATED)
231522135240Seric 		tx->evp.flags |= EF_AUTHENTICATED;
23165e3ff182Seric 
231744f4452eSeric 	if ((tx->parser = rfc5322_parser_new()) == NULL) {
231844f4452eSeric 		free(tx);
231944f4452eSeric 		return 0;
23205e3ff182Seric 	}
23215e3ff182Seric 
23225e3ff182Seric 	return 1;
23235e3ff182Seric }
23245e3ff182Seric 
23255e3ff182Seric static void
23265e3ff182Seric smtp_tx_free(struct smtp_tx *tx)
23275e3ff182Seric {
23285e3ff182Seric 	struct smtp_rcpt *rcpt;
23295e3ff182Seric 
233044f4452eSeric 	rfc5322_free(tx->parser);
23315e3ff182Seric 
23325e3ff182Seric 	while ((rcpt = TAILQ_FIRST(&tx->rcpts))) {
23335e3ff182Seric 		TAILQ_REMOVE(&tx->rcpts, rcpt, entry);
23345e3ff182Seric 		free(rcpt);
23355e3ff182Seric 	}
23365e3ff182Seric 
23375e3ff182Seric 	if (tx->ofile)
23385e3ff182Seric 		fclose(tx->ofile);
23395e3ff182Seric 
23405e3ff182Seric 	tx->session->tx = NULL;
23415e3ff182Seric 
23425e3ff182Seric 	free(tx);
23435e3ff182Seric }
23445e3ff182Seric 
234508becfa3Seric static void
2346aaf790e0Sgilles smtp_tx_mail_from(struct smtp_tx *tx, const char *line)
234772cde3cbSeric {
23486b0385feSeric 	char *opt;
2349aaf790e0Sgilles 	char *copy;
2350aaf790e0Sgilles 	char tmp[SMTP_LINE_MAX];
23516b0385feSeric 
2352aaf790e0Sgilles 	(void)strlcpy(tmp, line, sizeof tmp);
2353aaf790e0Sgilles 	copy = tmp;
2354aaf790e0Sgilles 
2355aaf790e0Sgilles 	if (smtp_mailaddr(&tx->evp.sender, copy, 1, &copy,
235672cde3cbSeric 		tx->session->smtpname) == 0) {
23575fe3cdcbSgilles 		smtp_reply(tx->session, "553 %s Sender address syntax error",
235872cde3cbSeric 		    esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS));
23594768c36cSeric 		smtp_tx_free(tx);
236072cde3cbSeric 		return;
236172cde3cbSeric 	}
236272cde3cbSeric 
2363aaf790e0Sgilles 	while ((opt = strsep(&copy, " "))) {
23646b0385feSeric 		if (*opt == '\0')
23656b0385feSeric 			continue;
23666b0385feSeric 
23676b0385feSeric 		if (strncasecmp(opt, "AUTH=", 5) == 0)
23686b0385feSeric 			log_debug("debug: smtp: AUTH in MAIL FROM command");
23696b0385feSeric 		else if (strncasecmp(opt, "SIZE=", 5) == 0)
23706b0385feSeric 			log_debug("debug: smtp: SIZE in MAIL FROM command");
23716b0385feSeric 		else if (strcasecmp(opt, "BODY=7BIT") == 0)
23726b0385feSeric 			/* XXX only for this transaction */
23736b0385feSeric 			tx->session->flags &= ~SF_8BITMIME;
23746b0385feSeric 		else if (strcasecmp(opt, "BODY=8BITMIME") == 0)
23756b0385feSeric 			;
23766b0385feSeric 		else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "RET=", 4) == 0) {
23776b0385feSeric 			opt += 4;
23786b0385feSeric 			if (strcasecmp(opt, "HDRS") == 0)
23796b0385feSeric 				tx->evp.dsn_ret = DSN_RETHDRS;
23806b0385feSeric 			else if (strcasecmp(opt, "FULL") == 0)
23816b0385feSeric 				tx->evp.dsn_ret = DSN_RETFULL;
23826b0385feSeric 		} else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "ENVID=", 6) == 0) {
23836b0385feSeric 			opt += 6;
23846b0385feSeric 			if (strlcpy(tx->evp.dsn_envid, opt, sizeof(tx->evp.dsn_envid))
23856b0385feSeric 			    >= sizeof(tx->evp.dsn_envid)) {
23866b0385feSeric 				smtp_reply(tx->session,
23876b0385feSeric 				    "503 %s %s: option too large, truncated: %s",
23886b0385feSeric 				    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
23896b0385feSeric 				    esc_description(ESC_INVALID_COMMAND_ARGUMENTS), opt);
239072cde3cbSeric 				smtp_tx_free(tx);
239172cde3cbSeric 				return;
239272cde3cbSeric 			}
23936b0385feSeric 		} else {
23946b0385feSeric 			smtp_reply(tx->session, "503 %s %s: Unsupported option %s",
23956b0385feSeric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
23966b0385feSeric 			    esc_description(ESC_INVALID_COMMAND_ARGUMENTS), opt);
23976b0385feSeric 			smtp_tx_free(tx);
23986b0385feSeric 			return;
23996b0385feSeric 		}
24006b0385feSeric 	}
240172cde3cbSeric 
240272cde3cbSeric 	/* only check sendertable if defined and user has authenticated */
240372cde3cbSeric 	if (tx->session->flags & SF_AUTHENTICATED &&
240472cde3cbSeric 	    tx->session->listener->sendertable[0]) {
240572cde3cbSeric 		m_create(p_lka, IMSG_SMTP_CHECK_SENDER, 0, 0, -1);
240672cde3cbSeric 		m_add_id(p_lka, tx->session->id);
240772cde3cbSeric 		m_add_string(p_lka, tx->session->listener->sendertable);
240872cde3cbSeric 		m_add_string(p_lka, tx->session->username);
240972cde3cbSeric 		m_add_mailaddr(p_lka, &tx->evp.sender);
241072cde3cbSeric 		m_close(p_lka);
241172cde3cbSeric 		tree_xset(&wait_lka_mail, tx->session->id, tx->session);
241272cde3cbSeric 	}
241372cde3cbSeric 	else
241472cde3cbSeric 		smtp_tx_create_message(tx);
241572cde3cbSeric }
241672cde3cbSeric 
241772cde3cbSeric static void
2418008f3f72Seric smtp_tx_create_message(struct smtp_tx *tx)
2419a2e141b1Seric {
2420a2e141b1Seric 	m_create(p_queue, IMSG_SMTP_MESSAGE_CREATE, 0, 0, -1);
2421008f3f72Seric 	m_add_id(p_queue, tx->session->id);
2422a2e141b1Seric 	m_close(p_queue);
2423008f3f72Seric 	tree_xset(&wait_queue_msg, tx->session->id, tx->session);
2424a2e141b1Seric }
2425a2e141b1Seric 
2426a2e141b1Seric static void
2427aaf790e0Sgilles smtp_tx_rcpt_to(struct smtp_tx *tx, const char *line)
242872cde3cbSeric {
24296b0385feSeric 	char *opt, *p;
2430aaf790e0Sgilles 	char *copy;
2431aaf790e0Sgilles 	char tmp[SMTP_LINE_MAX];
2432aaf790e0Sgilles 
2433aaf790e0Sgilles 	(void)strlcpy(tmp, line, sizeof tmp);
2434aaf790e0Sgilles 	copy = tmp;
24356b0385feSeric 
243672cde3cbSeric 	if (tx->rcptcount >= env->sc_session_max_rcpt) {
243772cde3cbSeric 		smtp_reply(tx->session, "451 %s %s: Too many recipients",
243872cde3cbSeric 		    esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS),
243972cde3cbSeric 		    esc_description(ESC_TOO_MANY_RECIPIENTS));
244072cde3cbSeric 		return;
244172cde3cbSeric 	}
244272cde3cbSeric 
2443aaf790e0Sgilles 	if (smtp_mailaddr(&tx->evp.rcpt, copy, 0, &copy,
244472cde3cbSeric 	    tx->session->smtpname) == 0) {
244572cde3cbSeric 		smtp_reply(tx->session,
24465fe3cdcbSgilles 		    "501 %s Recipient address syntax error",
244772cde3cbSeric 		    esc_code(ESC_STATUS_PERMFAIL,
244872cde3cbSeric 		        ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX));
244972cde3cbSeric 		return;
245072cde3cbSeric 	}
245172cde3cbSeric 
2452aaf790e0Sgilles 	while ((opt = strsep(&copy, " "))) {
24536b0385feSeric 		if (*opt == '\0')
24546b0385feSeric 			continue;
24556b0385feSeric 
24566b0385feSeric 		if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "NOTIFY=", 7) == 0) {
24576b0385feSeric 			opt += 7;
24586b0385feSeric 			while ((p = strsep(&opt, ","))) {
24596b0385feSeric 				if (strcasecmp(p, "SUCCESS") == 0)
24606b0385feSeric 					tx->evp.dsn_notify |= DSN_SUCCESS;
24616b0385feSeric 				else if (strcasecmp(p, "FAILURE") == 0)
24626b0385feSeric 					tx->evp.dsn_notify |= DSN_FAILURE;
24636b0385feSeric 				else if (strcasecmp(p, "DELAY") == 0)
24646b0385feSeric 					tx->evp.dsn_notify |= DSN_DELAY;
24656b0385feSeric 				else if (strcasecmp(p, "NEVER") == 0)
24666b0385feSeric 					tx->evp.dsn_notify |= DSN_NEVER;
24676b0385feSeric 			}
24686b0385feSeric 
24696b0385feSeric 			if (tx->evp.dsn_notify & DSN_NEVER &&
24706b0385feSeric 			    tx->evp.dsn_notify & (DSN_SUCCESS | DSN_FAILURE |
24716b0385feSeric 			    DSN_DELAY)) {
24726b0385feSeric 				smtp_reply(tx->session,
24736b0385feSeric 				    "553 NOTIFY option NEVER cannot be"
24746b0385feSeric 				    " combined with other options");
247572cde3cbSeric 				return;
24766b0385feSeric 			}
2477cd8603dbSop 		} else if (ADVERTISE_EXT_DSN(tx->session) &&
2478cd8603dbSop 		    strncasecmp(opt, "ORCPT=", 6) == 0) {
2479cd8603dbSop 			size_t len = sizeof(tx->evp.dsn_orcpt);
2480cd8603dbSop 
24816b0385feSeric 			opt += 6;
24822d8fd64bSgilles 
2483cd8603dbSop 			if ((p = strchr(opt, ';')) == NULL ||
2484cd8603dbSop 			    !valid_xtext(p + 1) ||
248590974261Sop 			    strlcpy(tx->evp.dsn_orcpt, opt, len) >= len) {
24866b0385feSeric 				smtp_reply(tx->session,
24876b0385feSeric 				    "553 ORCPT address syntax error");
24886b0385feSeric 				return;
24896b0385feSeric 			}
24906b0385feSeric 		} else {
24916b0385feSeric 			smtp_reply(tx->session, "503 Unsupported option %s", opt);
24926b0385feSeric 			return;
24936b0385feSeric 		}
24946b0385feSeric 	}
249572cde3cbSeric 
249672cde3cbSeric 	m_create(p_lka, IMSG_SMTP_EXPAND_RCPT, 0, 0, -1);
249772cde3cbSeric 	m_add_id(p_lka, tx->session->id);
249872cde3cbSeric 	m_add_envelope(p_lka, &tx->evp);
249972cde3cbSeric 	m_close(p_lka);
250072cde3cbSeric 	tree_xset(&wait_lka_rcpt, tx->session->id, tx->session);
250172cde3cbSeric }
250272cde3cbSeric 
250372cde3cbSeric static void
2504008f3f72Seric smtp_tx_open_message(struct smtp_tx *tx)
2505a2e141b1Seric {
2506a2e141b1Seric 	m_create(p_queue, IMSG_SMTP_MESSAGE_OPEN, 0, 0, -1);
2507008f3f72Seric 	m_add_id(p_queue, tx->session->id);
2508008f3f72Seric 	m_add_msgid(p_queue, tx->msgid);
2509a2e141b1Seric 	m_close(p_queue);
2510008f3f72Seric 	tree_xset(&wait_queue_fd, tx->session->id, tx->session);
2511a2e141b1Seric }
2512a2e141b1Seric 
2513a2e141b1Seric static void
2514008f3f72Seric smtp_tx_commit(struct smtp_tx *tx)
2515a2e141b1Seric {
2516a2e141b1Seric 	m_create(p_queue, IMSG_SMTP_MESSAGE_COMMIT, 0, 0, -1);
2517008f3f72Seric 	m_add_id(p_queue, tx->session->id);
2518008f3f72Seric 	m_add_msgid(p_queue, tx->msgid);
2519a2e141b1Seric 	m_close(p_queue);
2520008f3f72Seric 	tree_xset(&wait_queue_commit, tx->session->id, tx->session);
2521590a6142Sgilles 	smtp_filter_data_end(tx->session);
2522a2e141b1Seric }
2523a2e141b1Seric 
2524a2e141b1Seric static void
2525008f3f72Seric smtp_tx_rollback(struct smtp_tx *tx)
2526a2e141b1Seric {
2527a2e141b1Seric 	m_create(p_queue, IMSG_SMTP_MESSAGE_ROLLBACK, 0, 0, -1);
2528008f3f72Seric 	m_add_msgid(p_queue, tx->msgid);
2529a2e141b1Seric 	m_close(p_queue);
25306771f06dSgilles 	smtp_report_tx_rollback(tx->session, tx->msgid);
25316771f06dSgilles 	smtp_report_tx_reset(tx->session, tx->msgid);
2532590a6142Sgilles 	smtp_filter_data_end(tx->session);
2533a2e141b1Seric }
2534a2e141b1Seric 
25355bfc3fefSeric static int
2536008f3f72Seric smtp_tx_dataline(struct smtp_tx *tx, const char *line)
2537a1bd4550Sgilles {
253844f4452eSeric 	struct rfc5322_result res;
253944f4452eSeric 	int r;
2540f7c2eb5aSgilles 
2541ad48cc20Seric 	log_trace(TRACE_SMTP, "<<< [MSG] %s", line);
2542ad48cc20Seric 
25435bfc3fefSeric 	if (!strcmp(line, ".")) {
25446771f06dSgilles 		smtp_report_protocol_client(tx->session, ".");
25455bfc3fefSeric 		log_trace(TRACE_SMTP, "<<< [EOM]");
254644f4452eSeric 		if (tx->error)
25475bfc3fefSeric 			return 1;
254844f4452eSeric 		line = NULL;
25495bfc3fefSeric 	}
255044f4452eSeric 	else {
2551f299ff90Seric 		/* ignore data line if an error is set */
2552008f3f72Seric 		if (tx->error)
25535bfc3fefSeric 			return 0;
2554f7c2eb5aSgilles 
2555ad48cc20Seric 		/* escape lines starting with a '.' */
2556ad48cc20Seric 		if (line[0] == '.')
2557ad48cc20Seric 			line += 1;
255844f4452eSeric 	}
2559f7c2eb5aSgilles 
256044f4452eSeric 	if (rfc5322_push(tx->parser, line) == -1) {
256144f4452eSeric 		log_warnx("failed to push dataline");
256244f4452eSeric 		tx->error = TX_ERROR_INTERNAL;
25635bfc3fefSeric 		return 0;
2564ad48cc20Seric 	}
2565ad48cc20Seric 
256644f4452eSeric 	for(;;) {
256744f4452eSeric 		r = rfc5322_next(tx->parser, &res);
256844f4452eSeric 		switch (r) {
256944f4452eSeric 		case -1:
257044f4452eSeric 			if (errno == ENOMEM)
257144f4452eSeric 				tx->error = TX_ERROR_INTERNAL;
257244f4452eSeric 			else
2573008f3f72Seric 				tx->error = TX_ERROR_MALFORMED;
25745bfc3fefSeric 			return 0;
257544f4452eSeric 
257644f4452eSeric 		case RFC5322_NONE:
257744f4452eSeric 			/* Need more data */
257844f4452eSeric 			return 0;
257944f4452eSeric 
258044f4452eSeric 		case RFC5322_HEADER_START:
258144f4452eSeric 			/* ignore bcc */
258244f4452eSeric 			if (!strcasecmp("Bcc", res.hdr))
258344f4452eSeric 				continue;
258444f4452eSeric 
258544f4452eSeric 			if (!strcasecmp("To", res.hdr) ||
258644f4452eSeric 			    !strcasecmp("Cc", res.hdr) ||
258744f4452eSeric 			    !strcasecmp("From", res.hdr)) {
258844f4452eSeric 				rfc5322_unfold_header(tx->parser);
258944f4452eSeric 				continue;
259044f4452eSeric 			}
259144f4452eSeric 
259244f4452eSeric 			if (!strcasecmp("Received", res.hdr)) {
259344f4452eSeric 				if (++tx->rcvcount >= MAX_HOPS_COUNT) {
259444f4452eSeric 					log_warnx("warn: loop detected");
259544f4452eSeric 					tx->error = TX_ERROR_LOOP;
259644f4452eSeric 					return 0;
259744f4452eSeric 				}
259844f4452eSeric 			}
259944f4452eSeric 			else if (!tx->has_date && !strcasecmp("Date", res.hdr))
260044f4452eSeric 				tx->has_date = 1;
260144f4452eSeric 			else if (!tx->has_message_id &&
260244f4452eSeric 			    !strcasecmp("Message-Id", res.hdr))
260344f4452eSeric 				tx->has_message_id = 1;
260444f4452eSeric 
260544f4452eSeric 			smtp_message_printf(tx, "%s:%s\n", res.hdr, res.value);
260644f4452eSeric 			break;
260744f4452eSeric 
260844f4452eSeric 		case RFC5322_HEADER_CONT:
260944f4452eSeric 
261044f4452eSeric 			if (!strcasecmp("Bcc", res.hdr) ||
261144f4452eSeric 			    !strcasecmp("To", res.hdr) ||
261244f4452eSeric 			    !strcasecmp("Cc", res.hdr) ||
261344f4452eSeric 			    !strcasecmp("From", res.hdr))
261444f4452eSeric 				continue;
261544f4452eSeric 
261644f4452eSeric 			smtp_message_printf(tx, "%s\n", res.value);
261744f4452eSeric 			break;
261844f4452eSeric 
261944f4452eSeric 		case RFC5322_HEADER_END:
262044f4452eSeric 			if (!strcasecmp("To", res.hdr) ||
262144f4452eSeric 			    !strcasecmp("Cc", res.hdr) ||
262244f4452eSeric 			    !strcasecmp("From", res.hdr))
262344f4452eSeric 				header_domain_append_callback(tx, res.hdr,
262444f4452eSeric 				    res.value);
262544f4452eSeric 			break;
262644f4452eSeric 
262744f4452eSeric 		case RFC5322_END_OF_HEADERS:
262844f4452eSeric 			if (tx->session->listener->local ||
2629ab096723Smillert 			    tx->session->listener->port == htons(587)) {
263044f4452eSeric 
263144f4452eSeric 				if (!tx->has_date) {
263244f4452eSeric 					log_debug("debug: %p: adding Date", tx);
263344f4452eSeric 					smtp_message_printf(tx, "Date: %s\n",
263444f4452eSeric 					    time_to_text(tx->time));
263544f4452eSeric 				}
263644f4452eSeric 
263744f4452eSeric 				if (!tx->has_message_id) {
263844f4452eSeric 					log_debug("debug: %p: adding Message-ID", tx);
263944f4452eSeric 					smtp_message_printf(tx,
264044f4452eSeric 					    "Message-ID: <%016"PRIx64"@%s>\n",
264144f4452eSeric 					    generate_uid(),
264244f4452eSeric 					    tx->session->listener->hostname);
264344f4452eSeric 				}
264444f4452eSeric 			}
264544f4452eSeric 			break;
264644f4452eSeric 
264744f4452eSeric 		case RFC5322_BODY_START:
264844f4452eSeric 		case RFC5322_BODY:
264944f4452eSeric 			smtp_message_printf(tx, "%s\n", res.value);
265044f4452eSeric 			break;
265144f4452eSeric 
265244f4452eSeric 		case RFC5322_END_OF_MESSAGE:
265344f4452eSeric 			return 1;
265444f4452eSeric 
265544f4452eSeric 		default:
265644f4452eSeric 			fatalx("%s", __func__);
265744f4452eSeric 		}
265844f4452eSeric 	}
2659a1bd4550Sgilles }
2660a1bd4550Sgilles 
2661590a6142Sgilles static int
2662590a6142Sgilles smtp_tx_filtered_dataline(struct smtp_tx *tx, const char *line)
2663590a6142Sgilles {
26643cf20826Sgilles 	if (!strcmp(line, "."))
2665590a6142Sgilles 		line = NULL;
2666590a6142Sgilles 	else {
2667590a6142Sgilles 		/* ignore data line if an error is set */
2668590a6142Sgilles 		if (tx->error)
2669590a6142Sgilles 			return 0;
2670590a6142Sgilles 	}
26713835491fSeric 	io_printf(tx->filter, "%s\n", line ? line : ".");
2672590a6142Sgilles 	return line ? 0 : 1;
2673590a6142Sgilles }
2674590a6142Sgilles 
267554355315Seric static void
2676590a6142Sgilles smtp_tx_eom(struct smtp_tx *tx)
2677590a6142Sgilles {
2678590a6142Sgilles 	smtp_filter_phase(FILTER_COMMIT, tx->session, NULL);
2679590a6142Sgilles }
2680590a6142Sgilles 
2681590a6142Sgilles static int
268254355315Seric smtp_message_fd(struct smtp_tx *tx, int fd)
268354355315Seric {
268454355315Seric 	struct smtp_session *s;
268554355315Seric 
268654355315Seric 	s = tx->session;
268754355315Seric 
268854355315Seric 	log_debug("smtp: %p: message fd %d", s, fd);
268954355315Seric 
269022135240Seric 	if ((tx->ofile = fdopen(fd, "w")) == NULL) {
269154355315Seric 		close(fd);
26925fe3cdcbSgilles 		smtp_reply(s, "421 %s Temporary Error",
269354355315Seric 		    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
269454355315Seric 		smtp_enter_state(s, STATE_QUIT);
2695590a6142Sgilles 		return 0;
2696590a6142Sgilles 	}
2697590a6142Sgilles 	return 1;
2698590a6142Sgilles }
2699590a6142Sgilles 
2700590a6142Sgilles static void
2701590a6142Sgilles filter_session_io(struct io *io, int evt, void *arg)
2702590a6142Sgilles {
2703590a6142Sgilles 	struct smtp_tx*tx = arg;
2704590a6142Sgilles 	char*line = NULL;
2705590a6142Sgilles 	ssize_t len;
2706590a6142Sgilles 
2707ec69ed85Sgilles 	log_trace(TRACE_IO, "filter session io (smtp): %p: %s %s", tx, io_strevent(evt),
2708590a6142Sgilles 	    io_strio(io));
2709590a6142Sgilles 
2710590a6142Sgilles 	switch (evt) {
2711590a6142Sgilles 	case IO_DATAIN:
2712590a6142Sgilles 	nextline:
2713590a6142Sgilles 		line = io_getline(tx->filter, &len);
2714590a6142Sgilles 		/* No complete line received */
2715590a6142Sgilles 		if (line == NULL)
2716590a6142Sgilles 			return;
2717590a6142Sgilles 
2718590a6142Sgilles 		if (smtp_tx_dataline(tx, line)) {
2719590a6142Sgilles 			smtp_tx_eom(tx);
272054355315Seric 			return;
272154355315Seric 		}
272254355315Seric 
2723590a6142Sgilles 		goto nextline;
2724590a6142Sgilles 	}
2725590a6142Sgilles }
2726590a6142Sgilles 
2727590a6142Sgilles static void
2728590a6142Sgilles smtp_filter_fd(struct smtp_tx *tx, int fd)
2729590a6142Sgilles {
2730590a6142Sgilles 	struct smtp_session *s;
2731590a6142Sgilles 
2732590a6142Sgilles 	s = tx->session;
2733590a6142Sgilles 
2734590a6142Sgilles 	log_debug("smtp: %p: filter fd %d", s, fd);
2735590a6142Sgilles 
2736590a6142Sgilles 	tx->filter = io_new();
2737590a6142Sgilles 	io_set_fd(tx->filter, fd);
2738590a6142Sgilles 	io_set_callback(tx->filter, filter_session_io, tx);
2739590a6142Sgilles }
2740590a6142Sgilles 
2741590a6142Sgilles static void
2742590a6142Sgilles smtp_message_begin(struct smtp_tx *tx)
2743590a6142Sgilles {
2744590a6142Sgilles 	struct smtp_session *s;
27453e47678eSmillert 	struct smtp_rcpt *rcpt;
2746590a6142Sgilles 	int	(*m_printf)(struct smtp_tx *, const char *, ...);
2747590a6142Sgilles 
2748590a6142Sgilles 	m_printf = smtp_message_printf;
2749590a6142Sgilles 	if (tx->filter)
2750590a6142Sgilles 		m_printf = smtp_filter_printf;
2751590a6142Sgilles 
2752590a6142Sgilles 	s = tx->session;
2753590a6142Sgilles 
2754590a6142Sgilles 	log_debug("smtp: %p: message begin", s);
2755590a6142Sgilles 
2756e7d82dd0Sgilles 	smtp_reply(s, "354 Enter mail, end with \".\""
2757e7d82dd0Sgilles 	    " on a line by itself");
2758e7d82dd0Sgilles 
2759f6b0bf9aSgilles 	if (s->junk || (s->tx && s->tx->junk))
2760f6b0bf9aSgilles 		m_printf(tx, "X-Spam: Yes\n");
2761f6b0bf9aSgilles 
2762590a6142Sgilles 	m_printf(tx, "Received: ");
276354355315Seric 	if (!(s->listener->flags & F_MASK_SOURCE)) {
276491affd4dSgilles 		m_printf(tx, "from %s (%s %s%s%s)",
276554355315Seric 		    s->helo,
2766744e25e4Sgilles 		    s->rdns,
276791affd4dSgilles 		    s->ss.ss_family == AF_INET6 ? "" : "[",
276891affd4dSgilles 		    ss_to_text(&s->ss),
276991affd4dSgilles 		    s->ss.ss_family == AF_INET6 ? "" : "]");
277054355315Seric 	}
2771590a6142Sgilles 	m_printf(tx, "\n\tby %s (%s) with %sSMTP%s%s id %08x",
277254355315Seric 	    s->smtpname,
277354355315Seric 	    SMTPD_NAME,
277454355315Seric 	    s->flags & SF_EHLO ? "E" : "",
277554355315Seric 	    s->flags & SF_SECURE ? "S" : "",
277654355315Seric 	    s->flags & SF_AUTHENTICATED ? "A" : "",
277722135240Seric 	    tx->msgid);
277854355315Seric 
277954355315Seric 	if (s->flags & SF_SECURE) {
2780590a6142Sgilles 		m_printf(tx, " (%s:%s:%d:%s)",
2781eed85469Seric 		    tls_conn_version(io_tls(s->io)),
2782eed85469Seric 		    tls_conn_cipher(io_tls(s->io)),
2783eed85469Seric 		    tls_conn_cipher_strength(io_tls(s->io)),
2784eed85469Seric 		    (s->flags & SF_VERIFIED) ? "YES" : "NO");
278554355315Seric 
278654355315Seric 		if (s->listener->flags & F_RECEIVEDAUTH) {
2787590a6142Sgilles 			m_printf(tx, " auth=%s",
278854355315Seric 			    s->username[0] ? "yes" : "no");
278954355315Seric 			if (s->username[0])
2790590a6142Sgilles 				m_printf(tx, " user=%s", s->username);
279154355315Seric 		}
279254355315Seric 	}
279354355315Seric 
279454355315Seric 	if (tx->rcptcount == 1) {
27953e47678eSmillert 		rcpt = TAILQ_FIRST(&tx->rcpts);
2796590a6142Sgilles 		m_printf(tx, "\n\tfor <%s@%s>",
27973e47678eSmillert 		    rcpt->maddr.user,
27983e47678eSmillert 		    rcpt->maddr.domain);
279954355315Seric 	}
280054355315Seric 
2801590a6142Sgilles 	m_printf(tx, ";\n\t%s\n", time_to_text(time(&tx->time)));
280254355315Seric 
280354355315Seric 	smtp_enter_state(s, STATE_BODY);
280454355315Seric }
280554355315Seric 
280654355315Seric static void
280754355315Seric smtp_message_end(struct smtp_tx *tx)
280854355315Seric {
280954355315Seric 	struct smtp_session *s;
281054355315Seric 
281154355315Seric 	s = tx->session;
281254355315Seric 
281354355315Seric 	log_debug("debug: %p: end of message, error=%d", s, tx->error);
281454355315Seric 
281554355315Seric 	fclose(tx->ofile);
281654355315Seric 	tx->ofile = NULL;
281754355315Seric 
281854355315Seric 	switch(tx->error) {
281954355315Seric 	case TX_OK:
282054355315Seric 		smtp_tx_commit(tx);
282154355315Seric 		return;
282254355315Seric 
282354355315Seric 	case TX_ERROR_SIZE:
282454355315Seric 		smtp_reply(s, "554 %s %s: Transaction failed, message too big",
282554355315Seric 		    esc_code(ESC_STATUS_PERMFAIL, ESC_MESSAGE_TOO_BIG_FOR_SYSTEM),
282654355315Seric 		    esc_description(ESC_MESSAGE_TOO_BIG_FOR_SYSTEM));
282754355315Seric 		break;
282854355315Seric 
282954355315Seric 	case TX_ERROR_LOOP:
283054355315Seric 		smtp_reply(s, "500 %s %s: Loop detected",
283154355315Seric 		   esc_code(ESC_STATUS_PERMFAIL, ESC_ROUTING_LOOP_DETECTED),
283254355315Seric 		   esc_description(ESC_ROUTING_LOOP_DETECTED));
283354355315Seric 		break;
283454355315Seric 
283554355315Seric 	case TX_ERROR_MALFORMED:
283654355315Seric 		smtp_reply(s, "550 %s %s: Message is not RFC 2822 compliant",
283754355315Seric 		    esc_code(ESC_STATUS_PERMFAIL, ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED),
283854355315Seric 		    esc_description(ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED));
283954355315Seric 		break;
284054355315Seric 
284154355315Seric 	case TX_ERROR_IO:
284254355315Seric 	case TX_ERROR_RESOURCES:
28435fe3cdcbSgilles 		smtp_reply(s, "421 %s Temporary Error",
284454355315Seric 		    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
284554355315Seric 		break;
284654355315Seric 
284754355315Seric 	default:
284854355315Seric 		/* fatal? */
284954355315Seric 		smtp_reply(s, "421 Internal server error");
285054355315Seric 	}
285154355315Seric 
285254355315Seric 	smtp_tx_rollback(tx);
285354355315Seric 	smtp_tx_free(tx);
285454355315Seric 	smtp_enter_state(s, STATE_HELO);
285554355315Seric }
285654355315Seric 
285754355315Seric static int
2858590a6142Sgilles smtp_filter_printf(struct smtp_tx *tx, const char *fmt, ...)
2859590a6142Sgilles {
2860590a6142Sgilles 	va_list	ap;
2861590a6142Sgilles 	int	len;
2862590a6142Sgilles 
2863590a6142Sgilles 	if (tx->error)
2864590a6142Sgilles 		return -1;
2865590a6142Sgilles 
2866590a6142Sgilles 	va_start(ap, fmt);
2867590a6142Sgilles 	len = io_vprintf(tx->filter, fmt, ap);
2868590a6142Sgilles 	va_end(ap);
2869590a6142Sgilles 
2870590a6142Sgilles 	if (len < 0) {
2871590a6142Sgilles 		log_warn("smtp-in: session %016"PRIx64": vfprintf", tx->session->id);
2872590a6142Sgilles 		tx->error = TX_ERROR_IO;
2873590a6142Sgilles 	}
2874590a6142Sgilles 	else
2875590a6142Sgilles 		tx->odatalen += len;
2876590a6142Sgilles 
2877590a6142Sgilles 	return len;
2878590a6142Sgilles }
2879590a6142Sgilles 
2880590a6142Sgilles static int
288154355315Seric smtp_message_printf(struct smtp_tx *tx, const char *fmt, ...)
288254355315Seric {
288354355315Seric 	va_list	ap;
288454355315Seric 	int	len;
288554355315Seric 
288654355315Seric 	if (tx->error)
288754355315Seric 		return -1;
288854355315Seric 
288954355315Seric 	va_start(ap, fmt);
289054355315Seric 	len = vfprintf(tx->ofile, fmt, ap);
289154355315Seric 	va_end(ap);
289254355315Seric 
2893df69c215Sderaadt 	if (len == -1) {
289454355315Seric 		log_warn("smtp-in: session %016"PRIx64": vfprintf", tx->session->id);
289554355315Seric 		tx->error = TX_ERROR_IO;
289654355315Seric 	}
289754355315Seric 	else
289854355315Seric 		tx->odatalen += len;
289954355315Seric 
290054355315Seric 	return len;
290154355315Seric }
290254355315Seric 
2903b6d81129Seric #define CASE(x) case x : return #x
2904b6d81129Seric 
2905b6d81129Seric const char *
290665c4fdfbSgilles smtp_strstate(int state)
2907b6d81129Seric {
2908b6d81129Seric 	static char	buf[32];
2909b6d81129Seric 
2910b6d81129Seric 	switch (state) {
291165c4fdfbSgilles 	CASE(STATE_NEW);
291265c4fdfbSgilles 	CASE(STATE_CONNECTED);
291365c4fdfbSgilles 	CASE(STATE_TLS);
291465c4fdfbSgilles 	CASE(STATE_HELO);
291565c4fdfbSgilles 	CASE(STATE_AUTH_INIT);
291665c4fdfbSgilles 	CASE(STATE_AUTH_USERNAME);
291765c4fdfbSgilles 	CASE(STATE_AUTH_PASSWORD);
291865c4fdfbSgilles 	CASE(STATE_AUTH_FINALIZE);
291965c4fdfbSgilles 	CASE(STATE_BODY);
292065c4fdfbSgilles 	CASE(STATE_QUIT);
2921b6d81129Seric 	default:
2922ef75c9feSgilles 		(void)snprintf(buf, sizeof(buf), "STATE_??? (%d)", state);
292365c4fdfbSgilles 		return (buf);
2924b6d81129Seric 	}
2925b6d81129Seric }
29266771f06dSgilles 
29276771f06dSgilles 
29286771f06dSgilles static void
29296771f06dSgilles smtp_report_link_connect(struct smtp_session *s, const char *rdns, int fcrdns,
29306771f06dSgilles     const struct sockaddr_storage *ss_src,
29316771f06dSgilles     const struct sockaddr_storage *ss_dest)
29326771f06dSgilles {
29336771f06dSgilles 	if (! SESSION_FILTERED(s))
29346771f06dSgilles 		return;
29356771f06dSgilles 
29366771f06dSgilles 	report_smtp_link_connect("smtp-in", s->id, rdns, fcrdns, ss_src, ss_dest);
29376771f06dSgilles }
29386771f06dSgilles 
29396771f06dSgilles static void
29406771f06dSgilles smtp_report_link_greeting(struct smtp_session *s,
29416771f06dSgilles     const char *domain)
29426771f06dSgilles {
29436771f06dSgilles 	if (! SESSION_FILTERED(s))
29446771f06dSgilles 		return;
29456771f06dSgilles 
29466771f06dSgilles 	report_smtp_link_greeting("smtp-in", s->id, domain);
29476771f06dSgilles }
29486771f06dSgilles 
29496771f06dSgilles static void
29506771f06dSgilles smtp_report_link_identify(struct smtp_session *s, const char *method, const char *identity)
29516771f06dSgilles {
29526771f06dSgilles 	if (! SESSION_FILTERED(s))
29536771f06dSgilles 		return;
29546771f06dSgilles 
29556771f06dSgilles 	report_smtp_link_identify("smtp-in", s->id, method, identity);
29566771f06dSgilles }
29576771f06dSgilles 
29586771f06dSgilles static void
29596771f06dSgilles smtp_report_link_tls(struct smtp_session *s, const char *ssl)
29606771f06dSgilles {
29616771f06dSgilles 	if (! SESSION_FILTERED(s))
29626771f06dSgilles 		return;
29636771f06dSgilles 
29646771f06dSgilles 	report_smtp_link_tls("smtp-in", s->id, ssl);
29656771f06dSgilles }
29666771f06dSgilles 
29676771f06dSgilles static void
29686771f06dSgilles smtp_report_link_disconnect(struct smtp_session *s)
29696771f06dSgilles {
29706771f06dSgilles 	if (! SESSION_FILTERED(s))
29716771f06dSgilles 		return;
29726771f06dSgilles 
29736771f06dSgilles 	report_smtp_link_disconnect("smtp-in", s->id);
29746771f06dSgilles }
29756771f06dSgilles 
29766771f06dSgilles static void
29776771f06dSgilles smtp_report_link_auth(struct smtp_session *s, const char *user, const char *result)
29786771f06dSgilles {
29796771f06dSgilles 	if (! SESSION_FILTERED(s))
29806771f06dSgilles 		return;
29816771f06dSgilles 
29826771f06dSgilles 	report_smtp_link_auth("smtp-in", s->id, user, result);
29836771f06dSgilles }
29846771f06dSgilles 
29856771f06dSgilles static void
29866771f06dSgilles smtp_report_tx_reset(struct smtp_session *s, uint32_t msgid)
29876771f06dSgilles {
29886771f06dSgilles 	if (! SESSION_FILTERED(s))
29896771f06dSgilles 		return;
29906771f06dSgilles 
29916771f06dSgilles 	report_smtp_tx_reset("smtp-in", s->id, msgid);
29926771f06dSgilles }
29936771f06dSgilles 
29946771f06dSgilles static void
29956771f06dSgilles smtp_report_tx_begin(struct smtp_session *s, uint32_t msgid)
29966771f06dSgilles {
29976771f06dSgilles 	if (! SESSION_FILTERED(s))
29986771f06dSgilles 		return;
29996771f06dSgilles 
30006771f06dSgilles 	report_smtp_tx_begin("smtp-in", s->id, msgid);
30016771f06dSgilles }
30026771f06dSgilles 
30036771f06dSgilles static void
30046771f06dSgilles smtp_report_tx_mail(struct smtp_session *s, uint32_t msgid, const char *address, int ok)
30056771f06dSgilles {
3006a2d3e7c3Sgilles 	char	mailaddr[SMTPD_MAXMAILADDRSIZE];
3007a2d3e7c3Sgilles 	char    *p;
3008a2d3e7c3Sgilles 
30096771f06dSgilles 	if (! SESSION_FILTERED(s))
30106771f06dSgilles 		return;
30116771f06dSgilles 
3012a2d3e7c3Sgilles 	if ((p = strchr(address, '<')) == NULL)
3013a2d3e7c3Sgilles 		return;
3014a2d3e7c3Sgilles 	(void)strlcpy(mailaddr, p + 1, sizeof mailaddr);
3015a2d3e7c3Sgilles 	if ((p = strchr(mailaddr, '>')) == NULL)
3016a2d3e7c3Sgilles 		return;
3017a2d3e7c3Sgilles 	*p = '\0';
3018a2d3e7c3Sgilles 
3019a2d3e7c3Sgilles 	report_smtp_tx_mail("smtp-in", s->id, msgid, mailaddr, ok);
30206771f06dSgilles }
30216771f06dSgilles 
30226771f06dSgilles static void
30236771f06dSgilles smtp_report_tx_rcpt(struct smtp_session *s, uint32_t msgid, const char *address, int ok)
30246771f06dSgilles {
3025a2d3e7c3Sgilles 	char	mailaddr[SMTPD_MAXMAILADDRSIZE];
3026a2d3e7c3Sgilles 	char    *p;
3027a2d3e7c3Sgilles 
30286771f06dSgilles 	if (! SESSION_FILTERED(s))
30296771f06dSgilles 		return;
30306771f06dSgilles 
3031a2d3e7c3Sgilles 	if ((p = strchr(address, '<')) == NULL)
3032a2d3e7c3Sgilles 		return;
3033a2d3e7c3Sgilles 	(void)strlcpy(mailaddr, p + 1, sizeof mailaddr);
3034a2d3e7c3Sgilles 	if ((p = strchr(mailaddr, '>')) == NULL)
3035a2d3e7c3Sgilles 		return;
3036a2d3e7c3Sgilles 	*p = '\0';
3037a2d3e7c3Sgilles 
3038a2d3e7c3Sgilles 	report_smtp_tx_rcpt("smtp-in", s->id, msgid, mailaddr, ok);
30396771f06dSgilles }
30406771f06dSgilles 
30416771f06dSgilles static void
30426771f06dSgilles smtp_report_tx_envelope(struct smtp_session *s, uint32_t msgid, uint64_t evpid)
30436771f06dSgilles {
30446771f06dSgilles 	if (! SESSION_FILTERED(s))
30456771f06dSgilles 		return;
30466771f06dSgilles 
30476771f06dSgilles 	report_smtp_tx_envelope("smtp-in", s->id, msgid, evpid);
30486771f06dSgilles }
30496771f06dSgilles 
30506771f06dSgilles static void
30516771f06dSgilles smtp_report_tx_data(struct smtp_session *s, uint32_t msgid, int ok)
30526771f06dSgilles {
30536771f06dSgilles 	if (! SESSION_FILTERED(s))
30546771f06dSgilles 		return;
30556771f06dSgilles 
30566771f06dSgilles 	report_smtp_tx_data("smtp-in", s->id, msgid, ok);
30576771f06dSgilles }
30586771f06dSgilles 
30596771f06dSgilles static void
30606771f06dSgilles smtp_report_tx_commit(struct smtp_session *s, uint32_t msgid, size_t msgsz)
30616771f06dSgilles {
30626771f06dSgilles 	if (! SESSION_FILTERED(s))
30636771f06dSgilles 		return;
30646771f06dSgilles 
30656771f06dSgilles 	report_smtp_tx_commit("smtp-in", s->id, msgid, msgsz);
30666771f06dSgilles }
30676771f06dSgilles 
30686771f06dSgilles static void
30696771f06dSgilles smtp_report_tx_rollback(struct smtp_session *s, uint32_t msgid)
30706771f06dSgilles {
30716771f06dSgilles 	if (! SESSION_FILTERED(s))
30726771f06dSgilles 		return;
30736771f06dSgilles 
30746771f06dSgilles 	report_smtp_tx_rollback("smtp-in", s->id, msgid);
30756771f06dSgilles }
30766771f06dSgilles 
30776771f06dSgilles static void
30786771f06dSgilles smtp_report_protocol_client(struct smtp_session *s, const char *command)
30796771f06dSgilles {
30806771f06dSgilles 	if (! SESSION_FILTERED(s))
30816771f06dSgilles 		return;
30826771f06dSgilles 
30836771f06dSgilles 	report_smtp_protocol_client("smtp-in", s->id, command);
30846771f06dSgilles }
30856771f06dSgilles 
30866771f06dSgilles static void
30876771f06dSgilles smtp_report_protocol_server(struct smtp_session *s, const char *response)
30886771f06dSgilles {
30896771f06dSgilles 	if (! SESSION_FILTERED(s))
30906771f06dSgilles 		return;
30916771f06dSgilles 
30926771f06dSgilles 	report_smtp_protocol_server("smtp-in", s->id, response);
30936771f06dSgilles }
30946771f06dSgilles 
30956771f06dSgilles static void
30966771f06dSgilles smtp_report_filter_response(struct smtp_session *s, int phase, int response, const char *param)
30976771f06dSgilles {
30986771f06dSgilles 	if (! SESSION_FILTERED(s))
30996771f06dSgilles 		return;
31006771f06dSgilles 
31016771f06dSgilles 	report_smtp_filter_response("smtp-in", s->id, phase, response, param);
31026771f06dSgilles }
31036771f06dSgilles 
31046771f06dSgilles static void
31056771f06dSgilles smtp_report_timeout(struct smtp_session *s)
31066771f06dSgilles {
31076771f06dSgilles 	if (! SESSION_FILTERED(s))
31086771f06dSgilles 		return;
31096771f06dSgilles 
31106771f06dSgilles 	report_smtp_timeout("smtp-in", s->id);
31116771f06dSgilles }
3112