bounce.c revision 1.52
1/* $OpenBSD: bounce.c,v 1.52 2012/11/12 14:58:53 eric Exp $ */ 2 3/* 4 * Copyright (c) 2009 Gilles Chehade <gilles@openbsd.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/param.h> 25#include <sys/socket.h> 26 27#include <err.h> 28#include <errno.h> 29#include <event.h> 30#include <imsg.h> 31#include <inttypes.h> 32#include <pwd.h> 33#include <signal.h> 34#include <stdio.h> 35#include <stdlib.h> 36#include <string.h> 37#include <time.h> 38#include <unistd.h> 39 40#include "smtpd.h" 41#include "log.h" 42 43#define BOUNCE_MAXRUN 10 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_QUIT, 54 BOUNCE_CLOSE, 55}; 56 57struct bounce { 58 TAILQ_ENTRY(bounce) entry; 59 uint64_t id; 60 uint32_t msgid; 61 TAILQ_HEAD(, envelope) envelopes; 62 size_t count; 63 FILE *msgfp; 64 int state; 65 struct iobuf iobuf; 66 struct io io; 67 68 struct event evt; 69}; 70 71static void bounce_drain(void); 72static void bounce_commit(uint32_t); 73static void bounce_send(struct bounce *, const char *, ...); 74static int bounce_next(struct bounce *); 75static void bounce_status(struct bounce *, const char *, ...); 76static void bounce_io(struct io *, int); 77static void bounce_timeout(int, short, void *); 78static void bounce_free(struct bounce *); 79 80static struct tree bounces_by_msgid = SPLAY_INITIALIZER(&bounces_by_msgid); 81static struct tree bounces_by_uid = SPLAY_INITIALIZER(&bounces_by_uid); 82 83static int running = 0; 84static TAILQ_HEAD(, bounce) runnable = TAILQ_HEAD_INITIALIZER(runnable); 85 86void 87bounce_add(uint64_t evpid) 88{ 89 struct envelope *evp; 90 struct bounce *bounce; 91 struct timeval tv; 92 93 evp = xcalloc(1, sizeof *evp, "bounce_add"); 94 95 if (queue_envelope_load(evpid, evp) == 0) { 96 evp->id = evpid; 97 imsg_compose_event(env->sc_ievs[PROC_SCHEDULER], 98 IMSG_QUEUE_DELIVERY_PERMFAIL, 0, 0, -1, evp, sizeof *evp); 99 free(evp); 100 return; 101 } 102 103 if (evp->type != D_BOUNCE) 104 errx(1, "bounce: evp:%016" PRIx64 " is not of type D_BOUNCE!", 105 evp->id); 106 evp->lasttry = time(NULL); 107 108 bounce = tree_get(&bounces_by_msgid, evpid_to_msgid(evpid)); 109 if (bounce == NULL) { 110 bounce = xcalloc(1, sizeof(*bounce), "bounce_add"); 111 bounce->msgid = evpid_to_msgid(evpid); 112 tree_xset(&bounces_by_msgid, bounce->msgid, bounce); 113 114 log_debug("debug: bounce: %p: new bounce for msg:%08" PRIx32, 115 bounce, bounce->msgid); 116 117 TAILQ_INIT(&bounce->envelopes); 118 evtimer_set(&bounce->evt, bounce_timeout, &bounce->msgid); 119 tv.tv_sec = 1; 120 tv.tv_usec = 0; 121 evtimer_add(&bounce->evt, &tv); 122 } 123 124 log_debug("debug: bounce: %p: adding evp:%16" PRIx64, bounce, evp->id); 125 126 TAILQ_INSERT_TAIL(&bounce->envelopes, evp, entry); 127 bounce->count += 1; 128 129 if (bounce->id) 130 return; 131 132 evtimer_del(&bounce->evt); 133 tv.tv_sec = 1; 134 tv.tv_usec = 0; 135 evtimer_add(&bounce->evt, &tv); 136} 137 138void 139bounce_run(uint64_t id, int fd) 140{ 141 struct bounce *bounce; 142 int msgfd; 143 144 log_trace(TRACE_BOUNCE, "bounce: run %016" PRIx64 " fd %i", id, fd); 145 146 bounce = tree_xpop(&bounces_by_uid, id); 147 148 if (fd == -1) { 149 bounce_status(bounce, "failed to receive enqueueing socket"); 150 bounce_free(bounce); 151 return; 152 } 153 154 if ((msgfd = queue_message_fd_r(bounce->msgid)) == -1) { 155 bounce_status(bounce, "could not open message fd"); 156 bounce_free(bounce); 157 return; 158 } 159 160 if ((bounce->msgfp = fdopen(msgfd, "r")) == NULL) { 161 log_warn("warn: bounce_run: fdopen"); 162 bounce_status(bounce, "error %i in fdopen", errno); 163 bounce_free(bounce); 164 close(msgfd); 165 return; 166 } 167 168 bounce->state = BOUNCE_EHLO; 169 iobuf_xinit(&bounce->iobuf, 0, 0, "bounce_run"); 170 io_init(&bounce->io, fd, bounce, bounce_io, &bounce->iobuf); 171 io_set_timeout(&bounce->io, 30000); 172 io_set_read(&bounce->io); 173} 174 175static void 176bounce_commit(uint32_t msgid) 177{ 178 struct bounce *bounce; 179 180 log_trace(TRACE_BOUNCE, "bounce: commit msg:%08" PRIx32, msgid); 181 182 bounce = tree_xget(&bounces_by_msgid, msgid); 183 bounce->id = generate_uid(); 184 evtimer_del(&bounce->evt); 185 TAILQ_INSERT_TAIL(&runnable, bounce, entry); 186 187 bounce_drain(); 188} 189 190static void 191bounce_timeout(int fd, short ev, void *arg) 192{ 193 uint32_t *msgid = arg; 194 195 bounce_commit(*msgid); 196} 197 198static void 199bounce_drain() 200{ 201 struct bounce *bounce; 202 203 while ((bounce = TAILQ_FIRST(&runnable))) { 204 205 if (running >= BOUNCE_MAXRUN) { 206 log_debug("debug: bounce: max session reached"); 207 return; 208 } 209 210 TAILQ_REMOVE(&runnable, bounce, entry); 211 if (TAILQ_FIRST(&bounce->envelopes) == NULL) { 212 log_debug("debug: bounce: %p: no envelopes", bounce); 213 bounce_free(bounce); 214 continue; 215 } 216 217 tree_xset(&bounces_by_uid, bounce->id, bounce); 218 219 log_debug("debug: bounce: %p: requesting enqueue socket with id 0x%016" PRIx64, 220 bounce, bounce->id); 221 222 imsg_compose_event(env->sc_ievs[PROC_SMTP], IMSG_SMTP_ENQUEUE, 223 0, 0, -1, &bounce->id, sizeof (bounce->id)); 224 225 running += 1; 226 } 227} 228 229static void 230bounce_send(struct bounce *bounce, const char *fmt, ...) 231{ 232 va_list ap; 233 char *p; 234 int len; 235 236 va_start(ap, fmt); 237 if ((len = vasprintf(&p, fmt, ap)) == -1) 238 fatal("bounce: vasprintf"); 239 va_end(ap); 240 241 log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", bounce, p); 242 243 iobuf_xfqueue(&bounce->iobuf, "bounce_send", "%s\n", p); 244 245 free(p); 246} 247 248/* This can simplified once we support PIPELINING */ 249static int 250bounce_next(struct bounce *bounce) 251{ 252 struct envelope *evp; 253 char *line; 254 size_t len, s; 255 256 switch(bounce->state) { 257 case BOUNCE_EHLO: 258 bounce_send(bounce, "EHLO %s", env->sc_hostname); 259 bounce->state = BOUNCE_MAIL; 260 break; 261 262 case BOUNCE_MAIL: 263 bounce_send(bounce, "MAIL FROM: <>"); 264 bounce->state = BOUNCE_RCPT; 265 break; 266 267 case BOUNCE_RCPT: 268 evp = TAILQ_FIRST(&bounce->envelopes); 269 bounce_send(bounce, "RCPT TO: <%s@%s>", 270 evp->sender.user, evp->sender.domain); 271 bounce->state = BOUNCE_DATA; 272 break; 273 274 case BOUNCE_DATA: 275 bounce_send(bounce, "DATA"); 276 bounce->state = BOUNCE_DATA_NOTICE; 277 break; 278 279 case BOUNCE_DATA_NOTICE: 280 /* Construct an appropriate reason line. */ 281 282 /* prevent more envelopes from being added to this bounce */ 283 tree_xpop(&bounces_by_msgid, bounce->msgid); 284 285 evp = TAILQ_FIRST(&bounce->envelopes); 286 287 iobuf_xfqueue(&bounce->iobuf, "bounce_next: DATA_NOTICE", 288 "Subject: Delivery status notification\n" 289 "From: Mailer Daemon <MAILER-DAEMON@%s>\n" 290 "To: %s@%s\n" 291 "Date: %s\n" 292 "\n" 293 "Hi !\n" 294 "\n" 295 "This is the MAILER-DAEMON, please DO NOT REPLY to this e-mail.\n" 296 "An error has occurred while attempting to deliver a message.\n" 297 "\n", 298 env->sc_hostname, 299 evp->sender.user, evp->sender.domain, 300 time_to_text(time(NULL))); 301 302 TAILQ_FOREACH(evp, &bounce->envelopes, entry) { 303 line = evp->errorline; 304 if (strlen(line) > 4 && (*line == '1' || *line == '6')) 305 line += 4; 306 iobuf_xfqueue(&bounce->iobuf, "bounce_next: DATA_NOTICE", 307 "Recipient: %s@%s\n" 308 "Reason: %s\n", 309 evp->dest.user, evp->dest.domain, line); 310 } 311 312 iobuf_xfqueue(&bounce->iobuf, "bounce_next: DATA_NOTICE", 313 "\n" 314 "Below is a copy of the original message:\n" 315 "\n"); 316 317 log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]", 318 bounce, iobuf_queued(&bounce->iobuf)); 319 320 bounce->state = BOUNCE_DATA_MESSAGE; 321 break; 322 323 case BOUNCE_DATA_MESSAGE: 324 325 s = iobuf_queued(&bounce->iobuf); 326 327 while (iobuf_len(&bounce->iobuf) < BOUNCE_HIWAT) { 328 line = fgetln(bounce->msgfp, &len); 329 if (line == NULL) 330 break; 331 line[len - 1] = '\0'; 332 iobuf_xfqueue(&bounce->iobuf, 333 "bounce_next: DATA_MESSAGE", "%s%s\n", 334 (len == 2 && line[0] == '.') ? "." : "", line); 335 } 336 337 if (ferror(bounce->msgfp)) { 338 bounce_status(bounce, "460 Error reading message"); 339 return (-1); 340 } 341 342 log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]", 343 bounce, iobuf_queued(&bounce->iobuf) - s); 344 345 if (feof(bounce->msgfp)) { 346 bounce_send(bounce, "."); 347 bounce->state = BOUNCE_QUIT; 348 } 349 break; 350 351 case BOUNCE_QUIT: 352 bounce_send(bounce, "QUIT"); 353 bounce->state = BOUNCE_CLOSE; 354 break; 355 356 default: 357 fatalx("bounce: bad state"); 358 } 359 360 return (0); 361} 362 363static void 364bounce_status(struct bounce *bounce, const char *fmt, ...) 365{ 366 va_list ap; 367 char *status; 368 int len, msg; 369 struct envelope *evp; 370 371 /* ignore if the envelopes have already been updated/deleted */ 372 if (TAILQ_FIRST(&bounce->envelopes) == NULL) 373 return; 374 375 va_start(ap, fmt); 376 if ((len = vasprintf(&status, fmt, ap)) == -1) 377 fatal("bounce: vasprintf"); 378 va_end(ap); 379 380 if (*status == '2') 381 msg = IMSG_QUEUE_DELIVERY_OK; 382 else if (*status == '5' || *status == '6') 383 msg = IMSG_QUEUE_DELIVERY_PERMFAIL; 384 else 385 msg = IMSG_QUEUE_DELIVERY_TEMPFAIL; 386 387 while ((evp = TAILQ_FIRST(&bounce->envelopes))) { 388 if (msg == IMSG_QUEUE_DELIVERY_TEMPFAIL) { 389 evp->retry++; 390 envelope_set_errormsg(evp, "%s", status); 391 queue_envelope_update(evp); 392 imsg_compose_event(env->sc_ievs[PROC_SCHEDULER], msg, 0, 0, -1, 393 evp, sizeof *evp); 394 } else { 395 queue_envelope_delete(evp); 396 imsg_compose_event(env->sc_ievs[PROC_SCHEDULER], msg, 0, 0, -1, 397 &evp->id, sizeof evp->id); 398 } 399 TAILQ_REMOVE(&bounce->envelopes, evp, entry); 400 free(evp); 401 } 402 403 free(status); 404} 405 406static void 407bounce_free(struct bounce *bounce) 408{ 409 struct envelope *evp; 410 411 log_debug("debug: bounce: %p: deleting session", bounce); 412 413 /* if the envelopes where not sent, it is still in the tree */ 414 tree_pop(&bounces_by_msgid, bounce->msgid); 415 416 while ((evp = TAILQ_FIRST(&bounce->envelopes))) { 417 TAILQ_REMOVE(&bounce->envelopes, evp, entry); 418 free(evp); 419 } 420 421 if (bounce->msgfp) 422 fclose(bounce->msgfp); 423 iobuf_clear(&bounce->iobuf); 424 io_clear(&bounce->io); 425 free(bounce); 426 427 running -= 1; 428 bounce_drain(); 429} 430 431static void 432bounce_io(struct io *io, int evt) 433{ 434 struct bounce *bounce = io->arg; 435 const char *error; 436 char *line, *msg; 437 int cont; 438 size_t len; 439 440 log_trace(TRACE_IO, "bounce: %p: %s %s", 441 bounce, io_strevent(evt), io_strio(io)); 442 443 switch (evt) { 444 case IO_DATAIN: 445 nextline: 446 line = iobuf_getline(&bounce->iobuf, &len); 447 if (line == NULL) { 448 if (iobuf_len(&bounce->iobuf) >= SMTP_LINE_MAX) { 449 bounce_status(bounce, "150 Input too long"); 450 bounce_free(bounce); 451 return; 452 } 453 iobuf_normalize(&bounce->iobuf); 454 break; 455 } 456 457 log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", bounce, line); 458 459 if ((error = parse_smtp_response(line, len, &msg, &cont))) { 460 bounce_status(bounce, "150 Bad response: %s", error); 461 bounce_free(bounce); 462 return; 463 } 464 if (cont) 465 goto nextline; 466 467 if (bounce->state == BOUNCE_CLOSE) { 468 bounce_free(bounce); 469 return; 470 } 471 472 if (line[0] != '2' && line[0] != '3') { /* fail */ 473 bounce_status(bounce, "%s", line); 474 bounce->state = BOUNCE_QUIT; 475 } else if (bounce->state == BOUNCE_QUIT) { /* accepted */ 476 bounce_status(bounce, "%s", line); 477 } 478 479 if (bounce_next(bounce) == -1) { 480 bounce_free(bounce); 481 return; 482 } 483 484 io_set_write(io); 485 break; 486 487 case IO_LOWAT: 488 if (bounce->state == BOUNCE_DATA_MESSAGE) 489 bounce_next(bounce); 490 if (iobuf_queued(&bounce->iobuf) == 0) 491 io_set_read(io); 492 break; 493 494 default: 495 bounce_status(bounce, "442 i/o error %i", evt); 496 bounce_free(bounce); 497 break; 498 } 499} 500