bounce.c revision 1.11
1/*	$OpenBSD: bounce.c,v 1.11 2009/11/05 12:08:41 jsing 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 <unistd.h>
34
35#include "smtpd.h"
36#include "client.h"
37
38struct client_ctx {
39	struct event		 ev;
40	struct message		 m;
41	struct smtp_client	*sp;
42};
43
44void		 bounce_event(int, short, void *);
45
46void
47bounce_process(struct smtpd *env, struct message *message)
48{
49	imsg_compose_event(env->sc_ievs[PROC_SMTP], IMSG_SMTP_ENQUEUE, 0, 0, -1,
50		message, sizeof(*message));
51}
52
53int
54bounce_session(struct smtpd *env, int fd, struct message *messagep)
55{
56	struct client_ctx	*cc = NULL;
57	int			 msgfd = -1;
58	char			*reason;
59
60	/* init smtp session */
61	if ((cc = calloc(1, sizeof(*cc))) == NULL)
62		goto fail;
63	if ((cc->sp = client_init(fd, env->sc_hostname)) == NULL)
64		goto fail;
65	cc->m = *messagep;
66
67	if (client_ssl_optional(cc->sp) < 0)
68		goto fail;
69
70	/* assign recipient */
71	if (client_rcpt(cc->sp, "%s@%s", messagep->sender.user,
72	    messagep->sender.domain) < 0)
73		goto fail;
74
75	/* Construct an appropriate reason line. */
76	reason = messagep->session_errorline;
77	if (strlen(reason) > 4 && (*reason == '1' || *reason == '6'))
78		reason += 4;
79
80	/* create message header */
81	/* XXX - The Date: header should be added during SMTP pickup. */
82	if (client_data_printf(cc->sp,
83	    "Subject: Delivery status notification\n"
84	    "From: Mailer Daemon <MAILER-DAEMON@%s>\n"
85	    "To: %s@%s\n"
86	    "Date: %s\n"
87	    "\n"
88	    "Hi !\n"
89	    "\n"
90	    "This is the MAILER-DAEMON, please DO NOT REPLY to this e-mail.\n"
91	    "An error has occurred while attempting to deliver a message.\n"
92	    "\n"
93	    "Recipient: %s@%s\n"
94	    "Reason:\n"
95	    "%s\n"
96	    "\n"
97	    "Below is a copy of the original message:\n"
98	    "\n",
99	    env->sc_hostname,
100	    messagep->sender.user, messagep->sender.domain,
101	    time_to_text(time(NULL)),
102	    messagep->recipient.user, messagep->recipient.domain,
103	    reason) < 0)
104		goto fail;
105
106	/* append original message */
107	if ((msgfd = queue_open_message_file(messagep->message_id)) == -1)
108		goto fail;
109	if (client_data_fd(cc->sp, msgfd) < 0)
110		goto fail;
111	close(msgfd);
112	msgfd = -1;
113
114	/* setup event */
115	session_socket_blockmode(fd, BM_NONBLOCK);
116	event_set(&cc->ev, fd, EV_WRITE, bounce_event, cc);
117	event_add(&cc->ev, client_timeout(cc->sp));
118
119	return 1;
120fail:
121	close(msgfd);
122	if (cc && cc->sp)
123		client_close(cc->sp);
124	free(cc);
125	return 0;
126}
127
128void
129bounce_event(int fd, short event, void *p)
130{
131	struct client_ctx	*cc = p;
132	char			*ep = NULL;
133	int			 error = 0;
134	int			(*iofunc)(struct smtp_client *);
135
136	if (event & EV_TIMEOUT) {
137		message_set_errormsg(&cc->m, "150 timeout");
138		cc->m.status = S_MESSAGE_TEMPFAILURE;
139		queue_message_update(&cc->m);
140		client_close(cc->sp);
141		free(cc);
142		return;
143	}
144
145	if (event & EV_READ)
146		iofunc = client_read;
147	else
148		iofunc = client_write;
149
150	switch (iofunc(cc->sp)) {
151	case CLIENT_WANT_READ:
152		event_set(&cc->ev, fd, EV_READ, bounce_event, cc);
153		event_add(&cc->ev, client_timeout(cc->sp));
154		return;
155	case CLIENT_WANT_WRITE:
156		event_set(&cc->ev, fd, EV_WRITE, bounce_event, cc);
157		event_add(&cc->ev, client_timeout(cc->sp));
158		return;
159	case CLIENT_ERROR:
160		error = 1;
161	case CLIENT_RCPT_FAIL:
162	case CLIENT_DONE:
163		break;
164	}
165
166	if (error)
167		ep = client_strerror(cc->sp);
168	else
169		ep = client_reply(cc->sp);
170
171	if (*ep == '2')
172		queue_remove_envelope(&cc->m);
173	else {
174		if (*ep == '5')
175			cc->m.status = S_MESSAGE_PERMFAILURE;
176		else
177			cc->m.status = S_MESSAGE_TEMPFAILURE;
178		message_set_errormsg(&cc->m, "%s", ep);
179		queue_message_update(&cc->m);
180	}
181
182	client_close(cc->sp);
183	free(cc);
184}
185