bounce.c revision 1.29
1/*	$OpenBSD: bounce.c,v 1.29 2011/04/15 17:01:05 gilles 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 <imsg.h>
29#include <pwd.h>
30#include <signal.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <time.h>
35#include <unistd.h>
36
37#include "smtpd.h"
38#include "client.h"
39#include "log.h"
40
41struct client_ctx {
42	struct event		 ev;
43	struct message		 m;
44	struct smtp_client	*pcb;
45	struct smtpd		*env;
46	FILE			*msgfp;
47};
48
49int
50bounce_session(struct smtpd *env, int fd, struct message *messagep)
51{
52	struct client_ctx	*cc = NULL;
53	int			 msgfd = -1;
54	char			*reason;
55	FILE			*msgfp = NULL;
56	u_int32_t		 msgid;
57
58	msgid = evpid_to_msgid(messagep->evpid);
59
60	/* get message content */
61	if ((msgfd = queue_message_fd_r(env, Q_QUEUE, msgid)) == -1)
62		goto fail;
63	msgfp = fdopen(msgfd, "r");
64	if (msgfp == NULL)
65		fatal("fdopen");
66
67	/* init smtp session */
68	if ((cc = calloc(1, sizeof(*cc))) == NULL)
69		goto fail;
70	cc->pcb = client_init(fd, msgfp, env->sc_hostname, 1);
71	cc->env = env;
72	cc->m = *messagep;
73	cc->msgfp = msgfp;
74
75	client_ssl_optional(cc->pcb);
76	client_sender(cc->pcb, "");
77	client_rcpt(cc->pcb, NULL, "%s@%s", messagep->sender.user,
78	    messagep->sender.domain);
79
80	/* Construct an appropriate reason line. */
81	reason = messagep->session_errorline;
82	if (strlen(reason) > 4 && (*reason == '1' || *reason == '6'))
83		reason += 4;
84
85	/* create message header */
86	/* XXX - The Date: header should be added during SMTP pickup. */
87	client_printf(cc->pcb,
88	    "Subject: Delivery status notification\n"
89	    "From: Mailer Daemon <MAILER-DAEMON@%s>\n"
90	    "To: %s@%s\n"
91	    "Date: %s\n"
92	    "\n"
93	    "Hi !\n"
94	    "\n"
95	    "This is the MAILER-DAEMON, please DO NOT REPLY to this e-mail.\n"
96	    "An error has occurred while attempting to deliver a message.\n"
97	    "\n"
98	    "Recipient: %s@%s\n"
99	    "Reason:\n"
100	    "%s\n"
101	    "\n"
102	    "Below is a copy of the original message:\n"
103	    "\n",
104	    env->sc_hostname,
105	    messagep->sender.user, messagep->sender.domain,
106	    time_to_text(time(NULL)),
107	    messagep->recipient.user, messagep->recipient.domain,
108	    reason);
109
110	/* setup event */
111	session_socket_blockmode(fd, BM_NONBLOCK);
112	event_set(&cc->ev, fd, EV_READ|EV_WRITE, bounce_event, cc);
113	event_add(&cc->ev, &cc->pcb->timeout);
114
115	return 1;
116fail:
117	if (cc)
118		fclose(cc->msgfp);
119	else if (msgfd != -1)
120		close(msgfd);
121	free(cc);
122	return 0;
123}
124
125void
126bounce_event(int fd, short event, void *p)
127{
128	struct client_ctx	*cc = p;
129	char			*ep = NULL;
130
131	if (event & EV_TIMEOUT) {
132		ep = "150 timeout";
133		goto out;
134	}
135
136	switch (client_talk(cc->pcb, event & EV_WRITE)) {
137	case CLIENT_STOP_WRITE:
138		goto ro;
139	case CLIENT_WANT_WRITE:
140		goto rw;
141	case CLIENT_RCPT_FAIL:
142		ep = cc->pcb->reply;
143		break;
144	case CLIENT_DONE:
145		ep = cc->pcb->status;
146		break;
147	default:
148		fatalx("bounce_event: unexpected code");
149	}
150
151out:
152	if (*ep == '2')
153		queue_envelope_delete(cc->env, Q_QUEUE, &cc->m);
154	else {
155		if (*ep == '5' || *ep == '6')
156			cc->m.status = S_MESSAGE_PERMFAILURE;
157		else
158			cc->m.status = S_MESSAGE_TEMPFAILURE;
159		message_set_errormsg(&cc->m, "%s", ep);
160		queue_message_update(cc->env, &cc->m);
161	}
162
163	cc->env->stats->runner.active--;
164	cc->env->stats->runner.bounces_active--;
165	client_close(cc->pcb);
166	fclose(cc->msgfp);
167	free(cc);
168	return;
169
170ro:
171	event_set(&cc->ev, fd, EV_READ, bounce_event, cc);
172	event_add(&cc->ev, &cc->pcb->timeout);
173	return;
174
175rw:
176	event_set(&cc->ev, fd, EV_READ|EV_WRITE, bounce_event, cc);
177	event_add(&cc->ev, &cc->pcb->timeout);
178}
179