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