bounce.c revision 1.44
1/* $OpenBSD: bounce.c,v 1.44 2012/08/09 09:48:02 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 <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_HIWAT 65535 43 44enum { 45 BOUNCE_EHLO, 46 BOUNCE_MAIL, 47 BOUNCE_RCPT, 48 BOUNCE_DATA, 49 BOUNCE_DATA_NOTICE, 50 BOUNCE_DATA_MESSAGE, 51 BOUNCE_QUIT, 52 BOUNCE_CLOSE, 53}; 54 55struct bounce { 56 struct envelope evp; 57 FILE *msgfp; 58 int state; 59 struct iobuf iobuf; 60 struct io io; 61}; 62 63static void bounce_send(struct bounce *, const char *, ...); 64static int bounce_next(struct bounce *); 65static void bounce_status(struct bounce *, const char *, ...); 66static void bounce_io(struct io *, int); 67 68static void 69bounce_send(struct bounce *bounce, const char *fmt, ...) 70{ 71 va_list ap; 72 char *p; 73 int len; 74 75 va_start(ap, fmt); 76 if ((len = vasprintf(&p, fmt, ap)) == -1) 77 fatal("bounce: vasprintf"); 78 va_end(ap); 79 80 log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", bounce, p); 81 82 iobuf_fqueue(&bounce->iobuf, "%s\n", p); 83 84 free(p); 85} 86 87/* This can simplified once we support PIPELINING */ 88static int 89bounce_next(struct bounce *bounce) 90{ 91 char *line; 92 size_t len, s; 93 94 switch(bounce->state) { 95 case BOUNCE_EHLO: 96 bounce_send(bounce, "EHLO %s", env->sc_hostname); 97 bounce->state = BOUNCE_MAIL; 98 break; 99 100 case BOUNCE_MAIL: 101 bounce_send(bounce, "MAIL FROM: <>"); 102 bounce->state = BOUNCE_RCPT; 103 break; 104 105 case BOUNCE_RCPT: 106 bounce_send(bounce, "RCPT TO: <%s@%s>", 107 bounce->evp.sender.user, bounce->evp.sender.domain); 108 bounce->state = BOUNCE_DATA; 109 break; 110 111 case BOUNCE_DATA: 112 bounce_send(bounce, "DATA"); 113 bounce->state = BOUNCE_DATA_NOTICE; 114 break; 115 116 case BOUNCE_DATA_NOTICE: 117 /* Construct an appropriate reason line. */ 118 line = bounce->evp.errorline; 119 if (strlen(line) > 4 && (*line == '1' || *line == '6')) 120 line += 4; 121 122 iobuf_fqueue(&bounce->iobuf, 123 "Subject: Delivery status notification\n" 124 "From: Mailer Daemon <MAILER-DAEMON@%s>\n" 125 "To: %s@%s\n" 126 "Date: %s\n" 127 "\n" 128 "Hi !\n" 129 "\n" 130 "This is the MAILER-DAEMON, please DO NOT REPLY to this e-mail.\n" 131 "An error has occurred while attempting to deliver a message.\n" 132 "\n" 133 "Recipient: %s@%s\n" 134 "Reason:\n" 135 "%s\n" 136 "\n" 137 "Below is a copy of the original message:\n" 138 "\n", 139 env->sc_hostname, 140 bounce->evp.sender.user, bounce->evp.sender.domain, 141 time_to_text(time(NULL)), 142 bounce->evp.dest.user, bounce->evp.dest.domain, 143 line); 144 145 log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]", 146 bounce, iobuf_queued(&bounce->iobuf)); 147 148 bounce->state = BOUNCE_DATA_MESSAGE; 149 break; 150 151 case BOUNCE_DATA_MESSAGE: 152 153 s = iobuf_queued(&bounce->iobuf); 154 155 while (iobuf_len(&bounce->iobuf) < BOUNCE_HIWAT) { 156 line = fgetln(bounce->msgfp, &len); 157 if (line == NULL) 158 break; 159 line[len - 1] = '\0'; 160 if(len == 2 && line[0] == '.') 161 iobuf_queue(&bounce->iobuf, ".", 1); 162 iobuf_queue(&bounce->iobuf, line, len); 163 iobuf_queue(&bounce->iobuf, "\n", 1); 164 } 165 166 if (ferror(bounce->msgfp)) { 167 bounce_status(bounce, "460 Error reading message"); 168 return (-1); 169 } 170 171 log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]", 172 bounce, iobuf_queued(&bounce->iobuf) - s); 173 174 if (feof(bounce->msgfp)) { 175 bounce_send(bounce, "."); 176 bounce->state = BOUNCE_QUIT; 177 } 178 179 break; 180 181 case BOUNCE_QUIT: 182 bounce_send(bounce, "QUIT"); 183 bounce->state = BOUNCE_CLOSE; 184 break; 185 186 default: 187 fatalx("bounce: bad state"); 188 } 189 190 return (0); 191} 192 193static void 194bounce_status(struct bounce *bounce, const char *fmt, ...) 195{ 196 va_list ap; 197 char *status; 198 int len, msg; 199 struct envelope *evp; 200 201 /* ignore if the envelope has already been updated/deleted */ 202 if (bounce->evp.id == 0) 203 return; 204 205 va_start(ap, fmt); 206 if ((len = vasprintf(&status, fmt, ap)) == -1) 207 fatal("bounce: vasprintf"); 208 va_end(ap); 209 210 if (*status == '2') 211 msg = IMSG_QUEUE_DELIVERY_OK; 212 else if (*status == '5' || *status == '6') 213 msg = IMSG_QUEUE_DELIVERY_PERMFAIL; 214 else 215 msg = IMSG_QUEUE_DELIVERY_TEMPFAIL; 216 217 evp = &bounce->evp; 218 if (msg == IMSG_QUEUE_DELIVERY_TEMPFAIL) { 219 evp->retry++; 220 envelope_set_errormsg(evp, "%s", status); 221 queue_envelope_update(evp); 222 imsg_compose_event(env->sc_ievs[PROC_SCHEDULER], msg, 0, 0, -1, 223 evp, sizeof *evp); 224 } else { 225 queue_envelope_delete(evp); 226 imsg_compose_event(env->sc_ievs[PROC_SCHEDULER], msg, 0, 0, -1, 227 &evp->id, sizeof evp->id); 228 } 229 230 bounce->evp.id = 0; 231 free(status); 232} 233 234static void 235bounce_free(struct bounce *bounce) 236{ 237 log_debug("bounce: %p: deleting session", bounce); 238 239 fclose(bounce->msgfp); 240 iobuf_clear(&bounce->iobuf); 241 io_clear(&bounce->io); 242 free(bounce); 243} 244 245static void 246bounce_io(struct io *io, int evt) 247{ 248 struct bounce *bounce = io->arg; 249 const char *error; 250 char *line, *msg; 251 int cont; 252 size_t len; 253 254 log_trace(TRACE_IO, "bounce: %p: %s %s", 255 bounce, io_strevent(evt), io_strio(io)); 256 257 switch (evt) { 258 case IO_DATAIN: 259 nextline: 260 line = iobuf_getline(&bounce->iobuf, &len); 261 if (line == NULL) { 262 if (iobuf_len(&bounce->iobuf) >= SMTP_LINE_MAX) { 263 bounce_status(bounce, "150 Input too long"); 264 bounce_free(bounce); 265 return; 266 } 267 iobuf_normalize(&bounce->iobuf); 268 break; 269 } 270 271 log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", bounce, line); 272 273 if ((error = parse_smtp_response(line, len, &msg, &cont))) { 274 bounce_status(bounce, "150 Bad response: %s", error); 275 bounce_free(bounce); 276 return; 277 } 278 if (cont) 279 goto nextline; 280 281 if (bounce->state == BOUNCE_CLOSE) { 282 bounce_free(bounce); 283 return; 284 } 285 286 if (line[0] != '2' && line[0] != '3') { /* fail */ 287 bounce_status(bounce, "%s", line); 288 bounce->state = BOUNCE_QUIT; 289 } else if (bounce->state == BOUNCE_QUIT) { /* accepted */ 290 bounce_status(bounce, "%s", line); 291 } 292 293 if (bounce_next(bounce) == -1) { 294 bounce_free(bounce); 295 return; 296 } 297 298 io_set_write(io); 299 break; 300 301 case IO_LOWAT: 302 if (bounce->state == BOUNCE_DATA_MESSAGE) 303 bounce_next(bounce); 304 if (iobuf_queued(&bounce->iobuf) == 0) 305 io_set_read(io); 306 break; 307 308 default: 309 bounce_status(bounce, "442 i/o error %i", evt); 310 bounce_free(bounce); 311 break; 312 } 313} 314 315int 316bounce_session(int fd, struct envelope *evp) 317{ 318 struct bounce *bounce = NULL; 319 int msgfd = -1; 320 FILE *msgfp = NULL; 321 u_int32_t msgid; 322 323 msgid = evpid_to_msgid(evp->id); 324 325 log_debug("bounce: bouncing envelope id %016" PRIx64 "", evp->id); 326 327 /* get message content */ 328 if ((msgfd = queue_message_fd_r(msgid)) == -1) 329 return (0); 330 331 msgfp = fdopen(msgfd, "r"); 332 if (msgfp == NULL) { 333 log_warn("bounce: fdopen"); 334 close(msgfd); 335 return (0); 336 } 337 338 if ((bounce = calloc(1, sizeof(*bounce))) == NULL) { 339 log_warn("bounce: calloc"); 340 fclose(msgfp); 341 return (0); 342 } 343 344 bounce->evp = *evp; 345 bounce->msgfp = msgfp; 346 bounce->state = BOUNCE_EHLO; 347 348 iobuf_init(&bounce->iobuf, 0, 0); 349 io_init(&bounce->io, fd, bounce, bounce_io, &bounce->iobuf); 350 io_set_timeout(&bounce->io, 30000); 351 io_set_read(&bounce->io); 352 return (1); 353} 354 355int 356bounce_record_message(struct envelope *e, struct envelope *bounce) 357{ 358 if (e->type == D_BOUNCE) { 359 log_debug("mailer daemons loop detected !"); 360 return 0; 361 } 362 363 *bounce = *e; 364 bounce->type = D_BOUNCE; 365 bounce->retry = 0; 366 bounce->lasttry = 0; 367 return (queue_envelope_create(bounce)); 368} 369