bounce.c revision 1.71
1/*	$OpenBSD: bounce.c,v 1.71 2015/12/24 16:54:37 mmcc 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#include <limits.h>
39
40#include "smtpd.h"
41#include "log.h"
42
43#define BOUNCE_MAXRUN	2
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_DATA_END,
54	BOUNCE_QUIT,
55	BOUNCE_CLOSE,
56};
57
58struct bounce_envelope {
59	TAILQ_ENTRY(bounce_envelope)	 entry;
60	uint64_t			 id;
61	struct mailaddr			 dest;
62	char				*report;
63	uint8_t				 esc_class;
64	uint8_t				 esc_code;
65};
66
67struct bounce_message {
68	SPLAY_ENTRY(bounce_message)	 sp_entry;
69	TAILQ_ENTRY(bounce_message)	 entry;
70	uint32_t			 msgid;
71	struct delivery_bounce		 bounce;
72	char				*smtpname;
73	char				*to;
74	time_t				 timeout;
75	TAILQ_HEAD(, bounce_envelope)	 envelopes;
76};
77
78struct bounce_session {
79	char				*smtpname;
80	struct bounce_message		*msg;
81	FILE				*msgfp;
82	int				 state;
83	struct iobuf			 iobuf;
84	struct io			 io;
85	uint64_t			 boundary;
86};
87
88SPLAY_HEAD(bounce_message_tree, bounce_message);
89static int bounce_message_cmp(const struct bounce_message *,
90    const struct bounce_message *);
91SPLAY_PROTOTYPE(bounce_message_tree, bounce_message, sp_entry,
92    bounce_message_cmp);
93
94static void bounce_drain(void);
95static void bounce_send(struct bounce_session *, const char *, ...);
96static int  bounce_next_message(struct bounce_session *);
97static int  bounce_next(struct bounce_session *);
98static void bounce_delivery(struct bounce_message *, int, const char *);
99static void bounce_status(struct bounce_session *, const char *, ...);
100static void bounce_io(struct io *, int);
101static void bounce_timeout(int, short, void *);
102static void bounce_free(struct bounce_session *);
103static const char *action_str(const struct delivery_bounce *);
104
105static struct tree			wait_fd;
106static struct bounce_message_tree	messages;
107static TAILQ_HEAD(, bounce_message)	pending;
108
109static int				nmessage = 0;
110static int				running = 0;
111static struct event			ev_timer;
112
113static void
114bounce_init(void)
115{
116	static int	init = 0;
117
118	if (init == 0) {
119		TAILQ_INIT(&pending);
120		SPLAY_INIT(&messages);
121		tree_init(&wait_fd);
122		evtimer_set(&ev_timer, bounce_timeout, NULL);
123		init = 1;
124	}
125}
126
127void
128bounce_add(uint64_t evpid)
129{
130	char			 buf[LINE_MAX], *line;
131	struct envelope		 evp;
132	struct bounce_message	 key, *msg;
133	struct bounce_envelope	*be;
134
135	bounce_init();
136
137	if (queue_envelope_load(evpid, &evp) == 0) {
138		m_create(p_scheduler, IMSG_QUEUE_DELIVERY_PERMFAIL, 0, 0, -1);
139		m_add_evpid(p_scheduler, evpid);
140		m_close(p_scheduler);
141		return;
142	}
143
144	if (evp.type != D_BOUNCE)
145		errx(1, "bounce: evp:%016" PRIx64 " is not of type D_BOUNCE!",
146		    evp.id);
147
148	key.msgid = evpid_to_msgid(evpid);
149	key.bounce = evp.agent.bounce;
150	key.smtpname = evp.smtpname;
151
152	if (evp.errorline[0] == '4')
153		key.bounce.type = B_WARNING;
154	else if (evp.errorline[0] == '5')
155		key.bounce.type = B_ERROR;
156	else
157		key.bounce.type = B_DSN;
158
159	key.bounce.dsn_ret = evp.dsn_ret;
160	key.bounce.expire = evp.expire;
161	msg = SPLAY_FIND(bounce_message_tree, &messages, &key);
162	if (msg == NULL) {
163		msg = xcalloc(1, sizeof(*msg), "bounce_add");
164		msg->msgid = key.msgid;
165		msg->bounce = key.bounce;
166
167		TAILQ_INIT(&msg->envelopes);
168
169		msg->smtpname = xstrdup(evp.smtpname, "bounce_add");
170		(void)snprintf(buf, sizeof(buf), "%s@%s", evp.sender.user,
171		    evp.sender.domain);
172		msg->to = xstrdup(buf, "bounce_add");
173		nmessage += 1;
174		SPLAY_INSERT(bounce_message_tree, &messages, msg);
175		log_debug("debug: bounce: new message %08" PRIx32,
176		    msg->msgid);
177		stat_increment("bounce.message", 1);
178	} else
179		TAILQ_REMOVE(&pending, msg, entry);
180
181	line = evp.errorline;
182	if (strlen(line) > 4 && (*line == '1' || *line == '6'))
183		line += 4;
184	(void)snprintf(buf, sizeof(buf), "%s@%s: %s\n", evp.dest.user,
185	    evp.dest.domain, line);
186
187	be = xmalloc(sizeof *be, "bounce_add");
188	be->id = evpid;
189	be->report = xstrdup(buf, "bounce_add");
190	(void)strlcpy(be->dest.user, evp.dest.user, sizeof(be->dest.user));
191	(void)strlcpy(be->dest.domain, evp.dest.domain,
192	    sizeof(be->dest.domain));
193	be->esc_class = evp.esc_class;
194	be->esc_code = evp.esc_code;
195	TAILQ_INSERT_TAIL(&msg->envelopes, be, entry);
196	buf[strcspn(buf, "\n")] = '\0';
197	log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, buf);
198
199	msg->timeout = time(NULL) + 1;
200	TAILQ_INSERT_TAIL(&pending, msg, entry);
201
202	stat_increment("bounce.envelope", 1);
203	bounce_drain();
204}
205
206void
207bounce_fd(int fd)
208{
209	struct bounce_session	*s;
210	struct bounce_message	*msg;
211
212	log_debug("debug: bounce: got enqueue socket %d", fd);
213
214	if (fd == -1 || TAILQ_EMPTY(&pending)) {
215		log_debug("debug: bounce: cancelling");
216		if (fd != -1)
217			close(fd);
218		running -= 1;
219		bounce_drain();
220		return;
221	}
222
223	msg = TAILQ_FIRST(&pending);
224
225	s = xcalloc(1, sizeof(*s), "bounce_fd");
226	s->smtpname = xstrdup(msg->smtpname, "bounce_fd");
227	s->state = BOUNCE_EHLO;
228	iobuf_xinit(&s->iobuf, 0, 0, "bounce_run");
229	io_init(&s->io, fd, s, bounce_io, &s->iobuf);
230	io_set_timeout(&s->io, 30000);
231	io_set_read(&s->io);
232	s->boundary = generate_uid();
233
234	log_debug("debug: bounce: new session %p", s);
235	stat_increment("bounce.session", 1);
236}
237
238static void
239bounce_timeout(int fd, short ev, void *arg)
240{
241	log_debug("debug: bounce: timeout");
242
243	bounce_drain();
244}
245
246static void
247bounce_drain()
248{
249	struct bounce_message	*msg;
250	struct timeval		 tv;
251	time_t			 t;
252
253	log_debug("debug: bounce: drain: nmessage=%d running=%d",
254	    nmessage, running);
255
256	while (1) {
257		if (running >= BOUNCE_MAXRUN) {
258			log_debug("debug: bounce: max session reached");
259			return;
260		}
261
262		if (nmessage == 0) {
263			log_debug("debug: bounce: no more messages");
264			return;
265		}
266
267		if (running >= nmessage) {
268			log_debug("debug: bounce: enough sessions running");
269			return;
270		}
271
272		if ((msg = TAILQ_FIRST(&pending)) == NULL) {
273			log_debug("debug: bounce: no more pending messages");
274			return;
275		}
276
277		t = time(NULL);
278		if (msg->timeout > t) {
279			log_debug("debug: bounce: next message not ready yet");
280			if (!evtimer_pending(&ev_timer, NULL)) {
281				log_debug("debug: bounce: setting timer");
282				tv.tv_sec = msg->timeout - t;
283				tv.tv_usec = 0;
284				evtimer_add(&ev_timer, &tv);
285			}
286			return;
287		}
288
289		log_debug("debug: bounce: requesting new enqueue socket...");
290		m_compose(p_pony, IMSG_QUEUE_SMTP_SESSION, 0, 0, -1, NULL, 0);
291
292		running += 1;
293	}
294}
295
296static void
297bounce_send(struct bounce_session *s, const char *fmt, ...)
298{
299	va_list	 ap;
300	char	*p;
301	int	 len;
302
303	va_start(ap, fmt);
304	if ((len = vasprintf(&p, fmt, ap)) == -1)
305		fatal("bounce: vasprintf");
306	va_end(ap);
307
308	log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", s, p);
309
310	iobuf_xfqueue(&s->iobuf, "bounce_send", "%s\n", p);
311
312	free(p);
313}
314
315static const char *
316bounce_duration(long long int d)
317{
318	static char buf[32];
319
320	if (d < 60) {
321		(void)snprintf(buf, sizeof buf, "%lld second%s", d,
322		    (d == 1) ? "" : "s");
323	} else if (d < 3600) {
324		d = d / 60;
325		(void)snprintf(buf, sizeof buf, "%lld minute%s", d,
326		    (d == 1) ? "" : "s");
327	}
328	else if (d < 3600 * 24) {
329		d = d / 3600;
330		(void)snprintf(buf, sizeof buf, "%lld hour%s", d,
331		    (d == 1) ? "" : "s");
332	}
333	else {
334		d = d / (3600 * 24);
335		(void)snprintf(buf, sizeof buf, "%lld day%s", d,
336		    (d == 1) ? "" : "s");
337	}
338	return (buf);
339}
340
341#define NOTICE_INTRO							    \
342	"    Hi!\n\n"							    \
343	"    This is the MAILER-DAEMON, please DO NOT REPLY to this email.\n"
344
345const char *notice_error =
346    "    An error has occurred while attempting to deliver a message for\n"
347    "    the following list of recipients:\n\n";
348
349const char *notice_warning =
350    "    A message is delayed for more than %s for the following\n"
351    "    list of recipients:\n\n";
352
353const char *notice_warning2 =
354    "    Please note that this is only a temporary failure report.\n"
355    "    The message is kept in the queue for up to %s.\n"
356    "    You DO NOT NEED to re-send the message to these recipients.\n\n";
357
358const char *notice_success =
359    "    Your message was successfully delivered to these recipients.\n\n";
360
361const char *notice_relay =
362    "    Your message was relayed to these recipients.\n\n";
363
364static int
365bounce_next_message(struct bounce_session *s)
366{
367	struct bounce_message	*msg;
368	char			 buf[LINE_MAX];
369	int			 fd;
370	time_t			 now;
371
372    again:
373
374	now = time(NULL);
375
376	TAILQ_FOREACH(msg, &pending, entry) {
377		if (msg->timeout > now)
378			continue;
379		if (strcmp(msg->smtpname, s->smtpname))
380			continue;
381		break;
382	}
383	if (msg == NULL)
384		return (0);
385
386	TAILQ_REMOVE(&pending, msg, entry);
387	SPLAY_REMOVE(bounce_message_tree, &messages, msg);
388
389	if ((fd = queue_message_fd_r(msg->msgid)) == -1) {
390		bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL,
391		    "Could not open message fd");
392		goto again;
393	}
394
395	if ((s->msgfp = fdopen(fd, "r")) == NULL) {
396		(void)snprintf(buf, sizeof(buf), "fdopen: %s", strerror(errno));
397		log_warn("warn: bounce: fdopen");
398		close(fd);
399		bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, buf);
400		goto again;
401	}
402
403	s->msg = msg;
404	return (1);
405}
406
407static int
408bounce_next(struct bounce_session *s)
409{
410	struct bounce_envelope	*evp;
411	char			*line = NULL;
412	size_t			 n, sz = 0;
413	ssize_t			 len;
414
415	switch (s->state) {
416	case BOUNCE_EHLO:
417		bounce_send(s, "EHLO %s", s->smtpname);
418		s->state = BOUNCE_MAIL;
419		break;
420
421	case BOUNCE_MAIL:
422	case BOUNCE_DATA_END:
423		log_debug("debug: bounce: %p: getting next message...", s);
424		if (bounce_next_message(s) == 0) {
425			log_debug("debug: bounce: %p: no more messages", s);
426			bounce_send(s, "QUIT");
427			s->state = BOUNCE_CLOSE;
428			break;
429		}
430		log_debug("debug: bounce: %p: found message %08"PRIx32,
431		    s, s->msg->msgid);
432		bounce_send(s, "MAIL FROM: <>");
433		s->state = BOUNCE_RCPT;
434		break;
435
436	case BOUNCE_RCPT:
437		bounce_send(s, "RCPT TO: <%s>", s->msg->to);
438		s->state = BOUNCE_DATA;
439		break;
440
441	case BOUNCE_DATA:
442		bounce_send(s, "DATA");
443		s->state = BOUNCE_DATA_NOTICE;
444		break;
445
446	case BOUNCE_DATA_NOTICE:
447		/* Construct an appropriate notice. */
448
449		iobuf_xfqueue(&s->iobuf, "bounce_next: HEADER",
450		    "Subject: Delivery status notification: %s\n"
451		    "From: Mailer Daemon <MAILER-DAEMON@%s>\n"
452		    "To: %s\n"
453		    "Date: %s\n"
454		    "MIME-Version: 1.0\n"
455		    "Content-Type: multipart/mixed;"
456		    "boundary=\"%16" PRIu64 "/%s\"\n"
457		    "\n"
458		    "This is a MIME-encapsulated message.\n"
459		    "\n",
460		    action_str(&s->msg->bounce),
461		    s->smtpname,
462		    s->msg->to,
463		    time_to_text(time(NULL)),
464		    s->boundary,
465		    s->smtpname);
466
467		iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
468		    "--%16" PRIu64 "/%s\n"
469		    "Content-Description: Notification\n"
470		    "Content-Type: text/plain; charset=us-ascii\n"
471		    "\n"
472		    NOTICE_INTRO
473		    "\n",
474		    s->boundary, s->smtpname);
475
476		switch (s->msg->bounce.type) {
477		case B_ERROR:
478			iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
479			    notice_error);
480			break;
481		case B_WARNING:
482			iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
483			    notice_warning,
484			    bounce_duration(s->msg->bounce.delay));
485			break;
486		case B_DSN:
487			iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
488			    s->msg->bounce.mta_without_dsn ?
489			    notice_relay : notice_success);
490			break;
491		default:
492			log_warn("warn: bounce: unknown bounce_type");
493		}
494
495		TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
496			iobuf_xfqueue(&s->iobuf,
497			    "bounce_next: DATA_NOTICE",
498			    "%s", evp->report);
499		}
500		iobuf_xfqueue(&s->iobuf, "bounce_next: DATA_NOTICE", "\n");
501
502		if (s->msg->bounce.type == B_WARNING)
503			iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
504			    notice_warning2,
505			    bounce_duration(s->msg->bounce.expire));
506
507		iobuf_xfqueue(&s->iobuf, "bounce_next: DATA_NOTICE",
508		    "    Below is a copy of the original message:\n"
509		    "\n");
510
511		iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
512		    "--%16" PRIu64 "/%s\n"
513		    "Content-Description: Delivery Report\n"
514		    "Content-Type: message/delivery-status\n"
515		    "\n",
516		    s->boundary, s->smtpname);
517
518		iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
519		    "Reporting-MTA: dns; %s\n"
520		    "\n",
521		    s->smtpname);
522
523		TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
524			iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
525			    "Final-Recipient: rfc822; %s@%s\n"
526			    "Action: %s\n"
527			    "Status: %s\n"
528			    "\n",
529			    evp->dest.user,
530			    evp->dest.domain,
531			    action_str(&s->msg->bounce),
532			    esc_code(evp->esc_class, evp->esc_code));
533		}
534
535		log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
536		    s, iobuf_queued(&s->iobuf));
537
538		s->state = BOUNCE_DATA_MESSAGE;
539		break;
540
541	case BOUNCE_DATA_MESSAGE:
542		iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
543		    "--%16" PRIu64 "/%s\n"
544		    "Content-Description: Message headers\n"
545		    "Content-Type: text/rfc822-headers\n"
546		    "\n",
547		    s->boundary, s->smtpname);
548
549		n = iobuf_queued(&s->iobuf);
550		while (iobuf_queued(&s->iobuf) < BOUNCE_HIWAT) {
551			if ((len = getline(&line, &sz, s->msgfp)) == -1)
552				break;
553			if (len == 1 && line[0] == '\n' && /* end of headers */
554			    s->msg->bounce.type == B_DSN &&
555			    s->msg->bounce.dsn_ret ==  DSN_RETHDRS) {
556				free(line);
557				fclose(s->msgfp);
558				s->msgfp = NULL;
559				iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
560				    "\n--%16" PRIu64 "/%s--\n", s->boundary,
561				    s->smtpname);
562				bounce_send(s, ".");
563				s->state = BOUNCE_DATA_END;
564				return (0);
565			}
566			line[len - 1] = '\0';
567			iobuf_xfqueue(&s->iobuf,
568			    "bounce_next: DATA_MESSAGE", "%s%s\n",
569			    (len == 2 && line[0] == '.') ? "." : "", line);
570		}
571		free(line);
572
573		if (ferror(s->msgfp)) {
574			fclose(s->msgfp);
575			s->msgfp = NULL;
576			bounce_delivery(s->msg, IMSG_QUEUE_DELIVERY_TEMPFAIL,
577			    "Error reading message");
578			s->msg = NULL;
579			return (-1);
580		}
581
582		iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
583		    "\n--%16" PRIu64 "/%s--\n", s->boundary, s->smtpname);
584
585		log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
586		    s, iobuf_queued(&s->iobuf) - n);
587
588		if (feof(s->msgfp)) {
589			fclose(s->msgfp);
590			s->msgfp = NULL;
591			bounce_send(s, ".");
592			s->state = BOUNCE_DATA_END;
593		}
594		break;
595
596	case BOUNCE_QUIT:
597		bounce_send(s, "QUIT");
598		s->state = BOUNCE_CLOSE;
599		break;
600
601	default:
602		fatalx("bounce: bad state");
603	}
604
605	return (0);
606}
607
608
609static void
610bounce_delivery(struct bounce_message *msg, int delivery, const char *status)
611{
612	struct bounce_envelope	*be;
613	struct envelope		 evp;
614	size_t			 n;
615	const char		*f;
616
617	n = 0;
618	while ((be = TAILQ_FIRST(&msg->envelopes))) {
619		if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL) {
620			if (queue_envelope_load(be->id, &evp) == 0) {
621				fatalx("could not reload envelope!");
622			}
623			evp.retry++;
624			evp.lasttry = msg->timeout;
625			envelope_set_errormsg(&evp, "%s", status);
626			queue_envelope_update(&evp);
627			m_create(p_scheduler, delivery, 0, 0, -1);
628			m_add_envelope(p_scheduler, &evp);
629			m_close(p_scheduler);
630		} else {
631			m_create(p_scheduler, delivery, 0, 0, -1);
632			m_add_evpid(p_scheduler, be->id);
633			m_close(p_scheduler);
634			queue_envelope_delete(be->id);
635		}
636		TAILQ_REMOVE(&msg->envelopes, be, entry);
637		free(be->report);
638		free(be);
639		n += 1;
640	}
641
642
643	if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL)
644		f = "TempFail";
645	else if (delivery == IMSG_QUEUE_DELIVERY_PERMFAIL)
646		f = "PermFail";
647	else
648		f = NULL;
649
650	if (f)
651		log_warnx("warn: %s injecting failure report on message %08"
652		    PRIx32 " to <%s> for %zu envelope%s: %s",
653		    f, msg->msgid, msg->to, n, n > 1 ? "s":"", status);
654
655	nmessage -= 1;
656	stat_decrement("bounce.message", 1);
657	stat_decrement("bounce.envelope", n);
658	free(msg->smtpname);
659	free(msg->to);
660	free(msg);
661}
662
663static void
664bounce_status(struct bounce_session *s, const char *fmt, ...)
665{
666	va_list		 ap;
667	char		*status;
668	int		 len, delivery;
669
670	/* Ignore if there is no message */
671	if (s->msg == NULL)
672		return;
673
674	va_start(ap, fmt);
675	if ((len = vasprintf(&status, fmt, ap)) == -1)
676		fatal("bounce: vasprintf");
677	va_end(ap);
678
679	if (*status == '2')
680		delivery = IMSG_QUEUE_DELIVERY_OK;
681	else if (*status == '5' || *status == '6')
682		delivery = IMSG_QUEUE_DELIVERY_PERMFAIL;
683	else
684		delivery = IMSG_QUEUE_DELIVERY_TEMPFAIL;
685
686	bounce_delivery(s->msg, delivery, status);
687	s->msg = NULL;
688	if (s->msgfp)
689		fclose(s->msgfp);
690
691	free(status);
692}
693
694static void
695bounce_free(struct bounce_session *s)
696{
697	log_debug("debug: bounce: %p: deleting session", s);
698
699	iobuf_clear(&s->iobuf);
700	io_clear(&s->io);
701
702	free(s->smtpname);
703	free(s);
704
705	running -= 1;
706	stat_decrement("bounce.session", 1);
707	bounce_drain();
708}
709
710static void
711bounce_io(struct io *io, int evt)
712{
713	struct bounce_session	*s = io->arg;
714	const char		*error;
715	char			*line, *msg;
716	int			 cont;
717	size_t			 len;
718
719	log_trace(TRACE_IO, "bounce: %p: %s %s", s, io_strevent(evt),
720	    io_strio(io));
721
722	switch (evt) {
723	case IO_DATAIN:
724	    nextline:
725		line = iobuf_getline(&s->iobuf, &len);
726		if (line == NULL && iobuf_len(&s->iobuf) >= LINE_MAX) {
727			bounce_status(s, "Input too long");
728			bounce_free(s);
729			return;
730		}
731
732		if (line == NULL) {
733			iobuf_normalize(&s->iobuf);
734			break;
735		}
736
737		log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", s, line);
738
739		if ((error = parse_smtp_response(line, len, &msg, &cont))) {
740			bounce_status(s, "Bad response: %s", error);
741			bounce_free(s);
742			return;
743		}
744		if (cont)
745			goto nextline;
746
747		if (s->state == BOUNCE_CLOSE) {
748			bounce_free(s);
749			return;
750		}
751
752		if (line[0] != '2' && line[0] != '3') {		/* fail */
753			bounce_status(s, "%s", line);
754			s->state = BOUNCE_QUIT;
755		} else if (s->state == BOUNCE_DATA_END) {	/* accepted */
756			bounce_status(s, "%s", line);
757		}
758
759		if (bounce_next(s) == -1) {
760			bounce_free(s);
761			return;
762		}
763
764		io_set_write(io);
765		break;
766
767	case IO_LOWAT:
768		if (s->state == BOUNCE_DATA_MESSAGE)
769			if (bounce_next(s) == -1) {
770				bounce_free(s);
771				return;
772			}
773		if (iobuf_queued(&s->iobuf) == 0)
774			io_set_read(io);
775		break;
776
777	default:
778		bounce_status(s, "442 i/o error %d", evt);
779		bounce_free(s);
780		break;
781	}
782}
783
784static int
785bounce_message_cmp(const struct bounce_message *a,
786    const struct bounce_message *b)
787{
788	int r;
789
790	if (a->msgid < b->msgid)
791		return (-1);
792	if (a->msgid > b->msgid)
793		return (1);
794	if ((r = strcmp(a->smtpname, b->smtpname)))
795		return (r);
796
797	return memcmp(&a->bounce, &b->bounce, sizeof (a->bounce));
798}
799
800static const char *
801action_str(const struct delivery_bounce *b)
802{
803	switch (b->type) {
804	case B_ERROR:
805		return ("error");
806	case B_WARNING:
807		return ("delayed");
808	case B_DSN:
809		if (b->mta_without_dsn)
810			return ("relayed");
811
812		return ("success");
813	default:
814		log_warn("warn: bounce: unknown bounce_type");
815		return ("");
816	}
817}
818
819SPLAY_GENERATE(bounce_message_tree, bounce_message, sp_entry,
820    bounce_message_cmp);
821