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