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