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