xref: /openbsd/usr.sbin/smtpd/mda.c (revision be691f3b)
1 /*	$OpenBSD: mda.c,v 1.143 2021/06/14 17:58:15 eric Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
5  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6  * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
7  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 #include <ctype.h>
23 #include <inttypes.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sysexits.h>
27 #include <unistd.h>
28 #include <vis.h>
29 
30 #include "smtpd.h"
31 #include "log.h"
32 
33 #define MDA_HIWAT		65536
34 
35 struct mda_envelope {
36 	TAILQ_ENTRY(mda_envelope)	 entry;
37 	uint64_t			 session_id;
38 	uint64_t			 id;
39 	time_t				 creation;
40 	char				*sender;
41 	char				*rcpt;
42 	char				*dest;
43 	char				*user;
44 	char				*dispatcher;
45 	char				*mda_subaddress;
46 	char				*mda_exec;
47 };
48 
49 #define USER_WAITINFO	0x01
50 #define USER_RUNNABLE	0x02
51 #define USER_ONHOLD	0x04
52 #define USER_HOLDQ	0x08
53 
54 struct mda_user {
55 	uint64_t			id;
56 	TAILQ_ENTRY(mda_user)		entry;
57 	TAILQ_ENTRY(mda_user)		entry_runnable;
58 	char				name[LOGIN_NAME_MAX];
59 	char				usertable[PATH_MAX];
60 	size_t				evpcount;
61 	TAILQ_HEAD(, mda_envelope)	envelopes;
62 	int				flags;
63 	size_t				running;
64 	struct userinfo			userinfo;
65 };
66 
67 struct mda_session {
68 	uint64_t		 id;
69 	struct mda_user		*user;
70 	struct mda_envelope	*evp;
71 	struct io		*io;
72 	FILE			*datafp;
73 };
74 
75 static void mda_io(struct io *, int, void *);
76 static int mda_check_loop(FILE *, struct mda_envelope *);
77 static int mda_getlastline(int, char *, size_t);
78 static void mda_done(struct mda_session *);
79 static void mda_fail(struct mda_user *, int, const char *,
80     enum enhanced_status_code);
81 static void mda_drain(void);
82 static void mda_log(const struct mda_envelope *, const char *, const char *);
83 static void mda_queue_ok(uint64_t);
84 static void mda_queue_tempfail(uint64_t, const char *,
85     enum enhanced_status_code);
86 static void mda_queue_permfail(uint64_t, const char *, enum enhanced_status_code);
87 static void mda_queue_loop(uint64_t);
88 static struct mda_user *mda_user(const struct envelope *);
89 static void mda_user_free(struct mda_user *);
90 static const char *mda_user_to_text(const struct mda_user *);
91 static struct mda_envelope *mda_envelope(uint64_t, const struct envelope *);
92 static void mda_envelope_free(struct mda_envelope *);
93 static struct mda_session * mda_session(struct mda_user *);
94 static const char *mda_sysexit_to_str(int);
95 
96 static struct tree	sessions;
97 static struct tree	users;
98 
99 static TAILQ_HEAD(, mda_user)	runnable;
100 
101 void
102 mda_imsg(struct mproc *p, struct imsg *imsg)
103 {
104 	struct mda_session	*s;
105 	struct mda_user		*u;
106 	struct mda_envelope	*e;
107 	struct envelope		 evp;
108 	struct deliver		 deliver;
109 	struct msg		 m;
110 	const void		*data;
111 	const char		*error, *parent_error, *syserror;
112 	uint64_t		 reqid;
113 	size_t			 sz;
114 	char			 out[256], buf[LINE_MAX];
115 	int			 n;
116 	enum lka_resp_status	status;
117 	enum mda_resp_status	mda_status;
118 	int			mda_sysexit;
119 
120 	switch (imsg->hdr.type) {
121 	case IMSG_MDA_LOOKUP_USERINFO:
122 		m_msg(&m, imsg);
123 		m_get_id(&m, &reqid);
124 		m_get_int(&m, (int *)&status);
125 		if (status == LKA_OK)
126 			m_get_data(&m, &data, &sz);
127 		m_end(&m);
128 
129 		u = tree_xget(&users, reqid);
130 
131 		if (status == LKA_TEMPFAIL)
132 			mda_fail(u, 0,
133 			    "Temporary failure in user lookup",
134 			    ESC_OTHER_ADDRESS_STATUS);
135 		else if (status == LKA_PERMFAIL)
136 			mda_fail(u, 1,
137 			    "Permanent failure in user lookup",
138 			    ESC_DESTINATION_MAILBOX_HAS_MOVED);
139 		else {
140 			if (sz != sizeof(u->userinfo))
141 				fatalx("mda: userinfo size mismatch");
142 			memmove(&u->userinfo, data, sz);
143 			u->flags &= ~USER_WAITINFO;
144 			u->flags |= USER_RUNNABLE;
145 			TAILQ_INSERT_TAIL(&runnable, u, entry_runnable);
146 			mda_drain();
147 		}
148 		return;
149 
150 	case IMSG_QUEUE_DELIVER:
151 		m_msg(&m, imsg);
152 		m_get_envelope(&m, &evp);
153 		m_end(&m);
154 
155 		u = mda_user(&evp);
156 
157 		if (u->evpcount >= env->sc_mda_task_hiwat) {
158 			if (!(u->flags & USER_ONHOLD)) {
159 				log_debug("debug: mda: hiwat reached for "
160 				    "user \"%s\": holding envelopes",
161 				    mda_user_to_text(u));
162 				u->flags |= USER_ONHOLD;
163 			}
164 		}
165 
166 		if (u->flags & USER_ONHOLD) {
167 			u->flags |= USER_HOLDQ;
168 			m_create(p_queue, IMSG_MDA_DELIVERY_HOLD,
169 			    0, 0, -1);
170 			m_add_evpid(p_queue, evp.id);
171 			m_add_id(p_queue, u->id);
172 			m_close(p_queue);
173 			return;
174 		}
175 
176 		e = mda_envelope(u->id, &evp);
177 		TAILQ_INSERT_TAIL(&u->envelopes, e, entry);
178 		u->evpcount += 1;
179 		stat_increment("mda.pending", 1);
180 
181 		if (!(u->flags & USER_RUNNABLE) &&
182 		    !(u->flags & USER_WAITINFO)) {
183 			u->flags |= USER_RUNNABLE;
184 			TAILQ_INSERT_TAIL(&runnable, u, entry_runnable);
185 		}
186 
187 		mda_drain();
188 		return;
189 
190 	case IMSG_MDA_OPEN_MESSAGE:
191 		m_msg(&m, imsg);
192 		m_get_id(&m, &reqid);
193 		m_end(&m);
194 
195 		s = tree_xget(&sessions, reqid);
196 		e = s->evp;
197 
198 		if (imsg->fd == -1) {
199 			log_debug("debug: mda: cannot get message fd");
200 			mda_queue_tempfail(e->id,
201 			    "Cannot get message fd",
202 			    ESC_OTHER_MAIL_SYSTEM_STATUS);
203 			mda_log(e, "TempFail", "Cannot get message fd");
204 			mda_done(s);
205 			return;
206 		}
207 
208 		log_debug("debug: mda: got message fd %d "
209 		    "for session %016"PRIx64 " evpid %016"PRIx64,
210 		    imsg->fd, s->id, e->id);
211 
212 		if ((s->datafp = fdopen(imsg->fd, "r")) == NULL) {
213 			log_warn("warn: mda: fdopen");
214 			close(imsg->fd);
215 			mda_queue_tempfail(e->id, "fdopen failed",
216 			    ESC_OTHER_MAIL_SYSTEM_STATUS);
217 			mda_log(e, "TempFail", "fdopen failed");
218 			mda_done(s);
219 			return;
220 		}
221 
222 		/* check delivery loop */
223 		if (mda_check_loop(s->datafp, e)) {
224 			log_debug("debug: mda: loop detected");
225 			mda_queue_loop(e->id);
226 			mda_log(e, "PermFail", "Loop detected");
227 			mda_done(s);
228 			return;
229 		}
230 
231 		/* start queueing delivery headers */
232 		if (e->sender[0])
233 			/*
234 			 * XXX: remove existing Return-Path,
235 			 * if any
236 			 */
237 			n = io_printf(s->io,
238 			    "Return-Path: <%s>\n"
239 			    "Delivered-To: %s\n",
240 			    e->sender,
241 			    e->rcpt ? e->rcpt : e->dest);
242 		else
243 			n = io_printf(s->io,
244 			    "Delivered-To: %s\n",
245 			    e->rcpt ? e->rcpt : e->dest);
246 		if (n == -1) {
247 			log_warn("warn: mda: "
248 			    "fail to write delivery info");
249 			mda_queue_tempfail(e->id, "Out of memory",
250 			    ESC_OTHER_MAIL_SYSTEM_STATUS);
251 			mda_log(e, "TempFail", "Out of memory");
252 			mda_done(s);
253 			return;
254 		}
255 
256 		/* request parent to fork a helper process */
257 		memset(&deliver, 0, sizeof deliver);
258 		(void)text_to_mailaddr(&deliver.sender, s->evp->sender);
259 		(void)text_to_mailaddr(&deliver.rcpt, s->evp->rcpt);
260 		(void)text_to_mailaddr(&deliver.dest, s->evp->dest);
261 		if (s->evp->mda_exec)
262 			(void)strlcpy(deliver.mda_exec, s->evp->mda_exec, sizeof deliver.mda_exec);
263 		if (s->evp->mda_subaddress)
264 			(void)strlcpy(deliver.mda_subaddress, s->evp->mda_subaddress, sizeof deliver.mda_subaddress);
265 		(void)strlcpy(deliver.dispatcher, s->evp->dispatcher, sizeof deliver.dispatcher);
266 		deliver.userinfo = s->user->userinfo;
267 
268 		log_debug("debug: mda: querying mda fd "
269 		    "for session %016"PRIx64 " evpid %016"PRIx64,
270 		    s->id, s->evp->id);
271 
272 		m_create(p_parent, IMSG_MDA_FORK, 0, 0, -1);
273 		m_add_id(p_parent, reqid);
274 		m_add_data(p_parent, &deliver, sizeof(deliver));
275 		m_close(p_parent);
276 		return;
277 
278 	case IMSG_MDA_FORK:
279 		m_msg(&m, imsg);
280 		m_get_id(&m, &reqid);
281 		m_end(&m);
282 
283 		s = tree_xget(&sessions, reqid);
284 		e = s->evp;
285 		if (imsg->fd == -1) {
286 			log_warn("warn: mda: fail to retrieve mda fd");
287 			mda_queue_tempfail(e->id, "Cannot get mda fd",
288 			    ESC_OTHER_MAIL_SYSTEM_STATUS);
289 			mda_log(e, "TempFail", "Cannot get mda fd");
290 			mda_done(s);
291 			return;
292 		}
293 
294 		log_debug("debug: mda: got mda fd %d "
295 		    "for session %016"PRIx64 " evpid %016"PRIx64,
296 		    imsg->fd, s->id, s->evp->id);
297 
298 		io_set_nonblocking(imsg->fd);
299 		io_set_fd(s->io, imsg->fd);
300 		io_set_write(s->io);
301 		return;
302 
303 	case IMSG_MDA_DONE:
304 		m_msg(&m, imsg);
305 		m_get_id(&m, &reqid);
306 		m_get_int(&m, (int *)&mda_status);
307 		m_get_int(&m, (int *)&mda_sysexit);
308 		m_get_string(&m, &parent_error);
309 		m_end(&m);
310 
311 		s = tree_xget(&sessions, reqid);
312 		e = s->evp;
313 		/*
314 		 * Grab last line of mda stdout/stderr if available.
315 		 */
316 		out[0] = '\0';
317 		if (imsg->fd != -1)
318 			mda_getlastline(imsg->fd, out, sizeof(out));
319 
320 		/*
321 		 * Choose between parent's description of error and
322 		 * child's output, the latter having preference over
323 		 * the former.
324 		 */
325 		error = NULL;
326 		if (mda_status == MDA_OK) {
327 			if (s->datafp || (s->io && io_queued(s->io))) {
328 				error = "mda exited prematurely";
329 				mda_status = MDA_TEMPFAIL;
330 			}
331 		} else
332 			error = out[0] ? out : parent_error;
333 
334 		syserror = NULL;
335 		if (mda_sysexit)
336 			syserror = mda_sysexit_to_str(mda_sysexit);
337 
338 		/* update queue entry */
339 		switch (mda_status) {
340 		case MDA_TEMPFAIL:
341 			mda_queue_tempfail(e->id, error,
342 			    ESC_OTHER_MAIL_SYSTEM_STATUS);
343 			(void)snprintf(buf, sizeof buf,
344 			    "Error (%s%s%s)",
345 				       syserror ? syserror : "",
346 				       syserror ? ": " : "",
347 				       error);
348 			mda_log(e, "TempFail", buf);
349 			break;
350 		case MDA_PERMFAIL:
351 			mda_queue_permfail(e->id, error,
352 			    ESC_OTHER_MAIL_SYSTEM_STATUS);
353 			(void)snprintf(buf, sizeof buf,
354 			    "Error (%s%s%s)",
355 				       syserror ? syserror : "",
356 				       syserror ? ": " : "",
357 				       error);
358 			mda_log(e, "PermFail", buf);
359 			break;
360 		case MDA_OK:
361 			mda_queue_ok(e->id);
362 			mda_log(e, "Ok", "Delivered");
363 			break;
364 		}
365 		mda_done(s);
366 		return;
367 	}
368 
369 	fatalx("mda_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
370 }
371 
372 void
373 mda_postfork()
374 {
375 }
376 
377 void
378 mda_postprivdrop()
379 {
380 	tree_init(&sessions);
381 	tree_init(&users);
382 	TAILQ_INIT(&runnable);
383 }
384 
385 static void
386 mda_io(struct io *io, int evt, void *arg)
387 {
388 	struct mda_session	*s = arg;
389 	char			*ln = NULL;
390 	size_t			 sz = 0;
391 	ssize_t			 len;
392 
393 	log_trace(TRACE_IO, "mda: %p: %s %s", s, io_strevent(evt),
394 	    io_strio(io));
395 
396 	switch (evt) {
397 	case IO_LOWAT:
398 
399 	/* done */
400 	done:
401 		if (s->datafp == NULL) {
402 			log_debug("debug: mda: all data sent for session"
403 			    " %016"PRIx64 " evpid %016"PRIx64,
404 			    s->id, s->evp->id);
405 			io_free(io);
406 			s->io = NULL;
407 			return;
408 		}
409 
410 		while (io_queued(s->io) < MDA_HIWAT) {
411 			if ((len = getline(&ln, &sz, s->datafp)) == -1)
412 				break;
413 			if (io_write(s->io, ln, len) == -1) {
414 				m_create(p_parent, IMSG_MDA_KILL,
415 				    0, 0, -1);
416 				m_add_id(p_parent, s->id);
417 				m_add_string(p_parent, "Out of memory");
418 				m_close(p_parent);
419 				io_pause(io, IO_OUT);
420 				free(ln);
421 				return;
422 			}
423 		}
424 
425 		free(ln);
426 		ln = NULL;
427 		if (ferror(s->datafp)) {
428 			log_debug("debug: mda: ferror on session %016"PRIx64,
429 			    s->id);
430 			m_create(p_parent, IMSG_MDA_KILL, 0, 0, -1);
431 			m_add_id(p_parent, s->id);
432 			m_add_string(p_parent, "Error reading body");
433 			m_close(p_parent);
434 			io_pause(io, IO_OUT);
435 			return;
436 		}
437 
438 		if (feof(s->datafp)) {
439 			log_debug("debug: mda: end-of-file for session"
440 			    " %016"PRIx64 " evpid %016"PRIx64,
441 			    s->id, s->evp->id);
442 			fclose(s->datafp);
443 			s->datafp = NULL;
444 			if (io_queued(s->io) == 0)
445 				goto done;
446 		}
447 		return;
448 
449 	case IO_TIMEOUT:
450 		log_debug("debug: mda: timeout on session %016"PRIx64, s->id);
451 		io_pause(io, IO_OUT);
452 		return;
453 
454 	case IO_ERROR:
455 		log_debug("debug: mda: io error on session %016"PRIx64": %s",
456 		    s->id, io_error(io));
457 		io_pause(io, IO_OUT);
458 		return;
459 
460 	case IO_DISCONNECTED:
461 		log_debug("debug: mda: io disconnected on session %016"PRIx64,
462 		    s->id);
463 		io_pause(io, IO_OUT);
464 		return;
465 
466 	default:
467 		log_debug("debug: mda: unexpected event on session %016"PRIx64,
468 		    s->id);
469 		io_pause(io, IO_OUT);
470 		return;
471 	}
472 }
473 
474 static int
475 mda_check_loop(FILE *fp, struct mda_envelope *e)
476 {
477 	char		*buf = NULL;
478 	size_t		 sz = 0;
479 	ssize_t		 len;
480 	int		 ret = 0;
481 
482 	while ((len = getline(&buf, &sz, fp)) != -1) {
483 		if (buf[len - 1] == '\n')
484 			buf[len - 1] = '\0';
485 
486 		if (strchr(buf, ':') == NULL && !isspace((unsigned char)*buf))
487 			break;
488 
489 		if (strncasecmp("Delivered-To: ", buf, 14) == 0) {
490 			if (strcasecmp(buf + 14, e->dest) == 0) {
491 				ret = 1;
492 				break;
493 			}
494 		}
495 	}
496 
497 	free(buf);
498 	fseek(fp, SEEK_SET, 0);
499 	return (ret);
500 }
501 
502 static int
503 mda_getlastline(int fd, char *dst, size_t dstsz)
504 {
505 	FILE	*fp;
506 	char	*ln = NULL;
507 	size_t	 sz = 0;
508 	ssize_t	 len;
509 	int	 out = 0;
510 
511 	if (lseek(fd, 0, SEEK_SET) == -1) {
512 		log_warn("warn: mda: lseek");
513 		close(fd);
514 		return (-1);
515 	}
516 	fp = fdopen(fd, "r");
517 	if (fp == NULL) {
518 		log_warn("warn: mda: fdopen");
519 		close(fd);
520 		return (-1);
521 	}
522 	while ((len = getline(&ln, &sz, fp)) != -1) {
523 		if (ln[len - 1] == '\n')
524 			ln[len - 1] = '\0';
525 		out = 1;
526 	}
527 	fclose(fp);
528 
529 	if (out) {
530 		(void)strlcpy(dst, "\"", dstsz);
531 		(void)strnvis(dst + 1, ln, dstsz - 2, VIS_SAFE | VIS_CSTYLE | VIS_NL);
532 		(void)strlcat(dst, "\"", dstsz);
533 	}
534 
535 	free(ln);
536 	return (0);
537 }
538 
539 static void
540 mda_fail(struct mda_user *user, int permfail, const char *error,
541     enum enhanced_status_code code)
542 {
543 	struct mda_envelope	*e;
544 
545 	while ((e = TAILQ_FIRST(&user->envelopes))) {
546 		TAILQ_REMOVE(&user->envelopes, e, entry);
547 		if (permfail) {
548 			mda_log(e, "PermFail", error);
549 			mda_queue_permfail(e->id, error, code);
550 		}
551 		else {
552 			mda_log(e, "TempFail", error);
553 			mda_queue_tempfail(e->id, error, code);
554 		}
555 		mda_envelope_free(e);
556 	}
557 
558 	mda_user_free(user);
559 }
560 
561 static void
562 mda_drain(void)
563 {
564 	struct mda_user		*u;
565 
566 	while ((u = (TAILQ_FIRST(&runnable)))) {
567 
568 		TAILQ_REMOVE(&runnable, u, entry_runnable);
569 
570 		if (u->evpcount == 0 && u->running == 0) {
571 			log_debug("debug: mda: all done for user \"%s\"",
572 			    mda_user_to_text(u));
573 			mda_user_free(u);
574 			continue;
575 		}
576 
577 		if (u->evpcount == 0) {
578 			log_debug("debug: mda: no more envelope for \"%s\"",
579 			    mda_user_to_text(u));
580 			u->flags &= ~USER_RUNNABLE;
581 			continue;
582 		}
583 
584 		if (u->running >= env->sc_mda_max_user_session) {
585 			log_debug("debug: mda: "
586 			    "maximum number of session reached for user \"%s\"",
587 			    mda_user_to_text(u));
588 			u->flags &= ~USER_RUNNABLE;
589 			continue;
590 		}
591 
592 		if (tree_count(&sessions) >= env->sc_mda_max_session) {
593 			log_debug("debug: mda: "
594 			    "maximum number of session reached");
595 			TAILQ_INSERT_HEAD(&runnable, u, entry_runnable);
596 			return;
597 		}
598 
599 		mda_session(u);
600 
601 		if (u->evpcount == env->sc_mda_task_lowat) {
602 			if (u->flags & USER_ONHOLD) {
603 				log_debug("debug: mda: down to lowat for user "
604 				    "\"%s\": releasing",
605 				    mda_user_to_text(u));
606 				u->flags &= ~USER_ONHOLD;
607 			}
608 			if (u->flags & USER_HOLDQ) {
609 				m_create(p_queue, IMSG_MDA_HOLDQ_RELEASE,
610 				    0, 0, -1);
611 				m_add_id(p_queue, u->id);
612 				m_add_int(p_queue, env->sc_mda_task_release);
613 				m_close(p_queue);
614 			}
615 		}
616 
617 		/* re-add the user at the tail of the queue */
618 		TAILQ_INSERT_TAIL(&runnable, u, entry_runnable);
619 	}
620 }
621 
622 static void
623 mda_done(struct mda_session *s)
624 {
625 	log_debug("debug: mda: session %016" PRIx64 " done", s->id);
626 
627 	tree_xpop(&sessions, s->id);
628 
629 	mda_envelope_free(s->evp);
630 
631 	s->user->running--;
632 	if (!(s->user->flags & USER_RUNNABLE)) {
633 		log_debug("debug: mda: user \"%s\" becomes runnable",
634 		    s->user->name);
635 		TAILQ_INSERT_TAIL(&runnable, s->user, entry_runnable);
636 		s->user->flags |= USER_RUNNABLE;
637 	}
638 
639 	if (s->datafp)
640 		fclose(s->datafp);
641 	if (s->io)
642 		io_free(s->io);
643 
644 	free(s);
645 
646 	stat_decrement("mda.running", 1);
647 
648 	mda_drain();
649 }
650 
651 static void
652 mda_log(const struct mda_envelope *evp, const char *prefix, const char *status)
653 {
654 	char rcpt[LINE_MAX];
655 
656 	rcpt[0] = '\0';
657 	if (evp->rcpt)
658 		(void)snprintf(rcpt, sizeof rcpt, "rcpt=<%s> ", evp->rcpt);
659 
660 	log_info("%016"PRIx64" mda delivery evpid=%016" PRIx64 " from=<%s> to=<%s> "
661 	    "%suser=%s delay=%s result=%s stat=%s",
662 	    evp->session_id,
663 	    evp->id,
664 	    evp->sender ? evp->sender : "",
665 	    evp->dest,
666 	    rcpt,
667 	    evp->user,
668 	    duration_to_text(time(NULL) - evp->creation),
669 	    prefix,
670 	    status);
671 }
672 
673 static void
674 mda_queue_ok(uint64_t evpid)
675 {
676 	m_create(p_queue, IMSG_MDA_DELIVERY_OK, 0, 0, -1);
677 	m_add_evpid(p_queue, evpid);
678 	m_close(p_queue);
679 }
680 
681 static void
682 mda_queue_tempfail(uint64_t evpid, const char *reason,
683     enum enhanced_status_code code)
684 {
685 	m_create(p_queue, IMSG_MDA_DELIVERY_TEMPFAIL, 0, 0, -1);
686 	m_add_evpid(p_queue, evpid);
687 	m_add_string(p_queue, reason);
688 	m_add_int(p_queue, (int)code);
689 	m_close(p_queue);
690 }
691 
692 static void
693 mda_queue_permfail(uint64_t evpid, const char *reason,
694     enum enhanced_status_code code)
695 {
696 	m_create(p_queue, IMSG_MDA_DELIVERY_PERMFAIL, 0, 0, -1);
697 	m_add_evpid(p_queue, evpid);
698 	m_add_string(p_queue, reason);
699 	m_add_int(p_queue, (int)code);
700 	m_close(p_queue);
701 }
702 
703 static void
704 mda_queue_loop(uint64_t evpid)
705 {
706 	m_create(p_queue, IMSG_MDA_DELIVERY_LOOP, 0, 0, -1);
707 	m_add_evpid(p_queue, evpid);
708 	m_close(p_queue);
709 }
710 
711 static struct mda_user *
712 mda_user(const struct envelope *evp)
713 {
714 	struct dispatcher *dsp;
715 	struct mda_user	*u;
716 	void		*i;
717 
718 	i = NULL;
719 	dsp = dict_xget(env->sc_dispatchers, evp->dispatcher);
720 	while (tree_iter(&users, &i, NULL, (void**)(&u))) {
721 		if (!strcmp(evp->mda_user, u->name) &&
722 		    !strcmp(dsp->u.local.table_userbase, u->usertable))
723 			return (u);
724 	}
725 
726 	u = xcalloc(1, sizeof *u);
727 	u->id = generate_uid();
728 	TAILQ_INIT(&u->envelopes);
729 	(void)strlcpy(u->name, evp->mda_user, sizeof(u->name));
730 	(void)strlcpy(u->usertable, dsp->u.local.table_userbase,
731 	    sizeof(u->usertable));
732 
733 	tree_xset(&users, u->id, u);
734 
735 	m_create(p_lka, IMSG_MDA_LOOKUP_USERINFO, 0, 0, -1);
736 	m_add_id(p_lka, u->id);
737 	m_add_string(p_lka, dsp->u.local.table_userbase);
738 	m_add_string(p_lka, evp->mda_user);
739 	m_close(p_lka);
740 	u->flags |= USER_WAITINFO;
741 
742 	stat_increment("mda.user", 1);
743 
744 	if (dsp->u.local.user)
745 		log_debug("mda: new user %016" PRIx64
746 		    " for \"%s\" delivering as \"%s\"",
747 		    u->id, mda_user_to_text(u), dsp->u.local.user);
748 	else
749 		log_debug("mda: new user %016" PRIx64
750 		    " for \"%s\"", u->id, mda_user_to_text(u));
751 
752 	return (u);
753 }
754 
755 static void
756 mda_user_free(struct mda_user *u)
757 {
758 	tree_xpop(&users, u->id);
759 
760 	if (u->flags & USER_HOLDQ) {
761 		m_create(p_queue, IMSG_MDA_HOLDQ_RELEASE, 0, 0, -1);
762 		m_add_id(p_queue, u->id);
763 		m_add_int(p_queue, 0);
764 		m_close(p_queue);
765 	}
766 
767 	free(u);
768 	stat_decrement("mda.user", 1);
769 }
770 
771 static const char *
772 mda_user_to_text(const struct mda_user *u)
773 {
774 	static char buf[1024];
775 
776 	(void)snprintf(buf, sizeof(buf), "%s:%s", u->usertable, u->name);
777 
778 	return (buf);
779 }
780 
781 static struct mda_envelope *
782 mda_envelope(uint64_t session_id, const struct envelope *evp)
783 {
784 	struct mda_envelope	*e;
785 	char			 buf[LINE_MAX];
786 
787 	e = xcalloc(1, sizeof *e);
788 	e->session_id = session_id;
789 	e->id = evp->id;
790 	e->creation = evp->creation;
791 	buf[0] = '\0';
792 	if (evp->sender.user[0] && evp->sender.domain[0])
793 		(void)snprintf(buf, sizeof buf, "%s@%s",
794 		    evp->sender.user, evp->sender.domain);
795 	e->sender = xstrdup(buf);
796 	(void)snprintf(buf, sizeof buf, "%s@%s", evp->dest.user,
797 	    evp->dest.domain);
798 	e->dest = xstrdup(buf);
799 	(void)snprintf(buf, sizeof buf, "%s@%s", evp->rcpt.user,
800 	    evp->rcpt.domain);
801 	e->rcpt = xstrdup(buf);
802 	e->user = evp->mda_user[0] ?
803 	    xstrdup(evp->mda_user) : xstrdup(evp->dest.user);
804 	e->dispatcher = xstrdup(evp->dispatcher);
805 	if (evp->mda_exec[0])
806 		e->mda_exec = xstrdup(evp->mda_exec);
807 	if (evp->mda_subaddress[0])
808 		e->mda_subaddress = xstrdup(evp->mda_subaddress);
809 	stat_increment("mda.envelope", 1);
810 	return (e);
811 }
812 
813 static void
814 mda_envelope_free(struct mda_envelope *e)
815 {
816 	free(e->sender);
817 	free(e->dest);
818 	free(e->rcpt);
819 	free(e->user);
820 	free(e->mda_exec);
821 	free(e);
822 
823 	stat_decrement("mda.envelope", 1);
824 }
825 
826 static struct mda_session *
827 mda_session(struct mda_user * u)
828 {
829 	struct mda_session *s;
830 
831 	s = xcalloc(1, sizeof *s);
832 	s->id = generate_uid();
833 	s->user = u;
834 	s->io = io_new();
835 	io_set_callback(s->io, mda_io, s);
836 
837 	tree_xset(&sessions, s->id, s);
838 
839 	s->evp = TAILQ_FIRST(&u->envelopes);
840 	TAILQ_REMOVE(&u->envelopes, s->evp, entry);
841 	u->evpcount--;
842 	u->running++;
843 
844 	stat_decrement("mda.pending", 1);
845 	stat_increment("mda.running", 1);
846 
847 	log_debug("debug: mda: new session %016" PRIx64
848 	    " for user \"%s\" evpid %016" PRIx64, s->id,
849 	    mda_user_to_text(u), s->evp->id);
850 
851 	m_create(p_queue, IMSG_MDA_OPEN_MESSAGE, 0, 0, -1);
852 	m_add_id(p_queue, s->id);
853 	m_add_msgid(p_queue, evpid_to_msgid(s->evp->id));
854 	m_close(p_queue);
855 
856 	return (s);
857 }
858 
859 static const char *
860 mda_sysexit_to_str(int sysexit)
861 {
862 	switch (sysexit) {
863 	case EX_USAGE:
864 		return "command line usage error";
865 	case EX_DATAERR:
866 		return "data format error";
867 	case EX_NOINPUT:
868 		return "cannot open input";
869 	case EX_NOUSER:
870 		return "user unknown";
871 	case EX_NOHOST:
872 		return "host name unknown";
873 	case EX_UNAVAILABLE:
874 		return "service unavailable";
875 	case EX_SOFTWARE:
876 		return "internal software error";
877 	case EX_OSERR:
878 		return "system resource problem";
879 	case EX_OSFILE:
880 		return "critical OS file missing";
881 	case EX_CANTCREAT:
882 		return "can't create user output file";
883 	case EX_IOERR:
884 		return "input/output error";
885 	case EX_TEMPFAIL:
886 		return "temporary failure";
887 	case EX_PROTOCOL:
888 		return "remote error in protocol";
889 	case EX_NOPERM:
890 		return "permission denied";
891 	case EX_CONFIG:
892 		return "local configuration error";
893 	default:
894 		break;
895 	}
896 	return NULL;
897 }
898 
899