xref: /openbsd/usr.sbin/smtpd/mta.c (revision 3ef9cbf7)
1*3ef9cbf7Sgilles /*
2*3ef9cbf7Sgilles  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
3*3ef9cbf7Sgilles  * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
4*3ef9cbf7Sgilles  *
5*3ef9cbf7Sgilles  * Permission to use, copy, modify, and distribute this software for any
6*3ef9cbf7Sgilles  * purpose with or without fee is hereby granted, provided that the above
7*3ef9cbf7Sgilles  * copyright notice and this permission notice appear in all copies.
8*3ef9cbf7Sgilles  *
9*3ef9cbf7Sgilles  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10*3ef9cbf7Sgilles  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11*3ef9cbf7Sgilles  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12*3ef9cbf7Sgilles  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13*3ef9cbf7Sgilles  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14*3ef9cbf7Sgilles  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15*3ef9cbf7Sgilles  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*3ef9cbf7Sgilles  */
17*3ef9cbf7Sgilles 
18*3ef9cbf7Sgilles #include <sys/types.h>
19*3ef9cbf7Sgilles #include <sys/queue.h>
20*3ef9cbf7Sgilles #include <sys/tree.h>
21*3ef9cbf7Sgilles #include <sys/param.h>
22*3ef9cbf7Sgilles #include <sys/socket.h>
23*3ef9cbf7Sgilles 
24*3ef9cbf7Sgilles #include <netinet/in.h>
25*3ef9cbf7Sgilles #include <arpa/inet.h>
26*3ef9cbf7Sgilles 
27*3ef9cbf7Sgilles #include <err.h>
28*3ef9cbf7Sgilles #include <errno.h>
29*3ef9cbf7Sgilles #include <event.h>
30*3ef9cbf7Sgilles #include <fcntl.h>
31*3ef9cbf7Sgilles #include <pwd.h>
32*3ef9cbf7Sgilles #include <signal.h>
33*3ef9cbf7Sgilles #include <stdio.h>
34*3ef9cbf7Sgilles #include <stdint.h>
35*3ef9cbf7Sgilles #include <stdlib.h>
36*3ef9cbf7Sgilles #include <string.h>
37*3ef9cbf7Sgilles #include <unistd.h>
38*3ef9cbf7Sgilles 
39*3ef9cbf7Sgilles #include "smtpd.h"
40*3ef9cbf7Sgilles 
41*3ef9cbf7Sgilles __dead void	mta_shutdown(void);
42*3ef9cbf7Sgilles void		mta_sig_handler(int, short, void *);
43*3ef9cbf7Sgilles void		mta_dispatch_parent(int, short, void *);
44*3ef9cbf7Sgilles void		mta_dispatch_queue(int, short, void *);
45*3ef9cbf7Sgilles void		mta_setup_events(struct smtpd *);
46*3ef9cbf7Sgilles void		mta_disable_events(struct smtpd *);
47*3ef9cbf7Sgilles void		mta_timeout(int, short, void *);
48*3ef9cbf7Sgilles void		mta_write(int, short, void *);
49*3ef9cbf7Sgilles int		mta_connect(struct batch *);
50*3ef9cbf7Sgilles void		mta_read_handler(struct bufferevent *, void *);
51*3ef9cbf7Sgilles void		mta_write_handler(struct bufferevent *, void *);
52*3ef9cbf7Sgilles void		mta_error_handler(struct bufferevent *, short, void *);
53*3ef9cbf7Sgilles int		mta_reply_handler(struct bufferevent *, void *);
54*3ef9cbf7Sgilles void		mta_batch_update_queue(struct batch *);
55*3ef9cbf7Sgilles 
56*3ef9cbf7Sgilles void
57*3ef9cbf7Sgilles mta_sig_handler(int sig, short event, void *p)
58*3ef9cbf7Sgilles {
59*3ef9cbf7Sgilles 	switch (sig) {
60*3ef9cbf7Sgilles 	case SIGINT:
61*3ef9cbf7Sgilles 	case SIGTERM:
62*3ef9cbf7Sgilles 		mta_shutdown();
63*3ef9cbf7Sgilles 		break;
64*3ef9cbf7Sgilles 	default:
65*3ef9cbf7Sgilles 		fatalx("mta_sig_handler: unexpected signal");
66*3ef9cbf7Sgilles 	}
67*3ef9cbf7Sgilles }
68*3ef9cbf7Sgilles 
69*3ef9cbf7Sgilles void
70*3ef9cbf7Sgilles mta_dispatch_parent(int sig, short event, void *p)
71*3ef9cbf7Sgilles {
72*3ef9cbf7Sgilles 	struct smtpd		*env = p;
73*3ef9cbf7Sgilles 	struct imsgbuf		*ibuf;
74*3ef9cbf7Sgilles 	struct imsg		 imsg;
75*3ef9cbf7Sgilles 	ssize_t			 n;
76*3ef9cbf7Sgilles 
77*3ef9cbf7Sgilles 	ibuf = env->sc_ibufs[PROC_PARENT];
78*3ef9cbf7Sgilles 	switch (event) {
79*3ef9cbf7Sgilles 	case EV_READ:
80*3ef9cbf7Sgilles 		if ((n = imsg_read(ibuf)) == -1)
81*3ef9cbf7Sgilles 			fatal("imsg_read_error");
82*3ef9cbf7Sgilles 		if (n == 0) {
83*3ef9cbf7Sgilles 			/* this pipe is dead, so remove the event handler */
84*3ef9cbf7Sgilles 			event_del(&ibuf->ev);
85*3ef9cbf7Sgilles 			event_loopexit(NULL);
86*3ef9cbf7Sgilles 			return;
87*3ef9cbf7Sgilles 		}
88*3ef9cbf7Sgilles 		break;
89*3ef9cbf7Sgilles 	case EV_WRITE:
90*3ef9cbf7Sgilles 		if (msgbuf_write(&ibuf->w) == -1)
91*3ef9cbf7Sgilles 			fatal("msgbuf_write");
92*3ef9cbf7Sgilles 		imsg_event_add(ibuf);
93*3ef9cbf7Sgilles 		return;
94*3ef9cbf7Sgilles 	default:
95*3ef9cbf7Sgilles 		fatalx("unknown event");
96*3ef9cbf7Sgilles 	}
97*3ef9cbf7Sgilles 
98*3ef9cbf7Sgilles 	for (;;) {
99*3ef9cbf7Sgilles 		if ((n = imsg_get(ibuf, &imsg)) == -1)
100*3ef9cbf7Sgilles 			fatal("parent_dispatch_mta: imsg_read error");
101*3ef9cbf7Sgilles 		if (n == 0)
102*3ef9cbf7Sgilles 			break;
103*3ef9cbf7Sgilles 
104*3ef9cbf7Sgilles 		switch (imsg.hdr.type) {
105*3ef9cbf7Sgilles 		default:
106*3ef9cbf7Sgilles 			log_debug("parent_dispatch_mta: unexpected imsg %d",
107*3ef9cbf7Sgilles 			    imsg.hdr.type);
108*3ef9cbf7Sgilles 			break;
109*3ef9cbf7Sgilles 		}
110*3ef9cbf7Sgilles 		imsg_free(&imsg);
111*3ef9cbf7Sgilles 	}
112*3ef9cbf7Sgilles 	imsg_event_add(ibuf);
113*3ef9cbf7Sgilles }
114*3ef9cbf7Sgilles 
115*3ef9cbf7Sgilles void
116*3ef9cbf7Sgilles mta_dispatch_queue(int sig, short event, void *p)
117*3ef9cbf7Sgilles {
118*3ef9cbf7Sgilles 	struct smtpd		*env = p;
119*3ef9cbf7Sgilles 	struct imsgbuf		*ibuf;
120*3ef9cbf7Sgilles 	struct imsg		 imsg;
121*3ef9cbf7Sgilles 	ssize_t			 n;
122*3ef9cbf7Sgilles 
123*3ef9cbf7Sgilles 	ibuf = env->sc_ibufs[PROC_QUEUE];
124*3ef9cbf7Sgilles 	switch (event) {
125*3ef9cbf7Sgilles 	case EV_READ:
126*3ef9cbf7Sgilles 		if ((n = imsg_read(ibuf)) == -1)
127*3ef9cbf7Sgilles 			fatal("imsg_read_error");
128*3ef9cbf7Sgilles 		if (n == 0) {
129*3ef9cbf7Sgilles 			/* this pipe is dead, so remove the event handler */
130*3ef9cbf7Sgilles 			event_del(&ibuf->ev);
131*3ef9cbf7Sgilles 			event_loopexit(NULL);
132*3ef9cbf7Sgilles 			return;
133*3ef9cbf7Sgilles 		}
134*3ef9cbf7Sgilles 		break;
135*3ef9cbf7Sgilles 	case EV_WRITE:
136*3ef9cbf7Sgilles 		if (msgbuf_write(&ibuf->w) == -1)
137*3ef9cbf7Sgilles 			fatal("msgbuf_write");
138*3ef9cbf7Sgilles 		imsg_event_add(ibuf);
139*3ef9cbf7Sgilles 		return;
140*3ef9cbf7Sgilles 	default:
141*3ef9cbf7Sgilles 		fatalx("unknown event");
142*3ef9cbf7Sgilles 	}
143*3ef9cbf7Sgilles 
144*3ef9cbf7Sgilles 	for (;;) {
145*3ef9cbf7Sgilles 		if ((n = imsg_get(ibuf, &imsg)) == -1)
146*3ef9cbf7Sgilles 			fatal("parent_dispatch_mta: imsg_read error");
147*3ef9cbf7Sgilles 		if (n == 0)
148*3ef9cbf7Sgilles 			break;
149*3ef9cbf7Sgilles 
150*3ef9cbf7Sgilles 		switch (imsg.hdr.type) {
151*3ef9cbf7Sgilles 		case IMSG_CREATE_BATCH: {
152*3ef9cbf7Sgilles 			struct batch *batchp;
153*3ef9cbf7Sgilles 
154*3ef9cbf7Sgilles 			batchp = calloc(1, sizeof (struct batch));
155*3ef9cbf7Sgilles 			if (batchp == NULL)
156*3ef9cbf7Sgilles 				err(1, "calloc");
157*3ef9cbf7Sgilles 
158*3ef9cbf7Sgilles 			*batchp = *(struct batch *)imsg.data;
159*3ef9cbf7Sgilles 			batchp->ss_off = 0;
160*3ef9cbf7Sgilles 			batchp->env = env;
161*3ef9cbf7Sgilles 			batchp->flags = 0;
162*3ef9cbf7Sgilles 
163*3ef9cbf7Sgilles 			TAILQ_INIT(&batchp->messages);
164*3ef9cbf7Sgilles 			SPLAY_INSERT(batchtree, &env->batch_queue, batchp);
165*3ef9cbf7Sgilles 
166*3ef9cbf7Sgilles 			break;
167*3ef9cbf7Sgilles 		}
168*3ef9cbf7Sgilles 		case IMSG_BATCH_APPEND: {
169*3ef9cbf7Sgilles 			struct batch	*batchp;
170*3ef9cbf7Sgilles 			struct message	*messagep;
171*3ef9cbf7Sgilles 
172*3ef9cbf7Sgilles 			messagep = calloc(1, sizeof (struct message));
173*3ef9cbf7Sgilles 			if (messagep == NULL)
174*3ef9cbf7Sgilles 				fatal("calloc");
175*3ef9cbf7Sgilles 
176*3ef9cbf7Sgilles 			*messagep = *(struct message *)imsg.data;
177*3ef9cbf7Sgilles 
178*3ef9cbf7Sgilles 			batchp = batch_by_id(env, messagep->batch_id);
179*3ef9cbf7Sgilles 			if (batchp == NULL)
180*3ef9cbf7Sgilles 				errx(1, "%s: internal inconsistency.", __func__);
181*3ef9cbf7Sgilles 
182*3ef9cbf7Sgilles 			TAILQ_INSERT_TAIL(&batchp->messages, messagep, entry);
183*3ef9cbf7Sgilles 
184*3ef9cbf7Sgilles 			break;
185*3ef9cbf7Sgilles 		}
186*3ef9cbf7Sgilles 		case IMSG_BATCH_CLOSE: {
187*3ef9cbf7Sgilles 			struct batch	*batchp;
188*3ef9cbf7Sgilles 
189*3ef9cbf7Sgilles 			batchp = (struct batch *)imsg.data;
190*3ef9cbf7Sgilles 			batchp = batch_by_id(env, batchp->id);
191*3ef9cbf7Sgilles 			if (batchp == NULL)
192*3ef9cbf7Sgilles 				errx(1, "%s: internal inconsistency.", __func__);
193*3ef9cbf7Sgilles 
194*3ef9cbf7Sgilles 			batchp->flags |= F_BATCH_COMPLETE;
195*3ef9cbf7Sgilles 
196*3ef9cbf7Sgilles 			while (! mta_connect(batchp)) {
197*3ef9cbf7Sgilles 				if (batchp->ss_off == batchp->ss_cnt) {
198*3ef9cbf7Sgilles 					break;
199*3ef9cbf7Sgilles 				}
200*3ef9cbf7Sgilles 			}
201*3ef9cbf7Sgilles 			break;
202*3ef9cbf7Sgilles 		}
203*3ef9cbf7Sgilles 		case IMSG_QUEUE_MESSAGE_FD: {
204*3ef9cbf7Sgilles 			struct batch	*batchp;
205*3ef9cbf7Sgilles 			int fd;
206*3ef9cbf7Sgilles 
207*3ef9cbf7Sgilles 			if ((fd = imsg_get_fd(ibuf, &imsg)) == -1) {
208*3ef9cbf7Sgilles 				/* NEEDS_FIX - unsure yet how it must be handled */
209*3ef9cbf7Sgilles 				errx(1, "imsg_get_fd");
210*3ef9cbf7Sgilles 			}
211*3ef9cbf7Sgilles 
212*3ef9cbf7Sgilles 			batchp = (struct batch *)imsg.data;
213*3ef9cbf7Sgilles 			batchp = batch_by_id(env, batchp->id);
214*3ef9cbf7Sgilles 
215*3ef9cbf7Sgilles 			if ((batchp->messagefp = fdopen(fd, "r")) == NULL)
216*3ef9cbf7Sgilles 				err(1, "fdopen");
217*3ef9cbf7Sgilles 
218*3ef9cbf7Sgilles 			evbuffer_add_printf(batchp->bev->output, "DATA\r\n");
219*3ef9cbf7Sgilles 
220*3ef9cbf7Sgilles 			bufferevent_enable(batchp->bev, EV_WRITE|EV_READ);
221*3ef9cbf7Sgilles 			break;
222*3ef9cbf7Sgilles 		}
223*3ef9cbf7Sgilles 		default:
224*3ef9cbf7Sgilles 			log_debug("parent_dispatch_mta: unexpected imsg %d",
225*3ef9cbf7Sgilles 			    imsg.hdr.type);
226*3ef9cbf7Sgilles 			break;
227*3ef9cbf7Sgilles 		}
228*3ef9cbf7Sgilles 		imsg_free(&imsg);
229*3ef9cbf7Sgilles 	}
230*3ef9cbf7Sgilles 	imsg_event_add(ibuf);
231*3ef9cbf7Sgilles }
232*3ef9cbf7Sgilles 
233*3ef9cbf7Sgilles void
234*3ef9cbf7Sgilles mta_shutdown(void)
235*3ef9cbf7Sgilles {
236*3ef9cbf7Sgilles 	log_info("mail transfer agent exiting");
237*3ef9cbf7Sgilles 	_exit(0);
238*3ef9cbf7Sgilles }
239*3ef9cbf7Sgilles 
240*3ef9cbf7Sgilles void
241*3ef9cbf7Sgilles mta_setup_events(struct smtpd *env)
242*3ef9cbf7Sgilles {
243*3ef9cbf7Sgilles 	struct timeval	 tv;
244*3ef9cbf7Sgilles 
245*3ef9cbf7Sgilles 	evtimer_set(&env->sc_ev, mta_timeout, env);
246*3ef9cbf7Sgilles 	tv.tv_sec = 3;
247*3ef9cbf7Sgilles 	tv.tv_usec = 0;
248*3ef9cbf7Sgilles 	evtimer_add(&env->sc_ev, &tv);
249*3ef9cbf7Sgilles }
250*3ef9cbf7Sgilles 
251*3ef9cbf7Sgilles void
252*3ef9cbf7Sgilles mta_disable_events(struct smtpd *env)
253*3ef9cbf7Sgilles {
254*3ef9cbf7Sgilles 	evtimer_del(&env->sc_ev);
255*3ef9cbf7Sgilles }
256*3ef9cbf7Sgilles 
257*3ef9cbf7Sgilles void
258*3ef9cbf7Sgilles mta_timeout(int fd, short event, void *p)
259*3ef9cbf7Sgilles {
260*3ef9cbf7Sgilles 	struct smtpd		*env = p;
261*3ef9cbf7Sgilles 	struct timeval		 tv;
262*3ef9cbf7Sgilles 
263*3ef9cbf7Sgilles 	tv.tv_sec = 3;
264*3ef9cbf7Sgilles 	tv.tv_usec = 0;
265*3ef9cbf7Sgilles 	evtimer_add(&env->sc_ev, &tv);
266*3ef9cbf7Sgilles }
267*3ef9cbf7Sgilles 
268*3ef9cbf7Sgilles pid_t
269*3ef9cbf7Sgilles mta(struct smtpd *env)
270*3ef9cbf7Sgilles {
271*3ef9cbf7Sgilles 	pid_t		 pid;
272*3ef9cbf7Sgilles 
273*3ef9cbf7Sgilles 	struct passwd	*pw;
274*3ef9cbf7Sgilles 	struct event	 ev_sigint;
275*3ef9cbf7Sgilles 	struct event	 ev_sigterm;
276*3ef9cbf7Sgilles 
277*3ef9cbf7Sgilles 	struct peer peers[] = {
278*3ef9cbf7Sgilles 		{ PROC_QUEUE,	mta_dispatch_queue }
279*3ef9cbf7Sgilles 	};
280*3ef9cbf7Sgilles 
281*3ef9cbf7Sgilles 	switch (pid = fork()) {
282*3ef9cbf7Sgilles 	case -1:
283*3ef9cbf7Sgilles 		fatal("mta: cannot fork");
284*3ef9cbf7Sgilles 	case 0:
285*3ef9cbf7Sgilles 		break;
286*3ef9cbf7Sgilles 	default:
287*3ef9cbf7Sgilles 		return (pid);
288*3ef9cbf7Sgilles 	}
289*3ef9cbf7Sgilles 
290*3ef9cbf7Sgilles 	purge_config(env, PURGE_EVERYTHING);
291*3ef9cbf7Sgilles 
292*3ef9cbf7Sgilles 	pw = env->sc_pw;
293*3ef9cbf7Sgilles #ifndef DEBUG
294*3ef9cbf7Sgilles 	if (chroot(pw->pw_dir) == -1)
295*3ef9cbf7Sgilles 		fatal("mta: chroot");
296*3ef9cbf7Sgilles 	if (chdir("/") == -1)
297*3ef9cbf7Sgilles 		fatal("mta: chdir(\"/\")");
298*3ef9cbf7Sgilles #else
299*3ef9cbf7Sgilles #warning disabling privilege revocation and chroot in DEBUG MODE
300*3ef9cbf7Sgilles #endif
301*3ef9cbf7Sgilles 
302*3ef9cbf7Sgilles 	setproctitle("mail transfer agent");
303*3ef9cbf7Sgilles 	smtpd_process = PROC_MTA;
304*3ef9cbf7Sgilles 
305*3ef9cbf7Sgilles #ifndef DEBUG
306*3ef9cbf7Sgilles 	if (setgroups(1, &pw->pw_gid) ||
307*3ef9cbf7Sgilles 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
308*3ef9cbf7Sgilles 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
309*3ef9cbf7Sgilles 		fatal("mta: cannot drop privileges");
310*3ef9cbf7Sgilles #endif
311*3ef9cbf7Sgilles 
312*3ef9cbf7Sgilles 	event_init();
313*3ef9cbf7Sgilles 
314*3ef9cbf7Sgilles 	signal_set(&ev_sigint, SIGINT, mta_sig_handler, env);
315*3ef9cbf7Sgilles 	signal_set(&ev_sigterm, SIGTERM, mta_sig_handler, env);
316*3ef9cbf7Sgilles 	signal_add(&ev_sigint, NULL);
317*3ef9cbf7Sgilles 	signal_add(&ev_sigterm, NULL);
318*3ef9cbf7Sgilles 	signal(SIGPIPE, SIG_IGN);
319*3ef9cbf7Sgilles 	signal(SIGHUP, SIG_IGN);
320*3ef9cbf7Sgilles 
321*3ef9cbf7Sgilles 	config_peers(env, peers, 1);
322*3ef9cbf7Sgilles 
323*3ef9cbf7Sgilles 	SPLAY_INIT(&env->batch_queue);
324*3ef9cbf7Sgilles 
325*3ef9cbf7Sgilles 	mta_setup_events(env);
326*3ef9cbf7Sgilles 	event_dispatch();
327*3ef9cbf7Sgilles 	mta_shutdown();
328*3ef9cbf7Sgilles 
329*3ef9cbf7Sgilles 	return (0);
330*3ef9cbf7Sgilles }
331*3ef9cbf7Sgilles 
332*3ef9cbf7Sgilles /* shamelessly ripped usr.sbin/relayd/check_tcp.c ;) */
333*3ef9cbf7Sgilles int
334*3ef9cbf7Sgilles mta_connect(struct batch *batchp)
335*3ef9cbf7Sgilles {
336*3ef9cbf7Sgilles 	int s;
337*3ef9cbf7Sgilles 	int type;
338*3ef9cbf7Sgilles 	struct linger lng;
339*3ef9cbf7Sgilles 	struct sockaddr_in ssin;
340*3ef9cbf7Sgilles 	struct sockaddr_in6 ssin6;
341*3ef9cbf7Sgilles 
342*3ef9cbf7Sgilles 	if ((s = socket(batchp->ss[batchp->ss_off].ss_family, SOCK_STREAM, 0)) == -1) {
343*3ef9cbf7Sgilles 		goto bad;
344*3ef9cbf7Sgilles 	}
345*3ef9cbf7Sgilles 
346*3ef9cbf7Sgilles 	bzero(&lng, sizeof(lng));
347*3ef9cbf7Sgilles 	if (setsockopt(s, SOL_SOCKET, SO_LINGER, &lng, sizeof (lng)) == -1) {
348*3ef9cbf7Sgilles 		goto bad;
349*3ef9cbf7Sgilles 	}
350*3ef9cbf7Sgilles 
351*3ef9cbf7Sgilles 	type = 1;
352*3ef9cbf7Sgilles 	if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &type, sizeof (type)) == -1) {
353*3ef9cbf7Sgilles 		goto bad;
354*3ef9cbf7Sgilles 	}
355*3ef9cbf7Sgilles 
356*3ef9cbf7Sgilles 	if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) {
357*3ef9cbf7Sgilles 		goto bad;
358*3ef9cbf7Sgilles 	}
359*3ef9cbf7Sgilles 
360*3ef9cbf7Sgilles 	if (batchp->ss[batchp->ss_off].ss_family == PF_INET) {
361*3ef9cbf7Sgilles 		ssin = *(struct sockaddr_in *)&batchp->ss[batchp->ss_off];
362*3ef9cbf7Sgilles 		if (connect(s, (struct sockaddr *)&ssin, sizeof(struct sockaddr_in)) == -1) {
363*3ef9cbf7Sgilles 			if (errno != EINPROGRESS) {
364*3ef9cbf7Sgilles 				goto bad;
365*3ef9cbf7Sgilles 			}
366*3ef9cbf7Sgilles 		}
367*3ef9cbf7Sgilles 	}
368*3ef9cbf7Sgilles 
369*3ef9cbf7Sgilles 	if (batchp->ss[batchp->ss_off].ss_family == PF_INET6) {
370*3ef9cbf7Sgilles 		ssin6 = *(struct sockaddr_in6 *)&batchp->ss[batchp->ss_off];
371*3ef9cbf7Sgilles 		if (connect(s, (struct sockaddr *)&ssin6, sizeof(struct sockaddr_in6)) == -1) {
372*3ef9cbf7Sgilles 			if (errno != EINPROGRESS) {
373*3ef9cbf7Sgilles 				goto bad;
374*3ef9cbf7Sgilles 			}
375*3ef9cbf7Sgilles 		}
376*3ef9cbf7Sgilles 	}
377*3ef9cbf7Sgilles 
378*3ef9cbf7Sgilles 	batchp->tv.tv_sec = SMTPD_CONNECT_TIMEOUT;
379*3ef9cbf7Sgilles 	batchp->tv.tv_usec = 0;
380*3ef9cbf7Sgilles 	batchp->peerfd = s;
381*3ef9cbf7Sgilles 	event_set(&batchp->ev, s, EV_TIMEOUT|EV_WRITE, mta_write, batchp);
382*3ef9cbf7Sgilles 	event_add(&batchp->ev, &batchp->tv);
383*3ef9cbf7Sgilles 
384*3ef9cbf7Sgilles 	return 1;
385*3ef9cbf7Sgilles 
386*3ef9cbf7Sgilles bad:
387*3ef9cbf7Sgilles 	batchp->ss_off++;
388*3ef9cbf7Sgilles 	close(s);
389*3ef9cbf7Sgilles 	return 0;
390*3ef9cbf7Sgilles }
391*3ef9cbf7Sgilles 
392*3ef9cbf7Sgilles void
393*3ef9cbf7Sgilles mta_write(int s, short event, void *arg)
394*3ef9cbf7Sgilles {
395*3ef9cbf7Sgilles 	struct batch *batchp = arg;
396*3ef9cbf7Sgilles 	int ret;
397*3ef9cbf7Sgilles 
398*3ef9cbf7Sgilles 	if (event == EV_TIMEOUT) {
399*3ef9cbf7Sgilles 		batchp->ss_off++;
400*3ef9cbf7Sgilles 		close(s);
401*3ef9cbf7Sgilles 		if (batchp->bev) {
402*3ef9cbf7Sgilles 			bufferevent_free(batchp->bev);
403*3ef9cbf7Sgilles 			batchp->bev = NULL;
404*3ef9cbf7Sgilles 		}
405*3ef9cbf7Sgilles 		strlcpy(batchp->errorline, "connection timed-out.", STRLEN);
406*3ef9cbf7Sgilles 
407*3ef9cbf7Sgilles 		ret = 0;
408*3ef9cbf7Sgilles 		while (batchp->ss_off < batchp->ss_cnt &&
409*3ef9cbf7Sgilles 		    (ret = mta_connect(batchp)) == 0) {
410*3ef9cbf7Sgilles 			continue;
411*3ef9cbf7Sgilles 		}
412*3ef9cbf7Sgilles 		if (ret)
413*3ef9cbf7Sgilles 			return;
414*3ef9cbf7Sgilles 
415*3ef9cbf7Sgilles 		mta_batch_update_queue(batchp);
416*3ef9cbf7Sgilles 		return;
417*3ef9cbf7Sgilles 	}
418*3ef9cbf7Sgilles 
419*3ef9cbf7Sgilles 	batchp->bev = bufferevent_new(s, mta_read_handler, mta_write_handler,
420*3ef9cbf7Sgilles 	    mta_error_handler, batchp);
421*3ef9cbf7Sgilles 
422*3ef9cbf7Sgilles 	if (batchp->bev == NULL) {
423*3ef9cbf7Sgilles 		mta_batch_update_queue(batchp);
424*3ef9cbf7Sgilles 		close(s);
425*3ef9cbf7Sgilles 		return;
426*3ef9cbf7Sgilles 	}
427*3ef9cbf7Sgilles 
428*3ef9cbf7Sgilles 	bufferevent_enable(batchp->bev, EV_READ|EV_WRITE);
429*3ef9cbf7Sgilles }
430*3ef9cbf7Sgilles 
431*3ef9cbf7Sgilles void
432*3ef9cbf7Sgilles mta_read_handler(struct bufferevent *bev, void *arg)
433*3ef9cbf7Sgilles {
434*3ef9cbf7Sgilles 	while (mta_reply_handler(bev, arg))
435*3ef9cbf7Sgilles 		;
436*3ef9cbf7Sgilles }
437*3ef9cbf7Sgilles 
438*3ef9cbf7Sgilles int
439*3ef9cbf7Sgilles mta_reply_handler(struct bufferevent *bev, void *arg)
440*3ef9cbf7Sgilles {
441*3ef9cbf7Sgilles 	struct batch *batchp = arg;
442*3ef9cbf7Sgilles 	struct smtpd *env = batchp->env;
443*3ef9cbf7Sgilles 	struct message *messagep = NULL;
444*3ef9cbf7Sgilles 	char *line;
445*3ef9cbf7Sgilles 	int i;
446*3ef9cbf7Sgilles 	int code;
447*3ef9cbf7Sgilles #define F_ISINFO	0x1
448*3ef9cbf7Sgilles #define F_ISPROTOERROR	0x2
449*3ef9cbf7Sgilles 	char codebuf[4];
450*3ef9cbf7Sgilles 	char *errstr;
451*3ef9cbf7Sgilles 	int flags = 0;
452*3ef9cbf7Sgilles 
453*3ef9cbf7Sgilles 	line = evbuffer_readline(bev->input);
454*3ef9cbf7Sgilles 	if (line == NULL) {
455*3ef9cbf7Sgilles 		bufferevent_enable(bev, EV_READ|EV_WRITE);
456*3ef9cbf7Sgilles 		return 0;
457*3ef9cbf7Sgilles 	}
458*3ef9cbf7Sgilles 
459*3ef9cbf7Sgilles 	line[strcspn(line, "\r")] = '\0';
460*3ef9cbf7Sgilles 
461*3ef9cbf7Sgilles 	bufferevent_enable(bev, EV_READ|EV_WRITE);
462*3ef9cbf7Sgilles 
463*3ef9cbf7Sgilles 	log_debug("remote server sent: [%s]", line);
464*3ef9cbf7Sgilles 
465*3ef9cbf7Sgilles 	strlcpy(codebuf, line, sizeof codebuf);
466*3ef9cbf7Sgilles 	code = strtonum(codebuf, 0, UINT16_MAX, (const char **)&errstr);
467*3ef9cbf7Sgilles 	if (errstr || code < 100) {
468*3ef9cbf7Sgilles 		/* Server sent invalid line, protocol error */
469*3ef9cbf7Sgilles 		batchp->status |= S_BATCH_PERMFAILURE;
470*3ef9cbf7Sgilles 		strlcpy(batchp->errorline, line, STRLEN);
471*3ef9cbf7Sgilles 		mta_batch_update_queue(batchp);
472*3ef9cbf7Sgilles 		return 0;
473*3ef9cbf7Sgilles 	}
474*3ef9cbf7Sgilles 
475*3ef9cbf7Sgilles 	if (line[3] == '-') {
476*3ef9cbf7Sgilles 		return 1;
477*3ef9cbf7Sgilles 	}
478*3ef9cbf7Sgilles 
479*3ef9cbf7Sgilles 	switch (code) {
480*3ef9cbf7Sgilles 	case 250:
481*3ef9cbf7Sgilles 		if (batchp->state == S_DONE) {
482*3ef9cbf7Sgilles 			mta_batch_update_queue(batchp);
483*3ef9cbf7Sgilles 			return 0;
484*3ef9cbf7Sgilles 		}
485*3ef9cbf7Sgilles 		break;
486*3ef9cbf7Sgilles 
487*3ef9cbf7Sgilles 	case 220:
488*3ef9cbf7Sgilles 		evbuffer_add_printf(batchp->bev->output, "EHLO %s\r\n", env->sc_hostname);
489*3ef9cbf7Sgilles 		batchp->state = S_GREETED;
490*3ef9cbf7Sgilles 		return 1;
491*3ef9cbf7Sgilles 
492*3ef9cbf7Sgilles 	case 421:
493*3ef9cbf7Sgilles 	case 450:
494*3ef9cbf7Sgilles 	case 451:
495*3ef9cbf7Sgilles 		batchp->status |= S_BATCH_TEMPFAILURE;
496*3ef9cbf7Sgilles 		strlcpy(batchp->errorline, line, STRLEN);
497*3ef9cbf7Sgilles 		mta_batch_update_queue(batchp);
498*3ef9cbf7Sgilles 		return 0;
499*3ef9cbf7Sgilles 
500*3ef9cbf7Sgilles 		/* The following codes are state dependant and will cause
501*3ef9cbf7Sgilles 		 * a batch rejection if returned at the wrong state.
502*3ef9cbf7Sgilles 		 */
503*3ef9cbf7Sgilles 	case 530:
504*3ef9cbf7Sgilles 	case 550:
505*3ef9cbf7Sgilles 		if (batchp->state == S_RCPT) {
506*3ef9cbf7Sgilles 			batchp->messagep->status = (S_MESSAGE_REJECTED|S_MESSAGE_PERMFAILURE);
507*3ef9cbf7Sgilles 			strlcpy(batchp->messagep->session_errorline, line, STRLEN);
508*3ef9cbf7Sgilles 			break;
509*3ef9cbf7Sgilles 		}
510*3ef9cbf7Sgilles 	case 354:
511*3ef9cbf7Sgilles 		if (batchp->state == S_RCPT && batchp->messagep == NULL) {
512*3ef9cbf7Sgilles 			batchp->state = S_DATA;
513*3ef9cbf7Sgilles 			break;
514*3ef9cbf7Sgilles 		}
515*3ef9cbf7Sgilles 
516*3ef9cbf7Sgilles 	case 221:
517*3ef9cbf7Sgilles 		if (batchp->state == S_DONE) {
518*3ef9cbf7Sgilles 			mta_batch_update_queue(batchp);
519*3ef9cbf7Sgilles 			return 0;
520*3ef9cbf7Sgilles 		}
521*3ef9cbf7Sgilles 
522*3ef9cbf7Sgilles 	case 552:
523*3ef9cbf7Sgilles 	case 553:
524*3ef9cbf7Sgilles 		flags |= F_ISPROTOERROR;
525*3ef9cbf7Sgilles 	default:
526*3ef9cbf7Sgilles 		/* Server sent code we know nothing about, error */
527*3ef9cbf7Sgilles 		if (!(flags & F_ISPROTOERROR))
528*3ef9cbf7Sgilles 			log_debug("Ouch, SMTP session returned unhandled %d status.", code);
529*3ef9cbf7Sgilles 
530*3ef9cbf7Sgilles 		batchp->status |= S_BATCH_PERMFAILURE;
531*3ef9cbf7Sgilles 		strlcpy(batchp->errorline, line, STRLEN);
532*3ef9cbf7Sgilles 		mta_batch_update_queue(batchp);
533*3ef9cbf7Sgilles 		return 0;
534*3ef9cbf7Sgilles 	}
535*3ef9cbf7Sgilles 
536*3ef9cbf7Sgilles 
537*3ef9cbf7Sgilles 	switch (batchp->state) {
538*3ef9cbf7Sgilles 	case S_GREETED: {
539*3ef9cbf7Sgilles 		char *user;
540*3ef9cbf7Sgilles 		char *domain;
541*3ef9cbf7Sgilles 
542*3ef9cbf7Sgilles 		if (batchp->type & T_DAEMON_BATCH) {
543*3ef9cbf7Sgilles 			user = "MAILER-DAEMON";
544*3ef9cbf7Sgilles 			domain = env->sc_hostname;
545*3ef9cbf7Sgilles 		}
546*3ef9cbf7Sgilles 		else {
547*3ef9cbf7Sgilles 			messagep = TAILQ_FIRST(&batchp->messages);
548*3ef9cbf7Sgilles 			user = messagep->sender.user;
549*3ef9cbf7Sgilles 			domain = messagep->sender.domain;
550*3ef9cbf7Sgilles 		}
551*3ef9cbf7Sgilles 
552*3ef9cbf7Sgilles 		if (user[0] == '\0' && domain[0] == '\0')
553*3ef9cbf7Sgilles 			evbuffer_add_printf(batchp->bev->output, "MAIL FROM:<>\r\n");
554*3ef9cbf7Sgilles 		else
555*3ef9cbf7Sgilles 			evbuffer_add_printf(batchp->bev->output, "MAIL FROM:<%s@%s>\r\n", user, domain);
556*3ef9cbf7Sgilles 		batchp->state = S_MAIL;
557*3ef9cbf7Sgilles 
558*3ef9cbf7Sgilles 		break;
559*3ef9cbf7Sgilles 	}
560*3ef9cbf7Sgilles 
561*3ef9cbf7Sgilles 	case S_MAIL:
562*3ef9cbf7Sgilles 		batchp->state = S_RCPT;
563*3ef9cbf7Sgilles 
564*3ef9cbf7Sgilles 	case S_RCPT: {
565*3ef9cbf7Sgilles 		char *user;
566*3ef9cbf7Sgilles 		char *domain;
567*3ef9cbf7Sgilles 
568*3ef9cbf7Sgilles 		/* Is this the first RCPT ? */
569*3ef9cbf7Sgilles 		if (batchp->messagep == NULL)
570*3ef9cbf7Sgilles 			messagep = TAILQ_FIRST(&batchp->messages);
571*3ef9cbf7Sgilles 		else {
572*3ef9cbf7Sgilles 			/* We already had a RCPT, mark is as accepted and
573*3ef9cbf7Sgilles 			 * fetch next one from queue if we aren't dealing
574*3ef9cbf7Sgilles 			 * with a daemon batch.
575*3ef9cbf7Sgilles 			 */
576*3ef9cbf7Sgilles 			if (batchp->type & T_DAEMON_BATCH)
577*3ef9cbf7Sgilles 				messagep = NULL;
578*3ef9cbf7Sgilles 			else {
579*3ef9cbf7Sgilles 				messagep = batchp->messagep;
580*3ef9cbf7Sgilles 				if ((messagep->status & S_MESSAGE_REJECTED) == 0)
581*3ef9cbf7Sgilles 					messagep->status = S_MESSAGE_ACCEPTED;
582*3ef9cbf7Sgilles 				messagep = TAILQ_NEXT(batchp->messagep, entry);
583*3ef9cbf7Sgilles 			}
584*3ef9cbf7Sgilles 		}
585*3ef9cbf7Sgilles 		batchp->messagep = messagep;
586*3ef9cbf7Sgilles 
587*3ef9cbf7Sgilles 		if (messagep) {
588*3ef9cbf7Sgilles 			if (batchp->type & T_DAEMON_BATCH) {
589*3ef9cbf7Sgilles 				user = messagep->sender.user;
590*3ef9cbf7Sgilles 				domain = messagep->sender.domain;
591*3ef9cbf7Sgilles 			}
592*3ef9cbf7Sgilles 			else {
593*3ef9cbf7Sgilles 				user = messagep->recipient.user;
594*3ef9cbf7Sgilles 				domain = messagep->recipient.domain;
595*3ef9cbf7Sgilles 			}
596*3ef9cbf7Sgilles 			evbuffer_add_printf(batchp->bev->output, "RCPT TO:<%s@%s>\r\n", user, domain);
597*3ef9cbf7Sgilles 		}
598*3ef9cbf7Sgilles 		else {
599*3ef9cbf7Sgilles 			/* Do we have at least one accepted recipient ? */
600*3ef9cbf7Sgilles 			if ((batchp->type & T_DAEMON_BATCH) == 0) {
601*3ef9cbf7Sgilles 				TAILQ_FOREACH(messagep, &batchp->messages, entry) {
602*3ef9cbf7Sgilles 					if (messagep->status & S_MESSAGE_ACCEPTED)
603*3ef9cbf7Sgilles 						break;
604*3ef9cbf7Sgilles 				}
605*3ef9cbf7Sgilles 				if (messagep == NULL) {
606*3ef9cbf7Sgilles 					batchp->status |= S_BATCH_PERMFAILURE;
607*3ef9cbf7Sgilles 					mta_batch_update_queue(batchp);
608*3ef9cbf7Sgilles 					return 0;
609*3ef9cbf7Sgilles 				}
610*3ef9cbf7Sgilles 			}
611*3ef9cbf7Sgilles 
612*3ef9cbf7Sgilles 			bufferevent_disable(batchp->bev, EV_WRITE|EV_READ);
613*3ef9cbf7Sgilles 			imsg_compose(env->sc_ibufs[PROC_QUEUE], IMSG_QUEUE_MESSAGE_FD,
614*3ef9cbf7Sgilles 			    0, 0, -1, batchp, sizeof(*batchp));
615*3ef9cbf7Sgilles 		}
616*3ef9cbf7Sgilles 		break;
617*3ef9cbf7Sgilles 	}
618*3ef9cbf7Sgilles 
619*3ef9cbf7Sgilles 	case S_DATA: {
620*3ef9cbf7Sgilles 		bufferevent_enable(batchp->bev, EV_READ|EV_WRITE);
621*3ef9cbf7Sgilles 
622*3ef9cbf7Sgilles 		evbuffer_add_printf(batchp->bev->output,
623*3ef9cbf7Sgilles 		    "Received: from %s (%s [%s])\r\n"
624*3ef9cbf7Sgilles 		    "\tby %s with ESMTP id %s\r\n",
625*3ef9cbf7Sgilles 		    "localhost", "localhost", "127.0.0.1",
626*3ef9cbf7Sgilles 		    "", batchp->message_id);
627*3ef9cbf7Sgilles 
628*3ef9cbf7Sgilles 		evbuffer_add_printf(batchp->bev->output, "X-OpenSMTPD: experiment\r\n");
629*3ef9cbf7Sgilles 
630*3ef9cbf7Sgilles 		if (batchp->type & T_DAEMON_BATCH) {
631*3ef9cbf7Sgilles 			evbuffer_add_printf(batchp->bev->output,
632*3ef9cbf7Sgilles 			    "Hi !\r\n\r\n"
633*3ef9cbf7Sgilles 			    "This is the MAILER-DAEMON, please DO NOT REPLY to this e-mail it is\r\n"
634*3ef9cbf7Sgilles 			    "just a notification to let you know that an error has occured.\r\n\r\n");
635*3ef9cbf7Sgilles 
636*3ef9cbf7Sgilles 			if (batchp->status & S_BATCH_PERMFAILURE) {
637*3ef9cbf7Sgilles 				evbuffer_add_printf(batchp->bev->output,
638*3ef9cbf7Sgilles 				    "You ran into a PERMANENT FAILURE, which means that the e-mail can't\r\n"
639*3ef9cbf7Sgilles 				    "be delivered to the remote host no matter how much I'll try.\r\n\r\n"
640*3ef9cbf7Sgilles 				    "Diagnostic:\r\n"
641*3ef9cbf7Sgilles 				    "%s\r\n\r\n", batchp->errorline);
642*3ef9cbf7Sgilles 			}
643*3ef9cbf7Sgilles 
644*3ef9cbf7Sgilles 			if (batchp->status & S_BATCH_TEMPFAILURE) {
645*3ef9cbf7Sgilles 				evbuffer_add_printf(batchp->bev->output,
646*3ef9cbf7Sgilles 				    "You ran into a TEMPORARY FAILURE, which means that the e-mail can't\r\n"
647*3ef9cbf7Sgilles 				    "be delivered right now, but could be deliberable at a later time. I\r\n"
648*3ef9cbf7Sgilles 				    "will attempt until it succeeds for the next four days, then let you\r\n"
649*3ef9cbf7Sgilles 				    "know if it didn't work out.\r\n"
650*3ef9cbf7Sgilles 				    "Diagnostic:\r\n"
651*3ef9cbf7Sgilles 				    "%s\r\n\r\n", batchp->errorline);
652*3ef9cbf7Sgilles 			}
653*3ef9cbf7Sgilles 
654*3ef9cbf7Sgilles 			i = 0;
655*3ef9cbf7Sgilles 			TAILQ_FOREACH(messagep, &batchp->messages, entry) {
656*3ef9cbf7Sgilles 				if (messagep->status & S_MESSAGE_TEMPFAILURE) {
657*3ef9cbf7Sgilles 					if (i == 0) {
658*3ef9cbf7Sgilles 						evbuffer_add_printf(batchp->bev->output,
659*3ef9cbf7Sgilles 						    "The following recipients caused a temporary failure:\r\n");
660*3ef9cbf7Sgilles 						++i;
661*3ef9cbf7Sgilles 					}
662*3ef9cbf7Sgilles 
663*3ef9cbf7Sgilles 					evbuffer_add_printf(batchp->bev->output,
664*3ef9cbf7Sgilles 					    "\t<%s@%s>:\r\n%s\r\n\r\n",
665*3ef9cbf7Sgilles 					    messagep->recipient.user, messagep->recipient.domain, messagep->session_errorline);
666*3ef9cbf7Sgilles 				}
667*3ef9cbf7Sgilles 			}
668*3ef9cbf7Sgilles 
669*3ef9cbf7Sgilles 			i = 0;
670*3ef9cbf7Sgilles 			TAILQ_FOREACH(messagep, &batchp->messages, entry) {
671*3ef9cbf7Sgilles 				if (messagep->status & S_MESSAGE_PERMFAILURE) {
672*3ef9cbf7Sgilles 					if (i == 0) {
673*3ef9cbf7Sgilles 						evbuffer_add_printf(batchp->bev->output,
674*3ef9cbf7Sgilles 						    "The following recipients caused a permanent failure:\r\n");
675*3ef9cbf7Sgilles 						++i;
676*3ef9cbf7Sgilles 					}
677*3ef9cbf7Sgilles 
678*3ef9cbf7Sgilles 					evbuffer_add_printf(batchp->bev->output,
679*3ef9cbf7Sgilles 					    "\t<%s@%s>:\r\n%s\r\n\r\n",
680*3ef9cbf7Sgilles 					    messagep->recipient.user, messagep->recipient.domain, messagep->session_errorline);
681*3ef9cbf7Sgilles 				}
682*3ef9cbf7Sgilles 			}
683*3ef9cbf7Sgilles 
684*3ef9cbf7Sgilles 			evbuffer_add_printf(batchp->bev->output,
685*3ef9cbf7Sgilles 			    "Below is a copy of the original message:\r\n\r\n");
686*3ef9cbf7Sgilles 		}
687*3ef9cbf7Sgilles 
688*3ef9cbf7Sgilles 		break;
689*3ef9cbf7Sgilles 	}
690*3ef9cbf7Sgilles 	case S_DONE:
691*3ef9cbf7Sgilles 		evbuffer_add_printf(batchp->bev->output, "QUIT\r\n");
692*3ef9cbf7Sgilles 		batchp->state = S_QUIT;
693*3ef9cbf7Sgilles 		break;
694*3ef9cbf7Sgilles 
695*3ef9cbf7Sgilles 	default:
696*3ef9cbf7Sgilles 		log_info("unknown command: %d", batchp->state);
697*3ef9cbf7Sgilles 	}
698*3ef9cbf7Sgilles 
699*3ef9cbf7Sgilles 	return 1;
700*3ef9cbf7Sgilles }
701*3ef9cbf7Sgilles 
702*3ef9cbf7Sgilles void
703*3ef9cbf7Sgilles mta_write_handler(struct bufferevent *bev, void *arg)
704*3ef9cbf7Sgilles {
705*3ef9cbf7Sgilles 	struct batch *batchp = arg;
706*3ef9cbf7Sgilles 	char *buf, *lbuf;
707*3ef9cbf7Sgilles 	size_t len;
708*3ef9cbf7Sgilles 
709*3ef9cbf7Sgilles 	if (batchp->state == S_QUIT) {
710*3ef9cbf7Sgilles 		bufferevent_disable(bev, EV_READ|EV_WRITE);
711*3ef9cbf7Sgilles 		close(batchp->peerfd);
712*3ef9cbf7Sgilles 		return;
713*3ef9cbf7Sgilles 	}
714*3ef9cbf7Sgilles 
715*3ef9cbf7Sgilles 	/* Progressively fill the output buffer with data */
716*3ef9cbf7Sgilles 	if (batchp->state == S_DATA) {
717*3ef9cbf7Sgilles 
718*3ef9cbf7Sgilles 		lbuf = NULL;
719*3ef9cbf7Sgilles 		if ((buf = fgetln(batchp->messagefp, &len))) {
720*3ef9cbf7Sgilles 			if (buf[len - 1] == '\n')
721*3ef9cbf7Sgilles 				buf[len - 1] = '\0';
722*3ef9cbf7Sgilles 			else {
723*3ef9cbf7Sgilles 				if ((lbuf = malloc(len + 1)) == NULL)
724*3ef9cbf7Sgilles 					err(1, "malloc");
725*3ef9cbf7Sgilles 				memcpy(lbuf, buf, len);
726*3ef9cbf7Sgilles 				lbuf[len] = '\0';
727*3ef9cbf7Sgilles 				buf = lbuf;
728*3ef9cbf7Sgilles 			}
729*3ef9cbf7Sgilles 			evbuffer_add_printf(batchp->bev->output, "%s\r\n", buf);
730*3ef9cbf7Sgilles 			free(lbuf);
731*3ef9cbf7Sgilles 		}
732*3ef9cbf7Sgilles 		else {
733*3ef9cbf7Sgilles 			evbuffer_add_printf(batchp->bev->output, ".\r\n");
734*3ef9cbf7Sgilles 			batchp->state = S_DONE;
735*3ef9cbf7Sgilles 			fclose(batchp->messagefp);
736*3ef9cbf7Sgilles 			batchp->messagefp = NULL;
737*3ef9cbf7Sgilles 		}
738*3ef9cbf7Sgilles 	}
739*3ef9cbf7Sgilles 	bufferevent_enable(batchp->bev, EV_READ|EV_WRITE);
740*3ef9cbf7Sgilles }
741*3ef9cbf7Sgilles 
742*3ef9cbf7Sgilles void
743*3ef9cbf7Sgilles mta_error_handler(struct bufferevent *bev, short error, void *arg)
744*3ef9cbf7Sgilles {
745*3ef9cbf7Sgilles 	struct batch *batchp = arg;
746*3ef9cbf7Sgilles 	if (error & (EVBUFFER_TIMEOUT|EVBUFFER_EOF)) {
747*3ef9cbf7Sgilles 		bufferevent_disable(bev, EV_READ|EV_WRITE);
748*3ef9cbf7Sgilles 		close(batchp->peerfd);
749*3ef9cbf7Sgilles 		return;
750*3ef9cbf7Sgilles 	}
751*3ef9cbf7Sgilles }
752*3ef9cbf7Sgilles 
753*3ef9cbf7Sgilles void
754*3ef9cbf7Sgilles mta_batch_update_queue(struct batch *batchp)
755*3ef9cbf7Sgilles {
756*3ef9cbf7Sgilles 	struct smtpd *env = batchp->env;
757*3ef9cbf7Sgilles 	struct message *messagep;
758*3ef9cbf7Sgilles 
759*3ef9cbf7Sgilles 	while ((messagep = TAILQ_FIRST(&batchp->messages)) != NULL) {
760*3ef9cbf7Sgilles 
761*3ef9cbf7Sgilles 		if (batchp->status & S_BATCH_PERMFAILURE) {
762*3ef9cbf7Sgilles 			messagep->status |= S_MESSAGE_PERMFAILURE;
763*3ef9cbf7Sgilles 		}
764*3ef9cbf7Sgilles 
765*3ef9cbf7Sgilles 		if (batchp->status & S_BATCH_TEMPFAILURE) {
766*3ef9cbf7Sgilles 			if (messagep->status != S_MESSAGE_PERMFAILURE)
767*3ef9cbf7Sgilles 				messagep->status |= S_MESSAGE_TEMPFAILURE;
768*3ef9cbf7Sgilles 		}
769*3ef9cbf7Sgilles 
770*3ef9cbf7Sgilles 		imsg_compose(env->sc_ibufs[PROC_QUEUE],
771*3ef9cbf7Sgilles 		    IMSG_QUEUE_MESSAGE_UPDATE, 0, 0, -1, messagep,
772*3ef9cbf7Sgilles 		    sizeof(struct message));
773*3ef9cbf7Sgilles 		TAILQ_REMOVE(&batchp->messages, messagep, entry);
774*3ef9cbf7Sgilles 		free(messagep);
775*3ef9cbf7Sgilles 	}
776*3ef9cbf7Sgilles 
777*3ef9cbf7Sgilles 	SPLAY_REMOVE(batchtree, &env->batch_queue, batchp);
778*3ef9cbf7Sgilles 
779*3ef9cbf7Sgilles 	if (batchp->messagefp)
780*3ef9cbf7Sgilles 		fclose(batchp->messagefp);
781*3ef9cbf7Sgilles 
782*3ef9cbf7Sgilles 	if (batchp->bev)
783*3ef9cbf7Sgilles 		bufferevent_free(batchp->bev);
784*3ef9cbf7Sgilles 
785*3ef9cbf7Sgilles 	free(batchp);
786*3ef9cbf7Sgilles }
787