1 /* $OpenBSD: bounce.c,v 1.83 2020/12/31 08:27:15 martijn 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 46 enum { 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 58 struct 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 67 struct 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 78 struct 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 87 SPLAY_HEAD(bounce_message_tree, bounce_message); 88 static int bounce_message_cmp(const struct bounce_message *, 89 const struct bounce_message *); 90 SPLAY_PROTOTYPE(bounce_message_tree, bounce_message, sp_entry, 91 bounce_message_cmp); 92 93 static void bounce_drain(void); 94 static void bounce_send(struct bounce_session *, const char *, ...); 95 static int bounce_next_message(struct bounce_session *); 96 static int bounce_next(struct bounce_session *); 97 static void bounce_delivery(struct bounce_message *, int, const char *); 98 static void bounce_status(struct bounce_session *, const char *, ...); 99 static void bounce_io(struct io *, int, void *); 100 static void bounce_timeout(int, short, void *); 101 static void bounce_free(struct bounce_session *); 102 static const char *action_str(const struct delivery_bounce *); 103 104 static struct tree wait_fd; 105 static struct bounce_message_tree messages; 106 static TAILQ_HEAD(, bounce_message) pending; 107 108 static int nmessage = 0; 109 static int running = 0; 110 static struct event ev_timer; 111 112 static void 113 bounce_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 126 void 127 bounce_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_DELIVERED; 154 break; 155 case ESC_STATUS_TEMPFAIL: 156 key.bounce.type = B_DELAYED; 157 break; 158 default: 159 key.bounce.type = B_FAILED; 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", 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 log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, be->report); 200 201 msg->timeout = time(NULL) + 1; 202 TAILQ_INSERT_TAIL(&pending, msg, entry); 203 204 stat_increment("bounce.envelope", 1); 205 bounce_drain(); 206 } 207 208 void 209 bounce_fd(int fd) 210 { 211 struct bounce_session *s; 212 struct bounce_message *msg; 213 214 log_debug("debug: bounce: got enqueue socket %d", fd); 215 216 if (fd == -1 || TAILQ_EMPTY(&pending)) { 217 log_debug("debug: bounce: cancelling"); 218 if (fd != -1) 219 close(fd); 220 running -= 1; 221 bounce_drain(); 222 return; 223 } 224 225 msg = TAILQ_FIRST(&pending); 226 227 s = xcalloc(1, sizeof(*s)); 228 s->smtpname = xstrdup(msg->smtpname); 229 s->state = BOUNCE_EHLO; 230 s->io = io_new(); 231 io_set_callback(s->io, bounce_io, s); 232 io_set_fd(s->io, fd); 233 io_set_timeout(s->io, 30000); 234 io_set_read(s->io); 235 s->boundary = generate_uid(); 236 237 log_debug("debug: bounce: new session %p", s); 238 stat_increment("bounce.session", 1); 239 } 240 241 static void 242 bounce_timeout(int fd, short ev, void *arg) 243 { 244 log_debug("debug: bounce: timeout"); 245 246 bounce_drain(); 247 } 248 249 static void 250 bounce_drain() 251 { 252 struct bounce_message *msg; 253 struct timeval tv; 254 time_t t; 255 256 log_debug("debug: bounce: drain: nmessage=%d running=%d", 257 nmessage, running); 258 259 while (1) { 260 if (running >= BOUNCE_MAXRUN) { 261 log_debug("debug: bounce: max session reached"); 262 return; 263 } 264 265 if (nmessage == 0) { 266 log_debug("debug: bounce: no more messages"); 267 return; 268 } 269 270 if (running >= nmessage) { 271 log_debug("debug: bounce: enough sessions running"); 272 return; 273 } 274 275 if ((msg = TAILQ_FIRST(&pending)) == NULL) { 276 log_debug("debug: bounce: no more pending messages"); 277 return; 278 } 279 280 t = time(NULL); 281 if (msg->timeout > t) { 282 log_debug("debug: bounce: next message not ready yet"); 283 if (!evtimer_pending(&ev_timer, NULL)) { 284 log_debug("debug: bounce: setting timer"); 285 tv.tv_sec = msg->timeout - t; 286 tv.tv_usec = 0; 287 evtimer_add(&ev_timer, &tv); 288 } 289 return; 290 } 291 292 log_debug("debug: bounce: requesting new enqueue socket..."); 293 m_compose(p_dispatcher, IMSG_QUEUE_SMTP_SESSION, 0, 0, -1, NULL, 0); 294 295 running += 1; 296 } 297 } 298 299 static void 300 bounce_send(struct bounce_session *s, const char *fmt, ...) 301 { 302 va_list ap; 303 char *p; 304 int len; 305 306 va_start(ap, fmt); 307 if ((len = vasprintf(&p, fmt, ap)) == -1) 308 fatal("bounce: vasprintf"); 309 va_end(ap); 310 311 log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", s, p); 312 313 io_xprintf(s->io, "%s\r\n", p); 314 315 free(p); 316 } 317 318 static const char * 319 bounce_duration(long long int d) 320 { 321 static char buf[32]; 322 323 if (d < 60) { 324 (void)snprintf(buf, sizeof buf, "%lld second%s", d, 325 (d == 1) ? "" : "s"); 326 } else if (d < 3600) { 327 d = d / 60; 328 (void)snprintf(buf, sizeof buf, "%lld minute%s", d, 329 (d == 1) ? "" : "s"); 330 } 331 else if (d < 3600 * 24) { 332 d = d / 3600; 333 (void)snprintf(buf, sizeof buf, "%lld hour%s", d, 334 (d == 1) ? "" : "s"); 335 } 336 else { 337 d = d / (3600 * 24); 338 (void)snprintf(buf, sizeof buf, "%lld day%s", d, 339 (d == 1) ? "" : "s"); 340 } 341 return (buf); 342 } 343 344 #define NOTICE_INTRO \ 345 " Hi!\r\n\r\n" \ 346 " This is the MAILER-DAEMON, please DO NOT REPLY to this email.\r\n" 347 348 const char *notice_error = 349 " An error has occurred while attempting to deliver a message for\r\n" 350 " the following list of recipients:\r\n\r\n"; 351 352 const char *notice_warning = 353 " A message is delayed for more than %s for the following\r\n" 354 " list of recipients:\r\n\r\n"; 355 356 const char *notice_warning2 = 357 " Please note that this is only a temporary failure report.\r\n" 358 " The message is kept in the queue for up to %s.\r\n" 359 " You DO NOT NEED to re-send the message to these recipients.\r\n\r\n"; 360 361 const char *notice_success = 362 " Your message was successfully delivered to these recipients.\r\n\r\n"; 363 364 const char *notice_relay = 365 " Your message was relayed to these recipients.\r\n\r\n"; 366 367 static int 368 bounce_next_message(struct bounce_session *s) 369 { 370 struct bounce_message *msg; 371 char buf[LINE_MAX]; 372 int fd; 373 time_t now; 374 375 again: 376 377 now = time(NULL); 378 379 TAILQ_FOREACH(msg, &pending, entry) { 380 if (msg->timeout > now) 381 continue; 382 if (strcmp(msg->smtpname, s->smtpname)) 383 continue; 384 break; 385 } 386 if (msg == NULL) 387 return (0); 388 389 TAILQ_REMOVE(&pending, msg, entry); 390 SPLAY_REMOVE(bounce_message_tree, &messages, msg); 391 392 if ((fd = queue_message_fd_r(msg->msgid)) == -1) { 393 bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, 394 "Could not open message fd"); 395 goto again; 396 } 397 398 if ((s->msgfp = fdopen(fd, "r")) == NULL) { 399 (void)snprintf(buf, sizeof(buf), "fdopen: %s", strerror(errno)); 400 log_warn("warn: bounce: fdopen"); 401 close(fd); 402 bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, buf); 403 goto again; 404 } 405 406 s->msg = msg; 407 return (1); 408 } 409 410 static int 411 bounce_next(struct bounce_session *s) 412 { 413 struct bounce_envelope *evp; 414 char *line = NULL; 415 size_t n, sz = 0; 416 ssize_t len; 417 418 switch (s->state) { 419 case BOUNCE_EHLO: 420 bounce_send(s, "EHLO %s", s->smtpname); 421 s->state = BOUNCE_MAIL; 422 break; 423 424 case BOUNCE_MAIL: 425 case BOUNCE_DATA_END: 426 log_debug("debug: bounce: %p: getting next message...", s); 427 if (bounce_next_message(s) == 0) { 428 log_debug("debug: bounce: %p: no more messages", s); 429 bounce_send(s, "QUIT"); 430 s->state = BOUNCE_CLOSE; 431 break; 432 } 433 log_debug("debug: bounce: %p: found message %08"PRIx32, 434 s, s->msg->msgid); 435 bounce_send(s, "MAIL FROM: <>"); 436 s->state = BOUNCE_RCPT; 437 break; 438 439 case BOUNCE_RCPT: 440 bounce_send(s, "RCPT TO: <%s>", s->msg->to); 441 s->state = BOUNCE_DATA; 442 break; 443 444 case BOUNCE_DATA: 445 bounce_send(s, "DATA"); 446 s->state = BOUNCE_DATA_NOTICE; 447 break; 448 449 case BOUNCE_DATA_NOTICE: 450 /* Construct an appropriate notice. */ 451 452 io_xprintf(s->io, 453 "Subject: Delivery status notification: %s\r\n" 454 "From: Mailer Daemon <MAILER-DAEMON@%s>\r\n" 455 "To: %s\r\n" 456 "Date: %s\r\n" 457 "MIME-Version: 1.0\r\n" 458 "Content-Type: multipart/mixed;" 459 "boundary=\"%16" PRIu64 "/%s\"\r\n" 460 "\r\n" 461 "This is a MIME-encapsulated message.\r\n" 462 "\r\n", 463 action_str(&s->msg->bounce), 464 s->smtpname, 465 s->msg->to, 466 time_to_text(time(NULL)), 467 s->boundary, 468 s->smtpname); 469 470 io_xprintf(s->io, 471 "--%16" PRIu64 "/%s\r\n" 472 "Content-Description: Notification\r\n" 473 "Content-Type: text/plain; charset=us-ascii\r\n" 474 "\r\n" 475 NOTICE_INTRO 476 "\r\n", 477 s->boundary, s->smtpname); 478 479 switch (s->msg->bounce.type) { 480 case B_FAILED: 481 io_xprint(s->io, notice_error); 482 break; 483 case B_DELAYED: 484 io_xprintf(s->io, notice_warning, 485 bounce_duration(s->msg->bounce.delay)); 486 break; 487 case B_DELIVERED: 488 io_xprint(s->io, s->msg->bounce.mta_without_dsn ? 489 notice_relay : notice_success); 490 break; 491 default: 492 log_warn("warn: bounce: unknown bounce_type"); 493 } 494 495 TAILQ_FOREACH(evp, &s->msg->envelopes, entry) { 496 io_xprint(s->io, evp->report); 497 io_xprint(s->io, "\r\n"); 498 } 499 io_xprint(s->io, "\r\n"); 500 501 if (s->msg->bounce.type == B_DELAYED) 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:\r\n" 507 "\r\n"); 508 509 io_xprintf(s->io, 510 "--%16" PRIu64 "/%s\r\n" 511 "Content-Description: Delivery Report\r\n" 512 "Content-Type: message/delivery-status\r\n" 513 "\r\n", 514 s->boundary, s->smtpname); 515 516 io_xprintf(s->io, 517 "Reporting-MTA: dns; %s\r\n" 518 "\r\n", 519 s->smtpname); 520 521 TAILQ_FOREACH(evp, &s->msg->envelopes, entry) { 522 io_xprintf(s->io, 523 "Final-Recipient: rfc822; %s@%s\r\n" 524 "Action: %s\r\n" 525 "Status: %s\r\n" 526 "\r\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\r\n" 542 "Content-Description: Message headers\r\n" 543 "Content-Type: text/rfc822-headers\r\n" 544 "\r\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_DELIVERED && 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 "\r\n--%16" PRIu64 "/%s--\r\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\r\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 "\r\n--%16" PRIu64 "/%s--\r\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 606 static void 607 bounce_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 660 static void 661 bounce_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 691 static void 692 bounce_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 706 static void 707 bounce_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 /* Strip trailing '\r' */ 732 if (len && line[len - 1] == '\r') 733 line[--len] = '\0'; 734 735 log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", s, line); 736 737 if ((error = parse_smtp_response(line, len, &msg, &cont))) { 738 bounce_status(s, "Bad response: %s", error); 739 bounce_free(s); 740 return; 741 } 742 if (cont) 743 goto nextline; 744 745 if (s->state == BOUNCE_CLOSE) { 746 bounce_free(s); 747 return; 748 } 749 750 if (line[0] != '2' && line[0] != '3') { /* fail */ 751 bounce_status(s, "%s", line); 752 s->state = BOUNCE_QUIT; 753 } else if (s->state == BOUNCE_DATA_END) { /* accepted */ 754 bounce_status(s, "%s", line); 755 } 756 757 if (bounce_next(s) == -1) { 758 bounce_free(s); 759 return; 760 } 761 762 io_set_write(io); 763 break; 764 765 case IO_LOWAT: 766 if (s->state == BOUNCE_DATA_MESSAGE) 767 if (bounce_next(s) == -1) { 768 bounce_free(s); 769 return; 770 } 771 if (io_queued(s->io) == 0) 772 io_set_read(io); 773 break; 774 775 default: 776 bounce_status(s, "442 i/o error %d", evt); 777 bounce_free(s); 778 break; 779 } 780 } 781 782 static int 783 bounce_message_cmp(const struct bounce_message *a, 784 const struct bounce_message *b) 785 { 786 int r; 787 788 if (a->msgid < b->msgid) 789 return (-1); 790 if (a->msgid > b->msgid) 791 return (1); 792 if ((r = strcmp(a->smtpname, b->smtpname))) 793 return (r); 794 795 return memcmp(&a->bounce, &b->bounce, sizeof (a->bounce)); 796 } 797 798 static const char * 799 action_str(const struct delivery_bounce *b) 800 { 801 switch (b->type) { 802 case B_FAILED: 803 return ("failed"); 804 case B_DELAYED: 805 return ("delayed"); 806 case B_DELIVERED: 807 if (b->mta_without_dsn) 808 return ("relayed"); 809 810 return ("delivered"); 811 default: 812 log_warn("warn: bounce: unknown bounce_type"); 813 return (""); 814 } 815 } 816 817 SPLAY_GENERATE(bounce_message_tree, bounce_message, sp_entry, 818 bounce_message_cmp); 819