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