xref: /dflybsd-src/libexec/dma/dma.c (revision 577b958f5e57e9712ddc260c6666d661d03397fe)
1f67bedddSMatthias Schmidt /*
237d59876SJohn Marino  * Copyright (c) 2008-2014, Simon Schubert <2@0x2c.org>.
3f67bedddSMatthias Schmidt  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
4f67bedddSMatthias Schmidt  *
5f67bedddSMatthias Schmidt  * This code is derived from software contributed to The DragonFly Project
637d59876SJohn Marino  * by Simon Schubert <2@0x2c.org>.
7f67bedddSMatthias Schmidt  *
8f67bedddSMatthias Schmidt  * Redistribution and use in source and binary forms, with or without
9f67bedddSMatthias Schmidt  * modification, are permitted provided that the following conditions
10f67bedddSMatthias Schmidt  * are met:
11f67bedddSMatthias Schmidt  *
12f67bedddSMatthias Schmidt  * 1. Redistributions of source code must retain the above copyright
13f67bedddSMatthias Schmidt  *    notice, this list of conditions and the following disclaimer.
14f67bedddSMatthias Schmidt  * 2. Redistributions in binary form must reproduce the above copyright
15f67bedddSMatthias Schmidt  *    notice, this list of conditions and the following disclaimer in
16f67bedddSMatthias Schmidt  *    the documentation and/or other materials provided with the
17f67bedddSMatthias Schmidt  *    distribution.
18f67bedddSMatthias Schmidt  * 3. Neither the name of The DragonFly Project nor the names of its
19f67bedddSMatthias Schmidt  *    contributors may be used to endorse or promote products derived
20f67bedddSMatthias Schmidt  *    from this software without specific, prior written permission.
21f67bedddSMatthias Schmidt  *
22f67bedddSMatthias Schmidt  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23f67bedddSMatthias Schmidt  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24f67bedddSMatthias Schmidt  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25f67bedddSMatthias Schmidt  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26f67bedddSMatthias Schmidt  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27f67bedddSMatthias Schmidt  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28f67bedddSMatthias Schmidt  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29f67bedddSMatthias Schmidt  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30f67bedddSMatthias Schmidt  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31f67bedddSMatthias Schmidt  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32f67bedddSMatthias Schmidt  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33f67bedddSMatthias Schmidt  * SUCH DAMAGE.
34f67bedddSMatthias Schmidt  */
35f67bedddSMatthias Schmidt 
36c8b07ee5SSascha Wildner #include "dfcompat.h"
37c8b07ee5SSascha Wildner 
38f67bedddSMatthias Schmidt #include <sys/param.h>
394a6863a4SSimon Schubert #include <sys/types.h>
40f67bedddSMatthias Schmidt #include <sys/queue.h>
41f67bedddSMatthias Schmidt #include <sys/stat.h>
42c8b07ee5SSascha Wildner #include <sys/time.h>
43bc7baf1dSSascha Wildner #include <sys/wait.h>
44f67bedddSMatthias Schmidt 
45f67bedddSMatthias Schmidt #include <dirent.h>
46f67bedddSMatthias Schmidt #include <err.h>
47f67bedddSMatthias Schmidt #include <errno.h>
48f67bedddSMatthias Schmidt #include <fcntl.h>
49f67bedddSMatthias Schmidt #include <inttypes.h>
5092fe556dSDaniel Fojt #include <libgen.h>
51f67bedddSMatthias Schmidt #include <paths.h>
52f67bedddSMatthias Schmidt #include <pwd.h>
53f67bedddSMatthias Schmidt #include <signal.h>
54f67bedddSMatthias Schmidt #include <stdarg.h>
55f67bedddSMatthias Schmidt #include <stdio.h>
56f67bedddSMatthias Schmidt #include <stdlib.h>
57f67bedddSMatthias Schmidt #include <string.h>
58f67bedddSMatthias Schmidt #include <syslog.h>
59f67bedddSMatthias Schmidt #include <unistd.h>
60f67bedddSMatthias Schmidt 
61f67bedddSMatthias Schmidt #include "dma.h"
62f67bedddSMatthias Schmidt 
6352e9aa73SPeter Avalos extern int yyparse(void);
6492fe556dSDaniel Fojt extern FILE *yyin;
6592fe556dSDaniel Fojt 
664a23bd3dSMatthias Schmidt static void deliver(struct qitem *);
67f67bedddSMatthias Schmidt 
68f67bedddSMatthias Schmidt struct aliases aliases = LIST_HEAD_INITIALIZER(aliases);
69f4e61a9fSSimon 'corecode' Schubert struct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs);
70f67bedddSMatthias Schmidt struct authusers authusers = LIST_HEAD_INITIALIZER(authusers);
71c8b07ee5SSascha Wildner char username[USERNAME_SIZE];
72c8b07ee5SSascha Wildner uid_t useruid;
731da0a9f2SSimon Schubert const char *logident_base;
74c8b07ee5SSascha Wildner char errmsg[ERRMSG_SIZE];
75f67bedddSMatthias Schmidt 
769afa363fSSimon Schubert static int daemonize = 1;
7714dfb991SJoris Giovannangeli static int doqueue = 0;
78f8633e71SSimon Schubert 
79ca259d14SSimon Schubert struct config config = {
80ca259d14SSimon Schubert 	.smarthost	= NULL,
81ca259d14SSimon Schubert 	.port		= 25,
82c8b07ee5SSascha Wildner 	.aliases	= "/etc/aliases",
83ca259d14SSimon Schubert 	.spooldir	= "/var/spool/dma",
84ca259d14SSimon Schubert 	.authpath	= NULL,
85ca259d14SSimon Schubert 	.certfile	= NULL,
86ca259d14SSimon Schubert 	.features	= 0,
87ca259d14SSimon Schubert 	.mailname	= NULL,
88c8b07ee5SSascha Wildner 	.masquerade_host = NULL,
89c8b07ee5SSascha Wildner 	.masquerade_user = NULL,
9092fe556dSDaniel Fojt 	.fingerprint = NULL,
91ca259d14SSimon Schubert };
92ca259d14SSimon Schubert 
93ca259d14SSimon Schubert 
94c8b07ee5SSascha Wildner static void
sighup_handler(int signo)95c8b07ee5SSascha Wildner sighup_handler(int signo)
96c8b07ee5SSascha Wildner {
97c8b07ee5SSascha Wildner 	(void)signo;	/* so that gcc doesn't complain */
98c8b07ee5SSascha Wildner }
99c8b07ee5SSascha Wildner 
100f67bedddSMatthias Schmidt static char *
set_from(struct queue * queue,const char * osender)1011c9e6b7bSSimon Schubert set_from(struct queue *queue, const char *osender)
102f67bedddSMatthias Schmidt {
10314dfb991SJoris Giovannangeli 	const char *addr;
104f67bedddSMatthias Schmidt 	char *sender;
105f67bedddSMatthias Schmidt 
10692fe556dSDaniel Fojt 	if (config.masquerade_user) {
10792fe556dSDaniel Fojt 		addr = config.masquerade_user;
10892fe556dSDaniel Fojt 	} else if (osender) {
10914dfb991SJoris Giovannangeli 		addr = osender;
110c8b07ee5SSascha Wildner 	} else if (getenv("EMAIL") != NULL) {
11114dfb991SJoris Giovannangeli 		addr = getenv("EMAIL");
112f67bedddSMatthias Schmidt 	} else {
11314dfb991SJoris Giovannangeli 		addr = username;
11414dfb991SJoris Giovannangeli 	}
11514dfb991SJoris Giovannangeli 
11614dfb991SJoris Giovannangeli 	if (!strchr(addr, '@')) {
117c8b07ee5SSascha Wildner 		const char *from_host = hostname();
118c8b07ee5SSascha Wildner 
119c8b07ee5SSascha Wildner 		if (config.masquerade_host)
120c8b07ee5SSascha Wildner 			from_host = config.masquerade_host;
12114dfb991SJoris Giovannangeli 
12214dfb991SJoris Giovannangeli 		if (asprintf(&sender, "%s@%s", addr, from_host) <= 0)
12314dfb991SJoris Giovannangeli 			return (NULL);
12414dfb991SJoris Giovannangeli 	} else {
12514dfb991SJoris Giovannangeli 		sender = strdup(addr);
12614dfb991SJoris Giovannangeli 		if (sender == NULL)
127f67bedddSMatthias Schmidt 			return (NULL);
128f67bedddSMatthias Schmidt 	}
129f67bedddSMatthias Schmidt 
130f67bedddSMatthias Schmidt 	if (strchr(sender, '\n') != NULL) {
131f67bedddSMatthias Schmidt 		errno = EINVAL;
132f67bedddSMatthias Schmidt 		return (NULL);
133f67bedddSMatthias Schmidt 	}
134f67bedddSMatthias Schmidt 
1351c9e6b7bSSimon Schubert 	queue->sender = sender;
136f67bedddSMatthias Schmidt 	return (sender);
137f67bedddSMatthias Schmidt }
138f67bedddSMatthias Schmidt 
139f67bedddSMatthias Schmidt static int
read_aliases(void)140f67bedddSMatthias Schmidt read_aliases(void)
141f67bedddSMatthias Schmidt {
142ca259d14SSimon Schubert 	yyin = fopen(config.aliases, "r");
143ca259d14SSimon Schubert 	if (yyin == NULL) {
144ca259d14SSimon Schubert 		/*
145ca259d14SSimon Schubert 		 * Non-existing aliases file is not a fatal error
146ca259d14SSimon Schubert 		 */
147ca259d14SSimon Schubert 		if (errno == ENOENT)
148ca259d14SSimon Schubert 			return (0);
149ca259d14SSimon Schubert 		/* Other problems are. */
150ca259d14SSimon Schubert 		return (-1);
151ca259d14SSimon Schubert 	}
152f67bedddSMatthias Schmidt 	if (yyparse())
153f67bedddSMatthias Schmidt 		return (-1);	/* fatal error, probably malloc() */
154f67bedddSMatthias Schmidt 	fclose(yyin);
155f67bedddSMatthias Schmidt 	return (0);
156f67bedddSMatthias Schmidt }
157f67bedddSMatthias Schmidt 
158c8b07ee5SSascha Wildner static int
do_alias(struct queue * queue,const char * addr)159c8b07ee5SSascha Wildner do_alias(struct queue *queue, const char *addr)
160c8b07ee5SSascha Wildner {
161c8b07ee5SSascha Wildner 	struct alias *al;
162c8b07ee5SSascha Wildner         struct stritem *sit;
163c8b07ee5SSascha Wildner 	int aliased = 0;
164c8b07ee5SSascha Wildner 
165c8b07ee5SSascha Wildner         LIST_FOREACH(al, &aliases, next) {
166c8b07ee5SSascha Wildner                 if (strcmp(al->alias, addr) != 0)
167c8b07ee5SSascha Wildner                         continue;
168c8b07ee5SSascha Wildner 		SLIST_FOREACH(sit, &al->dests, next) {
169c8b07ee5SSascha Wildner 			if (add_recp(queue, sit->str, EXPAND_ADDR) != 0)
170c8b07ee5SSascha Wildner 				return (-1);
171c8b07ee5SSascha Wildner 		}
172c8b07ee5SSascha Wildner 		aliased = 1;
173c8b07ee5SSascha Wildner         }
174c8b07ee5SSascha Wildner 
175c8b07ee5SSascha Wildner         return (aliased);
176c8b07ee5SSascha Wildner }
177c8b07ee5SSascha Wildner 
178f4e61a9fSSimon 'corecode' Schubert int
add_recp(struct queue * queue,const char * str,int expand)1791c9e6b7bSSimon Schubert add_recp(struct queue *queue, const char *str, int expand)
180f67bedddSMatthias Schmidt {
181f67bedddSMatthias Schmidt 	struct qitem *it, *tit;
182f67bedddSMatthias Schmidt 	struct passwd *pw;
183f67bedddSMatthias Schmidt 	char *host;
184f67bedddSMatthias Schmidt 	int aliased = 0;
185f67bedddSMatthias Schmidt 
186f67bedddSMatthias Schmidt 	it = calloc(1, sizeof(*it));
187f67bedddSMatthias Schmidt 	if (it == NULL)
188f67bedddSMatthias Schmidt 		return (-1);
189f67bedddSMatthias Schmidt 	it->addr = strdup(str);
190f67bedddSMatthias Schmidt 	if (it->addr == NULL)
191f67bedddSMatthias Schmidt 		return (-1);
192f67bedddSMatthias Schmidt 
1931c9e6b7bSSimon Schubert 	it->sender = queue->sender;
194f67bedddSMatthias Schmidt 	host = strrchr(it->addr, '@');
195f67bedddSMatthias Schmidt 	if (host != NULL &&
196f67bedddSMatthias Schmidt 	    (strcmp(host + 1, hostname()) == 0 ||
197f67bedddSMatthias Schmidt 	     strcmp(host + 1, "localhost") == 0)) {
198f67bedddSMatthias Schmidt 		*host = 0;
199f67bedddSMatthias Schmidt 	}
200f67bedddSMatthias Schmidt 	LIST_FOREACH(tit, &queue->queue, next) {
201f67bedddSMatthias Schmidt 		/* weed out duplicate dests */
202f67bedddSMatthias Schmidt 		if (strcmp(tit->addr, it->addr) == 0) {
203f67bedddSMatthias Schmidt 			free(it->addr);
204f67bedddSMatthias Schmidt 			free(it);
205f67bedddSMatthias Schmidt 			return (0);
206f67bedddSMatthias Schmidt 		}
207f67bedddSMatthias Schmidt 	}
208f67bedddSMatthias Schmidt 	LIST_INSERT_HEAD(&queue->queue, it, next);
20914dfb991SJoris Giovannangeli 
21014dfb991SJoris Giovannangeli 	/**
21114dfb991SJoris Giovannangeli 	 * Do local delivery if there is no @.
21214dfb991SJoris Giovannangeli 	 * Do not do local delivery when NULLCLIENT is set.
21314dfb991SJoris Giovannangeli 	 */
21414dfb991SJoris Giovannangeli 	if (strrchr(it->addr, '@') == NULL && (config.features & NULLCLIENT) == 0) {
2154a23bd3dSMatthias Schmidt 		it->remote = 0;
216f67bedddSMatthias Schmidt 		if (expand) {
217c8b07ee5SSascha Wildner 			aliased = do_alias(queue, it->addr);
218c8b07ee5SSascha Wildner 			if (!aliased && expand == EXPAND_WILDCARD)
219c8b07ee5SSascha Wildner 				aliased = do_alias(queue, "*");
220c8b07ee5SSascha Wildner 			if (aliased < 0)
221f67bedddSMatthias Schmidt 				return (-1);
222f67bedddSMatthias Schmidt 			if (aliased) {
223f67bedddSMatthias Schmidt 				LIST_REMOVE(it, next);
224f67bedddSMatthias Schmidt 			} else {
2254a23bd3dSMatthias Schmidt 				/* Local destination, check */
226f67bedddSMatthias Schmidt 				pw = getpwnam(it->addr);
227f67bedddSMatthias Schmidt 				if (pw == NULL)
228f67bedddSMatthias Schmidt 					goto out;
22913e288a1SSimon 'corecode' Schubert 				/* XXX read .forward */
2304a23bd3dSMatthias Schmidt 				endpwent();
231f67bedddSMatthias Schmidt 			}
232f67bedddSMatthias Schmidt 		}
233f67bedddSMatthias Schmidt 	} else {
2344a23bd3dSMatthias Schmidt 		it->remote = 1;
235f67bedddSMatthias Schmidt 	}
236f67bedddSMatthias Schmidt 
237f67bedddSMatthias Schmidt 	return (0);
238f67bedddSMatthias Schmidt 
239f67bedddSMatthias Schmidt out:
240f67bedddSMatthias Schmidt 	free(it->addr);
241f67bedddSMatthias Schmidt 	free(it);
242f67bedddSMatthias Schmidt 	return (-1);
243f67bedddSMatthias Schmidt }
244f67bedddSMatthias Schmidt 
2454a23bd3dSMatthias Schmidt static struct qitem *
go_background(struct queue * queue)2464a23bd3dSMatthias Schmidt go_background(struct queue *queue)
247f67bedddSMatthias Schmidt {
248f67bedddSMatthias Schmidt 	struct sigaction sa;
249f67bedddSMatthias Schmidt 	struct qitem *it;
250f67bedddSMatthias Schmidt 	pid_t pid;
251f67bedddSMatthias Schmidt 
252f67bedddSMatthias Schmidt 	if (daemonize && daemon(0, 0) != 0) {
2534a23bd3dSMatthias Schmidt 		syslog(LOG_ERR, "can not daemonize: %m");
25492fe556dSDaniel Fojt 		exit(EX_OSERR);
255f67bedddSMatthias Schmidt 	}
256f67bedddSMatthias Schmidt 	daemonize = 0;
2574a23bd3dSMatthias Schmidt 
258f67bedddSMatthias Schmidt 	bzero(&sa, sizeof(sa));
259f67bedddSMatthias Schmidt 	sa.sa_handler = SIG_IGN;
260f67bedddSMatthias Schmidt 	sigaction(SIGCHLD, &sa, NULL);
261f67bedddSMatthias Schmidt 
2624dcaa51bSMatthias Schmidt 	LIST_FOREACH(it, &queue->queue, next) {
2634a23bd3dSMatthias Schmidt 		/* No need to fork for the last dest */
2649afa363fSSimon Schubert 		if (LIST_NEXT(it, next) == NULL)
2659afa363fSSimon Schubert 			goto retit;
2664a23bd3dSMatthias Schmidt 
267f67bedddSMatthias Schmidt 		pid = fork();
268f67bedddSMatthias Schmidt 		switch (pid) {
269f67bedddSMatthias Schmidt 		case -1:
270f67bedddSMatthias Schmidt 			syslog(LOG_ERR, "can not fork: %m");
27192fe556dSDaniel Fojt 			exit(EX_OSERR);
272f67bedddSMatthias Schmidt 			break;
273f67bedddSMatthias Schmidt 
274f67bedddSMatthias Schmidt 		case 0:
275f67bedddSMatthias Schmidt 			/*
276f67bedddSMatthias Schmidt 			 * Child:
277f67bedddSMatthias Schmidt 			 *
278f67bedddSMatthias Schmidt 			 * return and deliver mail
279f67bedddSMatthias Schmidt 			 */
2809afa363fSSimon Schubert retit:
2819afa363fSSimon Schubert 			/*
28224c80b2bSSascha Wildner 			 * If necessary, acquire the queue and * mail files.
2839afa363fSSimon Schubert 			 * If this fails, we probably were raced by another
28414dfb991SJoris Giovannangeli 			 * process.  It is okay to be raced if we're supposed
28514dfb991SJoris Giovannangeli 			 * to flush the queue.
2869afa363fSSimon Schubert 			 */
287405f48eeSSimon Schubert 			setlogident("%s", it->queueid);
28814dfb991SJoris Giovannangeli 			switch (acquirespool(it)) {
28914dfb991SJoris Giovannangeli 			case 0:
29014dfb991SJoris Giovannangeli 				break;
29114dfb991SJoris Giovannangeli 			case 1:
29214dfb991SJoris Giovannangeli 				if (doqueue)
29392fe556dSDaniel Fojt 					exit(EX_OK);
29414dfb991SJoris Giovannangeli 				syslog(LOG_WARNING, "could not lock queue file");
29592fe556dSDaniel Fojt 				exit(EX_SOFTWARE);
29614dfb991SJoris Giovannangeli 			default:
29792fe556dSDaniel Fojt 				exit(EX_SOFTWARE);
29814dfb991SJoris Giovannangeli 			}
2999afa363fSSimon Schubert 			dropspool(queue, it);
3004a23bd3dSMatthias Schmidt 			return (it);
301f67bedddSMatthias Schmidt 
302f67bedddSMatthias Schmidt 		default:
303f67bedddSMatthias Schmidt 			/*
304f67bedddSMatthias Schmidt 			 * Parent:
305f67bedddSMatthias Schmidt 			 *
306f67bedddSMatthias Schmidt 			 * fork next child
307f67bedddSMatthias Schmidt 			 */
308f67bedddSMatthias Schmidt 			break;
309f67bedddSMatthias Schmidt 		}
310f67bedddSMatthias Schmidt 	}
311f67bedddSMatthias Schmidt 
312f67bedddSMatthias Schmidt 	syslog(LOG_CRIT, "reached dead code");
31392fe556dSDaniel Fojt 	exit(EX_SOFTWARE);
314f67bedddSMatthias Schmidt }
315f67bedddSMatthias Schmidt 
316f67bedddSMatthias Schmidt static void
deliver(struct qitem * it)3174a23bd3dSMatthias Schmidt deliver(struct qitem *it)
318f67bedddSMatthias Schmidt {
319f67bedddSMatthias Schmidt 	int error;
32014dfb991SJoris Giovannangeli 	unsigned int backoff = MIN_RETRY, slept;
321f67bedddSMatthias Schmidt 	struct timeval now;
322f67bedddSMatthias Schmidt 	struct stat st;
323f67bedddSMatthias Schmidt 
324c8b07ee5SSascha Wildner 	snprintf(errmsg, sizeof(errmsg), "unknown bounce reason");
325c8b07ee5SSascha Wildner 
326f67bedddSMatthias Schmidt retry:
32792fe556dSDaniel Fojt 	syslog(LOG_INFO, "<%s> trying delivery", it->addr);
328f67bedddSMatthias Schmidt 
3294a23bd3dSMatthias Schmidt 	if (it->remote)
330c8b07ee5SSascha Wildner 		error = deliver_remote(it);
3314a23bd3dSMatthias Schmidt 	else
332c8b07ee5SSascha Wildner 		error = deliver_local(it);
333f67bedddSMatthias Schmidt 
334f67bedddSMatthias Schmidt 	switch (error) {
335f67bedddSMatthias Schmidt 	case 0:
33692fe556dSDaniel Fojt 		syslog(LOG_INFO, "<%s> delivery successful", it->addr);
3374e8d31bcSDaniel Fojt 		delqueue(it);
33892fe556dSDaniel Fojt 		exit(EX_OK);
339f67bedddSMatthias Schmidt 
340f67bedddSMatthias Schmidt 	case 1:
341f67bedddSMatthias Schmidt 		if (stat(it->queuefn, &st) != 0) {
342405f48eeSSimon Schubert 			syslog(LOG_ERR, "lost queue file `%s'", it->queuefn);
34392fe556dSDaniel Fojt 			exit(EX_SOFTWARE);
344f67bedddSMatthias Schmidt 		}
345f67bedddSMatthias Schmidt 		if (gettimeofday(&now, NULL) == 0 &&
3462e570fe1SSimon Schubert 		    (now.tv_sec - st.st_mtim.tv_sec > MAX_TIMEOUT)) {
347c8b07ee5SSascha Wildner 			snprintf(errmsg, sizeof(errmsg),
348f67bedddSMatthias Schmidt 				 "Could not deliver for the last %d seconds. Giving up.",
34939f7c85aSSimon Schubert 				 MAX_TIMEOUT);
350f67bedddSMatthias Schmidt 			goto bounce;
351f67bedddSMatthias Schmidt 		}
35214dfb991SJoris Giovannangeli 		for (slept = 0; slept < backoff;) {
35314dfb991SJoris Giovannangeli 			slept += SLEEP_TIMEOUT - sleep(SLEEP_TIMEOUT);
35414dfb991SJoris Giovannangeli 			if (flushqueue_since(slept)) {
35514dfb991SJoris Giovannangeli 				backoff = MIN_RETRY;
35614dfb991SJoris Giovannangeli 				goto retry;
35714dfb991SJoris Giovannangeli 			}
35814dfb991SJoris Giovannangeli 		}
35914dfb991SJoris Giovannangeli 		if (slept >= backoff) {
360c8b07ee5SSascha Wildner 			/* pick the next backoff between [1.5, 2.5) times backoff */
361c8b07ee5SSascha Wildner 			backoff = backoff + backoff / 2 + random() % backoff;
362f67bedddSMatthias Schmidt 			if (backoff > MAX_RETRY)
363f67bedddSMatthias Schmidt 				backoff = MAX_RETRY;
364c8b07ee5SSascha Wildner 		}
365f67bedddSMatthias Schmidt 		goto retry;
366f67bedddSMatthias Schmidt 
367f67bedddSMatthias Schmidt 	case -1:
368f67bedddSMatthias Schmidt 	default:
369f67bedddSMatthias Schmidt 		break;
370f67bedddSMatthias Schmidt 	}
371f67bedddSMatthias Schmidt 
372f67bedddSMatthias Schmidt bounce:
3734a23bd3dSMatthias Schmidt 	bounce(it, errmsg);
374f67bedddSMatthias Schmidt 	/* NOTREACHED */
375f67bedddSMatthias Schmidt }
376f67bedddSMatthias Schmidt 
37730833a29SSimon Schubert void
run_queue(struct queue * queue)378f67bedddSMatthias Schmidt run_queue(struct queue *queue)
379f67bedddSMatthias Schmidt {
3804a23bd3dSMatthias Schmidt 	struct qitem *it;
3814a23bd3dSMatthias Schmidt 
382f67bedddSMatthias Schmidt 	if (LIST_EMPTY(&queue->queue))
383f67bedddSMatthias Schmidt 		return;
384f67bedddSMatthias Schmidt 
3854a23bd3dSMatthias Schmidt 	it = go_background(queue);
3864a23bd3dSMatthias Schmidt 	deliver(it);
387f67bedddSMatthias Schmidt 	/* NOTREACHED */
388f67bedddSMatthias Schmidt }
389f67bedddSMatthias Schmidt 
390f67bedddSMatthias Schmidt static void
show_queue(struct queue * queue)391f67bedddSMatthias Schmidt show_queue(struct queue *queue)
392f67bedddSMatthias Schmidt {
393f67bedddSMatthias Schmidt 	struct qitem *it;
3949afa363fSSimon Schubert 	int locked = 0;	/* XXX */
395f67bedddSMatthias Schmidt 
396f67bedddSMatthias Schmidt 	if (LIST_EMPTY(&queue->queue)) {
397f67bedddSMatthias Schmidt 		printf("Mail queue is empty\n");
398f67bedddSMatthias Schmidt 		return;
399f67bedddSMatthias Schmidt 	}
400f67bedddSMatthias Schmidt 
401f67bedddSMatthias Schmidt 	LIST_FOREACH(it, &queue->queue, next) {
4027b5c40d5SSimon Schubert 		printf("ID\t: %s%s\n"
4037b5c40d5SSimon Schubert 		       "From\t: %s\n"
40471b5e57dSSimon Schubert 		       "To\t: %s\n",
4057b5c40d5SSimon Schubert 		       it->queueid,
4069afa363fSSimon Schubert 		       locked ? "*" : "",
4077b5c40d5SSimon Schubert 		       it->sender, it->addr);
40871b5e57dSSimon Schubert 
40971b5e57dSSimon Schubert 		if (LIST_NEXT(it, next) != NULL)
41071b5e57dSSimon Schubert 			printf("--\n");
411f67bedddSMatthias Schmidt 	}
412f67bedddSMatthias Schmidt }
413f67bedddSMatthias Schmidt 
414f67bedddSMatthias Schmidt /*
415f67bedddSMatthias Schmidt  * TODO:
416f67bedddSMatthias Schmidt  *
417f67bedddSMatthias Schmidt  * - alias processing
418f67bedddSMatthias Schmidt  * - use group permissions
419f67bedddSMatthias Schmidt  * - proper sysexit codes
420f67bedddSMatthias Schmidt  */
421f67bedddSMatthias Schmidt 
4224a23bd3dSMatthias Schmidt int
main(int argc,char ** argv)4234a23bd3dSMatthias Schmidt main(int argc, char **argv)
424f67bedddSMatthias Schmidt {
425c8b07ee5SSascha Wildner 	struct sigaction act;
426f67bedddSMatthias Schmidt 	char *sender = NULL;
4274e8d31bcSDaniel Fojt 	char *own_name = NULL;
428f67bedddSMatthias Schmidt 	struct queue queue;
429f67bedddSMatthias Schmidt 	int i, ch;
430*577b958fSDaniel Fojt 	int nodot = 0, showq = 0, queue_only = 0, newaliases = 0;
431ff48fce6SSimon Schubert 	int recp_from_header = 0;
432f67bedddSMatthias Schmidt 
433c8b07ee5SSascha Wildner 	set_username();
434c8b07ee5SSascha Wildner 
435c8b07ee5SSascha Wildner 	/*
436c8b07ee5SSascha Wildner 	 * We never run as root.  If called by root, drop permissions
437c8b07ee5SSascha Wildner 	 * to the mail user.
438c8b07ee5SSascha Wildner 	 */
439c8b07ee5SSascha Wildner 	if (geteuid() == 0 || getuid() == 0) {
440c8b07ee5SSascha Wildner 		struct passwd *pw;
441c8b07ee5SSascha Wildner 
44214dfb991SJoris Giovannangeli 		errno = 0;
443c8b07ee5SSascha Wildner 		pw = getpwnam(DMA_ROOT_USER);
44414dfb991SJoris Giovannangeli 		if (pw == NULL) {
44514dfb991SJoris Giovannangeli 			if (errno == 0)
44692fe556dSDaniel Fojt 				errx(EX_CONFIG, "user '%s' not found", DMA_ROOT_USER);
44714dfb991SJoris Giovannangeli 			else
44892fe556dSDaniel Fojt 				err(EX_OSERR, "cannot drop root privileges");
44914dfb991SJoris Giovannangeli 		}
450c8b07ee5SSascha Wildner 
451c8b07ee5SSascha Wildner 		if (setuid(pw->pw_uid) != 0)
45292fe556dSDaniel Fojt 			err(EX_OSERR, "cannot drop root privileges");
453c8b07ee5SSascha Wildner 
454c8b07ee5SSascha Wildner 		if (geteuid() == 0 || getuid() == 0)
45592fe556dSDaniel Fojt 			errx(EX_OSERR, "cannot drop root privileges");
456c8b07ee5SSascha Wildner 	}
457c8b07ee5SSascha Wildner 
458f67bedddSMatthias Schmidt 	atexit(deltmp);
459c8b07ee5SSascha Wildner 	init_random();
460e50076f8SSimon Schubert 
461e50076f8SSimon Schubert 	bzero(&queue, sizeof(queue));
462f67bedddSMatthias Schmidt 	LIST_INIT(&queue.queue);
463f67bedddSMatthias Schmidt 
4644e8d31bcSDaniel Fojt 	own_name = basename(argv[0]);
4654e8d31bcSDaniel Fojt 
4664e8d31bcSDaniel Fojt 	if (strcmp(own_name, "mailq") == 0) {
467eb870081SSimon Schubert 		argv++; argc--;
468eb870081SSimon Schubert 		showq = 1;
46900db07ffSSascha Wildner 		if (argc != 0 && strcmp(argv[0], "-Ac") != 0)
47092fe556dSDaniel Fojt 			errx(EX_USAGE, "invalid arguments");
471eb870081SSimon Schubert 		goto skipopts;
4724e8d31bcSDaniel Fojt 	} else if (strcmp(own_name, "newaliases") == 0) {
473*577b958fSDaniel Fojt 		newaliases = 1;
474*577b958fSDaniel Fojt 		goto skipopts;
4754e8d31bcSDaniel Fojt 	} else if (strcmp(own_name, "hoststat") == 0 ||
4764e8d31bcSDaniel Fojt 	           strcmp(own_name, "purgestat") == 0) {
47792fe556dSDaniel Fojt 		exit(EX_OK);
478eb870081SSimon Schubert 	}
479eb870081SSimon Schubert 
480ae3c3bbdSMatthias Schmidt 	opterr = 0;
481ff48fce6SSimon Schubert 	while ((ch = getopt(argc, argv, ":A:b:B:C:d:Df:F:h:iL:N:no:O:q:r:R:tUV:vX:")) != -1) {
482f67bedddSMatthias Schmidt 		switch (ch) {
483f67bedddSMatthias Schmidt 		case 'A':
484f67bedddSMatthias Schmidt 			/* -AX is being ignored, except for -A{c,m} */
485f67bedddSMatthias Schmidt 			if (optarg[0] == 'c' || optarg[0] == 'm') {
486f67bedddSMatthias Schmidt 				break;
487f67bedddSMatthias Schmidt 			}
4884e8d31bcSDaniel Fojt 			/* Else FALLTHROUGH */
489f67bedddSMatthias Schmidt 		case 'b':
490f67bedddSMatthias Schmidt 			/* -bX is being ignored, except for -bp */
491f67bedddSMatthias Schmidt 			if (optarg[0] == 'p') {
492f67bedddSMatthias Schmidt 				showq = 1;
493f67bedddSMatthias Schmidt 				break;
494a9337db7SSimon Schubert 			} else if (optarg[0] == 'q') {
495a9337db7SSimon Schubert 				queue_only = 1;
496a9337db7SSimon Schubert 				break;
497f67bedddSMatthias Schmidt 			}
4984e8d31bcSDaniel Fojt 			/* Else FALLTHROUGH */
499f67bedddSMatthias Schmidt 		case 'D':
500f67bedddSMatthias Schmidt 			daemonize = 0;
501f67bedddSMatthias Schmidt 			break;
502f67bedddSMatthias Schmidt 		case 'L':
503405f48eeSSimon Schubert 			logident_base = optarg;
504f67bedddSMatthias Schmidt 			break;
505f67bedddSMatthias Schmidt 		case 'f':
506f67bedddSMatthias Schmidt 		case 'r':
507f67bedddSMatthias Schmidt 			sender = optarg;
508f67bedddSMatthias Schmidt 			break;
509f67bedddSMatthias Schmidt 
510ff48fce6SSimon Schubert 		case 't':
511ff48fce6SSimon Schubert 			recp_from_header = 1;
512ff48fce6SSimon Schubert 			break;
513ff48fce6SSimon Schubert 
514f67bedddSMatthias Schmidt 		case 'o':
515f67bedddSMatthias Schmidt 			/* -oX is being ignored, except for -oi */
516f67bedddSMatthias Schmidt 			if (optarg[0] != 'i')
517f67bedddSMatthias Schmidt 				break;
5184e8d31bcSDaniel Fojt 			/* Else FALLTHROUGH */
519f67bedddSMatthias Schmidt 		case 'i':
520f67bedddSMatthias Schmidt 			nodot = 1;
521f67bedddSMatthias Schmidt 			break;
522f67bedddSMatthias Schmidt 
523f67bedddSMatthias Schmidt 		case 'q':
52414dfb991SJoris Giovannangeli 			/* Don't let getopt slup up other arguments */
52514dfb991SJoris Giovannangeli 			if (optarg && *optarg == '-')
52614dfb991SJoris Giovannangeli 				optind--;
527f67bedddSMatthias Schmidt 			doqueue = 1;
528f67bedddSMatthias Schmidt 			break;
529f67bedddSMatthias Schmidt 
53098dfc119SSimon Schubert 		/* Ignored options */
53198dfc119SSimon Schubert 		case 'B':
53298dfc119SSimon Schubert 		case 'C':
53398dfc119SSimon Schubert 		case 'd':
53498dfc119SSimon Schubert 		case 'F':
53598dfc119SSimon Schubert 		case 'h':
53698dfc119SSimon Schubert 		case 'N':
53798dfc119SSimon Schubert 		case 'n':
538*577b958fSDaniel Fojt 		case 'O':
53998dfc119SSimon Schubert 		case 'R':
54098dfc119SSimon Schubert 		case 'U':
54198dfc119SSimon Schubert 		case 'V':
54298dfc119SSimon Schubert 		case 'v':
54398dfc119SSimon Schubert 		case 'X':
54498dfc119SSimon Schubert 			break;
54598dfc119SSimon Schubert 
546abbe6a2cSSimon Schubert 		case ':':
547abbe6a2cSSimon Schubert 			if (optopt == 'q') {
548abbe6a2cSSimon Schubert 				doqueue = 1;
549abbe6a2cSSimon Schubert 				break;
550abbe6a2cSSimon Schubert 			}
5514e8d31bcSDaniel Fojt 			/* Else FALLTHROUGH */
552abbe6a2cSSimon Schubert 
553f67bedddSMatthias Schmidt 		default:
554abbe6a2cSSimon Schubert 			fprintf(stderr, "invalid argument: `-%c'\n", optopt);
55592fe556dSDaniel Fojt 			exit(EX_USAGE);
556f67bedddSMatthias Schmidt 		}
557f67bedddSMatthias Schmidt 	}
558f67bedddSMatthias Schmidt 	argc -= optind;
559f67bedddSMatthias Schmidt 	argv += optind;
560ae3c3bbdSMatthias Schmidt 	opterr = 1;
561f67bedddSMatthias Schmidt 
56213e288a1SSimon 'corecode' Schubert 	if (argc != 0 && (showq || doqueue))
56392fe556dSDaniel Fojt 		errx(EX_USAGE, "sending mail and queue operations are mutually exclusive");
56413e288a1SSimon 'corecode' Schubert 
56513e288a1SSimon 'corecode' Schubert 	if (showq + doqueue > 1)
56692fe556dSDaniel Fojt 		errx(EX_USAGE, "conflicting queue operations");
56713e288a1SSimon 'corecode' Schubert 
568eb870081SSimon Schubert skipopts:
569405f48eeSSimon Schubert 	if (logident_base == NULL)
570405f48eeSSimon Schubert 		logident_base = "dma";
571405f48eeSSimon Schubert 	setlogident(NULL);
572f67bedddSMatthias Schmidt 
573c8b07ee5SSascha Wildner 	act.sa_handler = sighup_handler;
574c8b07ee5SSascha Wildner 	act.sa_flags = 0;
575c8b07ee5SSascha Wildner 	sigemptyset(&act.sa_mask);
576c8b07ee5SSascha Wildner 	if (sigaction(SIGHUP, &act, NULL) != 0)
577c8b07ee5SSascha Wildner 		syslog(LOG_WARNING, "can not set signal handler: %m");
578569443a9SSimon 'corecode' Schubert 
579c8b07ee5SSascha Wildner 	parse_conf(CONF_PATH "/dma.conf");
580f67bedddSMatthias Schmidt 
581ca259d14SSimon Schubert 	if (config.authpath != NULL)
582ca259d14SSimon Schubert 		parse_authfile(config.authpath);
583f67bedddSMatthias Schmidt 
584f67bedddSMatthias Schmidt 	if (showq) {
585e50076f8SSimon Schubert 		if (load_queue(&queue) < 0)
58692fe556dSDaniel Fojt 			errlog(EX_NOINPUT, "can not load queue");
587e50076f8SSimon Schubert 		show_queue(&queue);
588f67bedddSMatthias Schmidt 		return (0);
589f67bedddSMatthias Schmidt 	}
590f67bedddSMatthias Schmidt 
591f67bedddSMatthias Schmidt 	if (doqueue) {
59214dfb991SJoris Giovannangeli 		flushqueue_signal();
593e50076f8SSimon Schubert 		if (load_queue(&queue) < 0)
59492fe556dSDaniel Fojt 			errlog(EX_NOINPUT, "can not load queue");
595e50076f8SSimon Schubert 		run_queue(&queue);
596f67bedddSMatthias Schmidt 		return (0);
597f67bedddSMatthias Schmidt 	}
598f67bedddSMatthias Schmidt 
5994a23bd3dSMatthias Schmidt 	if (read_aliases() != 0)
60092fe556dSDaniel Fojt 		errlog(EX_SOFTWARE, "could not parse aliases file `%s'", config.aliases);
601f67bedddSMatthias Schmidt 
602*577b958fSDaniel Fojt 	if (newaliases)
603*577b958fSDaniel Fojt 		return(0);
604*577b958fSDaniel Fojt 
6051c9e6b7bSSimon Schubert 	if ((sender = set_from(&queue, sender)) == NULL)
60692fe556dSDaniel Fojt 		errlog(EX_SOFTWARE, NULL);
607f67bedddSMatthias Schmidt 
6081c9e6b7bSSimon Schubert 	if (newspoolf(&queue) != 0)
60992fe556dSDaniel Fojt 		errlog(EX_CANTCREAT, "can not create temp file in `%s'", config.spooldir);
6104a23bd3dSMatthias Schmidt 
611405f48eeSSimon Schubert 	setlogident("%s", queue.id);
612405f48eeSSimon Schubert 
613ff48fce6SSimon Schubert 	for (i = 0; i < argc; i++) {
614c8b07ee5SSascha Wildner 		if (add_recp(&queue, argv[i], EXPAND_WILDCARD) != 0)
61592fe556dSDaniel Fojt 			errlogx(EX_DATAERR, "invalid recipient `%s'", argv[i]);
616ff48fce6SSimon Schubert 	}
617ff48fce6SSimon Schubert 
618ff48fce6SSimon Schubert 	if (LIST_EMPTY(&queue.queue) && !recp_from_header)
61992fe556dSDaniel Fojt 		errlogx(EX_NOINPUT, "no recipients");
620ff48fce6SSimon Schubert 
621ff48fce6SSimon Schubert 	if (readmail(&queue, nodot, recp_from_header) != 0)
62292fe556dSDaniel Fojt 		errlog(EX_NOINPUT, "can not read mail");
623f67bedddSMatthias Schmidt 
624ff48fce6SSimon Schubert 	if (LIST_EMPTY(&queue.queue))
62592fe556dSDaniel Fojt 		errlogx(EX_NOINPUT, "no recipients");
626ff48fce6SSimon Schubert 
6271c9e6b7bSSimon Schubert 	if (linkspool(&queue) != 0)
62892fe556dSDaniel Fojt 		errlog(EX_CANTCREAT, "can not create spools");
629f67bedddSMatthias Schmidt 
630f67bedddSMatthias Schmidt 	/* From here on the mail is safe. */
631f67bedddSMatthias Schmidt 
632ca259d14SSimon Schubert 	if (config.features & DEFER || queue_only)
633f67bedddSMatthias Schmidt 		return (0);
634f67bedddSMatthias Schmidt 
63530833a29SSimon Schubert 	run_queue(&queue);
636f67bedddSMatthias Schmidt 
637f67bedddSMatthias Schmidt 	/* NOTREACHED */
638f67bedddSMatthias Schmidt 	return (0);
639f67bedddSMatthias Schmidt }
640