bounce.c revision 1.52
1/*	$OpenBSD: bounce.c,v 1.52 2012/11/12 14:58:53 eric Exp $	*/
2
3/*
4 * Copyright (c) 2009 Gilles Chehade <gilles@openbsd.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/param.h>
25#include <sys/socket.h>
26
27#include <err.h>
28#include <errno.h>
29#include <event.h>
30#include <imsg.h>
31#include <inttypes.h>
32#include <pwd.h>
33#include <signal.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <time.h>
38#include <unistd.h>
39
40#include "smtpd.h"
41#include "log.h"
42
43#define BOUNCE_MAXRUN	10
44#define BOUNCE_HIWAT	65535
45
46enum {
47	BOUNCE_EHLO,
48	BOUNCE_MAIL,
49	BOUNCE_RCPT,
50	BOUNCE_DATA,
51	BOUNCE_DATA_NOTICE,
52	BOUNCE_DATA_MESSAGE,
53	BOUNCE_QUIT,
54	BOUNCE_CLOSE,
55};
56
57struct bounce {
58	TAILQ_ENTRY(bounce)	 entry;
59	uint64_t		 id;
60	uint32_t		 msgid;
61	TAILQ_HEAD(, envelope)	 envelopes;
62	size_t			 count;
63	FILE			*msgfp;
64	int			 state;
65	struct iobuf		 iobuf;
66	struct io		 io;
67
68	struct event		 evt;
69};
70
71static void bounce_drain(void);
72static void bounce_commit(uint32_t);
73static void bounce_send(struct bounce *, const char *, ...);
74static int  bounce_next(struct bounce *);
75static void bounce_status(struct bounce *, const char *, ...);
76static void bounce_io(struct io *, int);
77static void bounce_timeout(int, short, void *);
78static void bounce_free(struct bounce *);
79
80static struct tree bounces_by_msgid = SPLAY_INITIALIZER(&bounces_by_msgid);
81static struct tree bounces_by_uid = SPLAY_INITIALIZER(&bounces_by_uid);
82
83static int running = 0;
84static TAILQ_HEAD(, bounce) runnable = TAILQ_HEAD_INITIALIZER(runnable);
85
86void
87bounce_add(uint64_t evpid)
88{
89	struct envelope	*evp;
90	struct bounce	*bounce;
91	struct timeval	 tv;
92
93	evp = xcalloc(1, sizeof *evp, "bounce_add");
94
95	if (queue_envelope_load(evpid, evp) == 0) {
96		evp->id = evpid;
97		imsg_compose_event(env->sc_ievs[PROC_SCHEDULER],
98		    IMSG_QUEUE_DELIVERY_PERMFAIL, 0, 0, -1, evp, sizeof *evp);
99		free(evp);
100		return;
101	}
102
103	if (evp->type != D_BOUNCE)
104		errx(1, "bounce: evp:%016" PRIx64 " is not of type D_BOUNCE!",
105		    evp->id);
106	evp->lasttry = time(NULL);
107
108	bounce = tree_get(&bounces_by_msgid, evpid_to_msgid(evpid));
109	if (bounce == NULL) {
110		bounce = xcalloc(1, sizeof(*bounce), "bounce_add");
111		bounce->msgid = evpid_to_msgid(evpid);
112		tree_xset(&bounces_by_msgid, bounce->msgid, bounce);
113
114		log_debug("debug: bounce: %p: new bounce for msg:%08" PRIx32,
115		    bounce, bounce->msgid);
116
117		TAILQ_INIT(&bounce->envelopes);
118		evtimer_set(&bounce->evt, bounce_timeout, &bounce->msgid);
119		tv.tv_sec = 1;
120		tv.tv_usec = 0;
121		evtimer_add(&bounce->evt, &tv);
122	}
123
124	log_debug("debug: bounce: %p: adding evp:%16" PRIx64, bounce, evp->id);
125
126	TAILQ_INSERT_TAIL(&bounce->envelopes, evp, entry);
127	bounce->count += 1;
128
129	if (bounce->id)
130		return;
131
132	evtimer_del(&bounce->evt);
133	tv.tv_sec = 1;
134	tv.tv_usec = 0;
135	evtimer_add(&bounce->evt, &tv);
136}
137
138void
139bounce_run(uint64_t id, int fd)
140{
141	struct bounce	*bounce;
142	int		 msgfd;
143
144	log_trace(TRACE_BOUNCE, "bounce: run %016" PRIx64 " fd %i", id, fd);
145
146	bounce = tree_xpop(&bounces_by_uid, id);
147
148	if (fd == -1) {
149		bounce_status(bounce, "failed to receive enqueueing socket");
150		bounce_free(bounce);
151		return;
152	}
153
154	if ((msgfd = queue_message_fd_r(bounce->msgid)) == -1) {
155		bounce_status(bounce, "could not open message fd");
156		bounce_free(bounce);
157		return;
158	}
159
160	if ((bounce->msgfp = fdopen(msgfd, "r")) == NULL) {
161		log_warn("warn: bounce_run: fdopen");
162		bounce_status(bounce, "error %i in fdopen", errno);
163		bounce_free(bounce);
164		close(msgfd);
165		return;
166	}
167
168	bounce->state = BOUNCE_EHLO;
169	iobuf_xinit(&bounce->iobuf, 0, 0, "bounce_run");
170	io_init(&bounce->io, fd, bounce, bounce_io, &bounce->iobuf);
171	io_set_timeout(&bounce->io, 30000);
172	io_set_read(&bounce->io);
173}
174
175static void
176bounce_commit(uint32_t msgid)
177{
178	struct bounce	*bounce;
179
180	log_trace(TRACE_BOUNCE, "bounce: commit msg:%08" PRIx32, msgid);
181
182	bounce = tree_xget(&bounces_by_msgid, msgid);
183	bounce->id = generate_uid();
184	evtimer_del(&bounce->evt);
185	TAILQ_INSERT_TAIL(&runnable, bounce, entry);
186
187	bounce_drain();
188}
189
190static void
191bounce_timeout(int fd, short ev, void *arg)
192{
193	uint32_t *msgid = arg;
194
195	bounce_commit(*msgid);
196}
197
198static void
199bounce_drain()
200{
201	struct bounce	*bounce;
202
203	while ((bounce = TAILQ_FIRST(&runnable))) {
204
205		if (running >= BOUNCE_MAXRUN) {
206			log_debug("debug: bounce: max session reached");
207			return;
208		}
209
210		TAILQ_REMOVE(&runnable, bounce, entry);
211		if (TAILQ_FIRST(&bounce->envelopes) == NULL) {
212			log_debug("debug: bounce: %p: no envelopes", bounce);
213			bounce_free(bounce);
214			continue;
215		}
216
217		tree_xset(&bounces_by_uid, bounce->id, bounce);
218
219		log_debug("debug: bounce: %p: requesting enqueue socket with id 0x%016" PRIx64,
220		    bounce, bounce->id);
221
222		imsg_compose_event(env->sc_ievs[PROC_SMTP], IMSG_SMTP_ENQUEUE,
223		    0, 0, -1, &bounce->id, sizeof (bounce->id));
224
225		running += 1;
226	}
227}
228
229static void
230bounce_send(struct bounce *bounce, const char *fmt, ...)
231{
232	va_list	 ap;
233	char	*p;
234	int	 len;
235
236	va_start(ap, fmt);
237	if ((len = vasprintf(&p, fmt, ap)) == -1)
238		fatal("bounce: vasprintf");
239	va_end(ap);
240
241	log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", bounce, p);
242
243	iobuf_xfqueue(&bounce->iobuf, "bounce_send", "%s\n", p);
244
245        free(p);
246}
247
248/* This can simplified once we support PIPELINING */
249static int
250bounce_next(struct bounce *bounce)
251{
252	struct envelope	*evp;
253	char		*line;
254	size_t		 len, s;
255
256	switch(bounce->state) {
257	case BOUNCE_EHLO:
258		bounce_send(bounce, "EHLO %s", env->sc_hostname);
259		bounce->state = BOUNCE_MAIL;
260		break;
261
262	case BOUNCE_MAIL:
263		bounce_send(bounce, "MAIL FROM: <>");
264		bounce->state = BOUNCE_RCPT;
265		break;
266
267	case BOUNCE_RCPT:
268		evp = TAILQ_FIRST(&bounce->envelopes);
269		bounce_send(bounce, "RCPT TO: <%s@%s>",
270		    evp->sender.user, evp->sender.domain);
271		bounce->state = BOUNCE_DATA;
272		break;
273
274	case BOUNCE_DATA:
275		bounce_send(bounce, "DATA");
276		bounce->state = BOUNCE_DATA_NOTICE;
277		break;
278
279	case BOUNCE_DATA_NOTICE:
280		/* Construct an appropriate reason line. */
281
282		/* prevent more envelopes from being added to this bounce */
283		tree_xpop(&bounces_by_msgid, bounce->msgid);
284
285		evp = TAILQ_FIRST(&bounce->envelopes);
286
287		iobuf_xfqueue(&bounce->iobuf, "bounce_next: DATA_NOTICE",
288		    "Subject: Delivery status notification\n"
289		    "From: Mailer Daemon <MAILER-DAEMON@%s>\n"
290		    "To: %s@%s\n"
291		    "Date: %s\n"
292		    "\n"
293		    "Hi !\n"
294		    "\n"
295		    "This is the MAILER-DAEMON, please DO NOT REPLY to this e-mail.\n"
296		    "An error has occurred while attempting to deliver a message.\n"
297		    "\n",
298		    env->sc_hostname,
299		    evp->sender.user, evp->sender.domain,
300		    time_to_text(time(NULL)));
301
302		TAILQ_FOREACH(evp, &bounce->envelopes, entry) {
303			line = evp->errorline;
304			if (strlen(line) > 4 && (*line == '1' || *line == '6'))
305				line += 4;
306			iobuf_xfqueue(&bounce->iobuf, "bounce_next: DATA_NOTICE",
307			    "Recipient: %s@%s\n"
308			    "Reason: %s\n",
309			    evp->dest.user, evp->dest.domain, line);
310		}
311
312		iobuf_xfqueue(&bounce->iobuf, "bounce_next: DATA_NOTICE",
313		    "\n"
314		    "Below is a copy of the original message:\n"
315		    "\n");
316
317		log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
318		    bounce, iobuf_queued(&bounce->iobuf));
319
320		bounce->state = BOUNCE_DATA_MESSAGE;
321		break;
322
323	case BOUNCE_DATA_MESSAGE:
324
325		s = iobuf_queued(&bounce->iobuf);
326
327		while (iobuf_len(&bounce->iobuf) < BOUNCE_HIWAT) {
328			line = fgetln(bounce->msgfp, &len);
329			if (line == NULL)
330				break;
331			line[len - 1] = '\0';
332			iobuf_xfqueue(&bounce->iobuf,
333			    "bounce_next: DATA_MESSAGE", "%s%s\n",
334			    (len == 2 && line[0] == '.') ? "." : "", line);
335		}
336
337		if (ferror(bounce->msgfp)) {
338			bounce_status(bounce, "460 Error reading message");
339			return (-1);
340		}
341
342		log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
343		    bounce, iobuf_queued(&bounce->iobuf) - s);
344
345		if (feof(bounce->msgfp)) {
346			bounce_send(bounce, ".");
347			bounce->state = BOUNCE_QUIT;
348		}
349		break;
350
351	case BOUNCE_QUIT:
352		bounce_send(bounce, "QUIT");
353		bounce->state = BOUNCE_CLOSE;
354		break;
355
356	default:
357		fatalx("bounce: bad state");
358	}
359
360	return (0);
361}
362
363static void
364bounce_status(struct bounce *bounce, const char *fmt, ...)
365{
366	va_list		 ap;
367	char		*status;
368	int		 len, msg;
369	struct envelope	*evp;
370
371	/* ignore if the envelopes have already been updated/deleted */
372	if (TAILQ_FIRST(&bounce->envelopes) == NULL)
373		return;
374
375	va_start(ap, fmt);
376	if ((len = vasprintf(&status, fmt, ap)) == -1)
377		fatal("bounce: vasprintf");
378	va_end(ap);
379
380	if (*status == '2')
381		msg = IMSG_QUEUE_DELIVERY_OK;
382	else if (*status == '5' || *status == '6')
383		msg = IMSG_QUEUE_DELIVERY_PERMFAIL;
384	else
385		msg = IMSG_QUEUE_DELIVERY_TEMPFAIL;
386
387	while ((evp = TAILQ_FIRST(&bounce->envelopes))) {
388		if (msg == IMSG_QUEUE_DELIVERY_TEMPFAIL) {
389			evp->retry++;
390			envelope_set_errormsg(evp, "%s", status);
391			queue_envelope_update(evp);
392			imsg_compose_event(env->sc_ievs[PROC_SCHEDULER], msg, 0, 0, -1,
393			    evp, sizeof *evp);
394		} else {
395			queue_envelope_delete(evp);
396			imsg_compose_event(env->sc_ievs[PROC_SCHEDULER], msg, 0, 0, -1,
397			    &evp->id, sizeof evp->id);
398		}
399		TAILQ_REMOVE(&bounce->envelopes, evp, entry);
400		free(evp);
401	}
402
403	free(status);
404}
405
406static void
407bounce_free(struct bounce *bounce)
408{
409	struct envelope	*evp;
410
411	log_debug("debug: bounce: %p: deleting session", bounce);
412
413	/* if the envelopes where not sent, it is still in the tree */
414	tree_pop(&bounces_by_msgid, bounce->msgid);
415
416	while ((evp = TAILQ_FIRST(&bounce->envelopes))) {
417		TAILQ_REMOVE(&bounce->envelopes, evp, entry);
418		free(evp);
419	}
420
421	if (bounce->msgfp)
422		fclose(bounce->msgfp);
423	iobuf_clear(&bounce->iobuf);
424	io_clear(&bounce->io);
425	free(bounce);
426
427	running -= 1;
428	bounce_drain();
429}
430
431static void
432bounce_io(struct io *io, int evt)
433{
434	struct bounce	*bounce = io->arg;
435	const char	*error;
436	char		*line, *msg;
437	int		 cont;
438	size_t		 len;
439
440	log_trace(TRACE_IO, "bounce: %p: %s %s",
441	    bounce, io_strevent(evt), io_strio(io));
442
443	switch (evt) {
444	case IO_DATAIN:
445	    nextline:
446		line = iobuf_getline(&bounce->iobuf, &len);
447		if (line == NULL) {
448			if (iobuf_len(&bounce->iobuf) >= SMTP_LINE_MAX) {
449				bounce_status(bounce, "150 Input too long");
450				bounce_free(bounce);
451				return;
452			}
453			iobuf_normalize(&bounce->iobuf);
454			break;
455		}
456
457		log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", bounce, line);
458
459		if ((error = parse_smtp_response(line, len, &msg, &cont))) {
460			bounce_status(bounce, "150 Bad response: %s", error);
461			bounce_free(bounce);
462			return;
463		}
464		if (cont)
465			goto nextline;
466
467		if (bounce->state == BOUNCE_CLOSE) {
468			bounce_free(bounce);
469			return;
470		}
471
472		if (line[0] != '2' && line[0] != '3') {		/* fail */
473			bounce_status(bounce, "%s", line);
474			bounce->state = BOUNCE_QUIT;
475		} else if (bounce->state == BOUNCE_QUIT) {	/* accepted */
476			bounce_status(bounce, "%s", line);
477		}
478
479		if (bounce_next(bounce) == -1) {
480			bounce_free(bounce);
481			return;
482		}
483
484		io_set_write(io);
485		break;
486
487	case IO_LOWAT:
488		if (bounce->state == BOUNCE_DATA_MESSAGE)
489			bounce_next(bounce);
490		if (iobuf_queued(&bounce->iobuf) == 0)
491			io_set_read(io);
492		break;
493
494	default:
495		bounce_status(bounce, "442 i/o error %i", evt);
496		bounce_free(bounce);
497		break;
498	}
499}
500