1*0dcffd0dSop /* $OpenBSD: bounce.c,v 1.90 2023/05/31 16:51:46 op Exp $ */
2f2a129b6Sgilles
3f2a129b6Sgilles /*
465c4fdfbSgilles * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
5f2a129b6Sgilles * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
6b04e6f27Seric * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
7f2a129b6Sgilles *
8f2a129b6Sgilles * Permission to use, copy, modify, and distribute this software for any
9f2a129b6Sgilles * purpose with or without fee is hereby granted, provided that the above
10f2a129b6Sgilles * copyright notice and this permission notice appear in all copies.
11f2a129b6Sgilles *
12f2a129b6Sgilles * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13f2a129b6Sgilles * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14f2a129b6Sgilles * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15f2a129b6Sgilles * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16f2a129b6Sgilles * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17f2a129b6Sgilles * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18f2a129b6Sgilles * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19f2a129b6Sgilles */
20f2a129b6Sgilles
217aa4c079Seric #include <errno.h>
22c50073caSchl #include <inttypes.h>
23f2a129b6Sgilles #include <stdlib.h>
24f2a129b6Sgilles #include <string.h>
25*0dcffd0dSop #include <time.h>
26f2a129b6Sgilles #include <unistd.h>
27f2a129b6Sgilles
28f2a129b6Sgilles #include "smtpd.h"
295eb8dddaSgilles #include "log.h"
30f2a129b6Sgilles
3165c4fdfbSgilles #define BOUNCE_MAXRUN 2
32b04e6f27Seric #define BOUNCE_HIWAT 65535
33b04e6f27Seric
34b04e6f27Seric enum {
35b04e6f27Seric BOUNCE_EHLO,
36b04e6f27Seric BOUNCE_MAIL,
37b04e6f27Seric BOUNCE_RCPT,
38b04e6f27Seric BOUNCE_DATA,
39b04e6f27Seric BOUNCE_DATA_NOTICE,
40b04e6f27Seric BOUNCE_DATA_MESSAGE,
4165c4fdfbSgilles BOUNCE_DATA_END,
42b04e6f27Seric BOUNCE_QUIT,
43b04e6f27Seric BOUNCE_CLOSE,
44f2a129b6Sgilles };
45f2a129b6Sgilles
4665c4fdfbSgilles struct bounce_envelope {
4765c4fdfbSgilles TAILQ_ENTRY(bounce_envelope) entry;
487aa4c079Seric uint64_t id;
493abffaecSsunil struct mailaddr dest;
5065c4fdfbSgilles char *report;
513abffaecSsunil uint8_t esc_class;
523abffaecSsunil uint8_t esc_code;
5365c4fdfbSgilles };
5465c4fdfbSgilles
5565c4fdfbSgilles struct bounce_message {
5665c4fdfbSgilles SPLAY_ENTRY(bounce_message) sp_entry;
5765c4fdfbSgilles TAILQ_ENTRY(bounce_message) entry;
587aa4c079Seric uint32_t msgid;
5965c4fdfbSgilles struct delivery_bounce bounce;
60cc81b7c6Seric char *smtpname;
6165c4fdfbSgilles char *to;
6265c4fdfbSgilles time_t timeout;
6365c4fdfbSgilles TAILQ_HEAD(, bounce_envelope) envelopes;
6465c4fdfbSgilles };
6565c4fdfbSgilles
6665c4fdfbSgilles struct bounce_session {
67cc81b7c6Seric char *smtpname;
6865c4fdfbSgilles struct bounce_message *msg;
69b04e6f27Seric FILE *msgfp;
70b04e6f27Seric int state;
718d3f7f0dSeric struct io *io;
723abffaecSsunil uint64_t boundary;
73b04e6f27Seric };
74b04e6f27Seric
7565c4fdfbSgilles SPLAY_HEAD(bounce_message_tree, bounce_message);
7665c4fdfbSgilles static int bounce_message_cmp(const struct bounce_message *,
7765c4fdfbSgilles const struct bounce_message *);
7865c4fdfbSgilles SPLAY_PROTOTYPE(bounce_message_tree, bounce_message, sp_entry,
7965c4fdfbSgilles bounce_message_cmp);
8065c4fdfbSgilles
819989e8aeSeric static void bounce_drain(void);
82ceeebefeSbenno static void bounce_send(struct bounce_session *, const char *, ...)
83ceeebefeSbenno __attribute__((__format__ (printf, 2, 3)));
8465c4fdfbSgilles static int bounce_next_message(struct bounce_session *);
8565c4fdfbSgilles static int bounce_next(struct bounce_session *);
8665c4fdfbSgilles static void bounce_delivery(struct bounce_message *, int, const char *);
87ceeebefeSbenno static void bounce_status(struct bounce_session *, const char *, ...)
88ceeebefeSbenno __attribute__((__format__ (printf, 2, 3)));
89b556a8d3Seric static void bounce_io(struct io *, int, void *);
907aa4c079Seric static void bounce_timeout(int, short, void *);
9165c4fdfbSgilles static void bounce_free(struct bounce_session *);
923abffaecSsunil static const char *action_str(const struct delivery_bounce *);
937aa4c079Seric
9465c4fdfbSgilles static struct tree wait_fd;
9565c4fdfbSgilles static struct bounce_message_tree messages;
9665c4fdfbSgilles static TAILQ_HEAD(, bounce_message) pending;
977aa4c079Seric
9865c4fdfbSgilles static int nmessage = 0;
999989e8aeSeric static int running = 0;
10065c4fdfbSgilles static struct event ev_timer;
10165c4fdfbSgilles
10265c4fdfbSgilles static void
bounce_init(void)10365c4fdfbSgilles bounce_init(void)
10465c4fdfbSgilles {
10565c4fdfbSgilles static int init = 0;
10665c4fdfbSgilles
10765c4fdfbSgilles if (init == 0) {
10865c4fdfbSgilles TAILQ_INIT(&pending);
10965c4fdfbSgilles SPLAY_INIT(&messages);
11065c4fdfbSgilles tree_init(&wait_fd);
11165c4fdfbSgilles evtimer_set(&ev_timer, bounce_timeout, NULL);
11265c4fdfbSgilles init = 1;
11365c4fdfbSgilles }
11465c4fdfbSgilles }
1159989e8aeSeric
1167aa4c079Seric void
bounce_add(uint64_t evpid)1177aa4c079Seric bounce_add(uint64_t evpid)
1187aa4c079Seric {
119953aae25Sderaadt char buf[LINE_MAX], *line;
12065c4fdfbSgilles struct envelope evp;
12165c4fdfbSgilles struct bounce_message key, *msg;
12265c4fdfbSgilles struct bounce_envelope *be;
1237aa4c079Seric
12465c4fdfbSgilles bounce_init();
1257aa4c079Seric
12665c4fdfbSgilles if (queue_envelope_load(evpid, &evp) == 0) {
127aa1d5973Seric m_create(p_scheduler, IMSG_QUEUE_DELIVERY_PERMFAIL, 0, 0, -1);
12865c4fdfbSgilles m_add_evpid(p_scheduler, evpid);
12965c4fdfbSgilles m_close(p_scheduler);
1307aa4c079Seric return;
1317aa4c079Seric }
1327aa4c079Seric
13365c4fdfbSgilles if (evp.type != D_BOUNCE)
134ff01b044Seric fatalx("bounce: evp:%016" PRIx64 " is not of type D_BOUNCE!",
13565c4fdfbSgilles evp.id);
1367aa4c079Seric
13765c4fdfbSgilles key.msgid = evpid_to_msgid(evpid);
13865c4fdfbSgilles key.bounce = evp.agent.bounce;
139cc81b7c6Seric key.smtpname = evp.smtpname;
1403abffaecSsunil
1413aaead3cSsunil switch (evp.esc_class) {
1423aaead3cSsunil case ESC_STATUS_OK:
1435ed42fc8Ssunil key.bounce.type = B_DELIVERED;
1443aaead3cSsunil break;
1453aaead3cSsunil case ESC_STATUS_TEMPFAIL:
1465ed42fc8Ssunil key.bounce.type = B_DELAYED;
1473aaead3cSsunil break;
1483aaead3cSsunil default:
1495ed42fc8Ssunil key.bounce.type = B_FAILED;
1503aaead3cSsunil }
1513abffaecSsunil
1523abffaecSsunil key.bounce.dsn_ret = evp.dsn_ret;
153a8e22235Sgilles key.bounce.ttl = evp.ttl;
15465c4fdfbSgilles msg = SPLAY_FIND(bounce_message_tree, &messages, &key);
15565c4fdfbSgilles if (msg == NULL) {
156118c16f3Sgilles msg = xcalloc(1, sizeof(*msg));
15765c4fdfbSgilles msg->msgid = key.msgid;
15865c4fdfbSgilles msg->bounce = key.bounce;
1597aa4c079Seric
16065c4fdfbSgilles TAILQ_INIT(&msg->envelopes);
1617aa4c079Seric
162118c16f3Sgilles msg->smtpname = xstrdup(evp.smtpname);
1632fa40787Sgilles (void)snprintf(buf, sizeof(buf), "%s@%s", evp.sender.user,
16465c4fdfbSgilles evp.sender.domain);
165118c16f3Sgilles msg->to = xstrdup(buf);
16665c4fdfbSgilles nmessage += 1;
167cc81b7c6Seric SPLAY_INSERT(bounce_message_tree, &messages, msg);
16865c4fdfbSgilles log_debug("debug: bounce: new message %08" PRIx32,
16965c4fdfbSgilles msg->msgid);
17065c4fdfbSgilles stat_increment("bounce.message", 1);
17165c4fdfbSgilles } else
17265c4fdfbSgilles TAILQ_REMOVE(&pending, msg, entry);
1737aa4c079Seric
17465c4fdfbSgilles line = evp.errorline;
17565c4fdfbSgilles if (strlen(line) > 4 && (*line == '1' || *line == '6'))
17665c4fdfbSgilles line += 4;
1775972552dSeric (void)snprintf(buf, sizeof(buf), "%s@%s: %s", evp.dest.user,
17865c4fdfbSgilles evp.dest.domain, line);
1797aa4c079Seric
180118c16f3Sgilles be = xmalloc(sizeof *be);
18165c4fdfbSgilles be->id = evpid;
182118c16f3Sgilles be->report = xstrdup(buf);
1833abffaecSsunil (void)strlcpy(be->dest.user, evp.dest.user, sizeof(be->dest.user));
1843abffaecSsunil (void)strlcpy(be->dest.domain, evp.dest.domain,
1853abffaecSsunil sizeof(be->dest.domain));
1863abffaecSsunil be->esc_class = evp.esc_class;
1873abffaecSsunil be->esc_code = evp.esc_code;
18865c4fdfbSgilles TAILQ_INSERT_TAIL(&msg->envelopes, be, entry);
1895972552dSeric log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, be->report);
1907aa4c079Seric
19165c4fdfbSgilles msg->timeout = time(NULL) + 1;
19265c4fdfbSgilles TAILQ_INSERT_TAIL(&pending, msg, entry);
1939989e8aeSeric
19465c4fdfbSgilles stat_increment("bounce.envelope", 1);
19565c4fdfbSgilles bounce_drain();
1967aa4c079Seric }
1977aa4c079Seric
1987aa4c079Seric void
bounce_fd(int fd)19965c4fdfbSgilles bounce_fd(int fd)
2007aa4c079Seric {
20165c4fdfbSgilles struct bounce_session *s;
202cc81b7c6Seric struct bounce_message *msg;
2037aa4c079Seric
204d7bcae4dSeric log_debug("debug: bounce: got enqueue socket %d", fd);
2057aa4c079Seric
206cc81b7c6Seric if (fd == -1 || TAILQ_EMPTY(&pending)) {
207cc81b7c6Seric log_debug("debug: bounce: cancelling");
208cc81b7c6Seric if (fd != -1)
209cc81b7c6Seric close(fd);
21065c4fdfbSgilles running -= 1;
2119989e8aeSeric bounce_drain();
21265c4fdfbSgilles return;
21365c4fdfbSgilles }
21465c4fdfbSgilles
215cc81b7c6Seric msg = TAILQ_FIRST(&pending);
216cc81b7c6Seric
217118c16f3Sgilles s = xcalloc(1, sizeof(*s));
218118c16f3Sgilles s->smtpname = xstrdup(msg->smtpname);
21965c4fdfbSgilles s->state = BOUNCE_EHLO;
2208d3f7f0dSeric s->io = io_new();
2218d3f7f0dSeric io_set_callback(s->io, bounce_io, s);
2228d3f7f0dSeric io_set_fd(s->io, fd);
2238d3f7f0dSeric io_set_timeout(s->io, 30000);
2248d3f7f0dSeric io_set_read(s->io);
2253abffaecSsunil s->boundary = generate_uid();
22665c4fdfbSgilles
22765c4fdfbSgilles log_debug("debug: bounce: new session %p", s);
22865c4fdfbSgilles stat_increment("bounce.session", 1);
2297aa4c079Seric }
2307aa4c079Seric
2317aa4c079Seric static void
bounce_timeout(int fd,short ev,void * arg)2327aa4c079Seric bounce_timeout(int fd, short ev, void *arg)
2337aa4c079Seric {
23465c4fdfbSgilles log_debug("debug: bounce: timeout");
2357aa4c079Seric
23665c4fdfbSgilles bounce_drain();
2377aa4c079Seric }
238b04e6f27Seric
239b04e6f27Seric static void
bounce_drain(void)24004fee684Stb bounce_drain(void)
2419989e8aeSeric {
24265c4fdfbSgilles struct bounce_message *msg;
24365c4fdfbSgilles struct timeval tv;
24465c4fdfbSgilles time_t t;
2459989e8aeSeric
246d7bcae4dSeric log_debug("debug: bounce: drain: nmessage=%d running=%d",
24765c4fdfbSgilles nmessage, running);
2489989e8aeSeric
24965c4fdfbSgilles while (1) {
2509989e8aeSeric if (running >= BOUNCE_MAXRUN) {
25182614934Seric log_debug("debug: bounce: max session reached");
2529989e8aeSeric return;
2539989e8aeSeric }
2549989e8aeSeric
25565c4fdfbSgilles if (nmessage == 0) {
25665c4fdfbSgilles log_debug("debug: bounce: no more messages");
25765c4fdfbSgilles return;
2589989e8aeSeric }
2599989e8aeSeric
26065c4fdfbSgilles if (running >= nmessage) {
26165c4fdfbSgilles log_debug("debug: bounce: enough sessions running");
26265c4fdfbSgilles return;
26365c4fdfbSgilles }
2649989e8aeSeric
26565c4fdfbSgilles if ((msg = TAILQ_FIRST(&pending)) == NULL) {
26665c4fdfbSgilles log_debug("debug: bounce: no more pending messages");
26765c4fdfbSgilles return;
26865c4fdfbSgilles }
2699989e8aeSeric
27065c4fdfbSgilles t = time(NULL);
27165c4fdfbSgilles if (msg->timeout > t) {
27265c4fdfbSgilles log_debug("debug: bounce: next message not ready yet");
27365c4fdfbSgilles if (!evtimer_pending(&ev_timer, NULL)) {
27465c4fdfbSgilles log_debug("debug: bounce: setting timer");
27565c4fdfbSgilles tv.tv_sec = msg->timeout - t;
27665c4fdfbSgilles tv.tv_usec = 0;
27765c4fdfbSgilles evtimer_add(&ev_timer, &tv);
27865c4fdfbSgilles }
27965c4fdfbSgilles return;
28065c4fdfbSgilles }
28165c4fdfbSgilles
28265c4fdfbSgilles log_debug("debug: bounce: requesting new enqueue socket...");
2831a5b831aSmartijn m_compose(p_dispatcher, IMSG_QUEUE_SMTP_SESSION, 0, 0, -1, NULL, 0);
2849989e8aeSeric
2859989e8aeSeric running += 1;
2869989e8aeSeric }
2879989e8aeSeric }
2889989e8aeSeric
2899989e8aeSeric static void
bounce_send(struct bounce_session * s,const char * fmt,...)29065c4fdfbSgilles bounce_send(struct bounce_session *s, const char *fmt, ...)
291f2a129b6Sgilles {
292b04e6f27Seric va_list ap;
293b04e6f27Seric char *p;
294b04e6f27Seric int len;
2958ea04193Sgilles
296b04e6f27Seric va_start(ap, fmt);
297b04e6f27Seric if ((len = vasprintf(&p, fmt, ap)) == -1)
298b04e6f27Seric fatal("bounce: vasprintf");
299b04e6f27Seric va_end(ap);
300f2a129b6Sgilles
30165c4fdfbSgilles log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", s, p);
302f2a129b6Sgilles
3035972552dSeric io_xprintf(s->io, "%s\r\n", p);
304f2a129b6Sgilles
305b04e6f27Seric free(p);
306b04e6f27Seric }
307f2a129b6Sgilles
30865c4fdfbSgilles static const char *
bounce_duration(long long d)309e7954c7dSop bounce_duration(long long d)
3102fa40787Sgilles {
31165c4fdfbSgilles static char buf[32];
312b04e6f27Seric
31365c4fdfbSgilles if (d < 60) {
3143abffaecSsunil (void)snprintf(buf, sizeof buf, "%lld second%s", d,
3153abffaecSsunil (d == 1) ? "" : "s");
31665c4fdfbSgilles } else if (d < 3600) {
31765c4fdfbSgilles d = d / 60;
3183abffaecSsunil (void)snprintf(buf, sizeof buf, "%lld minute%s", d,
3193abffaecSsunil (d == 1) ? "" : "s");
32065c4fdfbSgilles }
32165c4fdfbSgilles else if (d < 3600 * 24) {
32265c4fdfbSgilles d = d / 3600;
3233abffaecSsunil (void)snprintf(buf, sizeof buf, "%lld hour%s", d,
3243abffaecSsunil (d == 1) ? "" : "s");
32565c4fdfbSgilles }
32665c4fdfbSgilles else {
32765c4fdfbSgilles d = d / (3600 * 24);
3283abffaecSsunil (void)snprintf(buf, sizeof buf, "%lld day%s", d,
3293abffaecSsunil (d == 1) ? "" : "s");
33065c4fdfbSgilles }
33165c4fdfbSgilles return (buf);
3321f4ada3fSdaniel }
33365c4fdfbSgilles
33465c4fdfbSgilles #define NOTICE_INTRO \
3355972552dSeric " Hi!\r\n\r\n" \
3365972552dSeric " This is the MAILER-DAEMON, please DO NOT REPLY to this email.\r\n"
33765c4fdfbSgilles
33865c4fdfbSgilles const char *notice_error =
3395972552dSeric " An error has occurred while attempting to deliver a message for\r\n"
3405972552dSeric " the following list of recipients:\r\n\r\n";
34165c4fdfbSgilles
34265c4fdfbSgilles const char *notice_warning =
3435972552dSeric " A message is delayed for more than %s for the following\r\n"
3445972552dSeric " list of recipients:\r\n\r\n";
34565c4fdfbSgilles
34665c4fdfbSgilles const char *notice_warning2 =
3475972552dSeric " Please note that this is only a temporary failure report.\r\n"
3485972552dSeric " The message is kept in the queue for up to %s.\r\n"
3495972552dSeric " You DO NOT NEED to re-send the message to these recipients.\r\n\r\n";
35065c4fdfbSgilles
351fe95d8d0Seric const char *notice_success =
3525972552dSeric " Your message was successfully delivered to these recipients.\r\n\r\n";
353fe95d8d0Seric
354fe95d8d0Seric const char *notice_relay =
3555972552dSeric " Your message was relayed to these recipients.\r\n\r\n";
356fe95d8d0Seric
35765c4fdfbSgilles static int
bounce_next_message(struct bounce_session * s)35865c4fdfbSgilles bounce_next_message(struct bounce_session *s)
35965c4fdfbSgilles {
36065c4fdfbSgilles struct bounce_message *msg;
361953aae25Sderaadt char buf[LINE_MAX];
36265c4fdfbSgilles int fd;
363cc81b7c6Seric time_t now;
36465c4fdfbSgilles
36565c4fdfbSgilles again:
36665c4fdfbSgilles
367cc81b7c6Seric now = time(NULL);
368cc81b7c6Seric
369cc81b7c6Seric TAILQ_FOREACH(msg, &pending, entry) {
370cc81b7c6Seric if (msg->timeout > now)
371cc81b7c6Seric continue;
372cc81b7c6Seric if (strcmp(msg->smtpname, s->smtpname))
373cc81b7c6Seric continue;
374cc81b7c6Seric break;
375cc81b7c6Seric }
376cc81b7c6Seric if (msg == NULL)
37765c4fdfbSgilles return (0);
37865c4fdfbSgilles
37965c4fdfbSgilles TAILQ_REMOVE(&pending, msg, entry);
38065c4fdfbSgilles SPLAY_REMOVE(bounce_message_tree, &messages, msg);
38165c4fdfbSgilles
38265c4fdfbSgilles if ((fd = queue_message_fd_r(msg->msgid)) == -1) {
383aa1d5973Seric bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL,
38465c4fdfbSgilles "Could not open message fd");
38565c4fdfbSgilles goto again;
38665c4fdfbSgilles }
38765c4fdfbSgilles
38865c4fdfbSgilles if ((s->msgfp = fdopen(fd, "r")) == NULL) {
3892fa40787Sgilles (void)snprintf(buf, sizeof(buf), "fdopen: %s", strerror(errno));
39065c4fdfbSgilles log_warn("warn: bounce: fdopen");
39165c4fdfbSgilles close(fd);
392aa1d5973Seric bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, buf);
39365c4fdfbSgilles goto again;
39465c4fdfbSgilles }
39565c4fdfbSgilles
39665c4fdfbSgilles s->msg = msg;
39765c4fdfbSgilles return (1);
39865c4fdfbSgilles }
39965c4fdfbSgilles
40065c4fdfbSgilles static int
bounce_next(struct bounce_session * s)40165c4fdfbSgilles bounce_next(struct bounce_session *s)
40265c4fdfbSgilles {
40365c4fdfbSgilles struct bounce_envelope *evp;
4043e88df76Smillert char *line = NULL;
4053e88df76Smillert size_t n, sz = 0;
4063e88df76Smillert ssize_t len;
40765c4fdfbSgilles
40865c4fdfbSgilles switch (s->state) {
409b04e6f27Seric case BOUNCE_EHLO:
410cc81b7c6Seric bounce_send(s, "EHLO %s", s->smtpname);
41165c4fdfbSgilles s->state = BOUNCE_MAIL;
412b04e6f27Seric break;
413b04e6f27Seric
414b04e6f27Seric case BOUNCE_MAIL:
41565c4fdfbSgilles case BOUNCE_DATA_END:
41665c4fdfbSgilles log_debug("debug: bounce: %p: getting next message...", s);
41765c4fdfbSgilles if (bounce_next_message(s) == 0) {
41865c4fdfbSgilles log_debug("debug: bounce: %p: no more messages", s);
41965c4fdfbSgilles bounce_send(s, "QUIT");
42065c4fdfbSgilles s->state = BOUNCE_CLOSE;
42165c4fdfbSgilles break;
42265c4fdfbSgilles }
42365c4fdfbSgilles log_debug("debug: bounce: %p: found message %08"PRIx32,
42465c4fdfbSgilles s, s->msg->msgid);
42565c4fdfbSgilles bounce_send(s, "MAIL FROM: <>");
42665c4fdfbSgilles s->state = BOUNCE_RCPT;
427b04e6f27Seric break;
428b04e6f27Seric
429b04e6f27Seric case BOUNCE_RCPT:
43065c4fdfbSgilles bounce_send(s, "RCPT TO: <%s>", s->msg->to);
43165c4fdfbSgilles s->state = BOUNCE_DATA;
432b04e6f27Seric break;
433b04e6f27Seric
434b04e6f27Seric case BOUNCE_DATA:
43565c4fdfbSgilles bounce_send(s, "DATA");
43665c4fdfbSgilles s->state = BOUNCE_DATA_NOTICE;
437b04e6f27Seric break;
438b04e6f27Seric
439b04e6f27Seric case BOUNCE_DATA_NOTICE:
44065c4fdfbSgilles /* Construct an appropriate notice. */
4417aa4c079Seric
4428d3f7f0dSeric io_xprintf(s->io,
4435972552dSeric "Subject: Delivery status notification: %s\r\n"
4445972552dSeric "From: Mailer Daemon <MAILER-DAEMON@%s>\r\n"
4455972552dSeric "To: %s\r\n"
4465972552dSeric "Date: %s\r\n"
4475972552dSeric "MIME-Version: 1.0\r\n"
4483abffaecSsunil "Content-Type: multipart/mixed;"
4495972552dSeric "boundary=\"%16" PRIu64 "/%s\"\r\n"
4505972552dSeric "\r\n"
4515972552dSeric "This is a MIME-encapsulated message.\r\n"
4525972552dSeric "\r\n",
4533abffaecSsunil action_str(&s->msg->bounce),
4543abffaecSsunil s->smtpname,
4553abffaecSsunil s->msg->to,
4563abffaecSsunil time_to_text(time(NULL)),
4573abffaecSsunil s->boundary,
4583abffaecSsunil s->smtpname);
4593abffaecSsunil
4608d3f7f0dSeric io_xprintf(s->io,
4615972552dSeric "--%16" PRIu64 "/%s\r\n"
4625972552dSeric "Content-Description: Notification\r\n"
4635972552dSeric "Content-Type: text/plain; charset=us-ascii\r\n"
4645972552dSeric "\r\n"
46565c4fdfbSgilles NOTICE_INTRO
4665972552dSeric "\r\n",
4673abffaecSsunil s->boundary, s->smtpname);
4687aa4c079Seric
469fe95d8d0Seric switch (s->msg->bounce.type) {
4705ed42fc8Ssunil case B_FAILED:
4718d3f7f0dSeric io_xprint(s->io, notice_error);
472fe95d8d0Seric break;
4735ed42fc8Ssunil case B_DELAYED:
4748d3f7f0dSeric io_xprintf(s->io, notice_warning,
47565c4fdfbSgilles bounce_duration(s->msg->bounce.delay));
476fe95d8d0Seric break;
4775ed42fc8Ssunil case B_DELIVERED:
4788d3f7f0dSeric io_xprint(s->io, s->msg->bounce.mta_without_dsn ?
479fe95d8d0Seric notice_relay : notice_success);
480fe95d8d0Seric break;
481fe95d8d0Seric default:
482fe95d8d0Seric log_warn("warn: bounce: unknown bounce_type");
483fe95d8d0Seric }
4847aa4c079Seric
48565c4fdfbSgilles TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
4868d3f7f0dSeric io_xprint(s->io, evp->report);
4875972552dSeric io_xprint(s->io, "\r\n");
48865c4fdfbSgilles }
4895972552dSeric io_xprint(s->io, "\r\n");
49065c4fdfbSgilles
4915ed42fc8Ssunil if (s->msg->bounce.type == B_DELAYED)
4928d3f7f0dSeric io_xprintf(s->io, notice_warning2,
493a8e22235Sgilles bounce_duration(s->msg->bounce.ttl));
49465c4fdfbSgilles
4958d3f7f0dSeric io_xprintf(s->io,
4965972552dSeric " Below is a copy of the original message:\r\n"
4975972552dSeric "\r\n");
498f2a129b6Sgilles
4998d3f7f0dSeric io_xprintf(s->io,
5005972552dSeric "--%16" PRIu64 "/%s\r\n"
5015972552dSeric "Content-Description: Delivery Report\r\n"
5025972552dSeric "Content-Type: message/delivery-status\r\n"
5035972552dSeric "\r\n",
5043abffaecSsunil s->boundary, s->smtpname);
5053abffaecSsunil
5068d3f7f0dSeric io_xprintf(s->io,
5075972552dSeric "Reporting-MTA: dns; %s\r\n"
5085972552dSeric "\r\n",
5093abffaecSsunil s->smtpname);
5103abffaecSsunil
5113abffaecSsunil TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
5128d3f7f0dSeric io_xprintf(s->io,
5135972552dSeric "Final-Recipient: rfc822; %s@%s\r\n"
5145972552dSeric "Action: %s\r\n"
5155972552dSeric "Status: %s\r\n"
5165972552dSeric "\r\n",
5173abffaecSsunil evp->dest.user,
5183abffaecSsunil evp->dest.domain,
5193abffaecSsunil action_str(&s->msg->bounce),
5203abffaecSsunil esc_code(evp->esc_class, evp->esc_code));
5213abffaecSsunil }
5223abffaecSsunil
523b04e6f27Seric log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
5248d3f7f0dSeric s, io_queued(s->io));
525f2a129b6Sgilles
52665c4fdfbSgilles s->state = BOUNCE_DATA_MESSAGE;
527b04e6f27Seric break;
528b04e6f27Seric
529b04e6f27Seric case BOUNCE_DATA_MESSAGE:
5308d3f7f0dSeric io_xprintf(s->io,
5315972552dSeric "--%16" PRIu64 "/%s\r\n"
5325972552dSeric "Content-Description: Message headers\r\n"
5335972552dSeric "Content-Type: text/rfc822-headers\r\n"
5345972552dSeric "\r\n",
5353abffaecSsunil s->boundary, s->smtpname);
536b04e6f27Seric
5378d3f7f0dSeric n = io_queued(s->io);
5388d3f7f0dSeric while (io_queued(s->io) < BOUNCE_HIWAT) {
5393e88df76Smillert if ((len = getline(&line, &sz, s->msgfp)) == -1)
540b04e6f27Seric break;
541fe95d8d0Seric if (len == 1 && line[0] == '\n' && /* end of headers */
54230221c9eSchrisz (s->msg->bounce.type != B_FAILED ||
54330221c9eSchrisz s->msg->bounce.dsn_ret != DSN_RETFULL)) {
5443e88df76Smillert free(line);
545fe95d8d0Seric fclose(s->msgfp);
546fe95d8d0Seric s->msgfp = NULL;
5478d3f7f0dSeric io_xprintf(s->io,
5485972552dSeric "\r\n--%16" PRIu64 "/%s--\r\n", s->boundary,
5493abffaecSsunil s->smtpname);
550fe95d8d0Seric bounce_send(s, ".");
551fe95d8d0Seric s->state = BOUNCE_DATA_END;
552fe95d8d0Seric return (0);
553fe95d8d0Seric }
554b04e6f27Seric line[len - 1] = '\0';
5555972552dSeric io_xprintf(s->io, "%s%s\r\n",
55693f98431Schl (len == 2 && line[0] == '.') ? "." : "", line);
557f2a129b6Sgilles }
5583e88df76Smillert free(line);
559f2a129b6Sgilles
56065c4fdfbSgilles if (ferror(s->msgfp)) {
56165c4fdfbSgilles fclose(s->msgfp);
56265c4fdfbSgilles s->msgfp = NULL;
563aa1d5973Seric bounce_delivery(s->msg, IMSG_QUEUE_DELIVERY_TEMPFAIL,
56465c4fdfbSgilles "Error reading message");
56565c4fdfbSgilles s->msg = NULL;
566b04e6f27Seric return (-1);
567f2a129b6Sgilles }
568f2a129b6Sgilles
5698d3f7f0dSeric io_xprintf(s->io,
5705972552dSeric "\r\n--%16" PRIu64 "/%s--\r\n", s->boundary, s->smtpname);
5713abffaecSsunil
572b04e6f27Seric log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
5738d3f7f0dSeric s, io_queued(s->io) - n);
574b04e6f27Seric
57565c4fdfbSgilles if (feof(s->msgfp)) {
57665c4fdfbSgilles fclose(s->msgfp);
57765c4fdfbSgilles s->msgfp = NULL;
57865c4fdfbSgilles bounce_send(s, ".");
57965c4fdfbSgilles s->state = BOUNCE_DATA_END;
580b04e6f27Seric }
581f2a129b6Sgilles break;
582b04e6f27Seric
583b04e6f27Seric case BOUNCE_QUIT:
58465c4fdfbSgilles bounce_send(s, "QUIT");
58565c4fdfbSgilles s->state = BOUNCE_CLOSE;
586f2a129b6Sgilles break;
587b04e6f27Seric
588f2a129b6Sgilles default:
589b04e6f27Seric fatalx("bounce: bad state");
590f2a129b6Sgilles }
591f2a129b6Sgilles
592b04e6f27Seric return (0);
593f2a129b6Sgilles }
594f2a129b6Sgilles
59565c4fdfbSgilles
596b04e6f27Seric static void
bounce_delivery(struct bounce_message * msg,int delivery,const char * status)59765c4fdfbSgilles bounce_delivery(struct bounce_message *msg, int delivery, const char *status)
59865c4fdfbSgilles {
59965c4fdfbSgilles struct bounce_envelope *be;
60065c4fdfbSgilles struct envelope evp;
60165c4fdfbSgilles size_t n;
602e538e45eSeric const char *f;
60365c4fdfbSgilles
60465c4fdfbSgilles n = 0;
60565c4fdfbSgilles while ((be = TAILQ_FIRST(&msg->envelopes))) {
606aa1d5973Seric if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL) {
60765c4fdfbSgilles if (queue_envelope_load(be->id, &evp) == 0) {
60865c4fdfbSgilles fatalx("could not reload envelope!");
60965c4fdfbSgilles }
61065c4fdfbSgilles evp.retry++;
61165c4fdfbSgilles evp.lasttry = msg->timeout;
61265c4fdfbSgilles envelope_set_errormsg(&evp, "%s", status);
61365c4fdfbSgilles queue_envelope_update(&evp);
614299c4efeSeric m_create(p_scheduler, delivery, 0, 0, -1);
61565c4fdfbSgilles m_add_envelope(p_scheduler, &evp);
61665c4fdfbSgilles m_close(p_scheduler);
61765c4fdfbSgilles } else {
618299c4efeSeric m_create(p_scheduler, delivery, 0, 0, -1);
61965c4fdfbSgilles m_add_evpid(p_scheduler, be->id);
62065c4fdfbSgilles m_close(p_scheduler);
62165c4fdfbSgilles queue_envelope_delete(be->id);
62265c4fdfbSgilles }
62365c4fdfbSgilles TAILQ_REMOVE(&msg->envelopes, be, entry);
624cc81b7c6Seric free(be->report);
62565c4fdfbSgilles free(be);
62665c4fdfbSgilles n += 1;
62765c4fdfbSgilles }
62865c4fdfbSgilles
629e538e45eSeric
630aa1d5973Seric if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL)
631e538e45eSeric f = "TempFail";
632aa1d5973Seric else if (delivery == IMSG_QUEUE_DELIVERY_PERMFAIL)
633e538e45eSeric f = "PermFail";
634e538e45eSeric else
635e538e45eSeric f = NULL;
636e538e45eSeric
637e538e45eSeric if (f)
6383abffaecSsunil log_warnx("warn: %s injecting failure report on message %08"
6393abffaecSsunil PRIx32 " to <%s> for %zu envelope%s: %s",
640e538e45eSeric f, msg->msgid, msg->to, n, n > 1 ? "s":"", status);
641e538e45eSeric
64265c4fdfbSgilles nmessage -= 1;
64365c4fdfbSgilles stat_decrement("bounce.message", 1);
64465c4fdfbSgilles stat_decrement("bounce.envelope", n);
645cc81b7c6Seric free(msg->smtpname);
646cc81b7c6Seric free(msg->to);
64765c4fdfbSgilles free(msg);
64865c4fdfbSgilles }
64965c4fdfbSgilles
65065c4fdfbSgilles static void
bounce_status(struct bounce_session * s,const char * fmt,...)65165c4fdfbSgilles bounce_status(struct bounce_session *s, const char *fmt, ...)
652b04e6f27Seric {
653b04e6f27Seric va_list ap;
654b04e6f27Seric char *status;
65565c4fdfbSgilles int len, delivery;
656b04e6f27Seric
65765c4fdfbSgilles /* Ignore if there is no message */
65865c4fdfbSgilles if (s->msg == NULL)
659b04e6f27Seric return;
660b04e6f27Seric
661b04e6f27Seric va_start(ap, fmt);
662b04e6f27Seric if ((len = vasprintf(&status, fmt, ap)) == -1)
663b04e6f27Seric fatal("bounce: vasprintf");
664b04e6f27Seric va_end(ap);
665b04e6f27Seric
666913395bcSeric if (*status == '2')
667aa1d5973Seric delivery = IMSG_QUEUE_DELIVERY_OK;
668913395bcSeric else if (*status == '5' || *status == '6')
669aa1d5973Seric delivery = IMSG_QUEUE_DELIVERY_PERMFAIL;
670913395bcSeric else
671aa1d5973Seric delivery = IMSG_QUEUE_DELIVERY_TEMPFAIL;
672913395bcSeric
67365c4fdfbSgilles bounce_delivery(s->msg, delivery, status);
67465c4fdfbSgilles s->msg = NULL;
67565c4fdfbSgilles if (s->msgfp)
67665c4fdfbSgilles fclose(s->msgfp);
677913395bcSeric
678b04e6f27Seric free(status);
679b04e6f27Seric }
680b04e6f27Seric
681b04e6f27Seric static void
bounce_free(struct bounce_session * s)68265c4fdfbSgilles bounce_free(struct bounce_session *s)
683b04e6f27Seric {
68465c4fdfbSgilles log_debug("debug: bounce: %p: deleting session", s);
6857aa4c079Seric
6868d3f7f0dSeric io_free(s->io);
687cc81b7c6Seric
688cc81b7c6Seric free(s->smtpname);
68965c4fdfbSgilles free(s);
6909989e8aeSeric
6919989e8aeSeric running -= 1;
69265c4fdfbSgilles stat_decrement("bounce.session", 1);
6939989e8aeSeric bounce_drain();
694b04e6f27Seric }
695f2a129b6Sgilles
696b04e6f27Seric static void
bounce_io(struct io * io,int evt,void * arg)697b556a8d3Seric bounce_io(struct io *io, int evt, void *arg)
698b04e6f27Seric {
699b556a8d3Seric struct bounce_session *s = arg;
700b04e6f27Seric const char *error;
701b04e6f27Seric char *line, *msg;
702b04e6f27Seric int cont;
703b04e6f27Seric size_t len;
704f2a129b6Sgilles
70565c4fdfbSgilles log_trace(TRACE_IO, "bounce: %p: %s %s", s, io_strevent(evt),
70665c4fdfbSgilles io_strio(io));
707b04e6f27Seric
708b04e6f27Seric switch (evt) {
709b04e6f27Seric case IO_DATAIN:
710b04e6f27Seric nextline:
7118d3f7f0dSeric line = io_getline(s->io, &len);
7128d3f7f0dSeric if (line == NULL && io_datalen(s->io) >= LINE_MAX) {
71365c4fdfbSgilles bounce_status(s, "Input too long");
71465c4fdfbSgilles bounce_free(s);
715b04e6f27Seric return;
716b04e6f27Seric }
71765c4fdfbSgilles
7180e198904Seric if (line == NULL)
719b04e6f27Seric break;
720b04e6f27Seric
7210124f38dSeric /* Strip trailing '\r' */
7220124f38dSeric if (len && line[len - 1] == '\r')
7230124f38dSeric line[--len] = '\0';
7240124f38dSeric
72565c4fdfbSgilles log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", s, line);
726b04e6f27Seric
727b04e6f27Seric if ((error = parse_smtp_response(line, len, &msg, &cont))) {
72865c4fdfbSgilles bounce_status(s, "Bad response: %s", error);
72965c4fdfbSgilles bounce_free(s);
730b04e6f27Seric return;
731b04e6f27Seric }
732b04e6f27Seric if (cont)
733b04e6f27Seric goto nextline;
734b04e6f27Seric
73565c4fdfbSgilles if (s->state == BOUNCE_CLOSE) {
73665c4fdfbSgilles bounce_free(s);
737b04e6f27Seric return;
738b04e6f27Seric }
739b04e6f27Seric
740b04e6f27Seric if (line[0] != '2' && line[0] != '3') { /* fail */
74165c4fdfbSgilles bounce_status(s, "%s", line);
74265c4fdfbSgilles s->state = BOUNCE_QUIT;
74365c4fdfbSgilles } else if (s->state == BOUNCE_DATA_END) { /* accepted */
74465c4fdfbSgilles bounce_status(s, "%s", line);
745b04e6f27Seric }
746b04e6f27Seric
74765c4fdfbSgilles if (bounce_next(s) == -1) {
74865c4fdfbSgilles bounce_free(s);
749b04e6f27Seric return;
750b04e6f27Seric }
751b04e6f27Seric
752b04e6f27Seric io_set_write(io);
753b04e6f27Seric break;
754b04e6f27Seric
755b04e6f27Seric case IO_LOWAT:
75665c4fdfbSgilles if (s->state == BOUNCE_DATA_MESSAGE)
75765c4fdfbSgilles if (bounce_next(s) == -1) {
75865c4fdfbSgilles bounce_free(s);
75965c4fdfbSgilles return;
76065c4fdfbSgilles }
7618d3f7f0dSeric if (io_queued(s->io) == 0)
762b04e6f27Seric io_set_read(io);
763b04e6f27Seric break;
764b04e6f27Seric
765b04e6f27Seric default:
766d7bcae4dSeric bounce_status(s, "442 i/o error %d", evt);
76765c4fdfbSgilles bounce_free(s);
768b04e6f27Seric break;
769b04e6f27Seric }
770b04e6f27Seric }
77165c4fdfbSgilles
77265c4fdfbSgilles static int
bounce_message_cmp(const struct bounce_message * a,const struct bounce_message * b)77365c4fdfbSgilles bounce_message_cmp(const struct bounce_message *a,
77465c4fdfbSgilles const struct bounce_message *b)
77565c4fdfbSgilles {
776cc81b7c6Seric int r;
777cc81b7c6Seric
77865c4fdfbSgilles if (a->msgid < b->msgid)
77965c4fdfbSgilles return (-1);
78065c4fdfbSgilles if (a->msgid > b->msgid)
78165c4fdfbSgilles return (1);
782cc81b7c6Seric if ((r = strcmp(a->smtpname, b->smtpname)))
783cc81b7c6Seric return (r);
784cc81b7c6Seric
78565c4fdfbSgilles return memcmp(&a->bounce, &b->bounce, sizeof (a->bounce));
78665c4fdfbSgilles }
78765c4fdfbSgilles
788fe95d8d0Seric static const char *
action_str(const struct delivery_bounce * b)7893abffaecSsunil action_str(const struct delivery_bounce *b)
790fe95d8d0Seric {
7913abffaecSsunil switch (b->type) {
7925ed42fc8Ssunil case B_FAILED:
7935ed42fc8Ssunil return ("failed");
7945ed42fc8Ssunil case B_DELAYED:
7953abffaecSsunil return ("delayed");
7965ed42fc8Ssunil case B_DELIVERED:
7973abffaecSsunil if (b->mta_without_dsn)
7983abffaecSsunil return ("relayed");
7993abffaecSsunil
8005ed42fc8Ssunil return ("delivered");
801fe95d8d0Seric default:
802fe95d8d0Seric log_warn("warn: bounce: unknown bounce_type");
803fe95d8d0Seric return ("");
804fe95d8d0Seric }
805fe95d8d0Seric }
806fe95d8d0Seric
80765c4fdfbSgilles SPLAY_GENERATE(bounce_message_tree, bounce_message, sp_entry,
80865c4fdfbSgilles bounce_message_cmp);
809