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