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