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