bounce.c revision 1.21
1/* $OpenBSD: bounce.c,v 1.21 2010/06/01 19:47:08 jacekm Exp $ */ 2 3/* 4 * Copyright (c) 2009 Gilles Chehade <gilles@openbsd.org> 5 * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20#include <sys/types.h> 21#include <sys/queue.h> 22#include <sys/tree.h> 23#include <sys/param.h> 24#include <sys/socket.h> 25 26#include <err.h> 27#include <event.h> 28#include <pwd.h> 29#include <signal.h> 30#include <stdio.h> 31#include <stdlib.h> 32#include <string.h> 33#include <time.h> 34#include <unistd.h> 35 36#include "smtpd.h" 37#include "client.h" 38 39struct client_ctx { 40 struct event ev; 41 struct message m; 42 struct smtp_client *pcb; 43 struct smtpd *env; 44}; 45 46void bounce_event(int, short, void *); 47 48int 49bounce_session(struct smtpd *env, int fd, struct message *messagep) 50{ 51 struct client_ctx *cc = NULL; 52 int msgfd = -1; 53 char *reason; 54 55 /* get message content */ 56 if ((msgfd = queue_open_message_file(messagep->message_id)) == -1) 57 goto fail; 58 59 /* init smtp session */ 60 if ((cc = calloc(1, sizeof(*cc))) == NULL) 61 goto fail; 62 cc->pcb = client_init(fd, msgfd, env->sc_hostname, 1); 63 cc->env = env; 64 cc->m = *messagep; 65 66 client_ssl_optional(cc->pcb); 67 client_sender(cc->pcb, ""); 68 client_rcpt(cc->pcb, NULL, "%s@%s", messagep->sender.user, 69 messagep->sender.domain); 70 71 /* Construct an appropriate reason line. */ 72 reason = messagep->session_errorline; 73 if (strlen(reason) > 4 && (*reason == '1' || *reason == '6')) 74 reason += 4; 75 76 /* create message header */ 77 /* XXX - The Date: header should be added during SMTP pickup. */ 78 client_printf(cc->pcb, 79 "Subject: Delivery status notification\n" 80 "From: Mailer Daemon <MAILER-DAEMON@%s>\n" 81 "To: %s@%s\n" 82 "Date: %s\n" 83 "\n" 84 "Hi !\n" 85 "\n" 86 "This is the MAILER-DAEMON, please DO NOT REPLY to this e-mail.\n" 87 "An error has occurred while attempting to deliver a message.\n" 88 "\n" 89 "Recipient: %s@%s\n" 90 "Reason:\n" 91 "%s\n" 92 "\n" 93 "Below is a copy of the original message:\n" 94 "\n", 95 env->sc_hostname, 96 messagep->sender.user, messagep->sender.domain, 97 time_to_text(time(NULL)), 98 messagep->recipient.user, messagep->recipient.domain, 99 reason); 100 101 /* setup event */ 102 session_socket_blockmode(fd, BM_NONBLOCK); 103 event_set(&cc->ev, fd, EV_READ|EV_WRITE, bounce_event, cc); 104 event_add(&cc->ev, &cc->pcb->timeout); 105 106 return 1; 107fail: 108 close(msgfd); 109 if (cc && cc->pcb) 110 client_close(cc->pcb); 111 free(cc); 112 return 0; 113} 114 115void 116bounce_event(int fd, short event, void *p) 117{ 118 struct client_ctx *cc = p; 119 char *ep = NULL; 120 121 if (event & EV_TIMEOUT) { 122 ep = "150 timeout"; 123 goto out; 124 } 125 126 switch (client_talk(cc->pcb, event & EV_WRITE)) { 127 case CLIENT_STOP_WRITE: 128 goto ro; 129 case CLIENT_WANT_WRITE: 130 goto rw; 131 case CLIENT_RCPT_FAIL: 132 ep = cc->pcb->reply; 133 break; 134 case CLIENT_DONE: 135 ep = cc->pcb->status; 136 break; 137 default: 138 fatalx("bounce_event: unexpected code"); 139 } 140 141out: 142 if (*ep == '2') 143 queue_remove_envelope(&cc->m); 144 else { 145 if (*ep == '5' || *ep == '6') 146 cc->m.status = S_MESSAGE_PERMFAILURE; 147 else 148 cc->m.status = S_MESSAGE_TEMPFAILURE; 149 message_set_errormsg(&cc->m, "%s", ep); 150 queue_message_update(&cc->m); 151 } 152 153 cc->env->stats->runner.active--; 154 cc->env->stats->runner.bounces_active--; 155 client_close(cc->pcb); 156 free(cc); 157 return; 158 159ro: 160 event_set(&cc->ev, fd, EV_READ, bounce_event, cc); 161 event_add(&cc->ev, &cc->pcb->timeout); 162 return; 163 164rw: 165 event_set(&cc->ev, fd, EV_READ|EV_WRITE, bounce_event, cc); 166 event_add(&cc->ev, &cc->pcb->timeout); 167} 168