xref: /openbsd-src/usr.sbin/smtpd/queue.c (revision 510586ace9b1e48cf6ab0f5a87b7657547e6cf37)
1*510586acSclaudio /*	$OpenBSD: queue.c,v 1.196 2024/01/20 09:01:03 claudio Exp $	*/
21f3c2bcfSsobrado 
33ef9cbf7Sgilles /*
465c4fdfbSgilles  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
53ef9cbf7Sgilles  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
666bc57beSeric  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
73ef9cbf7Sgilles  *
83ef9cbf7Sgilles  * Permission to use, copy, modify, and distribute this software for any
93ef9cbf7Sgilles  * purpose with or without fee is hereby granted, provided that the above
103ef9cbf7Sgilles  * copyright notice and this permission notice appear in all copies.
113ef9cbf7Sgilles  *
123ef9cbf7Sgilles  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
133ef9cbf7Sgilles  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
143ef9cbf7Sgilles  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
153ef9cbf7Sgilles  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
163ef9cbf7Sgilles  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
173ef9cbf7Sgilles  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
183ef9cbf7Sgilles  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
193ef9cbf7Sgilles  */
203ef9cbf7Sgilles 
211ae37aa2Schl #include <inttypes.h>
223ef9cbf7Sgilles #include <pwd.h>
239623339fSchl #include <signal.h>
243ef9cbf7Sgilles #include <stdlib.h>
253ef9cbf7Sgilles #include <string.h>
260dcffd0dSop #include <time.h>
273ef9cbf7Sgilles #include <unistd.h>
283ef9cbf7Sgilles 
293ef9cbf7Sgilles #include "smtpd.h"
305eb8dddaSgilles #include "log.h"
313ef9cbf7Sgilles 
3265c4fdfbSgilles static void queue_imsg(struct mproc *, struct imsg *);
3366bc57beSeric static void queue_timeout(int, short, void *);
3465c4fdfbSgilles static void queue_bounce(struct envelope *, struct delivery_bounce *);
35be925435Sgilles static void queue_shutdown(void);
3665c4fdfbSgilles static void queue_log(const struct envelope *, const char *, const char *);
37a9835440Ssunil static void queue_msgid_walk(int, short, void *);
3865c4fdfbSgilles 
39bf2fef61Sgilles 
40be925435Sgilles static void
queue_imsg(struct mproc * p,struct imsg * imsg)4165c4fdfbSgilles queue_imsg(struct mproc *p, struct imsg *imsg)
42ed1929b6Sjacekm {
4365c4fdfbSgilles 	struct delivery_bounce	 bounce;
44a9835440Ssunil 	struct msg_walkinfo	*wi;
45a9835440Ssunil 	struct timeval		 tv;
4665c4fdfbSgilles 	struct bounce_req_msg	*req_bounce;
4765c4fdfbSgilles 	struct envelope		 evp;
4865c4fdfbSgilles 	struct msg		 m;
4965c4fdfbSgilles 	const char		*reason;
507eed50e8Seric 	uint64_t		 reqid, evpid, holdq;
51913395bcSeric 	uint32_t		 msgid;
5265c4fdfbSgilles 	time_t			 nexttry;
534fca4105Seric 	size_t			 n_evp;
54fe95d8d0Seric 	int			 fd, mta_ext, ret, v, flags, code;
55ed1929b6Sjacekm 
5643962b9cSeric 	if (imsg == NULL)
5743962b9cSeric 		queue_shutdown();
5843962b9cSeric 
59fe95d8d0Seric 	memset(&bounce, 0, sizeof(struct delivery_bounce));
60e5b07014Sgilles 
61ed1929b6Sjacekm 	switch (imsg->hdr.type) {
62aa1d5973Seric 	case IMSG_SMTP_MESSAGE_CREATE:
6365c4fdfbSgilles 		m_msg(&m, imsg);
6465c4fdfbSgilles 		m_get_id(&m, &reqid);
6565c4fdfbSgilles 		m_end(&m);
6665c4fdfbSgilles 
6765c4fdfbSgilles 		ret = queue_message_create(&msgid);
6865c4fdfbSgilles 
69aa1d5973Seric 		m_create(p, IMSG_SMTP_MESSAGE_CREATE, 0, 0, -1);
7065c4fdfbSgilles 		m_add_id(p, reqid);
71e5b07014Sgilles 		if (ret == 0)
7265c4fdfbSgilles 			m_add_int(p, 0);
7365c4fdfbSgilles 		else {
7465c4fdfbSgilles 			m_add_int(p, 1);
7565c4fdfbSgilles 			m_add_msgid(p, msgid);
7665c4fdfbSgilles 		}
7765c4fdfbSgilles 		m_close(p);
78ed1929b6Sjacekm 		return;
79ed1929b6Sjacekm 
80aa1d5973Seric 	case IMSG_SMTP_MESSAGE_ROLLBACK:
8165c4fdfbSgilles 		m_msg(&m, imsg);
8265c4fdfbSgilles 		m_get_msgid(&m, &msgid);
8365c4fdfbSgilles 		m_end(&m);
8465c4fdfbSgilles 
8565c4fdfbSgilles 		queue_message_delete(msgid);
8665c4fdfbSgilles 
87aa1d5973Seric 		m_create(p_scheduler, IMSG_QUEUE_MESSAGE_ROLLBACK,
88299c4efeSeric 		    0, 0, -1);
8965c4fdfbSgilles 		m_add_msgid(p_scheduler, msgid);
9065c4fdfbSgilles 		m_close(p_scheduler);
91ed1929b6Sjacekm 		return;
92ed1929b6Sjacekm 
93aa1d5973Seric 	case IMSG_SMTP_MESSAGE_COMMIT:
9465c4fdfbSgilles 		m_msg(&m, imsg);
9565c4fdfbSgilles 		m_get_id(&m, &reqid);
9665c4fdfbSgilles 		m_get_msgid(&m, &msgid);
9765c4fdfbSgilles 		m_end(&m);
9850caaca6Seric 
9965c4fdfbSgilles 		ret = queue_message_commit(msgid);
10065c4fdfbSgilles 
101aa1d5973Seric 		m_create(p, IMSG_SMTP_MESSAGE_COMMIT, 0, 0, -1);
10265c4fdfbSgilles 		m_add_id(p, reqid);
10365c4fdfbSgilles 		m_add_int(p, (ret == 0) ? 0 : 1);
10465c4fdfbSgilles 		m_close(p);
10565c4fdfbSgilles 
10665c4fdfbSgilles 		if (ret) {
107aa1d5973Seric 			m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT,
108299c4efeSeric 			    0, 0, -1);
10965c4fdfbSgilles 			m_add_msgid(p_scheduler, msgid);
11065c4fdfbSgilles 			m_close(p_scheduler);
11165c4fdfbSgilles 		}
1120beae34bSjacekm 		return;
1130beae34bSjacekm 
114aa1d5973Seric 	case IMSG_SMTP_MESSAGE_OPEN:
11565c4fdfbSgilles 		m_msg(&m, imsg);
11665c4fdfbSgilles 		m_get_id(&m, &reqid);
11765c4fdfbSgilles 		m_get_msgid(&m, &msgid);
11865c4fdfbSgilles 		m_end(&m);
11965c4fdfbSgilles 
12065c4fdfbSgilles 		fd = queue_message_fd_rw(msgid);
12165c4fdfbSgilles 
122aa1d5973Seric 		m_create(p, IMSG_SMTP_MESSAGE_OPEN, 0, 0, fd);
12365c4fdfbSgilles 		m_add_id(p, reqid);
12465c4fdfbSgilles 		m_add_int(p, (fd == -1) ? 0 : 1);
12565c4fdfbSgilles 		m_close(p);
126ed1929b6Sjacekm 		return;
127b28a97afSjacekm 
128aa1d5973Seric 	case IMSG_QUEUE_SMTP_SESSION:
129*510586acSclaudio 		bounce_fd(imsg_get_fd(imsg));
130b28a97afSjacekm 		return;
131ed1929b6Sjacekm 
132aa1d5973Seric 	case IMSG_LKA_ENVELOPE_SUBMIT:
13365c4fdfbSgilles 		m_msg(&m, imsg);
13465c4fdfbSgilles 		m_get_id(&m, &reqid);
13565c4fdfbSgilles 		m_get_envelope(&m, &evp);
13665c4fdfbSgilles 		m_end(&m);
13765c4fdfbSgilles 
13865c4fdfbSgilles 		if (evp.id == 0)
139299c4efeSeric 			log_warnx("warn: imsg_queue_submit_envelope: evpid=0");
14065c4fdfbSgilles 		if (evpid_to_msgid(evp.id) == 0)
141299c4efeSeric 			log_warnx("warn: imsg_queue_submit_envelope: msgid=0, "
14265c4fdfbSgilles 			    "evpid=%016"PRIx64, evp.id);
14365c4fdfbSgilles 		ret = queue_envelope_create(&evp);
1441a5b831aSmartijn 		m_create(p_dispatcher, IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1);
1451a5b831aSmartijn 		m_add_id(p_dispatcher, reqid);
14665c4fdfbSgilles 		if (ret == 0)
1471a5b831aSmartijn 			m_add_int(p_dispatcher, 0);
14865c4fdfbSgilles 		else {
1491a5b831aSmartijn 			m_add_int(p_dispatcher, 1);
1501a5b831aSmartijn 			m_add_evpid(p_dispatcher, evp.id);
15165c4fdfbSgilles 		}
1521a5b831aSmartijn 		m_close(p_dispatcher);
15365c4fdfbSgilles 		if (ret) {
15465c4fdfbSgilles 			m_create(p_scheduler,
155aa1d5973Seric 			    IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1);
15665c4fdfbSgilles 			m_add_envelope(p_scheduler, &evp);
15765c4fdfbSgilles 			m_close(p_scheduler);
158e5b07014Sgilles 		}
159e5b07014Sgilles 		return;
160e5b07014Sgilles 
161aa1d5973Seric 	case IMSG_LKA_ENVELOPE_COMMIT:
16265c4fdfbSgilles 		m_msg(&m, imsg);
16365c4fdfbSgilles 		m_get_id(&m, &reqid);
16465c4fdfbSgilles 		m_end(&m);
1651a5b831aSmartijn 		m_create(p_dispatcher, IMSG_QUEUE_ENVELOPE_COMMIT, 0, 0, -1);
1661a5b831aSmartijn 		m_add_id(p_dispatcher, reqid);
1671a5b831aSmartijn 		m_add_int(p_dispatcher, 1);
1681a5b831aSmartijn 		m_close(p_dispatcher);
169e5b07014Sgilles 		return;
170e5b07014Sgilles 
171aa1d5973Seric 	case IMSG_SCHED_ENVELOPE_REMOVE:
17265c4fdfbSgilles 		m_msg(&m, imsg);
17365c4fdfbSgilles 		m_get_evpid(&m, &evpid);
17465c4fdfbSgilles 		m_end(&m);
175299c4efeSeric 
176acfdf0daSeric 		m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_ACK, 0, 0, -1);
177acfdf0daSeric 		m_add_evpid(p_scheduler, evpid);
178acfdf0daSeric 		m_close(p_scheduler);
179acfdf0daSeric 
180299c4efeSeric 		/* already removed by scheduler */
18165c4fdfbSgilles 		if (queue_envelope_load(evpid, &evp) == 0)
182299c4efeSeric 			return;
183acfdf0daSeric 
18465c4fdfbSgilles 		queue_log(&evp, "Remove", "Removed by administrator");
18565c4fdfbSgilles 		queue_envelope_delete(evpid);
186913395bcSeric 		return;
187913395bcSeric 
188aa1d5973Seric 	case IMSG_SCHED_ENVELOPE_EXPIRE:
18965c4fdfbSgilles 		m_msg(&m, imsg);
19065c4fdfbSgilles 		m_get_evpid(&m, &evpid);
19165c4fdfbSgilles 		m_end(&m);
192299c4efeSeric 
193acfdf0daSeric 		m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_ACK, 0, 0, -1);
194acfdf0daSeric 		m_add_evpid(p_scheduler, evpid);
195acfdf0daSeric 		m_close(p_scheduler);
196acfdf0daSeric 
197299c4efeSeric 		/* already removed by scheduler*/
19865c4fdfbSgilles 		if (queue_envelope_load(evpid, &evp) == 0)
199299c4efeSeric 			return;
200299c4efeSeric 
2015ed42fc8Ssunil 		bounce.type = B_FAILED;
202a47ad22cSeric 		envelope_set_errormsg(&evp, "Envelope expired");
203139244a2Seric 		envelope_set_esc_class(&evp, ESC_STATUS_PERMFAIL);
204fe95d8d0Seric 		envelope_set_esc_code(&evp, ESC_DELIVERY_TIME_EXPIRED);
20565c4fdfbSgilles 		queue_bounce(&evp, &bounce);
20665c4fdfbSgilles 		queue_log(&evp, "Expire", "Envelope expired");
20765c4fdfbSgilles 		queue_envelope_delete(evpid);
208913395bcSeric 		return;
209913395bcSeric 
210aa1d5973Seric 	case IMSG_SCHED_ENVELOPE_BOUNCE:
21172bef77dSsunil 		CHECK_IMSG_DATA_SIZE(imsg, sizeof *req_bounce);
21265c4fdfbSgilles 		req_bounce = imsg->data;
21365c4fdfbSgilles 		evpid = req_bounce->evpid;
214299c4efeSeric 
215299c4efeSeric 		if (queue_envelope_load(evpid, &evp) == 0) {
216299c4efeSeric 			log_warnx("queue: bounce: failed to load envelope");
217aa1d5973Seric 			m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1);
218299c4efeSeric 			m_add_evpid(p_scheduler, evpid);
219299c4efeSeric 			m_add_u32(p_scheduler, 0); /* not in-flight */
220299c4efeSeric 			m_close(p_scheduler);
221299c4efeSeric 			return;
222299c4efeSeric 		}
22365c4fdfbSgilles 		queue_bounce(&evp, &req_bounce->bounce);
22465c4fdfbSgilles 		evp.lastbounce = req_bounce->timestamp;
22535e161d3Seric 		if (!queue_envelope_update(&evp))
22635e161d3Seric 			log_warnx("warn: could not update envelope %016"PRIx64, evpid);
22765c4fdfbSgilles 		return;
22865c4fdfbSgilles 
229aa1d5973Seric 	case IMSG_SCHED_ENVELOPE_DELIVER:
23065c4fdfbSgilles 		m_msg(&m, imsg);
23165c4fdfbSgilles 		m_get_evpid(&m, &evpid);
23265c4fdfbSgilles 		m_end(&m);
233299c4efeSeric 		if (queue_envelope_load(evpid, &evp) == 0) {
234299c4efeSeric 			log_warnx("queue: deliver: failed to load envelope");
235aa1d5973Seric 			m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1);
236299c4efeSeric 			m_add_evpid(p_scheduler, evpid);
237299c4efeSeric 			m_add_u32(p_scheduler, 1); /* in-flight */
238299c4efeSeric 			m_close(p_scheduler);
239299c4efeSeric 			return;
240299c4efeSeric 		}
241913395bcSeric 		evp.lasttry = time(NULL);
2421a5b831aSmartijn 		m_create(p_dispatcher, IMSG_QUEUE_DELIVER, 0, 0, -1);
2431a5b831aSmartijn 		m_add_envelope(p_dispatcher, &evp);
2441a5b831aSmartijn 		m_close(p_dispatcher);
245913395bcSeric 		return;
246913395bcSeric 
247aa1d5973Seric 	case IMSG_SCHED_ENVELOPE_INJECT:
24865c4fdfbSgilles 		m_msg(&m, imsg);
24965c4fdfbSgilles 		m_get_evpid(&m, &evpid);
25065c4fdfbSgilles 		m_end(&m);
25165c4fdfbSgilles 		bounce_add(evpid);
252913395bcSeric 		return;
253913395bcSeric 
254aa1d5973Seric 	case IMSG_SCHED_ENVELOPE_TRANSFER:
25565c4fdfbSgilles 		m_msg(&m, imsg);
25665c4fdfbSgilles 		m_get_evpid(&m, &evpid);
25765c4fdfbSgilles 		m_end(&m);
258299c4efeSeric 		if (queue_envelope_load(evpid, &evp) == 0) {
25935e161d3Seric 			log_warnx("queue: failed to load envelope");
260aa1d5973Seric 			m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1);
261299c4efeSeric 			m_add_evpid(p_scheduler, evpid);
262299c4efeSeric 			m_add_u32(p_scheduler, 1); /* in-flight */
263299c4efeSeric 			m_close(p_scheduler);
264299c4efeSeric 			return;
265299c4efeSeric 		}
266913395bcSeric 		evp.lasttry = time(NULL);
2671a5b831aSmartijn 		m_create(p_dispatcher, IMSG_QUEUE_TRANSFER, 0, 0, -1);
2681a5b831aSmartijn 		m_add_envelope(p_dispatcher, &evp);
2691a5b831aSmartijn 		m_close(p_dispatcher);
270913395bcSeric 		return;
271913395bcSeric 
27265c4fdfbSgilles 	case IMSG_CTL_LIST_ENVELOPES:
2734fe02f32Seric 		if (imsg->hdr.len == sizeof imsg->hdr) {
27465c4fdfbSgilles 			m_forward(p_control, imsg);
2754fe02f32Seric 			return;
2764fe02f32Seric 		}
27765c4fdfbSgilles 
27865c4fdfbSgilles 		m_msg(&m, imsg);
27965c4fdfbSgilles 		m_get_evpid(&m, &evpid);
28065c4fdfbSgilles 		m_get_int(&m, &flags);
28165c4fdfbSgilles 		m_get_time(&m, &nexttry);
28265c4fdfbSgilles 		m_end(&m);
28365c4fdfbSgilles 
28465c4fdfbSgilles 		if (queue_envelope_load(evpid, &evp) == 0)
2854fe02f32Seric 			return; /* Envelope is gone, drop it */
28665c4fdfbSgilles 
2874fe02f32Seric 		/*
2884fe02f32Seric 		 * XXX consistency: The envelope might already be on
2894fe02f32Seric 		 * its way back to the scheduler.  We need to detect
2904fe02f32Seric 		 * this properly and report that state.
2914fe02f32Seric 		 */
29235e161d3Seric 		if (flags & EF_INFLIGHT) {
2934fe02f32Seric 			/*
2944fe02f32Seric 			 * Not exactly correct but pretty close: The
2954fe02f32Seric 			 * value is not recorded on the envelope unless
2964fe02f32Seric 			 * a tempfail occurs.
2974fe02f32Seric 			 */
29865c4fdfbSgilles 			evp.lasttry = nexttry;
2994fe02f32Seric 		}
300f2ea53baSsunil 
3014fca4105Seric 		m_create(p_control, IMSG_CTL_LIST_ENVELOPES,
3024fca4105Seric 		    imsg->hdr.peerid, 0, -1);
3034fca4105Seric 		m_add_int(p_control, flags);
3044fca4105Seric 		m_add_time(p_control, nexttry);
3054fca4105Seric 		m_add_envelope(p_control, &evp);
3064fca4105Seric 		m_close(p_control);
3074fe02f32Seric 		return;
308e5b07014Sgilles 
309aa1d5973Seric 	case IMSG_MDA_OPEN_MESSAGE:
310aa1d5973Seric 	case IMSG_MTA_OPEN_MESSAGE:
31165c4fdfbSgilles 		m_msg(&m, imsg);
31265c4fdfbSgilles 		m_get_id(&m, &reqid);
31365c4fdfbSgilles 		m_get_msgid(&m, &msgid);
31465c4fdfbSgilles 		m_end(&m);
31565c4fdfbSgilles 		fd = queue_message_fd_r(msgid);
316aa1d5973Seric 		m_create(p, imsg->hdr.type, 0, 0, fd);
31765c4fdfbSgilles 		m_add_id(p, reqid);
31865c4fdfbSgilles 		m_close(p);
319e5b07014Sgilles 		return;
320e5b07014Sgilles 
321aa1d5973Seric 	case IMSG_MDA_DELIVERY_OK:
322aa1d5973Seric 	case IMSG_MTA_DELIVERY_OK:
32365c4fdfbSgilles 		m_msg(&m, imsg);
32465c4fdfbSgilles 		m_get_evpid(&m, &evpid);
325aa1d5973Seric 		if (imsg->hdr.type == IMSG_MTA_DELIVERY_OK)
326fe95d8d0Seric 			m_get_int(&m, &mta_ext);
32765c4fdfbSgilles 		m_end(&m);
328fe95d8d0Seric 		if (queue_envelope_load(evpid, &evp) == 0) {
329fe95d8d0Seric 			log_warn("queue: dsn: failed to load envelope");
330fe95d8d0Seric 			return;
331fe95d8d0Seric 		}
332fe95d8d0Seric 		if (evp.dsn_notify & DSN_SUCCESS) {
3335ed42fc8Ssunil 			bounce.type = B_DELIVERED;
334fe95d8d0Seric 			bounce.dsn_ret = evp.dsn_ret;
3353abffaecSsunil 			envelope_set_esc_class(&evp, ESC_STATUS_OK);
336aa1d5973Seric 			if (imsg->hdr.type == IMSG_MDA_DELIVERY_OK)
337fe95d8d0Seric 				queue_bounce(&evp, &bounce);
338aa1d5973Seric 			else if (imsg->hdr.type == IMSG_MTA_DELIVERY_OK &&
339fe95d8d0Seric 			    (mta_ext & MTA_EXT_DSN) == 0) {
340fe95d8d0Seric 				bounce.mta_without_dsn = 1;
341fe95d8d0Seric 				queue_bounce(&evp, &bounce);
342fe95d8d0Seric 			}
343fe95d8d0Seric 		}
34465c4fdfbSgilles 		queue_envelope_delete(evpid);
345aa1d5973Seric 		m_create(p_scheduler, IMSG_QUEUE_DELIVERY_OK, 0, 0, -1);
34665c4fdfbSgilles 		m_add_evpid(p_scheduler, evpid);
34765c4fdfbSgilles 		m_close(p_scheduler);
348ed1929b6Sjacekm 		return;
349ed1929b6Sjacekm 
350aa1d5973Seric 	case IMSG_MDA_DELIVERY_TEMPFAIL:
351aa1d5973Seric 	case IMSG_MTA_DELIVERY_TEMPFAIL:
35265c4fdfbSgilles 		m_msg(&m, imsg);
35365c4fdfbSgilles 		m_get_evpid(&m, &evpid);
35465c4fdfbSgilles 		m_get_string(&m, &reason);
355fe95d8d0Seric 		m_get_int(&m, &code);
35665c4fdfbSgilles 		m_end(&m);
357299c4efeSeric 		if (queue_envelope_load(evpid, &evp) == 0) {
358299c4efeSeric 			log_warnx("queue: tempfail: failed to load envelope");
359aa1d5973Seric 			m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1);
360299c4efeSeric 			m_add_evpid(p_scheduler, evpid);
361299c4efeSeric 			m_add_u32(p_scheduler, 1); /* in-flight */
362299c4efeSeric 			m_close(p_scheduler);
363299c4efeSeric 			return;
364299c4efeSeric 		}
36565c4fdfbSgilles 		envelope_set_errormsg(&evp, "%s", reason);
366fe95d8d0Seric 		envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL);
367fe95d8d0Seric 		envelope_set_esc_code(&evp, code);
36865c4fdfbSgilles 		evp.retry++;
36935e161d3Seric 		if (!queue_envelope_update(&evp))
37035e161d3Seric 			log_warnx("warn: could not update envelope %016"PRIx64, evpid);
371aa1d5973Seric 		m_create(p_scheduler, IMSG_QUEUE_DELIVERY_TEMPFAIL, 0, 0, -1);
37265c4fdfbSgilles 		m_add_envelope(p_scheduler, &evp);
37365c4fdfbSgilles 		m_close(p_scheduler);
374913395bcSeric 		return;
375913395bcSeric 
376aa1d5973Seric 	case IMSG_MDA_DELIVERY_PERMFAIL:
377aa1d5973Seric 	case IMSG_MTA_DELIVERY_PERMFAIL:
37865c4fdfbSgilles 		m_msg(&m, imsg);
37965c4fdfbSgilles 		m_get_evpid(&m, &evpid);
38065c4fdfbSgilles 		m_get_string(&m, &reason);
381fe95d8d0Seric 		m_get_int(&m, &code);
38265c4fdfbSgilles 		m_end(&m);
383299c4efeSeric 		if (queue_envelope_load(evpid, &evp) == 0) {
384299c4efeSeric 			log_warnx("queue: permfail: failed to load envelope");
385aa1d5973Seric 			m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1);
386299c4efeSeric 			m_add_evpid(p_scheduler, evpid);
387299c4efeSeric 			m_add_u32(p_scheduler, 1); /* in-flight */
388299c4efeSeric 			m_close(p_scheduler);
389299c4efeSeric 			return;
390299c4efeSeric 		}
3915ed42fc8Ssunil 		bounce.type = B_FAILED;
39265c4fdfbSgilles 		envelope_set_errormsg(&evp, "%s", reason);
393fe95d8d0Seric 		envelope_set_esc_class(&evp, ESC_STATUS_PERMFAIL);
394fe95d8d0Seric 		envelope_set_esc_code(&evp, code);
39565c4fdfbSgilles 		queue_bounce(&evp, &bounce);
39665c4fdfbSgilles 		queue_envelope_delete(evpid);
397aa1d5973Seric 		m_create(p_scheduler, IMSG_QUEUE_DELIVERY_PERMFAIL, 0, 0, -1);
39865c4fdfbSgilles 		m_add_evpid(p_scheduler, evpid);
39965c4fdfbSgilles 		m_close(p_scheduler);
400b28a97afSjacekm 		return;
401a7b2e833Seric 
402aa1d5973Seric 	case IMSG_MDA_DELIVERY_LOOP:
403aa1d5973Seric 	case IMSG_MTA_DELIVERY_LOOP:
40465c4fdfbSgilles 		m_msg(&m, imsg);
40565c4fdfbSgilles 		m_get_evpid(&m, &evpid);
40665c4fdfbSgilles 		m_end(&m);
407299c4efeSeric 		if (queue_envelope_load(evpid, &evp) == 0) {
408299c4efeSeric 			log_warnx("queue: loop: failed to load envelope");
409aa1d5973Seric 			m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1);
410299c4efeSeric 			m_add_evpid(p_scheduler, evpid);
411299c4efeSeric 			m_add_u32(p_scheduler, 1); /* in-flight */
412299c4efeSeric 			m_close(p_scheduler);
413299c4efeSeric 			return;
414299c4efeSeric 		}
41565c4fdfbSgilles 		envelope_set_errormsg(&evp, "%s", "Loop detected");
416fe95d8d0Seric 		envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL);
417fe95d8d0Seric 		envelope_set_esc_code(&evp, ESC_ROUTING_LOOP_DETECTED);
4185ed42fc8Ssunil 		bounce.type = B_FAILED;
41965c4fdfbSgilles 		queue_bounce(&evp, &bounce);
42065c4fdfbSgilles 		queue_envelope_delete(evp.id);
421aa1d5973Seric 		m_create(p_scheduler, IMSG_QUEUE_DELIVERY_LOOP, 0, 0, -1);
42265c4fdfbSgilles 		m_add_evpid(p_scheduler, evp.id);
42365c4fdfbSgilles 		m_close(p_scheduler);
424a7b2e833Seric 		return;
42535e161d3Seric 
426aa1d5973Seric 	case IMSG_MTA_DELIVERY_HOLD:
427aa1d5973Seric 	case IMSG_MDA_DELIVERY_HOLD:
42806e5d42dSeric 		imsg->hdr.type = IMSG_QUEUE_HOLDQ_HOLD;
42935e161d3Seric 		m_forward(p_scheduler, imsg);
43035e161d3Seric 		return;
431aa1d5973Seric 
432aa1d5973Seric 	case IMSG_MTA_SCHEDULE:
433aa1d5973Seric 		imsg->hdr.type = IMSG_QUEUE_ENVELOPE_SCHEDULE;
434aa1d5973Seric 		m_forward(p_scheduler, imsg);
435aa1d5973Seric 		return;
436aa1d5973Seric 
437aa1d5973Seric 	case IMSG_MTA_HOLDQ_RELEASE:
438aa1d5973Seric 	case IMSG_MDA_HOLDQ_RELEASE:
4397eed50e8Seric 		m_msg(&m, imsg);
4407eed50e8Seric 		m_get_id(&m, &holdq);
4417eed50e8Seric 		m_get_int(&m, &v);
4427eed50e8Seric 		m_end(&m);
443aa1d5973Seric 		m_create(p_scheduler, IMSG_QUEUE_HOLDQ_RELEASE, 0, 0, -1);
444aa1d5973Seric 		if (imsg->hdr.type == IMSG_MTA_HOLDQ_RELEASE)
4457eed50e8Seric 			m_add_int(p_scheduler, D_MTA);
4467eed50e8Seric 		else
4477eed50e8Seric 			m_add_int(p_scheduler, D_MDA);
4487eed50e8Seric 		m_add_id(p_scheduler, holdq);
4497eed50e8Seric 		m_add_int(p_scheduler, v);
4507eed50e8Seric 		m_close(p_scheduler);
4517eed50e8Seric 		return;
452b28a97afSjacekm 
45365c4fdfbSgilles 	case IMSG_CTL_PAUSE_MDA:
45465c4fdfbSgilles 	case IMSG_CTL_PAUSE_MTA:
45565c4fdfbSgilles 	case IMSG_CTL_RESUME_MDA:
45665c4fdfbSgilles 	case IMSG_CTL_RESUME_MTA:
45765c4fdfbSgilles 		m_forward(p_scheduler, imsg);
458ed1929b6Sjacekm 		return;
459ed1929b6Sjacekm 
460ed1929b6Sjacekm 	case IMSG_CTL_VERBOSE:
46165c4fdfbSgilles 		m_msg(&m, imsg);
46265c4fdfbSgilles 		m_get_int(&m, &v);
46365c4fdfbSgilles 		m_end(&m);
464f24248b7Sreyk 		log_trace_verbose(v);
46565c4fdfbSgilles 		return;
46665c4fdfbSgilles 
46765c4fdfbSgilles 	case IMSG_CTL_PROFILE:
46865c4fdfbSgilles 		m_msg(&m, imsg);
46965c4fdfbSgilles 		m_get_int(&m, &v);
47065c4fdfbSgilles 		m_end(&m);
47165c4fdfbSgilles 		profiling = v;
472ed1929b6Sjacekm 		return;
473a9835440Ssunil 
474a9835440Ssunil 	case IMSG_CTL_DISCOVER_EVPID:
475a9835440Ssunil 		m_msg(&m, imsg);
476a9835440Ssunil 		m_get_evpid(&m, &evpid);
477a9835440Ssunil 		m_end(&m);
478a9835440Ssunil 		if (queue_envelope_load(evpid, &evp) == 0) {
479a9835440Ssunil 			log_warnx("queue: discover: failed to load "
480a9835440Ssunil 			    "envelope %016" PRIx64, evpid);
481a9835440Ssunil 			n_evp = 0;
482a9835440Ssunil 			m_compose(p_control, imsg->hdr.type,
483a9835440Ssunil 			    imsg->hdr.peerid, 0, -1,
484a9835440Ssunil 			    &n_evp, sizeof n_evp);
485a9835440Ssunil 			return;
486a9835440Ssunil 		}
487a9835440Ssunil 
488a9835440Ssunil 		m_create(p_scheduler, IMSG_QUEUE_DISCOVER_EVPID,
489a9835440Ssunil 		    0, 0, -1);
490a9835440Ssunil 		m_add_envelope(p_scheduler, &evp);
491a9835440Ssunil 		m_close(p_scheduler);
492a9835440Ssunil 
493a9835440Ssunil 		m_create(p_scheduler, IMSG_QUEUE_DISCOVER_MSGID,
494a9835440Ssunil 		    0, 0, -1);
495a9835440Ssunil 		m_add_msgid(p_scheduler, evpid_to_msgid(evpid));
496a9835440Ssunil 		m_close(p_scheduler);
497a9835440Ssunil 		n_evp = 1;
498a9835440Ssunil 		m_compose(p_control, imsg->hdr.type, imsg->hdr.peerid,
499a9835440Ssunil 		    0, -1, &n_evp, sizeof n_evp);
500a9835440Ssunil 		return;
501a9835440Ssunil 
502a9835440Ssunil 	case IMSG_CTL_DISCOVER_MSGID:
503a9835440Ssunil 		m_msg(&m, imsg);
504a9835440Ssunil 		m_get_msgid(&m, &msgid);
505a9835440Ssunil 		m_end(&m);
506a9835440Ssunil 		/* handle concurrent walk requests */
507118c16f3Sgilles 		wi = xcalloc(1, sizeof *wi);
508a9835440Ssunil 		wi->msgid = msgid;
509a9835440Ssunil 		wi->peerid = imsg->hdr.peerid;
510a9835440Ssunil 		evtimer_set(&wi->ev, queue_msgid_walk, wi);
511a9835440Ssunil 		tv.tv_sec = 0;
512a9835440Ssunil 		tv.tv_usec = 10;
513a9835440Ssunil 		evtimer_add(&wi->ev, &tv);
514a9835440Ssunil 		return;
515ed1929b6Sjacekm 	}
516ed1929b6Sjacekm 
517ff01b044Seric 	fatalx("queue_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
518ed1929b6Sjacekm }
519122e5285Sgilles 
520be925435Sgilles static void
queue_msgid_walk(int fd,short event,void * arg)521a9835440Ssunil queue_msgid_walk(int fd, short event, void *arg)
522a9835440Ssunil {
523a9835440Ssunil 	struct envelope		 evp;
524a9835440Ssunil 	struct timeval		 tv;
525a9835440Ssunil 	struct msg_walkinfo	*wi = arg;
526a9835440Ssunil 	int			 r;
527a9835440Ssunil 
528a9835440Ssunil 	r = queue_message_walk(&evp, wi->msgid, &wi->done, &wi->data);
529a9835440Ssunil 	if (r == -1) {
530a9835440Ssunil 		if (wi->n_evp) {
531a9835440Ssunil 			m_create(p_scheduler, IMSG_QUEUE_DISCOVER_MSGID,
532a9835440Ssunil 			    0, 0, -1);
533a9835440Ssunil 			m_add_msgid(p_scheduler, wi->msgid);
534a9835440Ssunil 			m_close(p_scheduler);
535a9835440Ssunil 		}
536a9835440Ssunil 
537a9835440Ssunil 		m_compose(p_control, IMSG_CTL_DISCOVER_MSGID, wi->peerid, 0, -1,
538a9835440Ssunil 		    &wi->n_evp, sizeof wi->n_evp);
539a9835440Ssunil 		evtimer_del(&wi->ev);
540a9835440Ssunil 		free(wi);
541a9835440Ssunil 		return;
542a9835440Ssunil 	}
543a9835440Ssunil 
544a9835440Ssunil 	if (r) {
545a9835440Ssunil 		m_create(p_scheduler, IMSG_QUEUE_DISCOVER_EVPID, 0, 0, -1);
546a9835440Ssunil 		m_add_envelope(p_scheduler, &evp);
547a9835440Ssunil 		m_close(p_scheduler);
548a9835440Ssunil 		wi->n_evp += 1;
549a9835440Ssunil 	}
550a9835440Ssunil 
551a9835440Ssunil 	tv.tv_sec = 0;
552a9835440Ssunil 	tv.tv_usec = 10;
553a9835440Ssunil 	evtimer_set(&wi->ev, queue_msgid_walk, wi);
554a9835440Ssunil 	evtimer_add(&wi->ev, &tv);
555a9835440Ssunil }
556a9835440Ssunil 
557a9835440Ssunil static void
queue_bounce(struct envelope * e,struct delivery_bounce * d)55865c4fdfbSgilles queue_bounce(struct envelope *e, struct delivery_bounce *d)
559913395bcSeric {
560913395bcSeric 	struct envelope	b;
561913395bcSeric 
562913395bcSeric 	b = *e;
563913395bcSeric 	b.type = D_BOUNCE;
56465c4fdfbSgilles 	b.agent.bounce = *d;
565913395bcSeric 	b.retry = 0;
566913395bcSeric 	b.lasttry = 0;
567913395bcSeric 	b.creation = time(NULL);
568a8e22235Sgilles 	b.ttl = 3600 * 24 * 7;
569913395bcSeric 
570fe95d8d0Seric 	if (e->dsn_notify & DSN_NEVER)
571fe95d8d0Seric 		return;
572fe95d8d0Seric 
57365c4fdfbSgilles 	if (b.id == 0)
574299c4efeSeric 		log_warnx("warn: queue_bounce: evpid=0");
57565c4fdfbSgilles 	if (evpid_to_msgid(b.id) == 0)
576299c4efeSeric 		log_warnx("warn: queue_bounce: msgid=0, evpid=%016"PRIx64,
57765c4fdfbSgilles 			b.id);
578913395bcSeric 	if (e->type == D_BOUNCE) {
57982614934Seric 		log_warnx("warn: queue: double bounce!");
580913395bcSeric 	} else if (e->sender.user[0] == '\0') {
58182614934Seric 		log_warnx("warn: queue: no return path!");
582913395bcSeric 	} else if (!queue_envelope_create(&b)) {
58382614934Seric 		log_warnx("warn: queue: cannot bounce!");
584913395bcSeric 	} else {
58582614934Seric 		log_debug("debug: queue: bouncing evp:%016" PRIx64
586913395bcSeric 		    " as evp:%016" PRIx64, e->id, b.id);
58765c4fdfbSgilles 
588aa1d5973Seric 		m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1);
58965c4fdfbSgilles 		m_add_envelope(p_scheduler, &b);
59065c4fdfbSgilles 		m_close(p_scheduler);
59165c4fdfbSgilles 
592aa1d5973Seric 		m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT, 0, 0, -1);
59365c4fdfbSgilles 		m_add_msgid(p_scheduler, evpid_to_msgid(b.id));
59465c4fdfbSgilles 		m_close(p_scheduler);
59565c4fdfbSgilles 
59682614934Seric 		stat_increment("queue.bounce", 1);
597913395bcSeric 	}
598913395bcSeric }
599913395bcSeric 
600913395bcSeric static void
queue_shutdown(void)6013ef9cbf7Sgilles queue_shutdown(void)
6023ef9cbf7Sgilles {
60343962b9cSeric 	log_debug("debug: queue agent exiting");
60498f67d16Seric 	queue_close();
6053ef9cbf7Sgilles 	_exit(0);
6063ef9cbf7Sgilles }
6073ef9cbf7Sgilles 
608b88ab68dSeric int
queue(void)609e4d36f12Seric queue(void)
6103ef9cbf7Sgilles {
6113ef9cbf7Sgilles 	struct passwd	*pw;
61266bc57beSeric 	struct timeval	 tv;
61366bc57beSeric 	struct event	 ev_qload;
6143ef9cbf7Sgilles 
615a8e22235Sgilles 	purge_config(PURGE_EVERYTHING & ~PURGE_DISPATCHERS);
6163ef9cbf7Sgilles 
61711d04e02Seric 	if ((pw = getpwnam(SMTPD_QUEUE_USER)) == NULL)
61811d04e02Seric 		if ((pw = getpwnam(SMTPD_USER)) == NULL)
61911d04e02Seric 			fatalx("unknown user " SMTPD_USER);
62011d04e02Seric 
6213f70ecafSeric 	env->sc_queue_flags |= QUEUE_EVPCACHE;
6223f70ecafSeric 	env->sc_queue_evpcache_size = 1024;
6233f70ecafSeric 
6243ef9cbf7Sgilles 	if (chroot(PATH_SPOOL) == -1)
6253ef9cbf7Sgilles 		fatal("queue: chroot");
6263ef9cbf7Sgilles 	if (chdir("/") == -1)
6273ef9cbf7Sgilles 		fatal("queue: chdir(\"/\")");
6283ef9cbf7Sgilles 
62965c4fdfbSgilles 	config_process(PROC_QUEUE);
6303ef9cbf7Sgilles 
6313f70ecafSeric 	if (env->sc_queue_flags & QUEUE_COMPRESSION)
6323f70ecafSeric 		log_info("queue: queue compression enabled");
6333f70ecafSeric 
6343f70ecafSeric 	if (env->sc_queue_key) {
6353f70ecafSeric 		if (!crypto_setup(env->sc_queue_key, strlen(env->sc_queue_key)))
6363f70ecafSeric 			fatalx("crypto_setup: invalid key for queue encryption");
6373f70ecafSeric 		log_info("queue: queue encryption enabled");
6383f70ecafSeric 	}
6393f70ecafSeric 
6403ef9cbf7Sgilles 	if (setgroups(1, &pw->pw_gid) ||
6413ef9cbf7Sgilles 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
6423ef9cbf7Sgilles 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
6433ef9cbf7Sgilles 		fatal("queue: cannot drop privileges");
6443ef9cbf7Sgilles 
645ed1929b6Sjacekm 	imsg_callback = queue_imsg;
6463ef9cbf7Sgilles 	event_init();
6473ef9cbf7Sgilles 
64843962b9cSeric 	signal(SIGINT, SIG_IGN);
64943962b9cSeric 	signal(SIGTERM, SIG_IGN);
6503ef9cbf7Sgilles 	signal(SIGPIPE, SIG_IGN);
6513ef9cbf7Sgilles 	signal(SIGHUP, SIG_IGN);
6523ef9cbf7Sgilles 
65365c4fdfbSgilles 	config_peer(PROC_PARENT);
65465c4fdfbSgilles 	config_peer(PROC_CONTROL);
65565c4fdfbSgilles 	config_peer(PROC_LKA);
65665c4fdfbSgilles 	config_peer(PROC_SCHEDULER);
6571a5b831aSmartijn 	config_peer(PROC_DISPATCHER);
65865c4fdfbSgilles 
65966bc57beSeric 	/* setup queue loading task */
66066bc57beSeric 	evtimer_set(&ev_qload, queue_timeout, &ev_qload);
66166bc57beSeric 	tv.tv_sec = 0;
66266bc57beSeric 	tv.tv_usec = 10;
66366bc57beSeric 	evtimer_add(&ev_qload, &tv);
66466bc57beSeric 
665cbbcd319Smillert 	if (pledge("stdio rpath wpath cpath flock recvfd sendfd", NULL) == -1)
666ff01b044Seric 		fatal("pledge");
667a539da47Sgilles 
668f94528c3Seric 	event_dispatch();
669f94528c3Seric 	fatalx("exited event loop");
6703ef9cbf7Sgilles 
6713ef9cbf7Sgilles 	return (0);
6723ef9cbf7Sgilles }
6733ef9cbf7Sgilles 
674913395bcSeric static void
queue_timeout(int fd,short event,void * p)67566bc57beSeric queue_timeout(int fd, short event, void *p)
67666bc57beSeric {
67782614934Seric 	static uint32_t	 msgid = 0;
6784d64cfa3Seric 	struct envelope	 evp;
67966bc57beSeric 	struct event	*ev = p;
68066bc57beSeric 	struct timeval	 tv;
6814d64cfa3Seric 	int		 r;
68266bc57beSeric 
6834d64cfa3Seric 	r = queue_envelope_walk(&evp);
6844d64cfa3Seric 	if (r == -1) {
68565c4fdfbSgilles 		if (msgid) {
686aa1d5973Seric 			m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT,
687299c4efeSeric 			    0, 0, -1);
68865c4fdfbSgilles 			m_add_msgid(p_scheduler, msgid);
68965c4fdfbSgilles 			m_close(p_scheduler);
69065c4fdfbSgilles 		}
6914d64cfa3Seric 		log_debug("debug: queue: done loading queue into scheduler");
69266bc57beSeric 		return;
69366bc57beSeric 	}
69466bc57beSeric 
6954d64cfa3Seric 	if (r) {
69665c4fdfbSgilles 		if (msgid && evpid_to_msgid(evp.id) != msgid) {
697aa1d5973Seric 			m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT,
698299c4efeSeric 			    0, 0, -1);
69965c4fdfbSgilles 			m_add_msgid(p_scheduler, msgid);
70065c4fdfbSgilles 			m_close(p_scheduler);
70165c4fdfbSgilles 		}
7024d64cfa3Seric 		msgid = evpid_to_msgid(evp.id);
703aa1d5973Seric 		m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1);
70465c4fdfbSgilles 		m_add_envelope(p_scheduler, &evp);
70565c4fdfbSgilles 		m_close(p_scheduler);
70666bc57beSeric 	}
70766bc57beSeric 
7084d64cfa3Seric 	tv.tv_sec = 0;
7094d64cfa3Seric 	tv.tv_usec = 10;
7104d64cfa3Seric 	evtimer_add(ev, &tv);
71166bc57beSeric }
71265c4fdfbSgilles 
71365c4fdfbSgilles static void
queue_log(const struct envelope * e,const char * prefix,const char * status)71465c4fdfbSgilles queue_log(const struct envelope *e, const char *prefix, const char *status)
71565c4fdfbSgilles {
716953aae25Sderaadt 	char rcpt[LINE_MAX];
71765c4fdfbSgilles 
7181f035edbSgilles 	(void)strlcpy(rcpt, "-", sizeof rcpt);
71965c4fdfbSgilles 	if (strcmp(e->rcpt.user, e->dest.user) ||
72065c4fdfbSgilles 	    strcmp(e->rcpt.domain, e->dest.domain))
7211f035edbSgilles 		(void)snprintf(rcpt, sizeof rcpt, "%s@%s",
72265c4fdfbSgilles 		    e->rcpt.user, e->rcpt.domain);
72365c4fdfbSgilles 
72465c4fdfbSgilles 	log_info("%s: %s for %016" PRIx64 ": from=<%s@%s>, to=<%s@%s>, "
7253f70ecafSeric 	    "rcpt=<%s>, delay=%s, stat=%s",
72665c4fdfbSgilles 	    e->type == D_MDA ? "delivery" : "relay",
72765c4fdfbSgilles 	    prefix,
72865c4fdfbSgilles 	    e->id, e->sender.user, e->sender.domain,
72965c4fdfbSgilles 	    e->dest.user, e->dest.domain,
73065c4fdfbSgilles 	    rcpt,
73165c4fdfbSgilles 	    duration_to_text(time(NULL) - e->creation),
73265c4fdfbSgilles 	    status);
73365c4fdfbSgilles }
734