bounce.c revision 1.49
1/* $OpenBSD: bounce.c,v 1.49 2012/10/02 12:37:38 chl 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("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("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("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 if (iobuf_init(&bounce->iobuf, 0, 0) == -1) { 170 bounce_status(bounce, "iobuf_init"); 171 bounce_free(bounce); 172 close(msgfd); 173 return; 174 } 175 io_init(&bounce->io, fd, bounce, bounce_io, &bounce->iobuf); 176 io_set_timeout(&bounce->io, 30000); 177 io_set_read(&bounce->io); 178} 179 180static void 181bounce_commit(uint32_t msgid) 182{ 183 struct bounce *bounce; 184 185 log_trace(TRACE_BOUNCE, "bounce: commit msg:%08" PRIx32, msgid); 186 187 bounce = tree_xget(&bounces_by_msgid, msgid); 188 bounce->id = generate_uid(); 189 evtimer_del(&bounce->evt); 190 TAILQ_INSERT_TAIL(&runnable, bounce, entry); 191 192 bounce_drain(); 193} 194 195static void 196bounce_timeout(int fd, short ev, void *arg) 197{ 198 uint32_t *msgid = arg; 199 200 bounce_commit(*msgid); 201} 202 203static void 204bounce_drain() 205{ 206 struct bounce *bounce; 207 208 while ((bounce = TAILQ_FIRST(&runnable))) { 209 210 if (running >= BOUNCE_MAXRUN) { 211 log_debug("bounce: max session reached"); 212 return; 213 } 214 215 TAILQ_REMOVE(&runnable, bounce, entry); 216 if (TAILQ_FIRST(&bounce->envelopes) == NULL) { 217 log_debug("bounce: %p: no envelopes", bounce); 218 bounce_free(bounce); 219 continue; 220 } 221 222 tree_xset(&bounces_by_uid, bounce->id, bounce); 223 224 log_debug("bounce: %p: requesting enqueue socket with id 0x%016" PRIx64, 225 bounce, bounce->id); 226 227 imsg_compose_event(env->sc_ievs[PROC_SMTP], IMSG_SMTP_ENQUEUE, 228 0, 0, -1, &bounce->id, sizeof (bounce->id)); 229 230 running += 1; 231 } 232} 233 234static void 235bounce_send(struct bounce *bounce, const char *fmt, ...) 236{ 237 va_list ap; 238 char *p; 239 int len; 240 241 va_start(ap, fmt); 242 if ((len = vasprintf(&p, fmt, ap)) == -1) 243 fatal("bounce: vasprintf"); 244 va_end(ap); 245 246 log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", bounce, p); 247 248 iobuf_fqueue(&bounce->iobuf, "%s\n", p); 249 250 free(p); 251} 252 253/* This can simplified once we support PIPELINING */ 254static int 255bounce_next(struct bounce *bounce) 256{ 257 struct envelope *evp; 258 char *line; 259 size_t len, s; 260 261 switch(bounce->state) { 262 case BOUNCE_EHLO: 263 bounce_send(bounce, "EHLO %s", env->sc_hostname); 264 bounce->state = BOUNCE_MAIL; 265 break; 266 267 case BOUNCE_MAIL: 268 bounce_send(bounce, "MAIL FROM: <>"); 269 bounce->state = BOUNCE_RCPT; 270 break; 271 272 case BOUNCE_RCPT: 273 evp = TAILQ_FIRST(&bounce->envelopes); 274 bounce_send(bounce, "RCPT TO: <%s@%s>", 275 evp->sender.user, evp->sender.domain); 276 bounce->state = BOUNCE_DATA; 277 break; 278 279 case BOUNCE_DATA: 280 bounce_send(bounce, "DATA"); 281 bounce->state = BOUNCE_DATA_NOTICE; 282 break; 283 284 case BOUNCE_DATA_NOTICE: 285 /* Construct an appropriate reason line. */ 286 287 /* prevent more envelopes from being added to this bounce */ 288 tree_xpop(&bounces_by_msgid, bounce->msgid); 289 290 evp = TAILQ_FIRST(&bounce->envelopes); 291 292 iobuf_fqueue(&bounce->iobuf, 293 "Subject: Delivery status notification\n" 294 "From: Mailer Daemon <MAILER-DAEMON@%s>\n" 295 "To: %s@%s\n" 296 "Date: %s\n" 297 "\n" 298 "Hi !\n" 299 "\n" 300 "This is the MAILER-DAEMON, please DO NOT REPLY to this e-mail.\n" 301 "An error has occurred while attempting to deliver a message.\n" 302 "\n", 303 env->sc_hostname, 304 evp->sender.user, evp->sender.domain, 305 time_to_text(time(NULL))); 306 307 TAILQ_FOREACH(evp, &bounce->envelopes, entry) { 308 line = evp->errorline; 309 if (strlen(line) > 4 && (*line == '1' || *line == '6')) 310 line += 4; 311 iobuf_fqueue(&bounce->iobuf, 312 "Recipient: %s@%s\n" 313 "Reason: %s\n", 314 evp->dest.user, evp->dest.domain, line); 315 } 316 317 iobuf_fqueue(&bounce->iobuf, 318 "\n" 319 "Below is a copy of the original message:\n" 320 "\n"); 321 322 log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]", 323 bounce, iobuf_queued(&bounce->iobuf)); 324 325 bounce->state = BOUNCE_DATA_MESSAGE; 326 break; 327 328 case BOUNCE_DATA_MESSAGE: 329 330 s = iobuf_queued(&bounce->iobuf); 331 332 while (iobuf_len(&bounce->iobuf) < BOUNCE_HIWAT) { 333 line = fgetln(bounce->msgfp, &len); 334 if (line == NULL) 335 break; 336 line[len - 1] = '\0'; 337 if(len == 2 && line[0] == '.') 338 iobuf_queue(&bounce->iobuf, ".", 1); 339 iobuf_queue(&bounce->iobuf, line, len); 340 iobuf_queue(&bounce->iobuf, "\n", 1); 341 } 342 343 if (ferror(bounce->msgfp)) { 344 bounce_status(bounce, "460 Error reading message"); 345 return (-1); 346 } 347 348 log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]", 349 bounce, iobuf_queued(&bounce->iobuf) - s); 350 351 if (feof(bounce->msgfp)) { 352 bounce_send(bounce, "."); 353 bounce->state = BOUNCE_QUIT; 354 } 355 break; 356 357 case BOUNCE_QUIT: 358 bounce_send(bounce, "QUIT"); 359 bounce->state = BOUNCE_CLOSE; 360 break; 361 362 default: 363 fatalx("bounce: bad state"); 364 } 365 366 return (0); 367} 368 369static void 370bounce_status(struct bounce *bounce, const char *fmt, ...) 371{ 372 va_list ap; 373 char *status; 374 int len, msg; 375 struct envelope *evp; 376 377 /* ignore if the envelopes have already been updated/deleted */ 378 if (TAILQ_FIRST(&bounce->envelopes) == NULL) 379 return; 380 381 va_start(ap, fmt); 382 if ((len = vasprintf(&status, fmt, ap)) == -1) 383 fatal("bounce: vasprintf"); 384 va_end(ap); 385 386 if (*status == '2') 387 msg = IMSG_QUEUE_DELIVERY_OK; 388 else if (*status == '5' || *status == '6') 389 msg = IMSG_QUEUE_DELIVERY_PERMFAIL; 390 else 391 msg = IMSG_QUEUE_DELIVERY_TEMPFAIL; 392 393 while ((evp = TAILQ_FIRST(&bounce->envelopes))) { 394 if (msg == IMSG_QUEUE_DELIVERY_TEMPFAIL) { 395 evp->retry++; 396 envelope_set_errormsg(evp, "%s", status); 397 queue_envelope_update(evp); 398 imsg_compose_event(env->sc_ievs[PROC_SCHEDULER], msg, 0, 0, -1, 399 evp, sizeof *evp); 400 } else { 401 queue_envelope_delete(evp); 402 imsg_compose_event(env->sc_ievs[PROC_SCHEDULER], msg, 0, 0, -1, 403 &evp->id, sizeof evp->id); 404 } 405 TAILQ_REMOVE(&bounce->envelopes, evp, entry); 406 free(evp); 407 } 408 409 free(status); 410} 411 412static void 413bounce_free(struct bounce *bounce) 414{ 415 struct envelope *evp; 416 417 log_debug("bounce: %p: deleting session", bounce); 418 419 /* if the envelopes where not sent, it is still in the tree */ 420 tree_pop(&bounces_by_msgid, bounce->msgid); 421 422 while ((evp = TAILQ_FIRST(&bounce->envelopes))) { 423 TAILQ_REMOVE(&bounce->envelopes, evp, entry); 424 free(evp); 425 } 426 427 if (bounce->msgfp) 428 fclose(bounce->msgfp); 429 iobuf_clear(&bounce->iobuf); 430 io_clear(&bounce->io); 431 free(bounce); 432 433 running -= 1; 434 bounce_drain(); 435} 436 437static void 438bounce_io(struct io *io, int evt) 439{ 440 struct bounce *bounce = io->arg; 441 const char *error; 442 char *line, *msg; 443 int cont; 444 size_t len; 445 446 log_trace(TRACE_IO, "bounce: %p: %s %s", 447 bounce, io_strevent(evt), io_strio(io)); 448 449 switch (evt) { 450 case IO_DATAIN: 451 nextline: 452 line = iobuf_getline(&bounce->iobuf, &len); 453 if (line == NULL) { 454 if (iobuf_len(&bounce->iobuf) >= SMTP_LINE_MAX) { 455 bounce_status(bounce, "150 Input too long"); 456 bounce_free(bounce); 457 return; 458 } 459 iobuf_normalize(&bounce->iobuf); 460 break; 461 } 462 463 log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", bounce, line); 464 465 if ((error = parse_smtp_response(line, len, &msg, &cont))) { 466 bounce_status(bounce, "150 Bad response: %s", error); 467 bounce_free(bounce); 468 return; 469 } 470 if (cont) 471 goto nextline; 472 473 if (bounce->state == BOUNCE_CLOSE) { 474 bounce_free(bounce); 475 return; 476 } 477 478 if (line[0] != '2' && line[0] != '3') { /* fail */ 479 bounce_status(bounce, "%s", line); 480 bounce->state = BOUNCE_QUIT; 481 } else if (bounce->state == BOUNCE_QUIT) { /* accepted */ 482 bounce_status(bounce, "%s", line); 483 } 484 485 if (bounce_next(bounce) == -1) { 486 bounce_free(bounce); 487 return; 488 } 489 490 io_set_write(io); 491 break; 492 493 case IO_LOWAT: 494 if (bounce->state == BOUNCE_DATA_MESSAGE) 495 bounce_next(bounce); 496 if (iobuf_queued(&bounce->iobuf) == 0) 497 io_set_read(io); 498 break; 499 500 default: 501 bounce_status(bounce, "442 i/o error %i", evt); 502 bounce_free(bounce); 503 break; 504 } 505} 506