xref: /openbsd-src/usr.sbin/smtpd/smtpctl.c (revision bf921b2a265b968a018032bb165729419e711549)
1*bf921b2aSclaudio /*	$OpenBSD: smtpctl.c,v 1.176 2024/11/21 13:42:22 claudio Exp $	*/
2c4f9d530Sgilles 
3c4f9d530Sgilles /*
47a7bc169Seric  * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
565c4fdfbSgilles  * Copyright (c) 2006 Gilles Chehade <gilles@poolp.org>
6c4f9d530Sgilles  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
7c4f9d530Sgilles  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
8c4f9d530Sgilles  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
9c4f9d530Sgilles  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
10c4f9d530Sgilles  *
11c4f9d530Sgilles  * Permission to use, copy, modify, and distribute this software for any
12c4f9d530Sgilles  * purpose with or without fee is hereby granted, provided that the above
13c4f9d530Sgilles  * copyright notice and this permission notice appear in all copies.
14c4f9d530Sgilles  *
15c4f9d530Sgilles  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16c4f9d530Sgilles  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17c4f9d530Sgilles  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
18c4f9d530Sgilles  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19c4f9d530Sgilles  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20c4f9d530Sgilles  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21c4f9d530Sgilles  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22c4f9d530Sgilles  */
23c4f9d530Sgilles 
24c4f9d530Sgilles #include <sys/un.h>
258351d18bSgilles #include <sys/stat.h>
26c4f9d530Sgilles 
27c4f9d530Sgilles #include <err.h>
28ed5c65a8Sgilles #include <errno.h>
297a7bc169Seric #include <fts.h>
3034cb135fSchl #include <inttypes.h>
317b5d776dSgilles #include <pwd.h>
32c4f9d530Sgilles #include <stdlib.h>
33c4f9d530Sgilles #include <string.h>
34ccfb4053Seric #include <syslog.h>
350dcffd0dSop #include <time.h>
36c4f9d530Sgilles #include <unistd.h>
37a8bde366Seric #include <vis.h>
38c4f9d530Sgilles 
39c4f9d530Sgilles #include "smtpd.h"
40c4f9d530Sgilles #include "parser.h"
4138f0b910Seric #include "log.h"
42c4f9d530Sgilles 
43299c4efeSeric #define PATH_GZCAT	"/usr/bin/gzcat"
447a7bc169Seric #define	PATH_CAT	"/bin/cat"
45d5e710d9Sgilles #define PATH_QUEUE	"/queue"
467b682ccfSeric #define PATH_ENCRYPT	"/usr/bin/encrypt"
47d5e710d9Sgilles 
480fcb81a3Seric int srv_connect(void);
498351d18bSgilles int srv_connected(void);
500fcb81a3Seric 
51be925435Sgilles void usage(void);
529e4f546eSgilles static void show_queue_envelope(struct envelope *, int);
53d2241734Schl static void getflag(uint *, int, char *, char *, size_t);
54d5e710d9Sgilles static void display(const char *);
557a7bc169Seric static int str_to_trace(const char *);
567a7bc169Seric static int str_to_profile(const char *);
577a7bc169Seric static void show_offline_envelope(uint64_t);
587a7bc169Seric static int is_gzip_fp(FILE *);
597a7bc169Seric static int is_encrypted_fp(FILE *);
607a7bc169Seric static int is_encrypted_buffer(const char *);
617a7bc169Seric static int is_gzip_buffer(const char *);
628351d18bSgilles static FILE *offline_file(void);
633511367dSsunil static void sendmail_compat(int, char **);
64c4f9d530Sgilles 
65aee31bfdSeric extern int	spfwalk(int, struct parameter *);
664636661aSsunil 
67f607a12cSgilles extern char	*__progname;
687a7bc169Seric int		 sendmail;
697a7bc169Seric struct smtpd	*env;
707a7bc169Seric struct imsgbuf	*ibuf;
717a7bc169Seric struct imsg	 imsg;
727a7bc169Seric char		*rdata;
737a7bc169Seric size_t		 rlen;
744fe02f32Seric time_t		 now;
754fe02f32Seric 
7665c4fdfbSgilles struct queue_backend queue_backend_null;
777a7bc169Seric struct queue_backend queue_backend_proc;
7865c4fdfbSgilles struct queue_backend queue_backend_ram;
7965c4fdfbSgilles 
80c4f9d530Sgilles __dead void
81c4f9d530Sgilles usage(void)
82c4f9d530Sgilles {
83f607a12cSgilles 	if (sendmail)
844fe02f32Seric 		fprintf(stderr, "usage: %s [-tv] [-f from] [-F name] to ...\n",
8583d9e0c8Sjacekm 		    __progname);
86f607a12cSgilles 	else
874fe02f32Seric 		fprintf(stderr, "usage: %s command [argument ...]\n",
884fe02f32Seric 		    __progname);
89c4f9d530Sgilles 	exit(1);
90c4f9d530Sgilles }
91c4f9d530Sgilles 
927a7bc169Seric void stat_increment(const char *k, size_t v)
93cca8e8d2Schl {
947a7bc169Seric }
95cca8e8d2Schl 
967a7bc169Seric void stat_decrement(const char *k, size_t v)
977a7bc169Seric {
98cca8e8d2Schl }
99cca8e8d2Schl 
1000fcb81a3Seric int
1017a7bc169Seric srv_connect(void)
102c4f9d530Sgilles {
103ac61da4aSgilles 	struct sockaddr_un	s_un;
1045d09b982Seric 	int			ctl_sock, saved_errno;
105c4f9d530Sgilles 
106d68200d3Sjacekm 	/* connect to smtpd control socket */
107c4f9d530Sgilles 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
108c4f9d530Sgilles 		err(1, "socket");
109c4f9d530Sgilles 
110ac61da4aSgilles 	memset(&s_un, 0, sizeof(s_un));
111ac61da4aSgilles 	s_un.sun_family = AF_UNIX;
112ac61da4aSgilles 	(void)strlcpy(s_un.sun_path, SMTPD_SOCKET, sizeof(s_un.sun_path));
113ac61da4aSgilles 	if (connect(ctl_sock, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) {
1145d09b982Seric 		saved_errno = errno;
1155d09b982Seric 		close(ctl_sock);
1165d09b982Seric 		errno = saved_errno;
1175d09b982Seric 		return (0);
11825a5298cSjacekm 	}
119c4f9d530Sgilles 
120118c16f3Sgilles 	ibuf = xcalloc(1, sizeof(struct imsgbuf));
121*bf921b2aSclaudio 	if (imsgbuf_init(ibuf, ctl_sock) == -1)
122*bf921b2aSclaudio 		err(1, "imsgbuf_init");
123*bf921b2aSclaudio 	imsgbuf_allow_fdpass(ibuf);
124c4f9d530Sgilles 
1255d09b982Seric 	return (1);
1265d09b982Seric }
1275d09b982Seric 
1288351d18bSgilles int
1298351d18bSgilles srv_connected(void)
1308351d18bSgilles {
1318351d18bSgilles 	return ibuf != NULL ? 1 : 0;
1328351d18bSgilles }
1338351d18bSgilles 
1348351d18bSgilles FILE *
1358351d18bSgilles offline_file(void)
1368351d18bSgilles {
1378351d18bSgilles 	char	path[PATH_MAX];
1388351d18bSgilles 	int	fd;
1398351d18bSgilles 	FILE   *fp;
1408351d18bSgilles 
1418351d18bSgilles 	if (!bsnprintf(path, sizeof(path), "%s%s/%lld.XXXXXXXXXX", PATH_SPOOL,
142e7954c7dSop 	    PATH_OFFLINE, (long long)time(NULL)))
1438351d18bSgilles 		err(EX_UNAVAILABLE, "snprintf");
1448351d18bSgilles 
1458351d18bSgilles 	if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
1468351d18bSgilles 		if (fd != -1)
1478351d18bSgilles 			unlink(path);
1488351d18bSgilles 		err(EX_UNAVAILABLE, "cannot create temporary file %s", path);
1498351d18bSgilles 	}
1508351d18bSgilles 
1518351d18bSgilles 	if (fchmod(fd, 0600) == -1) {
1528351d18bSgilles 		unlink(path);
1538351d18bSgilles 		err(EX_SOFTWARE, "fchmod");
1548351d18bSgilles 	}
1558351d18bSgilles 
1568351d18bSgilles 	return fp;
1578351d18bSgilles }
1588351d18bSgilles 
1598351d18bSgilles 
1605d09b982Seric static void
1617a7bc169Seric srv_flush(void)
1625d09b982Seric {
163dd7efffeSclaudio 	if (imsgbuf_flush(ibuf) == -1)
1645d09b982Seric 		err(1, "write error");
1655d09b982Seric }
1665d09b982Seric 
1675d09b982Seric static void
1687a7bc169Seric srv_send(int msg, const void *data, size_t len)
1697a7bc169Seric {
1707a7bc169Seric 	if (ibuf == NULL && !srv_connect())
1717a7bc169Seric 		errx(1, "smtpd doesn't seem to be running");
1727a7bc169Seric 	imsg_compose(ibuf, msg, IMSG_VERSION, 0, -1, data, len);
1737a7bc169Seric }
1747a7bc169Seric 
1757a7bc169Seric static void
1767a7bc169Seric srv_recv(int type)
1775d09b982Seric {
1785d09b982Seric 	ssize_t	n;
1795d09b982Seric 
1807a7bc169Seric 	srv_flush();
1817a7bc169Seric 
1825d09b982Seric 	while (1) {
1837a7bc169Seric 		if ((n = imsg_get(ibuf, &imsg)) == -1)
1845d09b982Seric 			errx(1, "imsg_get error");
1857a7bc169Seric 		if (n) {
1867a7bc169Seric 			if (imsg.hdr.type == IMSG_CTL_FAIL &&
1877a7bc169Seric 			    imsg.hdr.peerid != 0 &&
1887a7bc169Seric 			    imsg.hdr.peerid != IMSG_VERSION)
1897a7bc169Seric 				errx(1, "incompatible smtpctl and smtpd");
1907a7bc169Seric 			if (type != -1 && type != (int)imsg.hdr.type)
1917a7bc169Seric 				errx(1, "bad message type");
1927a7bc169Seric 			rdata = imsg.data;
1937a7bc169Seric 			rlen = imsg.hdr.len - sizeof(imsg.hdr);
1947a7bc169Seric 			break;
1957a7bc169Seric 		}
1965d09b982Seric 
19716b0c81bSclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
198ef2e27a1Sclaudio 			err(1, "read error");
1995d09b982Seric 		if (n == 0)
2005d09b982Seric 			errx(1, "pipe closed");
2015d09b982Seric 	}
2025d09b982Seric }
2035d09b982Seric 
2047a7bc169Seric static void
2057a7bc169Seric srv_read(void *dst, size_t sz)
2065d09b982Seric {
2077a7bc169Seric 	if (sz == 0)
2087a7bc169Seric 		return;
2097a7bc169Seric 	if (rlen < sz)
2107a7bc169Seric 		errx(1, "message too short");
2117a7bc169Seric 	if (dst)
2127a7bc169Seric 		memmove(dst, rdata, sz);
2137a7bc169Seric 	rlen -= sz;
2147a7bc169Seric 	rdata += sz;
2157a7bc169Seric }
2165d09b982Seric 
2177a7bc169Seric static void
2184fca4105Seric srv_get_int(int *i)
2194fca4105Seric {
2204fca4105Seric 	srv_read(i, sizeof(*i));
2214fca4105Seric }
2224fca4105Seric 
2234fca4105Seric static void
2244fca4105Seric srv_get_time(time_t *t)
2254fca4105Seric {
2264fca4105Seric 	srv_read(t, sizeof(*t));
2274fca4105Seric }
2284fca4105Seric 
2294fca4105Seric static void
2304fca4105Seric srv_get_evpid(uint64_t *evpid)
2314fca4105Seric {
2324fca4105Seric 	srv_read(evpid, sizeof(*evpid));
2334fca4105Seric }
2344fca4105Seric 
2354fca4105Seric static void
23681a33c5dSeric srv_get_string(const char **s)
23781a33c5dSeric {
23881a33c5dSeric 	const char *end;
23981a33c5dSeric 	size_t len;
24081a33c5dSeric 
24181a33c5dSeric 	if (rlen == 0)
24281a33c5dSeric 		errx(1, "message too short");
24381a33c5dSeric 
2445b1f635fSeric 	rlen -= 1;
2455b1f635fSeric 	if (*rdata++ == '\0') {
2465b1f635fSeric 		*s = NULL;
2475b1f635fSeric 		return;
2485b1f635fSeric 	}
2495b1f635fSeric 
2505b1f635fSeric 	if (rlen == 0)
2515b1f635fSeric 		errx(1, "bogus string");
2525b1f635fSeric 
25381a33c5dSeric 	end = memchr(rdata, 0, rlen);
25481a33c5dSeric 	if (end == NULL)
25581a33c5dSeric 		errx(1, "unterminated string");
25681a33c5dSeric 
25781a33c5dSeric 	len = end + 1 - rdata;
25881a33c5dSeric 
25981a33c5dSeric 	*s = rdata;
26081a33c5dSeric 	rlen -= len;
26181a33c5dSeric 	rdata += len;
26281a33c5dSeric }
26381a33c5dSeric 
26481a33c5dSeric static void
2654fca4105Seric srv_get_envelope(struct envelope *evp)
2664fca4105Seric {
2674fca4105Seric 	uint64_t	 evpid;
26881a33c5dSeric 	const char	*str;
2694fca4105Seric 
2704fca4105Seric 	srv_get_evpid(&evpid);
27181a33c5dSeric 	srv_get_string(&str);
2724fca4105Seric 
27381a33c5dSeric 	envelope_load_buffer(evp, str, strlen(str));
2744fca4105Seric 	evp->id = evpid;
2754fca4105Seric }
2764fca4105Seric 
2774fca4105Seric static void
2787a7bc169Seric srv_end(void)
2797a7bc169Seric {
2807a7bc169Seric 	if (rlen)
2817a7bc169Seric 		errx(1, "bogus data");
2827a7bc169Seric 	imsg_free(&imsg);
2837a7bc169Seric }
2847a7bc169Seric 
2857a7bc169Seric static int
2860fcb81a3Seric srv_check_result(int verbose_)
2877a7bc169Seric {
2887a7bc169Seric 	srv_recv(-1);
2897a7bc169Seric 	srv_end();
2907a7bc169Seric 
2917a7bc169Seric 	switch (imsg.hdr.type) {
2927a7bc169Seric 	case IMSG_CTL_OK:
2930fcb81a3Seric 		if (verbose_)
2947a7bc169Seric 			printf("command succeeded\n");
2957a7bc169Seric 		return (0);
2967a7bc169Seric 	case IMSG_CTL_FAIL:
2970fcb81a3Seric 		if (verbose_) {
2987a7bc169Seric 			if (rlen)
2997a7bc169Seric 				printf("command failed: %s\n", rdata);
3007a7bc169Seric 			else
3017a7bc169Seric 				printf("command failed\n");
3020fcb81a3Seric 		}
3037a7bc169Seric 		return (1);
3047a7bc169Seric 	default:
3057a7bc169Seric 		errx(1, "wrong message in response: %u", imsg.hdr.type);
3067a7bc169Seric 	}
3077a7bc169Seric 	return (0);
3087a7bc169Seric }
3097a7bc169Seric 
3107a7bc169Seric static int
3117a7bc169Seric srv_iter_messages(uint32_t *res)
3127a7bc169Seric {
3137a7bc169Seric 	static uint32_t	*msgids = NULL, from = 0;
3147a7bc169Seric 	static size_t	 n, curr;
3157a7bc169Seric 	static int	 done = 0;
3167a7bc169Seric 
3177a7bc169Seric 	if (done)
3187a7bc169Seric 		return (0);
3197a7bc169Seric 
3207a7bc169Seric 	if (msgids == NULL) {
3217a7bc169Seric 		srv_send(IMSG_CTL_LIST_MESSAGES, &from, sizeof(from));
3227a7bc169Seric 		srv_recv(IMSG_CTL_LIST_MESSAGES);
3237a7bc169Seric 		if (rlen == 0) {
3247a7bc169Seric 			srv_end();
3257a7bc169Seric 			done = 1;
3267a7bc169Seric 			return (0);
3277a7bc169Seric 		}
3287a7bc169Seric 		msgids = malloc(rlen);
3297a7bc169Seric 		n = rlen / sizeof(*msgids);
3307a7bc169Seric 		srv_read(msgids, rlen);
3317a7bc169Seric 		srv_end();
3327a7bc169Seric 
3337a7bc169Seric 		curr = 0;
3347a7bc169Seric 		from = msgids[n - 1] + 1;
3357a7bc169Seric 		if (from == 0)
3367a7bc169Seric 			done = 1;
3377a7bc169Seric 	}
3387a7bc169Seric 
3397a7bc169Seric 	*res = msgids[curr++];
3407a7bc169Seric 	if (curr == n) {
3417a7bc169Seric 		free(msgids);
3427a7bc169Seric 		msgids = NULL;
3437a7bc169Seric 	}
3447a7bc169Seric 
3457a7bc169Seric 	return (1);
3467a7bc169Seric }
3477a7bc169Seric 
3487a7bc169Seric static int
3497a7bc169Seric srv_iter_envelopes(uint32_t msgid, struct envelope *evp)
3507a7bc169Seric {
3517a7bc169Seric 	static uint32_t	currmsgid = 0;
3527a7bc169Seric 	static uint64_t	from = 0;
3537a7bc169Seric 	static int	done = 0, need_send = 1, found;
3544fca4105Seric 	int		flags;
3554fca4105Seric 	time_t		nexttry;
3567a7bc169Seric 
3577a7bc169Seric 	if (currmsgid != msgid) {
3587a7bc169Seric 		if (currmsgid != 0 && !done)
3597a7bc169Seric 			errx(1, "must finish current iteration first");
3607a7bc169Seric 		currmsgid = msgid;
3617a7bc169Seric 		from = msgid_to_evpid(msgid);
3627a7bc169Seric 		done = 0;
3637a7bc169Seric 		found = 0;
3647a7bc169Seric 		need_send = 1;
3657a7bc169Seric 	}
3667a7bc169Seric 
3677a7bc169Seric 	if (done)
3687a7bc169Seric 		return (0);
3697a7bc169Seric 
3707a7bc169Seric     again:
3717a7bc169Seric 	if (need_send) {
3727a7bc169Seric 		found = 0;
3737a7bc169Seric 		srv_send(IMSG_CTL_LIST_ENVELOPES, &from, sizeof(from));
3747a7bc169Seric 	}
3757a7bc169Seric 	need_send = 0;
3767a7bc169Seric 
3777a7bc169Seric 	srv_recv(IMSG_CTL_LIST_ENVELOPES);
3787a7bc169Seric 	if (rlen == 0) {
3797a7bc169Seric 		srv_end();
3807a7bc169Seric 		if (!found || evpid_to_msgid(from) != msgid) {
3817a7bc169Seric 			done = 1;
3827a7bc169Seric 			return (0);
3837a7bc169Seric 		}
3847a7bc169Seric 		need_send = 1;
3857a7bc169Seric 		goto again;
3867a7bc169Seric 	}
3877a7bc169Seric 
3884fca4105Seric 	srv_get_int(&flags);
3894fca4105Seric 	srv_get_time(&nexttry);
3904fca4105Seric 	srv_get_envelope(evp);
3917a7bc169Seric 	srv_end();
3924fca4105Seric 
3934fca4105Seric 	evp->flags |= flags;
3944fca4105Seric 	evp->nexttry = nexttry;
3954fca4105Seric 
3967a7bc169Seric 	from = evp->id + 1;
3977a7bc169Seric 	found++;
3987a7bc169Seric 	return (1);
3997a7bc169Seric }
4007a7bc169Seric 
4017a7bc169Seric static int
4020fcb81a3Seric srv_iter_evpids(uint32_t msgid, uint64_t *evpid, int *offset)
4030fcb81a3Seric {
404483e8451Sgilles 	static uint64_t	*evpids = NULL, *tmp;
405f90bac70Seric 	static int	 n, tmpalloc, alloc = 0;
4060fcb81a3Seric 	struct envelope	 evp;
4070fcb81a3Seric 
4080fcb81a3Seric 	if (*offset == 0) {
4090fcb81a3Seric 		n = 0;
4100fcb81a3Seric 		while (srv_iter_envelopes(msgid, &evp)) {
4110fcb81a3Seric 			if (n == alloc) {
412f90bac70Seric 				tmpalloc = alloc ? (alloc * 2) : 128;
413f90bac70Seric 				tmp = recallocarray(evpids, alloc, tmpalloc,
414362966cbSespie 				    sizeof(*evpids));
415483e8451Sgilles 				if (tmp == NULL)
416f90bac70Seric 					err(1, "recallocarray");
417483e8451Sgilles 				evpids = tmp;
418f90bac70Seric 				alloc = tmpalloc;
4190fcb81a3Seric 			}
4200fcb81a3Seric 			evpids[n++] = evp.id;
4210fcb81a3Seric 		}
4220fcb81a3Seric 	}
4230fcb81a3Seric 
4240fcb81a3Seric 	if (*offset >= n)
4250fcb81a3Seric 		return (0);
4260fcb81a3Seric 	*evpid = evpids[*offset];
4270fcb81a3Seric 	*offset += 1;
4280fcb81a3Seric 	return (1);
4290fcb81a3Seric }
4300fcb81a3Seric 
4310fcb81a3Seric static void
4320fcb81a3Seric srv_foreach_envelope(struct parameter *argv, int ctl, size_t *total, size_t *ok)
4330fcb81a3Seric {
4340fcb81a3Seric 	uint32_t	msgid;
4350fcb81a3Seric 	uint64_t	evpid;
4360fcb81a3Seric 	int		i;
4370fcb81a3Seric 
4380fcb81a3Seric 	*total = 0;
4390fcb81a3Seric 	*ok = 0;
4400fcb81a3Seric 
4410fcb81a3Seric 	if (argv == NULL) {
4420fcb81a3Seric 		while (srv_iter_messages(&msgid)) {
4430fcb81a3Seric 			i = 0;
4440fcb81a3Seric 			while (srv_iter_evpids(msgid, &evpid, &i)) {
4450fcb81a3Seric 				*total += 1;
4460fcb81a3Seric 				srv_send(ctl, &evpid, sizeof(evpid));
4470fcb81a3Seric 				if (srv_check_result(0) == 0)
4480fcb81a3Seric 					*ok += 1;
4490fcb81a3Seric 			}
4500fcb81a3Seric 		}
4510fcb81a3Seric 	} else if (argv->type == P_MSGID) {
4520fcb81a3Seric 		i = 0;
4530fcb81a3Seric 		while (srv_iter_evpids(argv->u.u_msgid, &evpid, &i)) {
4540fcb81a3Seric 			srv_send(ctl, &evpid, sizeof(evpid));
4550fcb81a3Seric 			if (srv_check_result(0) == 0)
4560fcb81a3Seric 				*ok += 1;
4570fcb81a3Seric 		}
4580fcb81a3Seric 	} else {
4590fcb81a3Seric 		*total += 1;
4600fcb81a3Seric 		srv_send(ctl, &argv->u.u_evpid, sizeof(evpid));
4610fcb81a3Seric 		if (srv_check_result(0) == 0)
4620fcb81a3Seric 			*ok += 1;
4630fcb81a3Seric 	}
4640fcb81a3Seric }
4650fcb81a3Seric 
4660fcb81a3Seric static void
4670fcb81a3Seric srv_show_cmd(int cmd, const void *data, size_t len)
4680fcb81a3Seric {
4690fcb81a3Seric 	int	done = 0;
4700fcb81a3Seric 
4710fcb81a3Seric 	srv_send(cmd, data, len);
4720fcb81a3Seric 
4730fcb81a3Seric 	do {
4740fcb81a3Seric 		srv_recv(cmd);
4750fcb81a3Seric 		if (rlen) {
4760fcb81a3Seric 			printf("%s\n", rdata);
4770fcb81a3Seric 			srv_read(NULL, rlen);
4780fcb81a3Seric 		}
4790fcb81a3Seric 		else
4800fcb81a3Seric 			done = 1;
4810fcb81a3Seric 		srv_end();
4820fcb81a3Seric 	} while (!done);
4830fcb81a3Seric }
4840fcb81a3Seric 
485aee31bfdSeric static void
486aee31bfdSeric droppriv(void)
487aee31bfdSeric {
488aee31bfdSeric 	struct passwd *pw;
489aee31bfdSeric 
490aee31bfdSeric 	if (geteuid())
491aee31bfdSeric 		return;
492aee31bfdSeric 
493aee31bfdSeric 	if ((pw = getpwnam(SMTPD_USER)) == NULL)
494aee31bfdSeric 		errx(1, "unknown user " SMTPD_USER);
495aee31bfdSeric 
496aee31bfdSeric 	if ((setgroups(1, &pw->pw_gid) ||
497aee31bfdSeric 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
498aee31bfdSeric 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)))
499aee31bfdSeric 		err(1, "cannot drop privileges");
500aee31bfdSeric }
501aee31bfdSeric 
502aee31bfdSeric static int
503aee31bfdSeric do_permission_denied(int argc, struct parameter *argv)
504aee31bfdSeric {
505aee31bfdSeric 	errx(1, "need root privileges");
506aee31bfdSeric }
507aee31bfdSeric 
5080fcb81a3Seric static int
5097a7bc169Seric do_log_brief(int argc, struct parameter *argv)
5107a7bc169Seric {
5117a7bc169Seric 	int	v = 0;
5127a7bc169Seric 
5137a7bc169Seric 	srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v));
5140fcb81a3Seric 	return srv_check_result(1);
5157a7bc169Seric }
5167a7bc169Seric 
5177a7bc169Seric static int
5187a7bc169Seric do_log_verbose(int argc, struct parameter *argv)
5197a7bc169Seric {
5207a7bc169Seric 	int	v = TRACE_DEBUG;
5217a7bc169Seric 
5227a7bc169Seric 	srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v));
5230fcb81a3Seric 	return srv_check_result(1);
5247a7bc169Seric }
5257a7bc169Seric 
5267a7bc169Seric static int
5277a7bc169Seric do_monitor(int argc, struct parameter *argv)
5287a7bc169Seric {
5297a7bc169Seric 	struct stat_digest	last, digest;
5307a7bc169Seric 	size_t			count;
5317a7bc169Seric 
532c1392a69Seric 	memset(&last, 0, sizeof(last));
5337a7bc169Seric 	count = 0;
5347a7bc169Seric 
5357a7bc169Seric 	while (1) {
536aa1d5973Seric 		srv_send(IMSG_CTL_GET_DIGEST, NULL, 0);
537aa1d5973Seric 		srv_recv(IMSG_CTL_GET_DIGEST);
5387a7bc169Seric 		srv_read(&digest, sizeof(digest));
5397a7bc169Seric 		srv_end();
5407a7bc169Seric 
5417a7bc169Seric 		if (count % 25 == 0) {
5427a7bc169Seric 			if (count != 0)
5437a7bc169Seric 				printf("\n");
5447a7bc169Seric 			printf("--- client ---  "
5457a7bc169Seric 			    "-- envelope --   "
5467a7bc169Seric 			    "---- relay/delivery --- "
5477a7bc169Seric 			    "------- misc -------\n"
5487a7bc169Seric 			    "curr conn disc  "
5497a7bc169Seric 			    "curr  enq  deq   "
5507a7bc169Seric 			    "ok tmpfail prmfail loop "
5517a7bc169Seric 			    "expire remove bounce\n");
5527a7bc169Seric 		}
5537a7bc169Seric 		printf("%4zu %4zu %4zu  "
5547a7bc169Seric 		    "%4zu %4zu %4zu "
5557a7bc169Seric 		    "%4zu    %4zu    %4zu %4zu   "
5567a7bc169Seric 		    "%4zu   %4zu   %4zu\n",
5577a7bc169Seric 		    digest.clt_connect - digest.clt_disconnect,
5587a7bc169Seric 		    digest.clt_connect - last.clt_connect,
5597a7bc169Seric 		    digest.clt_disconnect - last.clt_disconnect,
5607a7bc169Seric 
5617a7bc169Seric 		    digest.evp_enqueued - digest.evp_dequeued,
5627a7bc169Seric 		    digest.evp_enqueued - last.evp_enqueued,
5637a7bc169Seric 		    digest.evp_dequeued - last.evp_dequeued,
5647a7bc169Seric 
5657a7bc169Seric 		    digest.dlv_ok - last.dlv_ok,
5667a7bc169Seric 		    digest.dlv_tempfail - last.dlv_tempfail,
5677a7bc169Seric 		    digest.dlv_permfail - last.dlv_permfail,
5687a7bc169Seric 		    digest.dlv_loop - last.dlv_loop,
5697a7bc169Seric 
5707a7bc169Seric 		    digest.evp_expired - last.evp_expired,
5717a7bc169Seric 		    digest.evp_removed - last.evp_removed,
5727a7bc169Seric 		    digest.evp_bounce - last.evp_bounce);
5737a7bc169Seric 
5747a7bc169Seric 		last = digest;
5757a7bc169Seric 		count++;
5767a7bc169Seric 		sleep(1);
5777a7bc169Seric 	}
5787a7bc169Seric 
5797a7bc169Seric 	return (0);
5807a7bc169Seric }
5817a7bc169Seric 
5827a7bc169Seric static int
58335e161d3Seric do_pause_envelope(int argc, struct parameter *argv)
58435e161d3Seric {
5850fcb81a3Seric 	size_t	total, ok;
58635e161d3Seric 
5870fcb81a3Seric 	srv_foreach_envelope(argv, IMSG_CTL_PAUSE_EVP, &total, &ok);
5880fcb81a3Seric 	printf("%zu envelope%s paused\n", ok, (ok > 1) ? "s" : "");
58935e161d3Seric 
59035e161d3Seric 	return (0);
59135e161d3Seric }
59235e161d3Seric 
59335e161d3Seric static int
5947a7bc169Seric do_pause_mda(int argc, struct parameter *argv)
5957a7bc169Seric {
5967a7bc169Seric 	srv_send(IMSG_CTL_PAUSE_MDA, NULL, 0);
5970fcb81a3Seric 	return srv_check_result(1);
5987a7bc169Seric }
5997a7bc169Seric 
6007a7bc169Seric static int
6017a7bc169Seric do_pause_mta(int argc, struct parameter *argv)
6027a7bc169Seric {
6037a7bc169Seric 	srv_send(IMSG_CTL_PAUSE_MTA, NULL, 0);
6040fcb81a3Seric 	return srv_check_result(1);
6057a7bc169Seric }
6067a7bc169Seric 
6077a7bc169Seric static int
6087a7bc169Seric do_pause_smtp(int argc, struct parameter *argv)
6097a7bc169Seric {
6107a7bc169Seric 	srv_send(IMSG_CTL_PAUSE_SMTP, NULL, 0);
6110fcb81a3Seric 	return srv_check_result(1);
6127a7bc169Seric }
6137a7bc169Seric 
6147a7bc169Seric static int
6157a7bc169Seric do_profile(int argc, struct parameter *argv)
6167a7bc169Seric {
6177a7bc169Seric 	int	v;
6187a7bc169Seric 
6197a7bc169Seric 	v = str_to_profile(argv[0].u.u_str);
6207a7bc169Seric 
621e267740eSeric 	srv_send(IMSG_CTL_PROFILE_ENABLE, &v, sizeof(v));
6220fcb81a3Seric 	return srv_check_result(1);
6237a7bc169Seric }
6247a7bc169Seric 
6257a7bc169Seric static int
6267a7bc169Seric do_remove(int argc, struct parameter *argv)
6277a7bc169Seric {
6280fcb81a3Seric 	size_t	total, ok;
6297a7bc169Seric 
6300fcb81a3Seric 	srv_foreach_envelope(argv, IMSG_CTL_REMOVE, &total, &ok);
6310fcb81a3Seric 	printf("%zu envelope%s removed\n", ok, (ok > 1) ? "s" : "");
6327a7bc169Seric 
6337a7bc169Seric 	return (0);
6347a7bc169Seric }
6357a7bc169Seric 
6367a7bc169Seric static int
63735e161d3Seric do_resume_envelope(int argc, struct parameter *argv)
63835e161d3Seric {
6390fcb81a3Seric 	size_t	total, ok;
64035e161d3Seric 
6410fcb81a3Seric 	srv_foreach_envelope(argv, IMSG_CTL_RESUME_EVP, &total, &ok);
6420fcb81a3Seric 	printf("%zu envelope%s resumed\n", ok, (ok > 1) ? "s" : "");
64335e161d3Seric 
64435e161d3Seric 	return (0);
64535e161d3Seric }
64635e161d3Seric 
64735e161d3Seric static int
6487a7bc169Seric do_resume_mda(int argc, struct parameter *argv)
6497a7bc169Seric {
6507a7bc169Seric 	srv_send(IMSG_CTL_RESUME_MDA, NULL, 0);
6510fcb81a3Seric 	return srv_check_result(1);
6527a7bc169Seric }
6537a7bc169Seric 
6547a7bc169Seric static int
6557a7bc169Seric do_resume_mta(int argc, struct parameter *argv)
6567a7bc169Seric {
6577a7bc169Seric 	srv_send(IMSG_CTL_RESUME_MTA, NULL, 0);
6580fcb81a3Seric 	return srv_check_result(1);
6597a7bc169Seric }
6607a7bc169Seric 
6617a7bc169Seric static int
662c5acbec8Seric do_resume_route(int argc, struct parameter *argv)
663c5acbec8Seric {
664c5acbec8Seric 	uint64_t	v;
665c5acbec8Seric 
666c5acbec8Seric 	if (argc == 0)
667c5acbec8Seric 		v = 0;
668c5acbec8Seric 	else
669c5acbec8Seric 		v = argv[0].u.u_routeid;
670c5acbec8Seric 
671c5acbec8Seric 	srv_send(IMSG_CTL_RESUME_ROUTE, &v, sizeof(v));
6720fcb81a3Seric 	return srv_check_result(1);
673c5acbec8Seric }
674c5acbec8Seric 
675c5acbec8Seric static int
6767a7bc169Seric do_resume_smtp(int argc, struct parameter *argv)
6777a7bc169Seric {
6787a7bc169Seric 	srv_send(IMSG_CTL_RESUME_SMTP, NULL, 0);
6790fcb81a3Seric 	return srv_check_result(1);
6807a7bc169Seric }
6817a7bc169Seric 
6827a7bc169Seric static int
6837a7bc169Seric do_schedule(int argc, struct parameter *argv)
6847a7bc169Seric {
6850fcb81a3Seric 	size_t	total, ok;
6867a7bc169Seric 
6870fcb81a3Seric 	srv_foreach_envelope(argv, IMSG_CTL_SCHEDULE, &total, &ok);
6880fcb81a3Seric 	printf("%zu envelope%s scheduled\n", ok, (ok > 1) ? "s" : "");
6897a7bc169Seric 
6907a7bc169Seric 	return (0);
6917a7bc169Seric }
6927a7bc169Seric 
6937a7bc169Seric static int
6947a7bc169Seric do_show_envelope(int argc, struct parameter *argv)
6957a7bc169Seric {
696953aae25Sderaadt 	char	 buf[PATH_MAX];
6977a7bc169Seric 
6987a7bc169Seric 	if (!bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/%016" PRIx64,
6997a7bc169Seric 	    PATH_SPOOL,
7007a7bc169Seric 	    PATH_QUEUE,
7017a7bc169Seric 	    (evpid_to_msgid(argv[0].u.u_evpid) & 0xff000000) >> 24,
7027a7bc169Seric 	    evpid_to_msgid(argv[0].u.u_evpid),
7037a7bc169Seric 	    argv[0].u.u_evpid))
7047a7bc169Seric 		errx(1, "unable to retrieve envelope");
7057a7bc169Seric 
7067a7bc169Seric 	display(buf);
7077a7bc169Seric 
7087a7bc169Seric 	return (0);
7097a7bc169Seric }
7107a7bc169Seric 
7117a7bc169Seric static int
712c5acbec8Seric do_show_hoststats(int argc, struct parameter *argv)
713c5acbec8Seric {
7140fcb81a3Seric 	srv_show_cmd(IMSG_CTL_MTA_SHOW_HOSTSTATS, NULL, 0);
715c5acbec8Seric 
716c5acbec8Seric 	return (0);
717c5acbec8Seric }
718c5acbec8Seric 
719c5acbec8Seric static int
7207a7bc169Seric do_show_message(int argc, struct parameter *argv)
7217a7bc169Seric {
722953aae25Sderaadt 	char	 buf[PATH_MAX];
7237a7bc169Seric 	uint32_t msgid;
7247a7bc169Seric 
7257a7bc169Seric 	if (argv[0].type == P_EVPID)
7267a7bc169Seric 		msgid = evpid_to_msgid(argv[0].u.u_evpid);
7277a7bc169Seric 	else
7287a7bc169Seric 		msgid = argv[0].u.u_msgid;
7297a7bc169Seric 
7307a7bc169Seric 	if (!bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/message",
7317a7bc169Seric 	    PATH_SPOOL,
7327a7bc169Seric 	    PATH_QUEUE,
7337a7bc169Seric 	    (msgid & 0xff000000) >> 24,
7347a7bc169Seric 	    msgid))
7357a7bc169Seric 		errx(1, "unable to retrieve message");
7367a7bc169Seric 
7377a7bc169Seric 	display(buf);
7387a7bc169Seric 
7397a7bc169Seric 	return (0);
7407a7bc169Seric }
7417a7bc169Seric 
7427a7bc169Seric static int
7437a7bc169Seric do_show_queue(int argc, struct parameter *argv)
7447a7bc169Seric {
7457a7bc169Seric 	struct envelope	 evp;
7467a7bc169Seric 	uint32_t	 msgid;
7477a7bc169Seric 	FTS		*fts;
7487a7bc169Seric 	FTSENT		*ftse;
7497a7bc169Seric 	char		*qpath[] = {"/queue", NULL};
7507a7bc169Seric 	char		*tmp;
7517a7bc169Seric 	uint64_t	 evpid;
7527a7bc169Seric 
7537a7bc169Seric 	now = time(NULL);
7547a7bc169Seric 
7557a7bc169Seric 	if (!srv_connect()) {
7567a7bc169Seric 		queue_init("fs", 0);
757a94ff62fSgilles 		if (chroot(PATH_SPOOL) == -1 || chdir("/") == -1)
7587a7bc169Seric 			err(1, "%s", PATH_SPOOL);
7597a7bc169Seric 		fts = fts_open(qpath, FTS_PHYSICAL|FTS_NOCHDIR, NULL);
7607a7bc169Seric 		if (fts == NULL)
7617a7bc169Seric 			err(1, "%s/queue", PATH_SPOOL);
7627a7bc169Seric 
7637a7bc169Seric 		while ((ftse = fts_read(fts)) != NULL) {
7647a7bc169Seric 			switch (ftse->fts_info) {
7657a7bc169Seric 			case FTS_DP:
7667a7bc169Seric 			case FTS_DNR:
7677a7bc169Seric 				break;
7687a7bc169Seric 			case FTS_F:
7697a7bc169Seric 				tmp = NULL;
7707a7bc169Seric 				evpid = strtoull(ftse->fts_name, &tmp, 16);
7717a7bc169Seric 				if (tmp && *tmp != '\0')
7727a7bc169Seric 					break;
7737a7bc169Seric 				show_offline_envelope(evpid);
7747a7bc169Seric 			}
7757a7bc169Seric 		}
7767a7bc169Seric 
7777a7bc169Seric 		fts_close(fts);
7787a7bc169Seric 		return (0);
7797a7bc169Seric 	}
7807a7bc169Seric 
7817a7bc169Seric 	if (argc == 0) {
7827a7bc169Seric 		msgid = 0;
7837a7bc169Seric 		while (srv_iter_messages(&msgid))
7847a7bc169Seric 			while (srv_iter_envelopes(msgid, &evp))
7857a7bc169Seric 				show_queue_envelope(&evp, 1);
7867a7bc169Seric 	} else if (argv[0].type == P_MSGID) {
7877a7bc169Seric 		while (srv_iter_envelopes(argv[0].u.u_msgid, &evp))
7887a7bc169Seric 			show_queue_envelope(&evp, 1);
7897a7bc169Seric 	}
7907a7bc169Seric 
7917a7bc169Seric 	return (0);
7927a7bc169Seric }
7937a7bc169Seric 
7947a7bc169Seric static int
795f9a337f4Seric do_show_hosts(int argc, struct parameter *argv)
796f9a337f4Seric {
797f9a337f4Seric 	srv_show_cmd(IMSG_CTL_MTA_SHOW_HOSTS, NULL, 0);
798f9a337f4Seric 
799f9a337f4Seric 	return (0);
800f9a337f4Seric }
801f9a337f4Seric 
802f9a337f4Seric static int
803f9a337f4Seric do_show_relays(int argc, struct parameter *argv)
804f9a337f4Seric {
805f9a337f4Seric 	srv_show_cmd(IMSG_CTL_MTA_SHOW_RELAYS, NULL, 0);
806f9a337f4Seric 
807f9a337f4Seric 	return (0);
808f9a337f4Seric }
809f9a337f4Seric 
810f9a337f4Seric static int
811c5acbec8Seric do_show_routes(int argc, struct parameter *argv)
812c5acbec8Seric {
8130fcb81a3Seric 	srv_show_cmd(IMSG_CTL_MTA_SHOW_ROUTES, NULL, 0);
814c5acbec8Seric 
815c5acbec8Seric 	return (0);
816c5acbec8Seric }
817c5acbec8Seric 
818c5acbec8Seric static int
8197a7bc169Seric do_show_stats(int argc, struct parameter *argv)
8207a7bc169Seric {
8217a7bc169Seric 	struct stat_kv	kv;
8227a7bc169Seric 	time_t		duration;
8237a7bc169Seric 
824c1392a69Seric 	memset(&kv, 0, sizeof kv);
8257a7bc169Seric 
8267a7bc169Seric 	while (1) {
827aa1d5973Seric 		srv_send(IMSG_CTL_GET_STATS, &kv, sizeof kv);
828aa1d5973Seric 		srv_recv(IMSG_CTL_GET_STATS);
8297a7bc169Seric 		srv_read(&kv, sizeof(kv));
8307a7bc169Seric 		srv_end();
8317a7bc169Seric 
8327a7bc169Seric 		if (kv.iter == NULL)
8337a7bc169Seric 			break;
8347a7bc169Seric 
8357a7bc169Seric 		if (strcmp(kv.key, "uptime") == 0) {
8367a7bc169Seric 			duration = time(NULL) - kv.val.u.counter;
8377a7bc169Seric 			printf("uptime=%lld\n", (long long)duration);
8387a7bc169Seric 			printf("uptime.human=%s\n",
8397a7bc169Seric 			    duration_to_text(duration));
8407a7bc169Seric 		}
8417a7bc169Seric 		else {
8427a7bc169Seric 			switch (kv.val.type) {
8437a7bc169Seric 			case STAT_COUNTER:
8447a7bc169Seric 				printf("%s=%zd\n",
8457a7bc169Seric 				    kv.key, kv.val.u.counter);
8467a7bc169Seric 				break;
8477a7bc169Seric 			case STAT_TIMESTAMP:
8487a7bc169Seric 				printf("%s=%" PRId64 "\n",
8497a7bc169Seric 				    kv.key, (int64_t)kv.val.u.timestamp);
8507a7bc169Seric 				break;
8517a7bc169Seric 			case STAT_TIMEVAL:
8527a7bc169Seric 				printf("%s=%lld.%lld\n",
8537a7bc169Seric 				    kv.key, (long long)kv.val.u.tv.tv_sec,
8547a7bc169Seric 				    (long long)kv.val.u.tv.tv_usec);
8557a7bc169Seric 				break;
8567a7bc169Seric 			case STAT_TIMESPEC:
857f70d44e6Seric 				printf("%s=%lld.%06ld\n",
8587a7bc169Seric 				    kv.key,
8597a7bc169Seric 				    (long long)kv.val.u.ts.tv_sec * 1000000 +
8607a7bc169Seric 				    kv.val.u.ts.tv_nsec / 1000000,
8617a7bc169Seric 				    kv.val.u.ts.tv_nsec % 1000000);
8627a7bc169Seric 				break;
8637a7bc169Seric 			}
8647a7bc169Seric 		}
8657a7bc169Seric 	}
8667a7bc169Seric 
8677a7bc169Seric 	return (0);
8687a7bc169Seric }
8697a7bc169Seric 
8707a7bc169Seric static int
871c37e9483Seric do_show_status(int argc, struct parameter *argv)
872c37e9483Seric {
873c37e9483Seric 	uint32_t	sc_flags;
874c37e9483Seric 
875c37e9483Seric 	srv_send(IMSG_CTL_SHOW_STATUS, NULL, 0);
876c37e9483Seric 	srv_recv(IMSG_CTL_SHOW_STATUS);
877c37e9483Seric 	srv_read(&sc_flags, sizeof(sc_flags));
878c37e9483Seric 	srv_end();
879c37e9483Seric 	printf("MDA %s\n",
880c37e9483Seric 	    (sc_flags & SMTPD_MDA_PAUSED) ? "paused" : "running");
881c37e9483Seric 	printf("MTA %s\n",
882c37e9483Seric 	    (sc_flags & SMTPD_MTA_PAUSED) ? "paused" : "running");
883c37e9483Seric 	printf("SMTP %s\n",
884c37e9483Seric 	    (sc_flags & SMTPD_SMTP_PAUSED) ? "paused" : "running");
885c37e9483Seric 	return (0);
886c37e9483Seric }
887c37e9483Seric 
888c37e9483Seric static int
8897a7bc169Seric do_trace(int argc, struct parameter *argv)
8907a7bc169Seric {
8917a7bc169Seric 	int	v;
8927a7bc169Seric 
8937a7bc169Seric 	v = str_to_trace(argv[0].u.u_str);
8947a7bc169Seric 
895aa1d5973Seric 	srv_send(IMSG_CTL_TRACE_ENABLE, &v, sizeof(v));
8960fcb81a3Seric 	return srv_check_result(1);
8977a7bc169Seric }
8987a7bc169Seric 
8997a7bc169Seric static int
9007a7bc169Seric do_unprofile(int argc, struct parameter *argv)
9017a7bc169Seric {
9027a7bc169Seric 	int	v;
9037a7bc169Seric 
9047a7bc169Seric 	v = str_to_profile(argv[0].u.u_str);
9057a7bc169Seric 
906aa1d5973Seric 	srv_send(IMSG_CTL_PROFILE_DISABLE, &v, sizeof(v));
9070fcb81a3Seric 	return srv_check_result(1);
9087a7bc169Seric }
9097a7bc169Seric 
9107a7bc169Seric static int
9117a7bc169Seric do_untrace(int argc, struct parameter *argv)
9127a7bc169Seric {
9137a7bc169Seric 	int	v;
9147a7bc169Seric 
9157a7bc169Seric 	v = str_to_trace(argv[0].u.u_str);
9167a7bc169Seric 
917aa1d5973Seric 	srv_send(IMSG_CTL_TRACE_DISABLE, &v, sizeof(v));
9180fcb81a3Seric 	return srv_check_result(1);
9197a7bc169Seric }
9207a7bc169Seric 
9217a7bc169Seric static int
9227a7bc169Seric do_update_table(int argc, struct parameter *argv)
9237a7bc169Seric {
9247a7bc169Seric 	const char	*name = argv[0].u.u_str;
9257a7bc169Seric 
926aa1d5973Seric 	srv_send(IMSG_CTL_UPDATE_TABLE, name, strlen(name) + 1);
9270fcb81a3Seric 	return srv_check_result(1);
9287a7bc169Seric }
9297a7bc169Seric 
9307b682ccfSeric static int
9317b682ccfSeric do_encrypt(int argc, struct parameter *argv)
9327b682ccfSeric {
9337b682ccfSeric 	const char *p = NULL;
9347b682ccfSeric 
935aee31bfdSeric 	droppriv();
936aee31bfdSeric 
9377b682ccfSeric 	if (argv)
9387b682ccfSeric 		p = argv[0].u.u_str;
939dd673463Sgilles 	execl(PATH_ENCRYPT, "encrypt", "--", p, (char *)NULL);
9407b682ccfSeric 	errx(1, "execl");
9417b682ccfSeric }
9427b682ccfSeric 
9435b6a9ce9Seric static int
9445b6a9ce9Seric do_block_mta(int argc, struct parameter *argv)
9455b6a9ce9Seric {
9465b6a9ce9Seric 	struct ibuf *m;
9475b6a9ce9Seric 
9485b6a9ce9Seric 	if (ibuf == NULL && !srv_connect())
9495b6a9ce9Seric 		errx(1, "smtpd doesn't seem to be running");
9505b6a9ce9Seric 	m = imsg_create(ibuf, IMSG_CTL_MTA_BLOCK, IMSG_VERSION, 0,
9515b6a9ce9Seric 	    sizeof(argv[0].u.u_ss) + strlen(argv[1].u.u_str) + 1);
9525b6a9ce9Seric 	if (imsg_add(m, &argv[0].u.u_ss, sizeof(argv[0].u.u_ss)) == -1)
9535b6a9ce9Seric 		errx(1, "imsg_add");
9545b6a9ce9Seric 	if (imsg_add(m, argv[1].u.u_str, strlen(argv[1].u.u_str) + 1) == -1)
9555b6a9ce9Seric 		errx(1, "imsg_add");
9565b6a9ce9Seric 	imsg_close(ibuf, m);
9575b6a9ce9Seric 
9585b6a9ce9Seric 	return srv_check_result(1);
9595b6a9ce9Seric }
9605b6a9ce9Seric 
9615b6a9ce9Seric static int
9625b6a9ce9Seric do_unblock_mta(int argc, struct parameter *argv)
9635b6a9ce9Seric {
9645b6a9ce9Seric 	struct ibuf *m;
9655b6a9ce9Seric 
9665b6a9ce9Seric 	if (ibuf == NULL && !srv_connect())
9675b6a9ce9Seric 		errx(1, "smtpd doesn't seem to be running");
9685b6a9ce9Seric 
9695b6a9ce9Seric 	m = imsg_create(ibuf, IMSG_CTL_MTA_UNBLOCK, IMSG_VERSION, 0,
9705b6a9ce9Seric 	    sizeof(argv[0].u.u_ss) + strlen(argv[1].u.u_str) + 1);
9715b6a9ce9Seric 	if (imsg_add(m, &argv[0].u.u_ss, sizeof(argv[0].u.u_ss)) == -1)
9725b6a9ce9Seric 		errx(1, "imsg_add");
9735b6a9ce9Seric 	if (imsg_add(m, argv[1].u.u_str, strlen(argv[1].u.u_str) + 1) == -1)
9745b6a9ce9Seric 		errx(1, "imsg_add");
9755b6a9ce9Seric 	imsg_close(ibuf, m);
9765b6a9ce9Seric 
9775b6a9ce9Seric 	return srv_check_result(1);
9785b6a9ce9Seric }
9795b6a9ce9Seric 
9805b6a9ce9Seric static int
9815b6a9ce9Seric do_show_mta_block(int argc, struct parameter *argv)
9825b6a9ce9Seric {
9835b6a9ce9Seric 	srv_show_cmd(IMSG_CTL_MTA_SHOW_BLOCK, NULL, 0);
9845b6a9ce9Seric 
9855b6a9ce9Seric 	return (0);
9865b6a9ce9Seric }
9875b6a9ce9Seric 
988a9835440Ssunil static int
989a9835440Ssunil do_discover(int argc, struct parameter *argv)
990a9835440Ssunil {
991a9835440Ssunil 	uint64_t evpid;
992a9835440Ssunil 	uint32_t msgid;
993a9835440Ssunil 	size_t	 n_evp;
994a9835440Ssunil 
995a9835440Ssunil 	if (ibuf == NULL && !srv_connect())
996a9835440Ssunil 		errx(1, "smtpd doesn't seem to be running");
997a9835440Ssunil 
998a9835440Ssunil 	if (argv[0].type == P_EVPID) {
999a9835440Ssunil 		evpid = argv[0].u.u_evpid;
1000a9835440Ssunil 		srv_send(IMSG_CTL_DISCOVER_EVPID, &evpid, sizeof evpid);
1001a9835440Ssunil 		srv_recv(IMSG_CTL_DISCOVER_EVPID);
1002a9835440Ssunil 	} else {
1003a9835440Ssunil 		msgid = argv[0].u.u_msgid;
1004a9835440Ssunil 		srv_send(IMSG_CTL_DISCOVER_MSGID, &msgid, sizeof msgid);
1005a9835440Ssunil 		srv_recv(IMSG_CTL_DISCOVER_MSGID);
1006a9835440Ssunil 	}
1007a9835440Ssunil 
1008a9835440Ssunil 	if (rlen == 0) {
1009a9835440Ssunil 		srv_end();
1010a9835440Ssunil 		return (0);
1011a9835440Ssunil 	} else {
1012a9835440Ssunil 		srv_read(&n_evp, sizeof n_evp);
1013a9835440Ssunil 		srv_end();
1014a9835440Ssunil 	}
1015a9835440Ssunil 
1016a9835440Ssunil 	printf("%zu envelope%s discovered\n", n_evp, (n_evp != 1) ? "s" : "");
1017a9835440Ssunil 	return (0);
1018a9835440Ssunil }
1019a9835440Ssunil 
1020d05af802Ssunil static int
1021aee31bfdSeric do_spf_walk(int argc, struct parameter *argv)
1022aee31bfdSeric {
1023aee31bfdSeric 	droppriv();
1024aee31bfdSeric 
1025aee31bfdSeric 	return spfwalk(argc, argv);
1026aee31bfdSeric }
1027aee31bfdSeric 
1028aee31bfdSeric #define cmd_install_priv(s, f) \
1029aee31bfdSeric 	cmd_install((s), privileged ? (f) : do_permission_denied)
1030aee31bfdSeric 
10317a7bc169Seric int
10327a7bc169Seric main(int argc, char **argv)
10337a7bc169Seric {
10348351d18bSgilles 	gid_t		 gid;
1035aee31bfdSeric 	int		 privileged;
10367a7bc169Seric 	char		*argv_mailq[] = { "show", "queue", NULL };
10377a7bc169Seric 
1038ff01b044Seric 	log_init(1, LOG_MAIL);
1039ff01b044Seric 
10403511367dSsunil 	sendmail_compat(argc, argv);
1041aee31bfdSeric 	privileged = geteuid() == 0;
10425d09b982Seric 
10433511367dSsunil 	gid = getgid();
10448351d18bSgilles 	if (setresgid(gid, gid, gid) == -1)
10458351d18bSgilles 		err(1, "setresgid");
10468351d18bSgilles 
1047aee31bfdSeric 	/* Privileged commands */
1048aee31bfdSeric 	cmd_install_priv("discover <evpid>",	do_discover);
1049aee31bfdSeric 	cmd_install_priv("discover <msgid>",	do_discover);
1050aee31bfdSeric 	cmd_install_priv("pause mta from <addr> for <str>", do_block_mta);
1051aee31bfdSeric 	cmd_install_priv("resume mta from <addr> for <str>", do_unblock_mta);
1052aee31bfdSeric 	cmd_install_priv("show mta paused",	do_show_mta_block);
1053aee31bfdSeric 	cmd_install_priv("log brief",		do_log_brief);
1054aee31bfdSeric 	cmd_install_priv("log verbose",		do_log_verbose);
1055aee31bfdSeric 	cmd_install_priv("monitor",		do_monitor);
1056aee31bfdSeric 	cmd_install_priv("pause envelope <evpid>", do_pause_envelope);
1057aee31bfdSeric 	cmd_install_priv("pause envelope <msgid>", do_pause_envelope);
1058aee31bfdSeric 	cmd_install_priv("pause envelope all",	do_pause_envelope);
1059aee31bfdSeric 	cmd_install_priv("pause mda",		do_pause_mda);
1060aee31bfdSeric 	cmd_install_priv("pause mta",		do_pause_mta);
1061aee31bfdSeric 	cmd_install_priv("pause smtp",		do_pause_smtp);
1062aee31bfdSeric 	cmd_install_priv("profile <str>",	do_profile);
1063aee31bfdSeric 	cmd_install_priv("remove <evpid>",	do_remove);
1064aee31bfdSeric 	cmd_install_priv("remove <msgid>",	do_remove);
1065aee31bfdSeric 	cmd_install_priv("remove all",		do_remove);
1066aee31bfdSeric 	cmd_install_priv("resume envelope <evpid>", do_resume_envelope);
1067aee31bfdSeric 	cmd_install_priv("resume envelope <msgid>", do_resume_envelope);
1068aee31bfdSeric 	cmd_install_priv("resume envelope all",	do_resume_envelope);
1069aee31bfdSeric 	cmd_install_priv("resume mda",		do_resume_mda);
1070aee31bfdSeric 	cmd_install_priv("resume mta",		do_resume_mta);
1071aee31bfdSeric 	cmd_install_priv("resume route <routeid>", do_resume_route);
1072aee31bfdSeric 	cmd_install_priv("resume smtp",		do_resume_smtp);
1073aee31bfdSeric 	cmd_install_priv("schedule <msgid>",	do_schedule);
1074aee31bfdSeric 	cmd_install_priv("schedule <evpid>",	do_schedule);
1075aee31bfdSeric 	cmd_install_priv("schedule all",	do_schedule);
1076aee31bfdSeric 	cmd_install_priv("show envelope <evpid>", do_show_envelope);
1077aee31bfdSeric 	cmd_install_priv("show hoststats",	do_show_hoststats);
1078aee31bfdSeric 	cmd_install_priv("show message <msgid>", do_show_message);
1079aee31bfdSeric 	cmd_install_priv("show message <evpid>", do_show_message);
1080aee31bfdSeric 	cmd_install_priv("show queue",		do_show_queue);
1081aee31bfdSeric 	cmd_install_priv("show queue <msgid>",	do_show_queue);
1082aee31bfdSeric 	cmd_install_priv("show hosts",		do_show_hosts);
1083aee31bfdSeric 	cmd_install_priv("show relays",		do_show_relays);
1084aee31bfdSeric 	cmd_install_priv("show routes",		do_show_routes);
1085aee31bfdSeric 	cmd_install_priv("show stats",		do_show_stats);
1086aee31bfdSeric 	cmd_install_priv("show status",		do_show_status);
1087aee31bfdSeric 	cmd_install_priv("trace <str>",		do_trace);
1088aee31bfdSeric 	cmd_install_priv("unprofile <str>",	do_unprofile);
1089aee31bfdSeric 	cmd_install_priv("untrace <str>",	do_untrace);
1090aee31bfdSeric 	cmd_install_priv("update table <str>",	do_update_table);
1091aee31bfdSeric 
1092aee31bfdSeric 	/* Unprivileged commands */
10937b682ccfSeric 	cmd_install("encrypt",			do_encrypt);
10947b682ccfSeric 	cmd_install("encrypt <str>",		do_encrypt);
1095aee31bfdSeric 	cmd_install("spf walk",			do_spf_walk);
10967a7bc169Seric 
10975d09b982Seric 	if (strcmp(__progname, "mailq") == 0)
10987a7bc169Seric 		return cmd_run(2, argv_mailq);
10997a7bc169Seric 	if (strcmp(__progname, "smtpctl") == 0)
11007a7bc169Seric 		return cmd_run(argc - 1, argv + 1);
11017a7bc169Seric 
11025d09b982Seric 	errx(1, "unsupported mode");
11034fe02f32Seric 	return (0);
11043511367dSsunil }
1105f607a12cSgilles 
11063511367dSsunil void
11073511367dSsunil sendmail_compat(int argc, char **argv)
11083511367dSsunil {
11093511367dSsunil 	FILE	*offlinefp = NULL;
11103511367dSsunil 	gid_t	 gid;
11115b9e028dSgilles 	int	 i, r;
11123511367dSsunil 
11133511367dSsunil 	if (strcmp(__progname, "sendmail") == 0 ||
11143511367dSsunil 	    strcmp(__progname, "send-mail") == 0) {
11153511367dSsunil 		/*
11163511367dSsunil 		 * determine whether we are called with flags
11173511367dSsunil 		 * that should invoke makemap/newaliases.
11183511367dSsunil 		 */
11193511367dSsunil 		for (i = 1; i < argc; i++)
11205ba55e18Ssunil 			if (strncmp(argv[i], "-bi", 3) == 0)
11210228dab0Smillert 				exit(makemap(P_SENDMAIL, argc, argv));
11223511367dSsunil 
11233511367dSsunil 		if (!srv_connect())
11243511367dSsunil 			offlinefp = offline_file();
11253511367dSsunil 
11263511367dSsunil 		gid = getgid();
11273511367dSsunil 		if (setresgid(gid, gid, gid) == -1)
11283511367dSsunil 			err(1, "setresgid");
11293511367dSsunil 
11303511367dSsunil 		/* we'll reduce further down the road */
11313511367dSsunil 		if (pledge("stdio rpath wpath cpath tmppath flock "
11323511367dSsunil 			"dns getpw recvfd", NULL) == -1)
11333511367dSsunil 			err(1, "pledge");
11343511367dSsunil 
11353511367dSsunil 		sendmail = 1;
11363511367dSsunil 		exit(enqueue(argc, argv, offlinefp));
11375ba55e18Ssunil 	} else if (strcmp(__progname, "makemap") == 0)
11385ba55e18Ssunil 		exit(makemap(P_MAKEMAP, argc, argv));
11395b9e028dSgilles 	else if (strcmp(__progname, "newaliases") == 0) {
11405b9e028dSgilles 		r = makemap(P_NEWALIASES, argc, argv);
11415b9e028dSgilles 		/*
11425b9e028dSgilles 		 * if server is available, notify of table update.
11435b9e028dSgilles 		 * only makes sense for static tables AND if server is up.
11445b9e028dSgilles 		 */
11455b9e028dSgilles 		if (srv_connect()) {
11465b9e028dSgilles 			srv_send(IMSG_CTL_UPDATE_TABLE, "aliases", strlen("aliases") + 1);
11475b9e028dSgilles 			srv_check_result(0);
11485b9e028dSgilles 		}
11495b9e028dSgilles 		exit(r);
11505b9e028dSgilles 	}
115138f0b910Seric }
115238f0b910Seric 
115338f0b910Seric static void
11544fe02f32Seric show_queue_envelope(struct envelope *e, int online)
115538f0b910Seric {
11564fe02f32Seric 	const char	*src = "?", *agent = "?";
1157a8bde366Seric 	char		 status[128], runstate[128], errline[LINE_MAX];
115838f0b910Seric 
115938f0b910Seric 	status[0] = '\0';
116038f0b910Seric 
11617a7bc169Seric 	getflag(&e->flags, EF_BOUNCE, "bounce", status, sizeof(status));
11627a7bc169Seric 	getflag(&e->flags, EF_AUTHENTICATED, "auth", status, sizeof(status));
11637a7bc169Seric 	getflag(&e->flags, EF_INTERNAL, "internal", status, sizeof(status));
116435e161d3Seric 	getflag(&e->flags, EF_SUSPEND, "suspend", status, sizeof(status));
11656dc81a07Seric 	getflag(&e->flags, EF_HOLD, "hold", status, sizeof(status));
116638f0b910Seric 
11674fe02f32Seric 	if (online) {
116865c4fdfbSgilles 		if (e->flags & EF_PENDING)
11695c9fb78eSeric 			(void)snprintf(runstate, sizeof runstate, "pending|%zd",
11704fe02f32Seric 			    (ssize_t)(e->nexttry - now));
117165c4fdfbSgilles 		else if (e->flags & EF_INFLIGHT)
11724c5b19cfSsunil 			(void)snprintf(runstate, sizeof runstate,
11734c5b19cfSsunil 			    "inflight|%zd", (ssize_t)(now - e->lasttry));
11744fe02f32Seric 		else
117586d491f8Sgilles 			(void)snprintf(runstate, sizeof runstate, "invalid|");
117665c4fdfbSgilles 		e->flags &= ~(EF_PENDING|EF_INFLIGHT);
11774fe02f32Seric 	}
11784fe02f32Seric 	else
117986d491f8Sgilles 		(void)strlcpy(runstate, "offline|", sizeof runstate);
11804fe02f32Seric 
118138f0b910Seric 	if (e->flags)
118238f0b910Seric 		errx(1, "%016" PRIx64 ": unexpected flags 0x%04x", e->id,
118338f0b910Seric 		    e->flags);
118438f0b910Seric 
118538f0b910Seric 	if (status[0])
118638f0b910Seric 		status[strlen(status) - 1] = '\0';
118738f0b910Seric 
11884fe02f32Seric 	if (e->type == D_MDA)
11894fe02f32Seric 		agent = "mda";
11904fe02f32Seric 	else if (e->type == D_MTA)
11914fe02f32Seric 		agent = "mta";
11924fe02f32Seric 	else if (e->type == D_BOUNCE)
11934fe02f32Seric 		agent = "bounce";
119438f0b910Seric 
1195468bf152Seric 	if (e->ss.ss_family == AF_LOCAL)
119656c4d9e6Seric 		src = "local";
1197468bf152Seric 	else if (e->ss.ss_family == AF_INET)
119856c4d9e6Seric 		src = "inet4";
1199468bf152Seric 	else if (e->ss.ss_family == AF_INET6)
120056c4d9e6Seric 		src = "inet6";
1201468bf152Seric 
1202a8bde366Seric 	strnvis(errline, e->errorline, sizeof(errline), 0);
1203a8bde366Seric 
12044fe02f32Seric 	printf("%016"PRIx64
12054fe02f32Seric 	    "|%s|%s|%s|%s@%s|%s@%s|%s@%s"
12064fe02f32Seric 	    "|%zu|%zu|%zu|%zu|%s|%s\n",
12074fe02f32Seric 
120838f0b910Seric 	    e->id,
12094fe02f32Seric 
1210468bf152Seric 	    src,
12114fe02f32Seric 	    agent,
121238f0b910Seric 	    status,
121338f0b910Seric 	    e->sender.user, e->sender.domain,
12144fe02f32Seric 	    e->rcpt.user, e->rcpt.domain,
121538f0b910Seric 	    e->dest.user, e->dest.domain,
121638f0b910Seric 
12174fe02f32Seric 	    (size_t) e->creation,
1218a8e22235Sgilles 	    (size_t) (e->creation + e->ttl),
12194fe02f32Seric 	    (size_t) e->lasttry,
12204fe02f32Seric 	    (size_t) e->retry,
12214fe02f32Seric 	    runstate,
1222a8bde366Seric 	    errline);
122338f0b910Seric }
122438f0b910Seric 
122538f0b910Seric static void
1226d2241734Schl getflag(uint *bitmap, int bit, char *bitstr, char *buf, size_t len)
122738f0b910Seric {
122838f0b910Seric 	if (*bitmap & bit) {
122938f0b910Seric 		*bitmap &= ~bit;
123086d491f8Sgilles 		(void)strlcat(buf, bitstr, len);
123186d491f8Sgilles 		(void)strlcat(buf, ",", len);
123238f0b910Seric 	}
123338f0b910Seric }
1234d5e710d9Sgilles 
1235d5e710d9Sgilles static void
12367a7bc169Seric show_offline_envelope(uint64_t evpid)
12377a7bc169Seric {
12387a7bc169Seric 	FILE   *fp = NULL;
1239953aae25Sderaadt 	char	pathname[PATH_MAX];
12407a7bc169Seric 	size_t	plen;
12417a7bc169Seric 	char   *p;
12427a7bc169Seric 	size_t	buflen;
12437a7bc169Seric 	char	buffer[sizeof(struct envelope)];
12447a7bc169Seric 
12457a7bc169Seric 	struct envelope	evp;
12467a7bc169Seric 
12477a7bc169Seric 	if (!bsnprintf(pathname, sizeof pathname,
12487a7bc169Seric 		"/queue/%02x/%08x/%016"PRIx64,
12497a7bc169Seric 		(evpid_to_msgid(evpid) & 0xff000000) >> 24,
12507a7bc169Seric 		evpid_to_msgid(evpid), evpid))
12517a7bc169Seric 		goto end;
12527a7bc169Seric 	fp = fopen(pathname, "r");
12537a7bc169Seric 	if (fp == NULL)
12547a7bc169Seric 		goto end;
12557a7bc169Seric 
12560fcb81a3Seric 	buflen = fread(buffer, 1, sizeof (buffer) - 1, fp);
12577a7bc169Seric 	p = buffer;
12587a7bc169Seric 	plen = buflen;
12590fcb81a3Seric 	buffer[buflen] = '\0';
12607a7bc169Seric 
12617a7bc169Seric 	if (is_encrypted_buffer(p)) {
12627a7bc169Seric 		warnx("offline encrypted queue is not supported yet");
12637a7bc169Seric 		goto end;
12647a7bc169Seric 	}
12657a7bc169Seric 
12667a7bc169Seric 	if (is_gzip_buffer(p)) {
12677a7bc169Seric 		warnx("offline compressed queue is not supported yet");
12687a7bc169Seric 		goto end;
12697a7bc169Seric 	}
12707a7bc169Seric 
12717a7bc169Seric 	if (!envelope_load_buffer(&evp, p, plen))
12727a7bc169Seric 		goto end;
12737a7bc169Seric 	evp.id = evpid;
12747a7bc169Seric 	show_queue_envelope(&evp, 0);
12757a7bc169Seric 
12767a7bc169Seric end:
12777a7bc169Seric 	if (fp)
12787a7bc169Seric 		fclose(fp);
12797a7bc169Seric }
12807a7bc169Seric 
12817a7bc169Seric static void
1282d5e710d9Sgilles display(const char *s)
1283d5e710d9Sgilles {
12847a7bc169Seric 	FILE   *fp;
12857a7bc169Seric 	char   *key;
12867a7bc169Seric 	int	gzipped;
12877a7bc169Seric 	char   *gzcat_argv0 = strrchr(PATH_GZCAT, '/') + 1;
1288d5e710d9Sgilles 
12897a7bc169Seric 	if ((fp = fopen(s, "r")) == NULL)
12907a7bc169Seric 		err(1, "fopen");
12917a7bc169Seric 
12927a7bc169Seric 	if (is_encrypted_fp(fp)) {
12937a7bc169Seric 		int	i;
1294230bac0bSgilles 		FILE   *ofp = NULL;
12957a7bc169Seric 
1296729f2a4cSmartijn 		if ((ofp = tmpfile()) == NULL)
1297729f2a4cSmartijn 			err(1, "tmpfile");
12987a7bc169Seric 
12997a7bc169Seric 		for (i = 0; i < 3; i++) {
13007a7bc169Seric 			key = getpass("key> ");
13017a7bc169Seric 			if (crypto_setup(key, strlen(key)))
13027a7bc169Seric 				break;
13037a7bc169Seric 		}
13047a7bc169Seric 		if (i == 3)
13057a7bc169Seric 			errx(1, "crypto-setup: invalid key");
13067a7bc169Seric 
13077a7bc169Seric 		if (!crypto_decrypt_file(fp, ofp)) {
13087a7bc169Seric 			printf("object is encrypted: %s\n", key);
13097a7bc169Seric 			exit(1);
1310d5e710d9Sgilles 		}
1311d5e710d9Sgilles 
13127a7bc169Seric 		fclose(fp);
13137a7bc169Seric 		fp = ofp;
13140fcb81a3Seric 		fseek(fp, 0, SEEK_SET);
13157a7bc169Seric 	}
13167a7bc169Seric 	gzipped = is_gzip_fp(fp);
13177a7bc169Seric 
13180fcb81a3Seric 	lseek(fileno(fp), 0, SEEK_SET);
13197a7bc169Seric 	(void)dup2(fileno(fp), STDIN_FILENO);
13207a7bc169Seric 	if (gzipped)
132153408464Skrw 		execl(PATH_GZCAT, gzcat_argv0, (char *)NULL);
13227a7bc169Seric 	else
132353408464Skrw 		execl(PATH_CAT, "cat", (char *)NULL);
13247a7bc169Seric 	err(1, "execl");
13257a7bc169Seric }
13267a7bc169Seric 
13277a7bc169Seric static int
13287a7bc169Seric str_to_trace(const char *str)
1329d5e710d9Sgilles {
13307a7bc169Seric 	if (!strcmp(str, "imsg"))
133165c4fdfbSgilles 		return TRACE_IMSG;
13327a7bc169Seric 	if (!strcmp(str, "io"))
133365c4fdfbSgilles 		return TRACE_IO;
13347a7bc169Seric 	if (!strcmp(str, "smtp"))
133565c4fdfbSgilles 		return TRACE_SMTP;
1336041b2d9aSeric 	if (!strcmp(str, "filters"))
1337041b2d9aSeric 		return TRACE_FILTERS;
13387a7bc169Seric 	if (!strcmp(str, "mta"))
133965c4fdfbSgilles 		return TRACE_MTA;
13407a7bc169Seric 	if (!strcmp(str, "bounce"))
134165c4fdfbSgilles 		return TRACE_BOUNCE;
13427a7bc169Seric 	if (!strcmp(str, "scheduler"))
134365c4fdfbSgilles 		return TRACE_SCHEDULER;
13447a7bc169Seric 	if (!strcmp(str, "lookup"))
13450cf935dfSgilles 		return TRACE_LOOKUP;
13467a7bc169Seric 	if (!strcmp(str, "stat"))
134765c4fdfbSgilles 		return TRACE_STAT;
13487a7bc169Seric 	if (!strcmp(str, "rules"))
134965c4fdfbSgilles 		return TRACE_RULES;
13507a7bc169Seric 	if (!strcmp(str, "mproc"))
1351299c4efeSeric 		return TRACE_MPROC;
13527a7bc169Seric 	if (!strcmp(str, "expand"))
135359a46edcSgilles 		return TRACE_EXPAND;
13547a7bc169Seric 	if (!strcmp(str, "all"))
1355299c4efeSeric 		return ~TRACE_DEBUG;
13567a7bc169Seric 	errx(1, "invalid trace keyword: %s", str);
13577a7bc169Seric 	return (0);
135865c4fdfbSgilles }
135965c4fdfbSgilles 
13607a7bc169Seric static int
13617a7bc169Seric str_to_profile(const char *str)
136265c4fdfbSgilles {
13637a7bc169Seric 	if (!strcmp(str, "imsg"))
136465c4fdfbSgilles 		return PROFILE_IMSG;
13657a7bc169Seric 	if (!strcmp(str, "queue"))
136665c4fdfbSgilles 		return PROFILE_QUEUE;
13677a7bc169Seric 	errx(1, "invalid profile keyword: %s", str);
13687a7bc169Seric 	return (0);
136965c4fdfbSgilles }
137065c4fdfbSgilles 
13717a7bc169Seric static int
13727a7bc169Seric is_gzip_buffer(const char *buffer)
13737a7bc169Seric {
13747a7bc169Seric 	uint16_t	magic;
13757a7bc169Seric 
13767a7bc169Seric 	memcpy(&magic, buffer, sizeof magic);
13777a7bc169Seric #define	GZIP_MAGIC	0x8b1f
13787a7bc169Seric 	return (magic == GZIP_MAGIC);
13797a7bc169Seric }
13807a7bc169Seric 
13817a7bc169Seric static int
13827a7bc169Seric is_gzip_fp(FILE *fp)
13837a7bc169Seric {
13847a7bc169Seric 	uint8_t		magic[2];
13857a7bc169Seric 	int		ret = 0;
13867a7bc169Seric 
13877a7bc169Seric 	if (fread(&magic, 1, sizeof magic, fp) != sizeof magic)
13887a7bc169Seric 		goto end;
13897a7bc169Seric 
13907a7bc169Seric 	ret = is_gzip_buffer((const char *)&magic);
13917a7bc169Seric end:
13920fcb81a3Seric 	fseek(fp, 0, SEEK_SET);
13937a7bc169Seric 	return ret;
13947a7bc169Seric }
13957a7bc169Seric 
13967a7bc169Seric 
13977a7bc169Seric /* XXX */
13987a7bc169Seric /*
13997a7bc169Seric  * queue supports transparent encryption.
14007a7bc169Seric  * encrypted chunks are prefixed with an API version byte
14017a7bc169Seric  * which we ensure is unambiguous with gzipped / plain
14027a7bc169Seric  * objects.
14037a7bc169Seric  */
14047a7bc169Seric 
14057a7bc169Seric static int
14067a7bc169Seric is_encrypted_buffer(const char *buffer)
14077a7bc169Seric {
14087a7bc169Seric 	uint8_t	magic;
14097a7bc169Seric 
14107a7bc169Seric 	magic = *buffer;
14117a7bc169Seric #define	ENCRYPTION_MAGIC	0x1
14127a7bc169Seric 	return (magic == ENCRYPTION_MAGIC);
14137a7bc169Seric }
14147a7bc169Seric 
14157a7bc169Seric static int
14167a7bc169Seric is_encrypted_fp(FILE *fp)
14177a7bc169Seric {
14187a7bc169Seric 	uint8_t	magic;
14197a7bc169Seric 	int	ret = 0;
14207a7bc169Seric 
14217a7bc169Seric 	if (fread(&magic, 1, sizeof magic, fp) != sizeof magic)
14227a7bc169Seric 		goto end;
14237a7bc169Seric 
14247a7bc169Seric 	ret = is_encrypted_buffer((const char *)&magic);
14257a7bc169Seric end:
14260fcb81a3Seric 	fseek(fp, 0, SEEK_SET);
14277a7bc169Seric 	return ret;
142865c4fdfbSgilles }
1429