bounce.c revision 1.65
1/*	$OpenBSD: bounce.c,v 1.65 2014/05/28 10:34:16 daniel 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_QUEUE_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		(void)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	(void)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_pony, IMSG_QUEUE_SMTP_SESSION, 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{
297	static char buf[32];
298
299	if (d < 60) {
300		(void)snprintf(buf, sizeof buf, "%lld second%s", d, (d == 1)?"":"s");
301	} else if (d < 3600) {
302		d = d / 60;
303		(void)snprintf(buf, sizeof buf, "%lld minute%s", d, (d == 1)?"":"s");
304	}
305	else if (d < 3600 * 24) {
306		d = d / 3600;
307		(void)snprintf(buf, sizeof buf, "%lld hour%s", d, (d == 1)?"":"s");
308	}
309	else {
310		d = d / (3600 * 24);
311		(void)snprintf(buf, sizeof buf, "%lld day%s", d, (d == 1)?"":"s");
312	}
313	return (buf);
314}
315
316#define NOTICE_INTRO							    \
317	"    Hi!\n\n"							    \
318	"    This is the MAILER-DAEMON, please DO NOT REPLY to this e-mail.\n"
319
320const char *notice_error =
321    "    An error has occurred while attempting to deliver a message for\n"
322    "    the following list of recipients:\n\n";
323
324const char *notice_warning =
325    "    A message is delayed for more than %s for the following\n"
326    "    list of recipients:\n\n";
327
328const char *notice_warning2 =
329    "    Please note that this is only a temporary failure report.\n"
330    "    The message is kept in the queue for up to %s.\n"
331    "    You DO NOT NEED to re-send the message to these recipients.\n\n";
332
333const char *notice_success =
334    "    Your message was successfully delivered to these recipients.\n\n";
335
336const char *notice_relay =
337    "    Your message was relayed to these recipients.\n\n";
338
339static int
340bounce_next_message(struct bounce_session *s)
341{
342	struct bounce_message	*msg;
343	char			 buf[SMTPD_MAXLINESIZE];
344	int			 fd;
345	time_t			 now;
346
347    again:
348
349	now = time(NULL);
350
351	TAILQ_FOREACH(msg, &pending, entry) {
352		if (msg->timeout > now)
353			continue;
354		if (strcmp(msg->smtpname, s->smtpname))
355			continue;
356		break;
357	}
358	if (msg == NULL)
359		return (0);
360
361	TAILQ_REMOVE(&pending, msg, entry);
362	SPLAY_REMOVE(bounce_message_tree, &messages, msg);
363
364	if ((fd = queue_message_fd_r(msg->msgid)) == -1) {
365		bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL,
366		    "Could not open message fd");
367		goto again;
368	}
369
370	if ((s->msgfp = fdopen(fd, "r")) == NULL) {
371		(void)snprintf(buf, sizeof(buf), "fdopen: %s", strerror(errno));
372		log_warn("warn: bounce: fdopen");
373		close(fd);
374		bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, buf);
375		goto again;
376	}
377
378	s->msg = msg;
379	return (1);
380}
381
382static int
383bounce_next(struct bounce_session *s)
384{
385	struct bounce_envelope	*evp;
386	char			*line;
387	size_t			 len, n;
388
389	switch (s->state) {
390	case BOUNCE_EHLO:
391		bounce_send(s, "EHLO %s", s->smtpname);
392		s->state = BOUNCE_MAIL;
393		break;
394
395	case BOUNCE_MAIL:
396	case BOUNCE_DATA_END:
397		log_debug("debug: bounce: %p: getting next message...", s);
398		if (bounce_next_message(s) == 0) {
399			log_debug("debug: bounce: %p: no more messages", s);
400			bounce_send(s, "QUIT");
401			s->state = BOUNCE_CLOSE;
402 			break;
403		}
404		log_debug("debug: bounce: %p: found message %08"PRIx32,
405		    s, s->msg->msgid);
406		bounce_send(s, "MAIL FROM: <>");
407		s->state = BOUNCE_RCPT;
408		break;
409
410	case BOUNCE_RCPT:
411		bounce_send(s, "RCPT TO: <%s>", s->msg->to);
412		s->state = BOUNCE_DATA;
413		break;
414
415	case BOUNCE_DATA:
416		bounce_send(s, "DATA");
417		s->state = BOUNCE_DATA_NOTICE;
418		break;
419
420	case BOUNCE_DATA_NOTICE:
421		/* Construct an appropriate notice. */
422
423		iobuf_xfqueue(&s->iobuf, "bounce_next: HEADER",
424		    "Subject: Delivery status notification: %s\n"
425		    "From: Mailer Daemon <MAILER-DAEMON@%s>\n"
426		    "To: %s\n"
427		    "Date: %s\n"
428		    "\n"
429		    NOTICE_INTRO
430		    "\n",
431		    bounce_strtype(s->msg->bounce.type),
432		    s->smtpname,
433		    s->msg->to,
434		    time_to_text(time(NULL)));
435
436		switch (s->msg->bounce.type) {
437		case B_ERROR:
438			iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
439			    notice_error);
440			break;
441		case B_WARNING:
442			iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
443			    notice_warning,
444			    bounce_duration(s->msg->bounce.delay));
445			break;
446		case B_DSN:
447			iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
448			    s->msg->bounce.mta_without_dsn ?
449			    notice_relay : notice_success);
450			break;
451		default:
452			log_warn("warn: bounce: unknown bounce_type");
453		}
454
455		TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
456			iobuf_xfqueue(&s->iobuf,
457			    "bounce_next: DATA_NOTICE",
458			    "%s", evp->report);
459		}
460		iobuf_xfqueue(&s->iobuf, "bounce_next: DATA_NOTICE", "\n");
461
462		if (s->msg->bounce.type == B_WARNING)
463			iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
464			    notice_warning2,
465			    bounce_duration(s->msg->bounce.expire));
466
467		iobuf_xfqueue(&s->iobuf, "bounce_next: DATA_NOTICE",
468		    "    Below is a copy of the original message:\n"
469		    "\n");
470
471		log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
472		    s, iobuf_queued(&s->iobuf));
473
474		s->state = BOUNCE_DATA_MESSAGE;
475		break;
476
477	case BOUNCE_DATA_MESSAGE:
478
479		n = iobuf_queued(&s->iobuf);
480
481		while (iobuf_queued(&s->iobuf) < BOUNCE_HIWAT) {
482			line = fgetln(s->msgfp, &len);
483			if (line == NULL)
484				break;
485			if (len == 1 && line[0] == '\n' && /* end of headers */
486			    s->msg->bounce.type == B_DSN &&
487			    s->msg->bounce.dsn_ret ==  DSN_RETHDRS) {
488				fclose(s->msgfp);
489				s->msgfp = NULL;
490				bounce_send(s, ".");
491				s->state = BOUNCE_DATA_END;
492				return (0);
493			}
494			line[len - 1] = '\0';
495			iobuf_xfqueue(&s->iobuf,
496			    "bounce_next: DATA_MESSAGE", "%s%s\n",
497			    (len == 2 && line[0] == '.') ? "." : "", line);
498		}
499
500		if (ferror(s->msgfp)) {
501			fclose(s->msgfp);
502			s->msgfp = NULL;
503			bounce_delivery(s->msg, IMSG_QUEUE_DELIVERY_TEMPFAIL,
504			    "Error reading message");
505			s->msg = NULL;
506			return (-1);
507		}
508
509		log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
510		    s, iobuf_queued(&s->iobuf) - n);
511
512		if (feof(s->msgfp)) {
513			fclose(s->msgfp);
514			s->msgfp = NULL;
515			bounce_send(s, ".");
516			s->state = BOUNCE_DATA_END;
517		}
518		break;
519
520	case BOUNCE_QUIT:
521		bounce_send(s, "QUIT");
522		s->state = BOUNCE_CLOSE;
523		break;
524
525	default:
526		fatalx("bounce: bad state");
527	}
528
529	return (0);
530}
531
532
533static void
534bounce_delivery(struct bounce_message *msg, int delivery, const char *status)
535{
536	struct bounce_envelope	*be;
537	struct envelope		 evp;
538	size_t			 n;
539	const char		*f;
540
541	n = 0;
542	while ((be = TAILQ_FIRST(&msg->envelopes))) {
543		if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL) {
544			if (queue_envelope_load(be->id, &evp) == 0) {
545				fatalx("could not reload envelope!");
546			}
547			evp.retry++;
548			evp.lasttry = msg->timeout;
549			envelope_set_errormsg(&evp, "%s", status);
550			queue_envelope_update(&evp);
551			m_create(p_scheduler, delivery, 0, 0, -1);
552			m_add_envelope(p_scheduler, &evp);
553			m_close(p_scheduler);
554		} else {
555			m_create(p_scheduler, delivery, 0, 0, -1);
556			m_add_evpid(p_scheduler, be->id);
557			m_close(p_scheduler);
558			queue_envelope_delete(be->id);
559		}
560		TAILQ_REMOVE(&msg->envelopes, be, entry);
561		free(be->report);
562		free(be);
563		n += 1;
564	}
565
566
567	if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL)
568		f = "TempFail";
569	else if (delivery == IMSG_QUEUE_DELIVERY_PERMFAIL)
570		f = "PermFail";
571	else
572		f = NULL;
573
574	if (f)
575		log_warnx("warn: %s injecting failure report on message %08"PRIx32
576		    " to <%s> for %zu envelope%s: %s",
577		    f, msg->msgid, msg->to, n, n > 1 ? "s":"", status);
578
579	nmessage -= 1;
580	stat_decrement("bounce.message", 1);
581	stat_decrement("bounce.envelope", n);
582	free(msg->smtpname);
583	free(msg->to);
584	free(msg);
585}
586
587static void
588bounce_status(struct bounce_session *s, const char *fmt, ...)
589{
590	va_list		 ap;
591	char		*status;
592	int		 len, delivery;
593
594	/* Ignore if there is no message */
595	if (s->msg == NULL)
596		return;
597
598	va_start(ap, fmt);
599	if ((len = vasprintf(&status, fmt, ap)) == -1)
600		fatal("bounce: vasprintf");
601	va_end(ap);
602
603	if (*status == '2')
604		delivery = IMSG_QUEUE_DELIVERY_OK;
605	else if (*status == '5' || *status == '6')
606		delivery = IMSG_QUEUE_DELIVERY_PERMFAIL;
607	else
608		delivery = IMSG_QUEUE_DELIVERY_TEMPFAIL;
609
610	bounce_delivery(s->msg, delivery, status);
611	s->msg = NULL;
612	if (s->msgfp)
613		fclose(s->msgfp);
614
615	free(status);
616}
617
618static void
619bounce_free(struct bounce_session *s)
620{
621	log_debug("debug: bounce: %p: deleting session", s);
622
623	iobuf_clear(&s->iobuf);
624	io_clear(&s->io);
625
626	free(s->smtpname);
627	free(s);
628
629	running -= 1;
630	stat_decrement("bounce.session", 1);
631	bounce_drain();
632}
633
634static void
635bounce_io(struct io *io, int evt)
636{
637	struct bounce_session	*s = io->arg;
638	const char		*error;
639	char			*line, *msg;
640	int			 cont;
641	size_t			 len;
642
643	log_trace(TRACE_IO, "bounce: %p: %s %s", s, io_strevent(evt),
644	    io_strio(io));
645
646	switch (evt) {
647	case IO_DATAIN:
648	    nextline:
649		line = iobuf_getline(&s->iobuf, &len);
650		if (line == NULL && iobuf_len(&s->iobuf) >= SMTPD_MAXLINESIZE) {
651			bounce_status(s, "Input too long");
652			bounce_free(s);
653			return;
654		}
655
656		if (line == NULL) {
657			iobuf_normalize(&s->iobuf);
658			break;
659		}
660
661		log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", s, line);
662
663		if ((error = parse_smtp_response(line, len, &msg, &cont))) {
664			bounce_status(s, "Bad response: %s", error);
665			bounce_free(s);
666			return;
667		}
668		if (cont)
669			goto nextline;
670
671		if (s->state == BOUNCE_CLOSE) {
672			bounce_free(s);
673			return;
674		}
675
676		if (line[0] != '2' && line[0] != '3') {		/* fail */
677			bounce_status(s, "%s", line);
678			s->state = BOUNCE_QUIT;
679		} else if (s->state == BOUNCE_DATA_END) {	/* accepted */
680			bounce_status(s, "%s", line);
681		}
682
683		if (bounce_next(s) == -1) {
684			bounce_free(s);
685			return;
686		}
687
688		io_set_write(io);
689		break;
690
691	case IO_LOWAT:
692		if (s->state == BOUNCE_DATA_MESSAGE)
693			if (bounce_next(s) == -1) {
694				bounce_free(s);
695				return;
696			}
697		if (iobuf_queued(&s->iobuf) == 0)
698			io_set_read(io);
699		break;
700
701	default:
702		bounce_status(s, "442 i/o error %d", evt);
703		bounce_free(s);
704		break;
705	}
706}
707
708static int
709bounce_message_cmp(const struct bounce_message *a,
710    const struct bounce_message *b)
711{
712	int r;
713
714	if (a->msgid < b->msgid)
715		return (-1);
716	if (a->msgid > b->msgid)
717		return (1);
718	if ((r = strcmp(a->smtpname, b->smtpname)))
719		return (r);
720
721	return memcmp(&a->bounce, &b->bounce, sizeof (a->bounce));
722}
723
724static const char *
725bounce_strtype(enum bounce_type t)
726{
727	switch (t) {
728	case B_ERROR:
729		return ("error");
730	case B_WARNING:
731		return ("warning");
732	case B_DSN:
733		return ("dsn");
734	default:
735		log_warn("warn: bounce: unknown bounce_type");
736		return ("");
737	}
738}
739
740SPLAY_GENERATE(bounce_message_tree, bounce_message, sp_entry,
741    bounce_message_cmp);
742