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