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