bounce.c revision 1.62
1/*	$OpenBSD: bounce.c,v 1.62 2014/02/04 15:44:05 eric Exp $	*/
2
3/*
4 * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
5 * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
6 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21#include <sys/types.h>
22#include <sys/queue.h>
23#include <sys/tree.h>
24#include <sys/socket.h>
25
26#include <err.h>
27#include <errno.h>
28#include <event.h>
29#include <imsg.h>
30#include <inttypes.h>
31#include <pwd.h>
32#include <signal.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <time.h>
37#include <unistd.h>
38
39#include "smtpd.h"
40#include "log.h"
41
42#define BOUNCE_MAXRUN	2
43#define BOUNCE_HIWAT	65535
44
45enum {
46	BOUNCE_EHLO,
47	BOUNCE_MAIL,
48	BOUNCE_RCPT,
49	BOUNCE_DATA,
50	BOUNCE_DATA_NOTICE,
51	BOUNCE_DATA_MESSAGE,
52	BOUNCE_DATA_END,
53	BOUNCE_QUIT,
54	BOUNCE_CLOSE,
55};
56
57struct bounce_envelope {
58	TAILQ_ENTRY(bounce_envelope)	 entry;
59	uint64_t			 id;
60	char				*report;
61};
62
63struct bounce_message {
64	SPLAY_ENTRY(bounce_message)	 sp_entry;
65	TAILQ_ENTRY(bounce_message)	 entry;
66	uint32_t			 msgid;
67	struct delivery_bounce		 bounce;
68	char				*smtpname;
69	char				*to;
70	time_t				 timeout;
71	TAILQ_HEAD(, bounce_envelope)	 envelopes;
72};
73
74struct bounce_session {
75	char				*smtpname;
76	struct bounce_message		*msg;
77	FILE				*msgfp;
78	int				 state;
79	struct iobuf			 iobuf;
80	struct io			 io;
81};
82
83SPLAY_HEAD(bounce_message_tree, bounce_message);
84static int bounce_message_cmp(const struct bounce_message *,
85    const struct bounce_message *);
86SPLAY_PROTOTYPE(bounce_message_tree, bounce_message, sp_entry,
87    bounce_message_cmp);
88
89static void bounce_drain(void);
90static void bounce_send(struct bounce_session *, const char *, ...);
91static int  bounce_next_message(struct bounce_session *);
92static int  bounce_next(struct bounce_session *);
93static void bounce_delivery(struct bounce_message *, int, const char *);
94static void bounce_status(struct bounce_session *, const char *, ...);
95static void bounce_io(struct io *, int);
96static void bounce_timeout(int, short, void *);
97static void bounce_free(struct bounce_session *);
98static const char *bounce_strtype(enum bounce_type);
99
100static struct tree			wait_fd;
101static struct bounce_message_tree	messages;
102static TAILQ_HEAD(, bounce_message)	pending;
103
104static int				nmessage = 0;
105static int				running = 0;
106static struct event			ev_timer;
107
108static void
109bounce_init(void)
110{
111	static int	init = 0;
112
113	if (init == 0) {
114		TAILQ_INIT(&pending);
115		SPLAY_INIT(&messages);
116		tree_init(&wait_fd);
117		evtimer_set(&ev_timer, bounce_timeout, NULL);
118		init = 1;
119	}
120}
121
122void
123bounce_add(uint64_t evpid)
124{
125	char			 buf[SMTPD_MAXLINESIZE], *line;
126	struct envelope		 evp;
127	struct bounce_message	 key, *msg;
128	struct bounce_envelope	*be;
129
130	bounce_init();
131
132	if (queue_envelope_load(evpid, &evp) == 0) {
133		m_create(p_scheduler, IMSG_DELIVERY_PERMFAIL, 0, 0, -1);
134		m_add_evpid(p_scheduler, evpid);
135		m_close(p_scheduler);
136		return;
137	}
138
139	if (evp.type != D_BOUNCE)
140		errx(1, "bounce: evp:%016" PRIx64 " is not of type D_BOUNCE!",
141		    evp.id);
142
143	key.msgid = evpid_to_msgid(evpid);
144	key.bounce = evp.agent.bounce;
145	key.smtpname = evp.smtpname;
146	msg = SPLAY_FIND(bounce_message_tree, &messages, &key);
147	if (msg == NULL) {
148		msg = xcalloc(1, sizeof(*msg), "bounce_add");
149		msg->msgid = key.msgid;
150		msg->bounce = key.bounce;
151
152		TAILQ_INIT(&msg->envelopes);
153
154		msg->smtpname = xstrdup(evp.smtpname, "bounce_add");
155		snprintf(buf, sizeof(buf), "%s@%s", evp.sender.user,
156		    evp.sender.domain);
157		msg->to = xstrdup(buf, "bounce_add");
158		nmessage += 1;
159		SPLAY_INSERT(bounce_message_tree, &messages, msg);
160		log_debug("debug: bounce: new message %08" PRIx32,
161		    msg->msgid);
162		stat_increment("bounce.message", 1);
163	} else
164		TAILQ_REMOVE(&pending, msg, entry);
165
166	line = evp.errorline;
167	if (strlen(line) > 4 && (*line == '1' || *line == '6'))
168		line += 4;
169	snprintf(buf, sizeof(buf), "%s@%s: %s\n", evp.dest.user,
170	    evp.dest.domain, line);
171
172	be = xmalloc(sizeof *be, "bounce_add");
173	be->id = evpid;
174	be->report = xstrdup(buf, "bounce_add");
175	TAILQ_INSERT_TAIL(&msg->envelopes, be, entry);
176	buf[strcspn(buf, "\n")] = '\0';
177	log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, buf);
178
179	msg->timeout = time(NULL) + 1;
180	TAILQ_INSERT_TAIL(&pending, msg, entry);
181
182	stat_increment("bounce.envelope", 1);
183	bounce_drain();
184}
185
186void
187bounce_fd(int fd)
188{
189	struct bounce_session	*s;
190	struct bounce_message	*msg;
191
192	log_debug("debug: bounce: got enqueue socket %d", fd);
193
194	if (fd == -1 || TAILQ_EMPTY(&pending)) {
195		log_debug("debug: bounce: cancelling");
196		if (fd != -1)
197			close(fd);
198		running -= 1;
199		bounce_drain();
200		return;
201	}
202
203	msg = TAILQ_FIRST(&pending);
204
205	s = xcalloc(1, sizeof(*s), "bounce_fd");
206	s->smtpname = xstrdup(msg->smtpname, "bounce_fd");
207	s->state = BOUNCE_EHLO;
208	iobuf_xinit(&s->iobuf, 0, 0, "bounce_run");
209	io_init(&s->io, fd, s, bounce_io, &s->iobuf);
210	io_set_timeout(&s->io, 30000);
211	io_set_read(&s->io);
212
213	log_debug("debug: bounce: new session %p", s);
214	stat_increment("bounce.session", 1);
215}
216
217static void
218bounce_timeout(int fd, short ev, void *arg)
219{
220	log_debug("debug: bounce: timeout");
221
222	bounce_drain();
223}
224
225static void
226bounce_drain()
227{
228	struct bounce_message	*msg;
229	struct timeval		 tv;
230	time_t			 t;
231
232	log_debug("debug: bounce: drain: nmessage=%d running=%d",
233	    nmessage, running);
234
235	while (1) {
236		if (running >= BOUNCE_MAXRUN) {
237			log_debug("debug: bounce: max session reached");
238			return;
239		}
240
241		if (nmessage == 0) {
242			log_debug("debug: bounce: no more messages");
243			return;
244		}
245
246		if (running >= nmessage) {
247			log_debug("debug: bounce: enough sessions running");
248			return;
249		}
250
251		if ((msg = TAILQ_FIRST(&pending)) == NULL) {
252			log_debug("debug: bounce: no more pending messages");
253			return;
254		}
255
256		t = time(NULL);
257		if (msg->timeout > t) {
258			log_debug("debug: bounce: next message not ready yet");
259			if (!evtimer_pending(&ev_timer, NULL)) {
260				log_debug("debug: bounce: setting timer");
261				tv.tv_sec = msg->timeout - t;
262				tv.tv_usec = 0;
263				evtimer_add(&ev_timer, &tv);
264			}
265			return;
266		}
267
268		log_debug("debug: bounce: requesting new enqueue socket...");
269		m_compose(p_smtp, IMSG_SMTP_ENQUEUE_FD, 0, 0, -1, NULL, 0);
270
271		running += 1;
272	}
273}
274
275static void
276bounce_send(struct bounce_session *s, const char *fmt, ...)
277{
278	va_list	 ap;
279	char	*p;
280	int	 len;
281
282	va_start(ap, fmt);
283	if ((len = vasprintf(&p, fmt, ap)) == -1)
284		fatal("bounce: vasprintf");
285	va_end(ap);
286
287	log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", s, p);
288
289	iobuf_xfqueue(&s->iobuf, "bounce_send", "%s\n", p);
290
291	free(p);
292}
293
294static const char *
295bounce_duration(long long int d) {
296	static char buf[32];
297
298	if (d < 60) {
299		snprintf(buf, sizeof buf, "%lld second%s", d, (d == 1)?"":"s");
300	} else if (d < 3600) {
301		d = d / 60;
302		snprintf(buf, sizeof buf, "%lld minute%s", d, (d == 1)?"":"s");
303	}
304	else if (d < 3600 * 24) {
305		d = d / 3600;
306		snprintf(buf, sizeof buf, "%lld hour%s", d, (d == 1)?"":"s");
307	}
308	else {
309		d = d / (3600 * 24);
310		snprintf(buf, sizeof buf, "%lld day%s", d, (d == 1)?"":"s");
311	}
312	return (buf);
313};
314
315#define NOTICE_INTRO							    \
316	"    Hi!\n\n"							    \
317	"    This is the MAILER-DAEMON, please DO NOT REPLY to this e-mail.\n"
318
319const char *notice_error =
320    "    An error has occurred while attempting to deliver a message for\n"
321    "    the following list of recipients:\n\n";
322
323const char *notice_warning =
324    "    A message is delayed for more than %s for the following\n"
325    "    list of recipients:\n\n";
326
327const char *notice_warning2 =
328    "    Please note that this is only a temporary failure report.\n"
329    "    The message is kept in the queue for up to %s.\n"
330    "    You DO NOT NEED to re-send the message to these recipients.\n\n";
331
332const char *notice_success =
333    "    Your message was successfully delivered to these recipients.\n\n";
334
335const char *notice_relay =
336    "    Your message was relayed to these recipients.\n\n";
337
338static int
339bounce_next_message(struct bounce_session *s)
340{
341	struct bounce_message	*msg;
342	char			 buf[SMTPD_MAXLINESIZE];
343	int			 fd;
344	time_t			 now;
345
346    again:
347
348	now = time(NULL);
349
350	TAILQ_FOREACH(msg, &pending, entry) {
351		if (msg->timeout > now)
352			continue;
353		if (strcmp(msg->smtpname, s->smtpname))
354			continue;
355		break;
356	}
357	if (msg == NULL)
358		return (0);
359
360	TAILQ_REMOVE(&pending, msg, entry);
361	SPLAY_REMOVE(bounce_message_tree, &messages, msg);
362
363	if ((fd = queue_message_fd_r(msg->msgid)) == -1) {
364		bounce_delivery(msg, IMSG_DELIVERY_TEMPFAIL,
365		    "Could not open message fd");
366		goto again;
367	}
368
369	if ((s->msgfp = fdopen(fd, "r")) == NULL) {
370		snprintf(buf, sizeof(buf), "fdopen: %s", strerror(errno));
371		log_warn("warn: bounce: fdopen");
372		close(fd);
373		bounce_delivery(msg, IMSG_DELIVERY_TEMPFAIL, buf);
374		goto again;
375	}
376
377	s->msg = msg;
378	return (1);
379}
380
381static int
382bounce_next(struct bounce_session *s)
383{
384	struct bounce_envelope	*evp;
385	char			*line;
386	size_t			 len, n;
387
388	switch (s->state) {
389	case BOUNCE_EHLO:
390		bounce_send(s, "EHLO %s", s->smtpname);
391		s->state = BOUNCE_MAIL;
392		break;
393
394	case BOUNCE_MAIL:
395	case BOUNCE_DATA_END:
396		log_debug("debug: bounce: %p: getting next message...", s);
397		if (bounce_next_message(s) == 0) {
398			log_debug("debug: bounce: %p: no more messages", s);
399			bounce_send(s, "QUIT");
400			s->state = BOUNCE_CLOSE;
401 			break;
402		}
403		log_debug("debug: bounce: %p: found message %08"PRIx32,
404		    s, s->msg->msgid);
405		bounce_send(s, "MAIL FROM: <>");
406		s->state = BOUNCE_RCPT;
407		break;
408
409	case BOUNCE_RCPT:
410		bounce_send(s, "RCPT TO: <%s>", s->msg->to);
411		s->state = BOUNCE_DATA;
412		break;
413
414	case BOUNCE_DATA:
415		bounce_send(s, "DATA");
416		s->state = BOUNCE_DATA_NOTICE;
417		break;
418
419	case BOUNCE_DATA_NOTICE:
420		/* Construct an appropriate notice. */
421
422		iobuf_xfqueue(&s->iobuf, "bounce_next: HEADER",
423		    "Subject: Delivery status notification: %s\n"
424		    "From: Mailer Daemon <MAILER-DAEMON@%s>\n"
425		    "To: %s\n"
426		    "Date: %s\n"
427		    "\n"
428		    NOTICE_INTRO
429		    "\n",
430		    bounce_strtype(s->msg->bounce.type),
431		    s->smtpname,
432		    s->msg->to,
433		    time_to_text(time(NULL)));
434
435		switch (s->msg->bounce.type) {
436		case B_ERROR:
437			iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
438			    notice_error);
439			break;
440		case B_WARNING:
441			iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
442			    notice_warning,
443			    bounce_duration(s->msg->bounce.delay));
444			break;
445		case B_DSN:
446			iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
447			    s->msg->bounce.mta_without_dsn ?
448			    notice_relay : notice_success);
449			break;
450		default:
451			log_warn("warn: bounce: unknown bounce_type");
452		}
453
454		TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
455			iobuf_xfqueue(&s->iobuf,
456			    "bounce_next: DATA_NOTICE",
457			    "%s", evp->report);
458		}
459		iobuf_xfqueue(&s->iobuf, "bounce_next: DATA_NOTICE", "\n");
460
461		if (s->msg->bounce.type == B_WARNING)
462			iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
463			    notice_warning2,
464			    bounce_duration(s->msg->bounce.expire));
465
466		iobuf_xfqueue(&s->iobuf, "bounce_next: DATA_NOTICE",
467		    "    Below is a copy of the original message:\n"
468		    "\n");
469
470		log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
471		    s, iobuf_queued(&s->iobuf));
472
473		s->state = BOUNCE_DATA_MESSAGE;
474		break;
475
476	case BOUNCE_DATA_MESSAGE:
477
478		n = iobuf_queued(&s->iobuf);
479
480		while (iobuf_queued(&s->iobuf) < BOUNCE_HIWAT) {
481			line = fgetln(s->msgfp, &len);
482			if (line == NULL)
483				break;
484			if (len == 1 && line[0] == '\n' && /* end of headers */
485			    s->msg->bounce.type == B_DSN &&
486			    s->msg->bounce.dsn_ret ==  DSN_RETHDRS) {
487				fclose(s->msgfp);
488				s->msgfp = NULL;
489				bounce_send(s, ".");
490				s->state = BOUNCE_DATA_END;
491				return (0);
492			}
493			line[len - 1] = '\0';
494			iobuf_xfqueue(&s->iobuf,
495			    "bounce_next: DATA_MESSAGE", "%s%s\n",
496			    (len == 2 && line[0] == '.') ? "." : "", line);
497		}
498
499		if (ferror(s->msgfp)) {
500			fclose(s->msgfp);
501			s->msgfp = NULL;
502			bounce_delivery(s->msg, IMSG_DELIVERY_TEMPFAIL,
503			    "Error reading message");
504			s->msg = NULL;
505			return (-1);
506		}
507
508		log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
509		    s, iobuf_queued(&s->iobuf) - n);
510
511		if (feof(s->msgfp)) {
512			fclose(s->msgfp);
513			s->msgfp = NULL;
514			bounce_send(s, ".");
515			s->state = BOUNCE_DATA_END;
516		}
517		break;
518
519	case BOUNCE_QUIT:
520		bounce_send(s, "QUIT");
521		s->state = BOUNCE_CLOSE;
522		break;
523
524	default:
525		fatalx("bounce: bad state");
526	}
527
528	return (0);
529}
530
531
532static void
533bounce_delivery(struct bounce_message *msg, int delivery, const char *status)
534{
535	struct bounce_envelope	*be;
536	struct envelope		 evp;
537	size_t			 n;
538	const char		*f;
539
540	n = 0;
541	while ((be = TAILQ_FIRST(&msg->envelopes))) {
542		if (delivery == IMSG_DELIVERY_TEMPFAIL) {
543			if (queue_envelope_load(be->id, &evp) == 0) {
544				fatalx("could not reload envelope!");
545			}
546			evp.retry++;
547			evp.lasttry = msg->timeout;
548			envelope_set_errormsg(&evp, "%s", status);
549			queue_envelope_update(&evp);
550			m_create(p_scheduler, delivery, 0, 0, -1);
551			m_add_envelope(p_scheduler, &evp);
552			m_close(p_scheduler);
553		} else {
554			m_create(p_scheduler, delivery, 0, 0, -1);
555			m_add_evpid(p_scheduler, be->id);
556			m_close(p_scheduler);
557			queue_envelope_delete(be->id);
558		}
559		TAILQ_REMOVE(&msg->envelopes, be, entry);
560		free(be->report);
561		free(be);
562		n += 1;
563	}
564
565
566	if (delivery == IMSG_DELIVERY_TEMPFAIL)
567		f = "TempFail";
568	else if (delivery == IMSG_DELIVERY_PERMFAIL)
569		f = "PermFail";
570	else
571		f = NULL;
572
573	if (f)
574		log_warnx("warn: %s injecting failure report on message %08"PRIx32
575		    " to <%s> for %zu envelope%s: %s",
576		    f, msg->msgid, msg->to, n, n > 1 ? "s":"", status);
577
578	nmessage -= 1;
579	stat_decrement("bounce.message", 1);
580	stat_decrement("bounce.envelope", n);
581	free(msg->smtpname);
582	free(msg->to);
583	free(msg);
584}
585
586static void
587bounce_status(struct bounce_session *s, const char *fmt, ...)
588{
589	va_list		 ap;
590	char		*status;
591	int		 len, delivery;
592
593	/* Ignore if there is no message */
594	if (s->msg == NULL)
595		return;
596
597	va_start(ap, fmt);
598	if ((len = vasprintf(&status, fmt, ap)) == -1)
599		fatal("bounce: vasprintf");
600	va_end(ap);
601
602	if (*status == '2')
603		delivery = IMSG_DELIVERY_OK;
604	else if (*status == '5' || *status == '6')
605		delivery = IMSG_DELIVERY_PERMFAIL;
606	else
607		delivery = IMSG_DELIVERY_TEMPFAIL;
608
609	bounce_delivery(s->msg, delivery, status);
610	s->msg = NULL;
611	if (s->msgfp)
612		fclose(s->msgfp);
613
614	free(status);
615}
616
617static void
618bounce_free(struct bounce_session *s)
619{
620	log_debug("debug: bounce: %p: deleting session", s);
621
622	iobuf_clear(&s->iobuf);
623	io_clear(&s->io);
624
625	free(s->smtpname);
626	free(s);
627
628	running -= 1;
629	stat_decrement("bounce.session", 1);
630	bounce_drain();
631}
632
633static void
634bounce_io(struct io *io, int evt)
635{
636	struct bounce_session	*s = io->arg;
637	const char		*error;
638	char			*line, *msg;
639	int			 cont;
640	size_t			 len;
641
642	log_trace(TRACE_IO, "bounce: %p: %s %s", s, io_strevent(evt),
643	    io_strio(io));
644
645	switch (evt) {
646	case IO_DATAIN:
647	    nextline:
648		line = iobuf_getline(&s->iobuf, &len);
649		if (line == NULL && iobuf_len(&s->iobuf) >= SMTPD_MAXLINESIZE) {
650			bounce_status(s, "Input too long");
651			bounce_free(s);
652			return;
653		}
654
655		if (line == NULL) {
656			iobuf_normalize(&s->iobuf);
657			break;
658		}
659
660		log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", s, line);
661
662		if ((error = parse_smtp_response(line, len, &msg, &cont))) {
663			bounce_status(s, "Bad response: %s", error);
664			bounce_free(s);
665			return;
666		}
667		if (cont)
668			goto nextline;
669
670		if (s->state == BOUNCE_CLOSE) {
671			bounce_free(s);
672			return;
673		}
674
675		if (line[0] != '2' && line[0] != '3') {		/* fail */
676			bounce_status(s, "%s", line);
677			s->state = BOUNCE_QUIT;
678		} else if (s->state == BOUNCE_DATA_END) {	/* accepted */
679			bounce_status(s, "%s", line);
680		}
681
682		if (bounce_next(s) == -1) {
683			bounce_free(s);
684			return;
685		}
686
687		io_set_write(io);
688		break;
689
690	case IO_LOWAT:
691		if (s->state == BOUNCE_DATA_MESSAGE)
692			if (bounce_next(s) == -1) {
693				bounce_free(s);
694				return;
695			}
696		if (iobuf_queued(&s->iobuf) == 0)
697			io_set_read(io);
698		break;
699
700	default:
701		bounce_status(s, "442 i/o error %d", evt);
702		bounce_free(s);
703		break;
704	}
705}
706
707static int
708bounce_message_cmp(const struct bounce_message *a,
709    const struct bounce_message *b)
710{
711	int r;
712
713	if (a->msgid < b->msgid)
714		return (-1);
715	if (a->msgid > b->msgid)
716		return (1);
717	if ((r = strcmp(a->smtpname, b->smtpname)))
718		return (r);
719
720	return memcmp(&a->bounce, &b->bounce, sizeof (a->bounce));
721}
722
723static const char *
724bounce_strtype(enum bounce_type t)
725{
726	switch (t) {
727	case B_ERROR:
728		return ("error");
729	case B_WARNING:
730		return ("warning");
731	case B_DSN:
732		return ("dsn");
733	default:
734		log_warn("warn: bounce: unknown bounce_type");
735		return ("");
736	}
737}
738
739SPLAY_GENERATE(bounce_message_tree, bounce_message, sp_entry,
740    bounce_message_cmp);
741