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