bounce.c revision 1.5
1/* $OpenBSD: bounce.c,v 1.5 2009/08/06 16:26:39 gilles Exp $ */ 2 3/* 4 * Copyright (c) 2009 Gilles Chehade <gilles@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/types.h> 20#include <sys/queue.h> 21#include <sys/tree.h> 22#include <sys/param.h> 23#include <sys/socket.h> 24 25#include <err.h> 26#include <event.h> 27#include <pwd.h> 28#include <signal.h> 29#include <stdio.h> 30#include <stdlib.h> 31#include <string.h> 32#include <unistd.h> 33 34#include "smtpd.h" 35 36void 37bounce_process(struct smtpd *env, struct message *message) 38{ 39 imsg_compose_event(env->sc_ievs[PROC_SMTP], IMSG_SMTP_ENQUEUE, 0, 0, -1, 40 message, sizeof(*message)); 41} 42 43int 44bounce_session(struct smtpd *env, int fd, struct message *messagep) 45{ 46 char *buf, *lbuf; 47 size_t len; 48 FILE *fp; 49 enum session_state state = S_INIT; 50 51 fp = fdopen(fd, "r+"); 52 if (fp == NULL) 53 goto fail; 54 55 lbuf = NULL; 56 while ((buf = fgetln(fp, &len))) { 57 if (buf[len - 1] == '\n') 58 buf[len - 1] = '\0'; 59 else { 60 if ((lbuf = malloc(len + 1)) == NULL) 61 err(1, "malloc"); 62 memcpy(lbuf, buf, len); 63 lbuf[len] = '\0'; 64 buf = lbuf; 65 } 66 if (! bounce_session_switch(env, fp, &state, buf, messagep)) 67 goto fail; 68 } 69 free(lbuf); 70 71 fclose(fp); 72 return 1; 73fail: 74 if (fp != NULL) 75 fclose(fp); 76 else 77 close(fd); 78 return 0; 79} 80 81int 82bounce_session_switch(struct smtpd *env, FILE *fp, enum session_state *state, char *line, 83 struct message *messagep) 84{ 85 switch (*state) { 86 case S_INIT: 87 if (strncmp(line, "220 ", 4) != 0) 88 return 0; 89 fprintf(fp, "HELO %s\r\n", env->sc_hostname); 90 *state = S_GREETED; 91 break; 92 93 case S_GREETED: 94 if (strncmp(line, "250 ", 4) != 0) 95 return 0; 96 97 fprintf(fp, "MAIL FROM: <MAILER-DAEMON@%s>\r\n", env->sc_hostname); 98 *state = S_MAIL; 99 break; 100 101 case S_MAIL: 102 if (strncmp(line, "250 ", 4) != 0) 103 return 0; 104 105 fprintf(fp, "RCPT TO: <%s@%s>\r\n", messagep->sender.user, 106 messagep->sender.domain); 107 *state = S_RCPT; 108 break; 109 110 case S_RCPT: 111 if (strncmp(line, "250 ", 4) != 0) 112 return 0; 113 114 fprintf(fp, "DATA\r\n"); 115 *state = S_DATA; 116 break; 117 118 case S_DATA: { 119 int msgfd; 120 FILE *srcfp; 121 122 if (strncmp(line, "354 ", 4) != 0) 123 return 0; 124 125 msgfd = queue_open_message_file(messagep->message_id); 126 if (msgfd == -1) 127 return 0; 128 129 srcfp = fdopen(msgfd, "r"); 130 if (srcfp == NULL) { 131 close(msgfd); 132 return 0; 133 } 134 135 fprintf(fp, "From: Mailer Daemon <MAILER-DAEMON@%s>\r\n", 136 env->sc_hostname); 137 fprintf(fp, "To: %s@%s\r\n", 138 messagep->sender.user, messagep->sender.domain); 139 fprintf(fp, "Subject: Delivery attempt failure\r\n"); 140 fprintf(fp, "\r\n"); 141 142 fprintf(fp, "Hi !\r\n"); 143 fprintf(fp, "This is the MAILER-DAEMON, please DO NOT REPLY to this e-mail.\r\n"); 144 fprintf(fp, "An error has occurred while attempting to deliver a message.\r\n"); 145 fprintf(fp, "\r\n"); 146 fprintf(fp, "Recipient: %s@%s\r\n", messagep->recipient.user, 147 messagep->recipient.domain); 148 fprintf(fp, "Reason:\r\n"); 149 fprintf(fp, "%s\r\n", messagep->session_errorline); 150 151 fprintf(fp, "\r\n"); 152 fprintf(fp, "Below is a copy of the original message:\r\n\r\n"); 153 154 if (! file_copy(fp, srcfp, NULL, 0, 1)) 155 return 0; 156 157 fprintf(fp, ".\r\n"); 158 159 *state = S_DONE; 160 break; 161 } 162 case S_DONE: 163 if (strncmp(line, "250 ", 4) != 0) 164 return 0; 165 166 fprintf(fp, "QUIT\r\n"); 167 *state = S_QUIT; 168 break; 169 170 case S_QUIT: 171 if (strncmp(line, "221 ", 4) != 0) 172 return 0; 173 174 break; 175 176 default: 177 errx(1, "bounce_session_switch: unknown state."); 178 } 179 180 fflush(fp); 181 return 1; 182} 183