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