xref: /openbsd-src/usr.sbin/tftp-proxy/tftp-proxy.c (revision 2ada0f0db45daf9f37eef4c03d301666acad74b3)
1*2ada0f0dSclaudio /* $OpenBSD: tftp-proxy.c,v 1.22 2021/01/17 13:38:52 claudio Exp $
2697c8096Sdlg  *
3697c8096Sdlg  * Copyright (c) 2005 DLS Internet Services
4697c8096Sdlg  * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
5697c8096Sdlg  *
6697c8096Sdlg  * Redistribution and use in source and binary forms, with or without
7697c8096Sdlg  * modification, are permitted provided that the following conditions
8697c8096Sdlg  * are met:
9697c8096Sdlg  *
10697c8096Sdlg  * 1. Redistributions of source code must retain the above copyright
11697c8096Sdlg  *    notice, this list of conditions and the following disclaimer.
12697c8096Sdlg  * 2. Redistributions in binary form must reproduce the above copyright
13697c8096Sdlg  *    notice, this list of conditions and the following disclaimer in the
14697c8096Sdlg  *    documentation and/or other materials provided with the distribution.
15697c8096Sdlg  * 3. The name of the author may not be used to endorse or promote products
16697c8096Sdlg  *    derived from this software without specific prior written permission.
17697c8096Sdlg  *
18697c8096Sdlg  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19697c8096Sdlg  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20697c8096Sdlg  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21697c8096Sdlg  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22697c8096Sdlg  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23697c8096Sdlg  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24697c8096Sdlg  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25697c8096Sdlg  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26697c8096Sdlg  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27697c8096Sdlg  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28697c8096Sdlg  */
29697c8096Sdlg 
30697c8096Sdlg #include <sys/types.h>
31b9fc9a72Sderaadt #include <sys/ioctl.h>
32697c8096Sdlg #include <sys/socket.h>
33697c8096Sdlg #include <sys/uio.h>
34697c8096Sdlg 
35697c8096Sdlg #include <netinet/in.h>
36697c8096Sdlg #include <arpa/inet.h>
37697c8096Sdlg #include <arpa/tftp.h>
38697c8096Sdlg #include <net/if.h>
39697c8096Sdlg #include <net/pfvar.h>
40697c8096Sdlg #include <netdb.h>
41697c8096Sdlg 
42697c8096Sdlg #include <unistd.h>
43697c8096Sdlg #include <errno.h>
44697c8096Sdlg #include <err.h>
45697c8096Sdlg #include <pwd.h>
46697c8096Sdlg #include <stdio.h>
47697c8096Sdlg #include <syslog.h>
48697c8096Sdlg #include <string.h>
49f9ed04f2Sflorian #include <stdarg.h>
50697c8096Sdlg #include <stdlib.h>
51697c8096Sdlg #include <event.h>
52697c8096Sdlg 
53697c8096Sdlg #include "filter.h"
54697c8096Sdlg 
55697c8096Sdlg #define CHROOT_DIR	"/var/empty"
5641f70b94Sderaadt #define NOPRIV_USER	"_tftp_proxy"
57697c8096Sdlg 
58697c8096Sdlg #define DEFTRANSWAIT	2
59697c8096Sdlg #define NTOP_BUFS	4
60697c8096Sdlg #define PKTSIZE		SEGSIZE+4
61697c8096Sdlg 
62697c8096Sdlg const char *opcode(int);
63697c8096Sdlg const char *sock_ntop(struct sockaddr *);
64697c8096Sdlg static void usage(void);
65697c8096Sdlg 
66697c8096Sdlg struct proxy_listener {
67697c8096Sdlg 	struct event ev;
68697c8096Sdlg 	TAILQ_ENTRY(proxy_listener) entry;
69697c8096Sdlg 	int (*cmsg2dst)(struct cmsghdr *, struct sockaddr_storage *);
70697c8096Sdlg 	int s;
71697c8096Sdlg };
72697c8096Sdlg 
73697c8096Sdlg void	proxy_listen(const char *, const char *, int);
74697c8096Sdlg void	proxy_listener_events(void);
75697c8096Sdlg int	proxy_dst4(struct cmsghdr *, struct sockaddr_storage *);
76697c8096Sdlg int	proxy_dst6(struct cmsghdr *, struct sockaddr_storage *);
77697c8096Sdlg void	proxy_recv(int, short, void *);
78697c8096Sdlg 
79697c8096Sdlg struct fd_reply {
80697c8096Sdlg 	TAILQ_ENTRY(fd_reply) entry;
81697c8096Sdlg 	int fd;
82697c8096Sdlg };
83697c8096Sdlg 
84697c8096Sdlg struct privproc {
85697c8096Sdlg 	struct event pop_ev;
86697c8096Sdlg 	struct event push_ev;
87697c8096Sdlg 	TAILQ_HEAD(, fd_reply) replies;
88697c8096Sdlg 	struct evbuffer *buf;
89697c8096Sdlg };
90697c8096Sdlg 
91697c8096Sdlg void	proxy_privproc(int, struct passwd *);
92697c8096Sdlg void	privproc_push(int, short, void *);
93697c8096Sdlg void	privproc_pop(int, short, void *);
94697c8096Sdlg 
95697c8096Sdlg void	unprivproc_push(int, short, void *);
96697c8096Sdlg void	unprivproc_pop(int, short, void *);
97697c8096Sdlg void	unprivproc_timeout(int, short, void *);
98697c8096Sdlg 
99697c8096Sdlg char	ntop_buf[NTOP_BUFS][INET6_ADDRSTRLEN];
100697c8096Sdlg 
101697c8096Sdlg struct loggers {
102cd68c002Sflorian 	__dead void (*err)(int, const char *, ...)
103cd68c002Sflorian 	    __attribute__((__format__ (printf, 2, 3)));
104cd68c002Sflorian 	__dead void (*errx)(int, const char *, ...)
105cd68c002Sflorian 	    __attribute__((__format__ (printf, 2, 3)));
106cd68c002Sflorian 	void (*warn)(const char *, ...)
107cd68c002Sflorian 	    __attribute__((__format__ (printf, 1, 2)));
108cd68c002Sflorian 	void (*warnx)(const char *, ...)
109cd68c002Sflorian 	    __attribute__((__format__ (printf, 1, 2)));
110cd68c002Sflorian 	void (*info)(const char *, ...)
111cd68c002Sflorian 	    __attribute__((__format__ (printf, 1, 2)));
112cd68c002Sflorian 	void (*debug)(const char *, ...)
113cd68c002Sflorian 	    __attribute__((__format__ (printf, 1, 2)));
114697c8096Sdlg };
115697c8096Sdlg 
116697c8096Sdlg const struct loggers conslogger = {
117697c8096Sdlg 	err,
118697c8096Sdlg 	errx,
119697c8096Sdlg 	warn,
120697c8096Sdlg 	warnx,
121cd68c002Sflorian 	warnx, /* info */
122cd68c002Sflorian 	warnx /* debug */
123697c8096Sdlg };
124697c8096Sdlg 
125cd68c002Sflorian __dead void	syslog_err(int, const char *, ...)
126cd68c002Sflorian 		    __attribute__((__format__ (printf, 2, 3)));
127cd68c002Sflorian __dead void	syslog_errx(int, const char *, ...)
128cd68c002Sflorian 		    __attribute__((__format__ (printf, 2, 3)));
129cd68c002Sflorian void		syslog_warn(const char *, ...)
130cd68c002Sflorian 		    __attribute__((__format__ (printf, 1, 2)));
131cd68c002Sflorian void		syslog_warnx(const char *, ...)
132cd68c002Sflorian 		    __attribute__((__format__ (printf, 1, 2)));
133cd68c002Sflorian void		syslog_info(const char *, ...)
134cd68c002Sflorian 		    __attribute__((__format__ (printf, 1, 2)));
135cd68c002Sflorian void		syslog_debug(const char *, ...)
136cd68c002Sflorian 		    __attribute__((__format__ (printf, 1, 2)));
137cd68c002Sflorian void		syslog_vstrerror(int, int, const char *, va_list)
138cd68c002Sflorian 		    __attribute__((__format__ (printf, 3, 0)));
139697c8096Sdlg 
140697c8096Sdlg const struct loggers syslogger = {
141697c8096Sdlg 	syslog_err,
142697c8096Sdlg 	syslog_errx,
143697c8096Sdlg 	syslog_warn,
144697c8096Sdlg 	syslog_warnx,
145697c8096Sdlg 	syslog_info,
146cd68c002Sflorian 	syslog_debug
147697c8096Sdlg };
148697c8096Sdlg 
149697c8096Sdlg const struct loggers *logger = &conslogger;
150697c8096Sdlg 
151697c8096Sdlg #define lerr(_e, _f...) logger->err((_e), _f)
152697c8096Sdlg #define lerrx(_e, _f...) logger->errx((_e), _f)
153697c8096Sdlg #define lwarn(_f...) logger->warn(_f)
154697c8096Sdlg #define lwarnx(_f...) logger->warnx(_f)
155697c8096Sdlg #define linfo(_f...) logger->info(_f)
156cd68c002Sflorian #define ldebug(_f...) logger->debug(_f)
157697c8096Sdlg 
158697c8096Sdlg __dead void
usage(void)159697c8096Sdlg usage(void)
160697c8096Sdlg {
161697c8096Sdlg 	extern char *__progname;
16219875b15Sflorian 	fprintf(stderr, "usage: %s [-46dv] [-a address] [-l address] [-p port]"
1632c4cc476Sjmc 	    " [-w transwait]\n", __progname);
164697c8096Sdlg 	exit(1);
165697c8096Sdlg }
166697c8096Sdlg 
167697c8096Sdlg int	debug = 0;
168697c8096Sdlg int	verbose = 0;
169697c8096Sdlg struct timeval transwait = { DEFTRANSWAIT, 0 };
170697c8096Sdlg 
171697c8096Sdlg int on = 1;
172697c8096Sdlg 
173697c8096Sdlg struct addr_pair {
174697c8096Sdlg 	struct sockaddr_storage src;
175697c8096Sdlg 	struct sockaddr_storage dst;
176697c8096Sdlg };
177697c8096Sdlg 
178697c8096Sdlg struct proxy_request {
179697c8096Sdlg 	char buf[SEGSIZE_MAX + 4];
180697c8096Sdlg 	size_t buflen;
181697c8096Sdlg 
182697c8096Sdlg 	struct addr_pair addrs;
183697c8096Sdlg 
184697c8096Sdlg 	struct event ev;
185697c8096Sdlg 	TAILQ_ENTRY(proxy_request) entry;
186697c8096Sdlg 	u_int32_t id;
187697c8096Sdlg };
188697c8096Sdlg 
189697c8096Sdlg struct proxy_child {
190697c8096Sdlg 	TAILQ_HEAD(, proxy_request) fdrequests;
191697c8096Sdlg 	TAILQ_HEAD(, proxy_request) tmrequests;
192697c8096Sdlg 	struct event push_ev;
193697c8096Sdlg 	struct event pop_ev;
194697c8096Sdlg 	struct evbuffer *buf;
195697c8096Sdlg };
196697c8096Sdlg 
197697c8096Sdlg struct proxy_child *child = NULL;
198697c8096Sdlg TAILQ_HEAD(, proxy_listener) proxy_listeners;
199697c8096Sdlg 
20019875b15Sflorian struct src_addr {
20119875b15Sflorian 	TAILQ_ENTRY(src_addr)	entry;
20219875b15Sflorian 	struct sockaddr_storage	addr;
20319875b15Sflorian 	socklen_t		addrlen;
20419875b15Sflorian };
20519875b15Sflorian TAILQ_HEAD(, src_addr) src_addrs;
20619875b15Sflorian 
20719875b15Sflorian void	source_addresses(const char*, int);
20819875b15Sflorian 
209697c8096Sdlg int
main(int argc,char * argv[])210697c8096Sdlg main(int argc, char *argv[])
211697c8096Sdlg {
212697c8096Sdlg 	extern char *__progname;
213697c8096Sdlg 
214697c8096Sdlg 	int c;
215697c8096Sdlg 	const char *errstr;
216697c8096Sdlg 
217a4dc3638Sflorian 	struct src_addr *saddr, *saddr2;
218697c8096Sdlg 	struct passwd *pw;
219697c8096Sdlg 
220697c8096Sdlg 	char *addr = "localhost";
221697c8096Sdlg 	char *port = "6969";
222697c8096Sdlg 	int family = AF_UNSPEC;
223697c8096Sdlg 
224697c8096Sdlg 	int pair[2];
225697c8096Sdlg 
22619875b15Sflorian 	TAILQ_INIT(&src_addrs);
22719875b15Sflorian 
22819875b15Sflorian 	while ((c = getopt(argc, argv, "46a:dvl:p:w:")) != -1) {
229697c8096Sdlg 		switch (c) {
230697c8096Sdlg 		case '4':
231697c8096Sdlg 			family = AF_INET;
232697c8096Sdlg 			break;
233697c8096Sdlg 		case '6':
234697c8096Sdlg 			family = AF_INET6;
235697c8096Sdlg 			break;
23619875b15Sflorian 		case 'a':
237a4dc3638Sflorian 			source_addresses(optarg, family);
23819875b15Sflorian 			break;
239697c8096Sdlg 		case 'd':
240697c8096Sdlg 			verbose = debug = 1;
241697c8096Sdlg 			break;
242697c8096Sdlg 		case 'l':
243697c8096Sdlg 			addr = optarg;
244697c8096Sdlg 			break;
245697c8096Sdlg 		case 'p':
246697c8096Sdlg 			port = optarg;
247697c8096Sdlg 			break;
248697c8096Sdlg 		case 'v':
249697c8096Sdlg 			verbose = 1;
250697c8096Sdlg 			break;
251697c8096Sdlg 		case 'w':
252697c8096Sdlg 			transwait.tv_sec = strtonum(optarg, 1, 30, &errstr);
253697c8096Sdlg 			if (errstr)
254697c8096Sdlg 				errx(1, "wait is %s", errstr);
255697c8096Sdlg 			break;
256697c8096Sdlg 		default:
257697c8096Sdlg 			usage();
258697c8096Sdlg 			/* NOTREACHED */
259697c8096Sdlg 		}
260697c8096Sdlg 	}
261697c8096Sdlg 
262697c8096Sdlg 	if (geteuid() != 0)
2635d08eca0Sjca 		lerrx(1, "need root privileges");
264697c8096Sdlg 
265e18a0880Sguenther 	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, PF_UNSPEC, pair)
266e18a0880Sguenther 	    == -1)
267697c8096Sdlg 		lerr(1, "socketpair");
268697c8096Sdlg 
269697c8096Sdlg 	pw = getpwnam(NOPRIV_USER);
270697c8096Sdlg 	if (pw == NULL)
271697c8096Sdlg 		lerrx(1, "no %s user", NOPRIV_USER);
272697c8096Sdlg 
273a4dc3638Sflorian 	/* Family option may have been specified late. */
274a4dc3638Sflorian 	if (family != AF_UNSPEC)
275a4dc3638Sflorian 		TAILQ_FOREACH_SAFE(saddr, &src_addrs, entry, saddr2)
276a4dc3638Sflorian 			if (saddr->addr.ss_family != family) {
277a4dc3638Sflorian 				TAILQ_REMOVE(&src_addrs, saddr, entry);
278a4dc3638Sflorian 				free(saddr);
279a4dc3638Sflorian 			}
28019875b15Sflorian 
2815d08eca0Sjca 	if (!debug) {
2825d08eca0Sjca 		if (daemon(1, 0) == -1)
2835d08eca0Sjca 			lerr(1, "daemon");
2845d08eca0Sjca 
2855d08eca0Sjca 		openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON);
2865d08eca0Sjca 		tzset();
2875d08eca0Sjca 		logger = &syslogger;
2885d08eca0Sjca 	}
2895d08eca0Sjca 
290697c8096Sdlg 	switch (fork()) {
291697c8096Sdlg 	case -1:
292697c8096Sdlg 		lerr(1, "fork");
293697c8096Sdlg 
294697c8096Sdlg 	case 0:
295697c8096Sdlg 		setproctitle("privproc");
296697c8096Sdlg 		close(pair[1]);
297697c8096Sdlg 		proxy_privproc(pair[0], pw);
298697c8096Sdlg 		/* this never returns */
299697c8096Sdlg 
300697c8096Sdlg 	default:
301697c8096Sdlg 		setproctitle("unprivproc");
302697c8096Sdlg 		close(pair[0]);
303697c8096Sdlg 		break;
304697c8096Sdlg 	}
305697c8096Sdlg 
306697c8096Sdlg 	child = calloc(1, sizeof(*child));
307697c8096Sdlg 	if (child == NULL)
308697c8096Sdlg 		lerr(1, "alloc(child)");
309697c8096Sdlg 
310697c8096Sdlg 	child->buf = evbuffer_new();
311697c8096Sdlg 	if (child->buf == NULL)
312697c8096Sdlg 		lerr(1, "child evbuffer");
313697c8096Sdlg 
314697c8096Sdlg 	TAILQ_INIT(&child->fdrequests);
315697c8096Sdlg 	TAILQ_INIT(&child->tmrequests);
316697c8096Sdlg 
317697c8096Sdlg 	proxy_listen(addr, port, family);
318697c8096Sdlg 
319697c8096Sdlg 	/* open /dev/pf */
320697c8096Sdlg 	init_filter(NULL, verbose);
321697c8096Sdlg 
322697c8096Sdlg 	/* revoke privs */
323697c8096Sdlg 	if (chroot(CHROOT_DIR) == -1)
324697c8096Sdlg 		lerr(1, "chroot %s", CHROOT_DIR);
325697c8096Sdlg 
326697c8096Sdlg 	if (chdir("/") == -1)
327697c8096Sdlg 		lerr(1, "chdir %s", CHROOT_DIR);
328697c8096Sdlg 
329697c8096Sdlg 	if (setgroups(1, &pw->pw_gid) ||
330697c8096Sdlg 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
331697c8096Sdlg 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
332697c8096Sdlg 		err(1, "unable to revoke privs");
333697c8096Sdlg 
334697c8096Sdlg 	event_init();
335697c8096Sdlg 
336697c8096Sdlg 	proxy_listener_events();
337697c8096Sdlg 
338697c8096Sdlg 	event_set(&child->pop_ev, pair[1], EV_READ | EV_PERSIST,
339697c8096Sdlg 	    unprivproc_pop, NULL);
340697c8096Sdlg 	event_set(&child->push_ev, pair[1], EV_WRITE,
341697c8096Sdlg 	    unprivproc_push, NULL);
342697c8096Sdlg 
343697c8096Sdlg 	event_add(&child->pop_ev, NULL);
344697c8096Sdlg 
345697c8096Sdlg 	event_dispatch();
346697c8096Sdlg 
347697c8096Sdlg 	return(0);
348697c8096Sdlg }
349697c8096Sdlg 
35019875b15Sflorian void
source_addresses(const char * name,int family)35119875b15Sflorian source_addresses(const char* name, int family)
35219875b15Sflorian {
35319875b15Sflorian 	struct addrinfo hints, *res, *res0;
35419875b15Sflorian 	struct src_addr *saddr;
35519875b15Sflorian 	int error;
35619875b15Sflorian 
35719875b15Sflorian 	memset(&hints, 0, sizeof(hints));
35819875b15Sflorian 	hints.ai_family = family;
35919875b15Sflorian 	hints.ai_socktype = SOCK_DGRAM;
36019875b15Sflorian 	hints.ai_flags = AI_PASSIVE;
36119875b15Sflorian 	error = getaddrinfo(name, NULL, &hints, &res0);
36219875b15Sflorian 	if (error)
36319875b15Sflorian 		lerrx(1, "%s: %s", name, gai_strerror(error));
36419875b15Sflorian 	for (res = res0; res != NULL; res = res->ai_next) {
36519875b15Sflorian 		if ((saddr = calloc(1, sizeof(struct src_addr))) == NULL)
36619875b15Sflorian 			lerrx(1, "calloc");
36719875b15Sflorian 		memcpy(&(saddr->addr), res->ai_addr, res->ai_addrlen);
36819875b15Sflorian 		saddr->addrlen = res->ai_addrlen;
36919875b15Sflorian 		TAILQ_INSERT_TAIL(&src_addrs, saddr, entry);
37019875b15Sflorian 	}
37119875b15Sflorian 	freeaddrinfo(res0);
37219875b15Sflorian }
373697c8096Sdlg 
374697c8096Sdlg void
proxy_privproc(int s,struct passwd * pw)375697c8096Sdlg proxy_privproc(int s, struct passwd *pw)
376697c8096Sdlg {
377697c8096Sdlg 	struct privproc p;
378697c8096Sdlg 
379697c8096Sdlg 	if (chroot(CHROOT_DIR) == -1)
380697c8096Sdlg 		lerr(1, "chroot to %s", CHROOT_DIR);
381697c8096Sdlg 
382697c8096Sdlg 	if (chdir("/") == -1)
383697c8096Sdlg 		lerr(1, "chdir to %s", CHROOT_DIR);
384697c8096Sdlg 
385697c8096Sdlg 	if (setgroups(1, &pw->pw_gid) ||
386697c8096Sdlg 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid))
387697c8096Sdlg 		lerr(1, "unable to set group ids");
388697c8096Sdlg 
3891179e37dSderaadt 	if (pledge("stdio inet sendfd", NULL) == -1)
3901179e37dSderaadt 		err(1, "pledge");
3911179e37dSderaadt 
392697c8096Sdlg 	TAILQ_INIT(&p.replies);
393697c8096Sdlg 
394697c8096Sdlg 	p.buf = evbuffer_new();
395697c8096Sdlg 	if (p.buf == NULL)
396697c8096Sdlg 		err(1, "pop evbuffer_new");
397697c8096Sdlg 
398697c8096Sdlg 	event_init();
399697c8096Sdlg 
400697c8096Sdlg 	event_set(&p.pop_ev, s, EV_READ | EV_PERSIST, privproc_pop, &p);
401697c8096Sdlg 	event_set(&p.push_ev, s, EV_WRITE, privproc_push, &p);
402697c8096Sdlg 
403697c8096Sdlg 	event_add(&p.pop_ev, NULL);
404697c8096Sdlg 
405697c8096Sdlg 	event_dispatch();
406697c8096Sdlg }
407697c8096Sdlg 
408697c8096Sdlg void
privproc_pop(int fd,short events,void * arg)409697c8096Sdlg privproc_pop(int fd, short events, void *arg)
410697c8096Sdlg {
411697c8096Sdlg 	struct addr_pair req;
412697c8096Sdlg 	struct privproc *p = arg;
413697c8096Sdlg 	struct fd_reply *rep;
41419875b15Sflorian 	struct src_addr *saddr;
415697c8096Sdlg 	int add = 0;
416697c8096Sdlg 
417697c8096Sdlg 	switch (evbuffer_read(p->buf, fd, sizeof(req))) {
418697c8096Sdlg 	case 0:
419697c8096Sdlg 		lerrx(1, "unprivproc has gone");
420697c8096Sdlg 	case -1:
421697c8096Sdlg 		switch (errno) {
422697c8096Sdlg 		case EAGAIN:
423697c8096Sdlg 		case EINTR:
424697c8096Sdlg 			return;
425697c8096Sdlg 		default:
426697c8096Sdlg 			lerr(1, "privproc_pop read");
427697c8096Sdlg 		}
428697c8096Sdlg 	default:
429697c8096Sdlg 		break;
430697c8096Sdlg 	}
431697c8096Sdlg 
432697c8096Sdlg 	while (EVBUFFER_LENGTH(p->buf) >= sizeof(req)) {
433697c8096Sdlg 		evbuffer_remove(p->buf, &req, sizeof(req));
434697c8096Sdlg 
435697c8096Sdlg 		/* do i really need to check this? */
436697c8096Sdlg 		if (req.src.ss_family != req.dst.ss_family)
437697c8096Sdlg 			lerrx(1, "family mismatch");
438697c8096Sdlg 
439697c8096Sdlg 		rep = calloc(1, sizeof(*rep));
440697c8096Sdlg 		if (rep == NULL)
441697c8096Sdlg 			lerr(1, "reply calloc");
442697c8096Sdlg 
443e18a0880Sguenther 		rep->fd = socket(req.src.ss_family, SOCK_DGRAM | SOCK_NONBLOCK,
444e18a0880Sguenther 		    IPPROTO_UDP);
445697c8096Sdlg 		if (rep->fd == -1)
446697c8096Sdlg 			lerr(1, "privproc socket");
447697c8096Sdlg 
448697c8096Sdlg 		if (setsockopt(rep->fd, SOL_SOCKET, SO_BINDANY,
449697c8096Sdlg 		    &on, sizeof(on)) == -1)
450697c8096Sdlg 			lerr(1, "privproc setsockopt(BINDANY)");
451697c8096Sdlg 
452697c8096Sdlg 		if (setsockopt(rep->fd, SOL_SOCKET, SO_REUSEADDR,
453697c8096Sdlg 		    &on, sizeof(on)) == -1)
454697c8096Sdlg 			lerr(1, "privproc setsockopt(REUSEADDR)");
455697c8096Sdlg 
456697c8096Sdlg 		if (setsockopt(rep->fd, SOL_SOCKET, SO_REUSEPORT,
457697c8096Sdlg 		    &on, sizeof(on)) == -1)
458697c8096Sdlg 			lerr(1, "privproc setsockopt(REUSEPORT)");
459697c8096Sdlg 
460b27441feSflorian 		TAILQ_FOREACH(saddr, &src_addrs, entry)
461b27441feSflorian 			if (saddr->addr.ss_family == req.src.ss_family)
462b27441feSflorian 				break;
463b27441feSflorian 		if (saddr == NULL) {
464697c8096Sdlg 			if (bind(rep->fd, (struct sockaddr *)&req.src,
465697c8096Sdlg 			    req.src.ss_len) == -1)
466697c8096Sdlg 				lerr(1, "privproc bind");
46719875b15Sflorian 		} else {
468b27441feSflorian 			if (bind(rep->fd, (struct sockaddr*)&saddr->addr,
469b27441feSflorian 			    saddr->addrlen) == -1)
47019875b15Sflorian 				lerr(1, "privproc bind");
47119875b15Sflorian 		}
472697c8096Sdlg 
473697c8096Sdlg 		if (TAILQ_EMPTY(&p->replies))
474697c8096Sdlg 			add = 1;
475697c8096Sdlg 
476697c8096Sdlg 		TAILQ_INSERT_TAIL(&p->replies, rep, entry);
477697c8096Sdlg 	}
478697c8096Sdlg 
479697c8096Sdlg 	if (add)
480697c8096Sdlg 		event_add(&p->push_ev, NULL);
481697c8096Sdlg }
482697c8096Sdlg 
483697c8096Sdlg void
privproc_push(int fd,short events,void * arg)484697c8096Sdlg privproc_push(int fd, short events, void *arg)
485697c8096Sdlg {
486697c8096Sdlg 	struct privproc *p = arg;
487697c8096Sdlg 	struct fd_reply *rep;
488697c8096Sdlg 
489697c8096Sdlg 	struct msghdr msg;
490697c8096Sdlg 	union {
491697c8096Sdlg 		struct cmsghdr hdr;
492697c8096Sdlg 		char buf[CMSG_SPACE(sizeof(int))];
493697c8096Sdlg 	} cmsgbuf;
494697c8096Sdlg 	struct cmsghdr *cmsg;
495697c8096Sdlg 	struct iovec iov;
496697c8096Sdlg 	int result = 0;
497697c8096Sdlg 
498697c8096Sdlg 	while ((rep = TAILQ_FIRST(&p->replies)) != NULL) {
499697c8096Sdlg 		memset(&msg, 0, sizeof(msg));
500697c8096Sdlg 
501697c8096Sdlg 		msg.msg_control = (caddr_t)&cmsgbuf.buf;
502697c8096Sdlg 		msg.msg_controllen = sizeof(cmsgbuf.buf);
503697c8096Sdlg 		cmsg = CMSG_FIRSTHDR(&msg);
504697c8096Sdlg 		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
505697c8096Sdlg 		cmsg->cmsg_level = SOL_SOCKET;
506697c8096Sdlg 		cmsg->cmsg_type = SCM_RIGHTS;
507697c8096Sdlg 		*(int *)CMSG_DATA(cmsg) = rep->fd;
508697c8096Sdlg 
509697c8096Sdlg 		iov.iov_base = &result;
510697c8096Sdlg 		iov.iov_len = sizeof(int);
511697c8096Sdlg 		msg.msg_iov = &iov;
512697c8096Sdlg 		msg.msg_iovlen = 1;
513697c8096Sdlg 
514697c8096Sdlg 		switch (sendmsg(fd, &msg, 0)) {
515697c8096Sdlg 		case sizeof(int):
516697c8096Sdlg 			break;
517697c8096Sdlg 
518697c8096Sdlg 		case -1:
519697c8096Sdlg 			if (errno == EAGAIN)
520697c8096Sdlg 				goto again;
521697c8096Sdlg 
522697c8096Sdlg 			lerr(1, "privproc sendmsg");
523697c8096Sdlg 			/* NOTREACHED */
524697c8096Sdlg 
525697c8096Sdlg 		default:
526697c8096Sdlg 			lerrx(1, "privproc sendmsg weird len");
527697c8096Sdlg 		}
528697c8096Sdlg 
529697c8096Sdlg 		TAILQ_REMOVE(&p->replies, rep, entry);
530697c8096Sdlg 		close(rep->fd);
531697c8096Sdlg 		free(rep);
532697c8096Sdlg 	}
533697c8096Sdlg 
534697c8096Sdlg 	if (TAILQ_EMPTY(&p->replies))
535697c8096Sdlg 		return;
536697c8096Sdlg 
537697c8096Sdlg again:
538697c8096Sdlg 	event_add(&p->push_ev, NULL);
539697c8096Sdlg }
540697c8096Sdlg 
541697c8096Sdlg void
proxy_listen(const char * addr,const char * port,int family)542697c8096Sdlg proxy_listen(const char *addr, const char *port, int family)
543697c8096Sdlg {
544697c8096Sdlg 	struct proxy_listener *l;
545697c8096Sdlg 
546697c8096Sdlg 	struct addrinfo hints, *res, *res0;
547697c8096Sdlg 	int error;
5488f072f6eSderaadt 	int s, on = 1;
549697c8096Sdlg 	int serrno;
550697c8096Sdlg 	const char *cause = NULL;
551697c8096Sdlg 
552697c8096Sdlg 	memset(&hints, 0, sizeof(hints));
553697c8096Sdlg 	hints.ai_family = family;
554697c8096Sdlg 	hints.ai_socktype = SOCK_DGRAM;
555697c8096Sdlg 	hints.ai_flags = AI_PASSIVE;
556697c8096Sdlg 
557697c8096Sdlg 	TAILQ_INIT(&proxy_listeners);
558697c8096Sdlg 
559697c8096Sdlg 	error = getaddrinfo(addr, port, &hints, &res0);
560697c8096Sdlg 	if (error)
561697c8096Sdlg 		errx(1, "%s:%s: %s", addr, port, gai_strerror(error));
562697c8096Sdlg 
563697c8096Sdlg 	for (res = res0; res != NULL; res = res->ai_next) {
564e18a0880Sguenther 		s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK,
565e18a0880Sguenther 		    res->ai_protocol);
566697c8096Sdlg 		if (s == -1) {
567697c8096Sdlg 			cause = "socket";
568697c8096Sdlg 			continue;
569697c8096Sdlg 		}
570697c8096Sdlg 
571697c8096Sdlg 		if (bind(s, res->ai_addr, res->ai_addrlen) == -1) {
572697c8096Sdlg 			cause = "bind";
573697c8096Sdlg 			serrno = errno;
574697c8096Sdlg 			close(s);
575697c8096Sdlg 			errno = serrno;
576697c8096Sdlg 			continue;
577697c8096Sdlg 		}
578697c8096Sdlg 
579697c8096Sdlg 		l = calloc(1, sizeof(*l));
580697c8096Sdlg 		if (l == NULL)
581697c8096Sdlg 			err(1, "listener alloc");
582697c8096Sdlg 
583697c8096Sdlg 		switch (res->ai_family) {
584697c8096Sdlg 		case AF_INET:
585697c8096Sdlg 			l->cmsg2dst = proxy_dst4;
586697c8096Sdlg 
587697c8096Sdlg 			if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR,
588697c8096Sdlg 			    &on, sizeof(on)) == -1)
589697c8096Sdlg 				errx(1, "setsockopt(IP_RECVDSTADDR)");
590697c8096Sdlg 			if (setsockopt(s, IPPROTO_IP, IP_RECVDSTPORT,
591697c8096Sdlg 			    &on, sizeof(on)) == -1)
592697c8096Sdlg 				errx(1, "setsockopt(IP_RECVDSTPORT)");
593697c8096Sdlg 			break;
594697c8096Sdlg 		case AF_INET6:
595697c8096Sdlg 			l->cmsg2dst = proxy_dst6;
596697c8096Sdlg 
597697c8096Sdlg 			if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
598697c8096Sdlg 			    &on, sizeof(on)) == -1)
599697c8096Sdlg 				errx(1, "setsockopt(IPV6_RECVPKTINFO)");
600bc7a20e3Sbenno 			if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVDSTPORT,
601bc7a20e3Sbenno 			    &on, sizeof(on)) == -1)
602bc7a20e3Sbenno 				errx(1, "setsockopt(IPV6_RECVDSTPORT)");
603697c8096Sdlg 			break;
604697c8096Sdlg 		}
605697c8096Sdlg 		l->s = s;
606697c8096Sdlg 
607697c8096Sdlg 		TAILQ_INSERT_TAIL(&proxy_listeners, l, entry);
608697c8096Sdlg 	}
60980b8c347Sflorian 	freeaddrinfo(res0);
610697c8096Sdlg 
611697c8096Sdlg 	if (TAILQ_EMPTY(&proxy_listeners))
612697c8096Sdlg 		err(1, "%s", cause);
613697c8096Sdlg }
614697c8096Sdlg 
615697c8096Sdlg void
proxy_listener_events(void)616697c8096Sdlg proxy_listener_events(void)
617697c8096Sdlg {
618697c8096Sdlg 	struct proxy_listener *l;
619697c8096Sdlg 
620697c8096Sdlg 	TAILQ_FOREACH(l, &proxy_listeners, entry) {
621697c8096Sdlg 		event_set(&l->ev, l->s, EV_READ | EV_PERSIST, proxy_recv, l);
622697c8096Sdlg 		event_add(&l->ev, NULL);
623697c8096Sdlg 	}
624697c8096Sdlg }
625697c8096Sdlg 
626697c8096Sdlg char safety[SEGSIZE_MAX + 4];
627697c8096Sdlg 
628697c8096Sdlg int
proxy_dst4(struct cmsghdr * cmsg,struct sockaddr_storage * ss)629697c8096Sdlg proxy_dst4(struct cmsghdr *cmsg, struct sockaddr_storage *ss)
630697c8096Sdlg {
631697c8096Sdlg 	struct sockaddr_in *sin = (struct sockaddr_in *)ss;
632697c8096Sdlg 
633697c8096Sdlg 	if (cmsg->cmsg_level != IPPROTO_IP)
634697c8096Sdlg 		return (0);
635697c8096Sdlg 
636697c8096Sdlg 	switch (cmsg->cmsg_type) {
637697c8096Sdlg 	case IP_RECVDSTADDR:
638697c8096Sdlg 		memcpy(&sin->sin_addr, CMSG_DATA(cmsg), sizeof(sin->sin_addr));
639697c8096Sdlg 		if (sin->sin_addr.s_addr == INADDR_BROADCAST)
640697c8096Sdlg 			return (-1);
641697c8096Sdlg 		break;
642697c8096Sdlg 
643697c8096Sdlg 	case IP_RECVDSTPORT:
644697c8096Sdlg 		memcpy(&sin->sin_port, CMSG_DATA(cmsg), sizeof(sin->sin_port));
645697c8096Sdlg 		break;
646697c8096Sdlg 	}
647697c8096Sdlg 
648697c8096Sdlg 	return (0);
649697c8096Sdlg }
650697c8096Sdlg 
651697c8096Sdlg int
proxy_dst6(struct cmsghdr * cmsg,struct sockaddr_storage * ss)652697c8096Sdlg proxy_dst6(struct cmsghdr *cmsg, struct sockaddr_storage *ss)
653697c8096Sdlg {
654697c8096Sdlg 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
655697c8096Sdlg 	struct in6_pktinfo *ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg);
656697c8096Sdlg 
657697c8096Sdlg 	if (cmsg->cmsg_level != IPPROTO_IPV6)
658697c8096Sdlg 		return (0);
659697c8096Sdlg 
660697c8096Sdlg 	switch (cmsg->cmsg_type) {
661697c8096Sdlg 	case IPV6_PKTINFO:
662697c8096Sdlg 		memcpy(&sin6->sin6_addr, &ipi->ipi6_addr,
663697c8096Sdlg 		    sizeof(sin6->sin6_addr));
664697c8096Sdlg 		if (IN6_IS_ADDR_LINKLOCAL(&ipi->ipi6_addr))
665697c8096Sdlg 		    sin6->sin6_scope_id = ipi->ipi6_ifindex;
666697c8096Sdlg 		break;
667bc7a20e3Sbenno 	case IPV6_RECVDSTPORT:
668bc7a20e3Sbenno 		memcpy(&sin6->sin6_port, CMSG_DATA(cmsg),
669bc7a20e3Sbenno 		    sizeof(sin6->sin6_port));
670bc7a20e3Sbenno 		break;
671697c8096Sdlg 	}
672697c8096Sdlg 
673697c8096Sdlg 	return (0);
674697c8096Sdlg }
675697c8096Sdlg 
676697c8096Sdlg void
proxy_recv(int fd,short events,void * arg)677697c8096Sdlg proxy_recv(int fd, short events, void *arg)
678697c8096Sdlg {
679697c8096Sdlg 	struct proxy_listener *l = arg;
680697c8096Sdlg 
681697c8096Sdlg 	union {
682697c8096Sdlg 		struct cmsghdr hdr;
683697c8096Sdlg 		char buf[CMSG_SPACE(sizeof(struct sockaddr_storage)) +
684697c8096Sdlg 		    CMSG_SPACE(sizeof(in_port_t))];
685697c8096Sdlg 	} cmsgbuf;
686697c8096Sdlg 	struct cmsghdr *cmsg;
687697c8096Sdlg 	struct msghdr msg;
688697c8096Sdlg 	struct iovec iov;
689697c8096Sdlg 	ssize_t n;
690697c8096Sdlg 
691697c8096Sdlg 	struct proxy_request *r;
692697c8096Sdlg 	struct tftphdr *tp;
693697c8096Sdlg 
694697c8096Sdlg 	r = calloc(1, sizeof(*r));
695697c8096Sdlg 	if (r == NULL) {
696697c8096Sdlg 		recv(fd, safety, sizeof(safety), 0);
697697c8096Sdlg 		return;
698697c8096Sdlg 	}
699697c8096Sdlg 	r->id = arc4random(); /* XXX unique? */
700697c8096Sdlg 
701697c8096Sdlg 	bzero(&msg, sizeof(msg));
702697c8096Sdlg 	iov.iov_base = r->buf;
703697c8096Sdlg 	iov.iov_len = sizeof(r->buf);
704697c8096Sdlg 	msg.msg_name = &r->addrs.src;
705697c8096Sdlg 	msg.msg_namelen = sizeof(r->addrs.src);
706697c8096Sdlg 	msg.msg_iov = &iov;
707697c8096Sdlg 	msg.msg_iovlen = 1;
708697c8096Sdlg 	msg.msg_control = &cmsgbuf.buf;
709697c8096Sdlg 	msg.msg_controllen = sizeof(cmsgbuf.buf);
710697c8096Sdlg 
711697c8096Sdlg 	n = recvmsg(fd, &msg, 0);
712697c8096Sdlg 	if (n == -1) {
713697c8096Sdlg 		switch (errno) {
714697c8096Sdlg 		case EAGAIN:
715697c8096Sdlg 		case EINTR:
716697c8096Sdlg 			goto err;
717697c8096Sdlg 		default:
718697c8096Sdlg 			lerr(1, "recvmsg");
719697c8096Sdlg 			/* NOTREACHED */
720697c8096Sdlg 		}
721697c8096Sdlg 	}
722697c8096Sdlg 	r->buflen = n;
723697c8096Sdlg 
724697c8096Sdlg 	/* check the packet */
725697c8096Sdlg 	if (n < 5) {
726697c8096Sdlg 		/* not enough to be a real packet */
727697c8096Sdlg 		goto err;
728697c8096Sdlg 	}
729697c8096Sdlg 	tp = (struct tftphdr *)r->buf;
730697c8096Sdlg 	switch (ntohs(tp->th_opcode)) {
731697c8096Sdlg 	case RRQ:
732697c8096Sdlg 	case WRQ:
733697c8096Sdlg 		break;
734697c8096Sdlg 	default:
735697c8096Sdlg 		goto err;
736697c8096Sdlg 	}
737697c8096Sdlg 
738697c8096Sdlg 	r->addrs.dst.ss_family = r->addrs.src.ss_family;
739697c8096Sdlg 	r->addrs.dst.ss_len = r->addrs.src.ss_len;
740697c8096Sdlg 
741697c8096Sdlg 	/* get local address if possible */
742697c8096Sdlg 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
743697c8096Sdlg 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
744697c8096Sdlg 		if (l->cmsg2dst(cmsg, &r->addrs.dst) == -1)
745697c8096Sdlg 			goto err;
746697c8096Sdlg 	}
747697c8096Sdlg 
748697c8096Sdlg 	if (verbose) {
749697c8096Sdlg 		linfo("%s:%d -> %s:%d \"%s %s\"",
750697c8096Sdlg 		    sock_ntop((struct sockaddr *)&r->addrs.src),
751697c8096Sdlg 		    ntohs(((struct sockaddr_in *)&r->addrs.src)->sin_port),
752697c8096Sdlg 		    sock_ntop((struct sockaddr *)&r->addrs.dst),
753697c8096Sdlg 		    ntohs(((struct sockaddr_in *)&r->addrs.dst)->sin_port),
754697c8096Sdlg 		    opcode(ntohs(tp->th_opcode)), tp->th_stuff);
755697c8096Sdlg 		/* XXX tp->th_stuff could be garbage */
756697c8096Sdlg 	}
757697c8096Sdlg 
758697c8096Sdlg 	TAILQ_INSERT_TAIL(&child->fdrequests, r, entry);
759697c8096Sdlg 	evbuffer_add(child->buf, &r->addrs, sizeof(r->addrs));
760697c8096Sdlg 	event_add(&child->push_ev, NULL);
761697c8096Sdlg 
762697c8096Sdlg 	return;
763697c8096Sdlg 
764697c8096Sdlg err:
765697c8096Sdlg 	free(r);
766697c8096Sdlg }
767697c8096Sdlg 
768697c8096Sdlg void
unprivproc_push(int fd,short events,void * arg)769697c8096Sdlg unprivproc_push(int fd, short events, void *arg)
770697c8096Sdlg {
771697c8096Sdlg 	if (evbuffer_write(child->buf, fd) == -1)
772697c8096Sdlg 		lerr(1, "child evbuffer_write");
773697c8096Sdlg 
774697c8096Sdlg 	if (EVBUFFER_LENGTH(child->buf))
775697c8096Sdlg 		event_add(&child->push_ev, NULL);
776697c8096Sdlg }
777697c8096Sdlg 
778697c8096Sdlg void
unprivproc_pop(int fd,short events,void * arg)779697c8096Sdlg unprivproc_pop(int fd, short events, void *arg)
780697c8096Sdlg {
781697c8096Sdlg 	struct proxy_request *r;
782697c8096Sdlg 
783697c8096Sdlg 	struct msghdr msg;
784697c8096Sdlg 	union {
785697c8096Sdlg 		struct cmsghdr hdr;
786697c8096Sdlg 		char buf[CMSG_SPACE(sizeof(int))];
787697c8096Sdlg 	} cmsgbuf;
788697c8096Sdlg 	struct cmsghdr *cmsg;
789697c8096Sdlg 	struct iovec iov;
790b27441feSflorian 	struct src_addr *src_addr;
79119875b15Sflorian 	struct sockaddr_storage saddr;
79219875b15Sflorian 	socklen_t len;
793697c8096Sdlg 	int result;
794697c8096Sdlg 	int s;
795697c8096Sdlg 
79619875b15Sflorian 	len = sizeof(saddr);
79719875b15Sflorian 
798697c8096Sdlg 	do {
799697c8096Sdlg 		memset(&msg, 0, sizeof(msg));
800697c8096Sdlg 		iov.iov_base = &result;
801697c8096Sdlg 		iov.iov_len = sizeof(int);
802697c8096Sdlg 		msg.msg_iov = &iov;
803697c8096Sdlg 		msg.msg_iovlen = 1;
804697c8096Sdlg 		msg.msg_control = &cmsgbuf.buf;
805697c8096Sdlg 		msg.msg_controllen = sizeof(cmsgbuf.buf);
806697c8096Sdlg 
807697c8096Sdlg 		switch (recvmsg(fd, &msg, 0)) {
808697c8096Sdlg 		case sizeof(int):
809697c8096Sdlg 			break;
810697c8096Sdlg 
811697c8096Sdlg 		case -1:
812697c8096Sdlg 			switch (errno) {
813697c8096Sdlg 			case EAGAIN:
814697c8096Sdlg 			case EINTR:
815697c8096Sdlg 				return;
816697c8096Sdlg 			default:
817697c8096Sdlg 				lerr(1, "child recvmsg");
818697c8096Sdlg 			}
819697c8096Sdlg 			/* NOTREACHED */
820697c8096Sdlg 
821697c8096Sdlg 		case 0:
822697c8096Sdlg 			lerrx(1, "privproc closed connection");
823697c8096Sdlg 
824697c8096Sdlg 		default:
825697c8096Sdlg 			lerrx(1, "child recvmsg was weird");
826697c8096Sdlg 			/* NOTREACHED */
827697c8096Sdlg 		}
828697c8096Sdlg 
829697c8096Sdlg 		if (result != 0) {
830697c8096Sdlg 			errno = result;
831697c8096Sdlg 			lerr(1, "child fdpass fail");
832697c8096Sdlg 		}
833697c8096Sdlg 
834697c8096Sdlg 		cmsg = CMSG_FIRSTHDR(&msg);
835697c8096Sdlg 		if (cmsg == NULL)
836697c8096Sdlg 			lerrx(1, "%s: no message header", __func__);
837697c8096Sdlg 
838697c8096Sdlg 		if (cmsg->cmsg_type != SCM_RIGHTS) {
839697c8096Sdlg 			lerrx(1, "%s: expected type %d got %d", __func__,
840697c8096Sdlg 			    SCM_RIGHTS, cmsg->cmsg_type);
841697c8096Sdlg 		}
842697c8096Sdlg 
843697c8096Sdlg 		s = (*(int *)CMSG_DATA(cmsg));
844697c8096Sdlg 
845697c8096Sdlg 		r = TAILQ_FIRST(&child->fdrequests);
846697c8096Sdlg 		if (r == NULL)
847697c8096Sdlg 			lerrx(1, "got fd without a pending request");
848697c8096Sdlg 
849697c8096Sdlg 		TAILQ_REMOVE(&child->fdrequests, r, entry);
850697c8096Sdlg 
851697c8096Sdlg 		/* get ready to add rules */
852697c8096Sdlg 		if (prepare_commit(r->id) == -1)
853697c8096Sdlg 			lerr(1, "%s: prepare_commit", __func__);
854697c8096Sdlg 
855b27441feSflorian 		TAILQ_FOREACH(src_addr, &src_addrs, entry)
856b27441feSflorian 			if (src_addr->addr.ss_family == r->addrs.dst.ss_family)
857b27441feSflorian 				break;
858b27441feSflorian 		if (src_addr == NULL) {
85919875b15Sflorian 			if (add_filter(r->id, PF_IN, (struct sockaddr *)
86019875b15Sflorian 			    &r->addrs.dst, (struct sockaddr *)&r->addrs.src,
86119875b15Sflorian 			    ntohs(((struct sockaddr_in *)&r->addrs.src)
86219875b15Sflorian 			    ->sin_port), IPPROTO_UDP) == -1)
863697c8096Sdlg 				lerr(1, "%s: couldn't add pass in", __func__);
86419875b15Sflorian 		} else {
86519875b15Sflorian 			if (getsockname(s, (struct sockaddr*)&saddr, &len) == -1)
86619875b15Sflorian 				lerr(1, "%s: getsockname", __func__);
86719875b15Sflorian 			if (add_rdr(r->id, (struct sockaddr *)&r->addrs.dst,
86819875b15Sflorian 			    (struct sockaddr*)&saddr,
86919875b15Sflorian 			    ntohs(((struct sockaddr_in *)&saddr)->sin_port),
87019875b15Sflorian 			    (struct sockaddr *)&r->addrs.src,
87119875b15Sflorian 			    ntohs(((struct sockaddr_in *)&r->addrs.src)->
87219875b15Sflorian 			    sin_port), IPPROTO_UDP ) == -1)
87319875b15Sflorian 				lerr(1, "%s: couldn't add rdr rule", __func__);
87419875b15Sflorian 		}
875697c8096Sdlg 
876697c8096Sdlg 		if (add_filter(r->id, PF_OUT, (struct sockaddr *)&r->addrs.dst,
877697c8096Sdlg 		    (struct sockaddr *)&r->addrs.src,
878697c8096Sdlg 		    ntohs(((struct sockaddr_in *)&r->addrs.src)->sin_port),
879697c8096Sdlg 		    IPPROTO_UDP) == -1)
880697c8096Sdlg 			lerr(1, "%s: couldn't add pass out", __func__);
881697c8096Sdlg 
882697c8096Sdlg 		if (do_commit() == -1)
883697c8096Sdlg 			lerr(1, "%s: couldn't commit rules", __func__);
884697c8096Sdlg 
885697c8096Sdlg 		/* forward the initial tftp request and start the insanity */
886697c8096Sdlg 		if (sendto(s, r->buf, r->buflen, 0,
887697c8096Sdlg 		    (struct sockaddr *)&r->addrs.dst,
888697c8096Sdlg 		    r->addrs.dst.ss_len) == -1)
889697c8096Sdlg 			lerr(1, "%s: unable to send", __func__);
890697c8096Sdlg 
891697c8096Sdlg 		close(s);
892697c8096Sdlg 
893697c8096Sdlg 		evtimer_set(&r->ev, unprivproc_timeout, r);
894697c8096Sdlg 		evtimer_add(&r->ev, &transwait);
895697c8096Sdlg 
896697c8096Sdlg 		TAILQ_INSERT_TAIL(&child->tmrequests, r, entry);
897697c8096Sdlg 	} while (!TAILQ_EMPTY(&child->fdrequests));
898697c8096Sdlg }
899697c8096Sdlg 
900697c8096Sdlg void
unprivproc_timeout(int fd,short events,void * arg)901697c8096Sdlg unprivproc_timeout(int fd, short events, void *arg)
902697c8096Sdlg {
903697c8096Sdlg 	struct proxy_request *r = arg;
904697c8096Sdlg 
905697c8096Sdlg 	TAILQ_REMOVE(&child->tmrequests, r, entry);
906697c8096Sdlg 
907697c8096Sdlg 	/* delete our rdr rule and clean up */
908697c8096Sdlg 	prepare_commit(r->id);
909697c8096Sdlg 	do_commit();
910697c8096Sdlg 
911697c8096Sdlg 	free(r);
912697c8096Sdlg }
913697c8096Sdlg 
914697c8096Sdlg 
915697c8096Sdlg const char *
opcode(int code)916697c8096Sdlg opcode(int code)
917697c8096Sdlg {
918697c8096Sdlg 	static char str[6];
919697c8096Sdlg 
920697c8096Sdlg 	switch (code) {
921697c8096Sdlg 	case 1:
922697c8096Sdlg 		(void)snprintf(str, sizeof(str), "RRQ");
923697c8096Sdlg 		break;
924697c8096Sdlg 	case 2:
925697c8096Sdlg 		(void)snprintf(str, sizeof(str), "WRQ");
926697c8096Sdlg 		break;
927697c8096Sdlg 	default:
928697c8096Sdlg 		(void)snprintf(str, sizeof(str), "(%d)", code);
929697c8096Sdlg 		break;
930697c8096Sdlg 	}
931697c8096Sdlg 
932697c8096Sdlg 	return (str);
933697c8096Sdlg }
934697c8096Sdlg 
935697c8096Sdlg const char *
sock_ntop(struct sockaddr * sa)936697c8096Sdlg sock_ntop(struct sockaddr *sa)
937697c8096Sdlg {
938697c8096Sdlg 	static int n = 0;
939697c8096Sdlg 
940697c8096Sdlg 	/* Cycle to next buffer. */
941697c8096Sdlg 	n = (n + 1) % NTOP_BUFS;
942697c8096Sdlg 	ntop_buf[n][0] = '\0';
943697c8096Sdlg 
944697c8096Sdlg 	if (sa->sa_family == AF_INET) {
945697c8096Sdlg 		struct sockaddr_in *sin = (struct sockaddr_in *)sa;
946697c8096Sdlg 
947697c8096Sdlg 		return (inet_ntop(AF_INET, &sin->sin_addr, ntop_buf[n],
948697c8096Sdlg 		    sizeof ntop_buf[0]));
949697c8096Sdlg 	}
950697c8096Sdlg 
951697c8096Sdlg 	if (sa->sa_family == AF_INET6) {
952697c8096Sdlg 		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
953697c8096Sdlg 
954697c8096Sdlg 		return (inet_ntop(AF_INET6, &sin6->sin6_addr, ntop_buf[n],
955697c8096Sdlg 		    sizeof ntop_buf[0]));
956697c8096Sdlg 	}
957697c8096Sdlg 
958697c8096Sdlg 	return (NULL);
959697c8096Sdlg }
960697c8096Sdlg 
961697c8096Sdlg void
syslog_vstrerror(int e,int priority,const char * fmt,va_list ap)962697c8096Sdlg syslog_vstrerror(int e, int priority, const char *fmt, va_list ap)
963697c8096Sdlg {
964697c8096Sdlg 	char *s;
965697c8096Sdlg 
966697c8096Sdlg 	if (vasprintf(&s, fmt, ap) == -1) {
967697c8096Sdlg 		syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror");
968697c8096Sdlg 		exit(1);
969697c8096Sdlg 	}
970697c8096Sdlg 
971697c8096Sdlg 	syslog(priority, "%s: %s", s, strerror(e));
972697c8096Sdlg 
973697c8096Sdlg 	free(s);
974697c8096Sdlg }
975697c8096Sdlg 
976697c8096Sdlg void
syslog_err(int ecode,const char * fmt,...)977697c8096Sdlg syslog_err(int ecode, const char *fmt, ...)
978697c8096Sdlg {
979697c8096Sdlg 	va_list ap;
980697c8096Sdlg 
981697c8096Sdlg 	va_start(ap, fmt);
982a52b97d1Sflorian 	syslog_vstrerror(errno, LOG_CRIT, fmt, ap);
983697c8096Sdlg 	va_end(ap);
984697c8096Sdlg 
985697c8096Sdlg 	exit(ecode);
986697c8096Sdlg }
987697c8096Sdlg 
988697c8096Sdlg void
syslog_errx(int ecode,const char * fmt,...)989697c8096Sdlg syslog_errx(int ecode, const char *fmt, ...)
990697c8096Sdlg {
991697c8096Sdlg 	va_list ap;
992697c8096Sdlg 
993697c8096Sdlg 	va_start(ap, fmt);
994a52b97d1Sflorian 	vsyslog(LOG_CRIT, fmt, ap);
995697c8096Sdlg 	va_end(ap);
996697c8096Sdlg 
997697c8096Sdlg 	exit(ecode);
998697c8096Sdlg }
999697c8096Sdlg 
1000697c8096Sdlg void
syslog_warn(const char * fmt,...)1001697c8096Sdlg syslog_warn(const char *fmt, ...)
1002697c8096Sdlg {
1003697c8096Sdlg 	va_list ap;
1004697c8096Sdlg 
1005697c8096Sdlg 	va_start(ap, fmt);
1006a52b97d1Sflorian 	syslog_vstrerror(errno, LOG_ERR, fmt, ap);
1007697c8096Sdlg 	va_end(ap);
1008697c8096Sdlg }
1009697c8096Sdlg 
1010697c8096Sdlg void
syslog_warnx(const char * fmt,...)1011697c8096Sdlg syslog_warnx(const char *fmt, ...)
1012697c8096Sdlg {
1013697c8096Sdlg 	va_list ap;
1014697c8096Sdlg 
1015697c8096Sdlg 	va_start(ap, fmt);
1016a52b97d1Sflorian 	vsyslog(LOG_ERR, fmt, ap);
1017697c8096Sdlg 	va_end(ap);
1018697c8096Sdlg }
1019697c8096Sdlg 
1020697c8096Sdlg void
syslog_info(const char * fmt,...)1021697c8096Sdlg syslog_info(const char *fmt, ...)
1022697c8096Sdlg {
1023697c8096Sdlg 	va_list ap;
1024697c8096Sdlg 
1025697c8096Sdlg 	va_start(ap, fmt);
1026697c8096Sdlg 	vsyslog(LOG_INFO, fmt, ap);
1027697c8096Sdlg 	va_end(ap);
1028697c8096Sdlg }
1029697c8096Sdlg 
1030cd68c002Sflorian void
syslog_debug(const char * fmt,...)1031cd68c002Sflorian syslog_debug(const char *fmt, ...)
1032cd68c002Sflorian {
1033cd68c002Sflorian 	va_list ap;
1034cd68c002Sflorian 
1035cd68c002Sflorian 	if (!debug)
1036cd68c002Sflorian 		return;
1037cd68c002Sflorian 
1038cd68c002Sflorian 	va_start(ap, fmt);
1039cd68c002Sflorian 	vsyslog(LOG_DEBUG, fmt, ap);
1040cd68c002Sflorian 	va_end(ap);
1041cd68c002Sflorian }
1042