1/* $OpenBSD: tftp-proxy.c,v 1.22 2021/01/17 13:38:52 claudio Exp $
2 *
3 * Copyright (c) 2005 DLS Internet Services
4 * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <sys/types.h>
31#include <sys/ioctl.h>
32#include <sys/socket.h>
33#include <sys/uio.h>
34
35#include <netinet/in.h>
36#include <arpa/inet.h>
37#include <arpa/tftp.h>
38#include <net/if.h>
39#include <net/pfvar.h>
40#include <netdb.h>
41
42#include <unistd.h>
43#include <errno.h>
44#include <err.h>
45#include <pwd.h>
46#include <stdio.h>
47#include <syslog.h>
48#include <string.h>
49#include <stdarg.h>
50#include <stdlib.h>
51#include <event.h>
52
53#include "filter.h"
54
55#define CHROOT_DIR	"/var/empty"
56#define NOPRIV_USER	"_tftp_proxy"
57
58#define DEFTRANSWAIT	2
59#define NTOP_BUFS	4
60#define PKTSIZE		SEGSIZE+4
61
62const char *opcode(int);
63const char *sock_ntop(struct sockaddr *);
64static void usage(void);
65
66struct proxy_listener {
67	struct event ev;
68	TAILQ_ENTRY(proxy_listener) entry;
69	int (*cmsg2dst)(struct cmsghdr *, struct sockaddr_storage *);
70	int s;
71};
72
73void	proxy_listen(const char *, const char *, int);
74void	proxy_listener_events(void);
75int	proxy_dst4(struct cmsghdr *, struct sockaddr_storage *);
76int	proxy_dst6(struct cmsghdr *, struct sockaddr_storage *);
77void	proxy_recv(int, short, void *);
78
79struct fd_reply {
80	TAILQ_ENTRY(fd_reply) entry;
81	int fd;
82};
83
84struct privproc {
85	struct event pop_ev;
86	struct event push_ev;
87	TAILQ_HEAD(, fd_reply) replies;
88	struct evbuffer *buf;
89};
90
91void	proxy_privproc(int, struct passwd *);
92void	privproc_push(int, short, void *);
93void	privproc_pop(int, short, void *);
94
95void	unprivproc_push(int, short, void *);
96void	unprivproc_pop(int, short, void *);
97void	unprivproc_timeout(int, short, void *);
98
99char	ntop_buf[NTOP_BUFS][INET6_ADDRSTRLEN];
100
101struct loggers {
102	__dead void (*err)(int, const char *, ...)
103	    __attribute__((__format__ (printf, 2, 3)));
104	__dead void (*errx)(int, const char *, ...)
105	    __attribute__((__format__ (printf, 2, 3)));
106	void (*warn)(const char *, ...)
107	    __attribute__((__format__ (printf, 1, 2)));
108	void (*warnx)(const char *, ...)
109	    __attribute__((__format__ (printf, 1, 2)));
110	void (*info)(const char *, ...)
111	    __attribute__((__format__ (printf, 1, 2)));
112	void (*debug)(const char *, ...)
113	    __attribute__((__format__ (printf, 1, 2)));
114};
115
116const struct loggers conslogger = {
117	err,
118	errx,
119	warn,
120	warnx,
121	warnx, /* info */
122	warnx /* debug */
123};
124
125__dead void	syslog_err(int, const char *, ...)
126		    __attribute__((__format__ (printf, 2, 3)));
127__dead void	syslog_errx(int, const char *, ...)
128		    __attribute__((__format__ (printf, 2, 3)));
129void		syslog_warn(const char *, ...)
130		    __attribute__((__format__ (printf, 1, 2)));
131void		syslog_warnx(const char *, ...)
132		    __attribute__((__format__ (printf, 1, 2)));
133void		syslog_info(const char *, ...)
134		    __attribute__((__format__ (printf, 1, 2)));
135void		syslog_debug(const char *, ...)
136		    __attribute__((__format__ (printf, 1, 2)));
137void		syslog_vstrerror(int, int, const char *, va_list)
138		    __attribute__((__format__ (printf, 3, 0)));
139
140const struct loggers syslogger = {
141	syslog_err,
142	syslog_errx,
143	syslog_warn,
144	syslog_warnx,
145	syslog_info,
146	syslog_debug
147};
148
149const struct loggers *logger = &conslogger;
150
151#define lerr(_e, _f...) logger->err((_e), _f)
152#define lerrx(_e, _f...) logger->errx((_e), _f)
153#define lwarn(_f...) logger->warn(_f)
154#define lwarnx(_f...) logger->warnx(_f)
155#define linfo(_f...) logger->info(_f)
156#define ldebug(_f...) logger->debug(_f)
157
158__dead void
159usage(void)
160{
161	extern char *__progname;
162	fprintf(stderr, "usage: %s [-46dv] [-a address] [-l address] [-p port]"
163	    " [-w transwait]\n", __progname);
164	exit(1);
165}
166
167int	debug = 0;
168int	verbose = 0;
169struct timeval transwait = { DEFTRANSWAIT, 0 };
170
171int on = 1;
172
173struct addr_pair {
174	struct sockaddr_storage src;
175	struct sockaddr_storage dst;
176};
177
178struct proxy_request {
179	char buf[SEGSIZE_MAX + 4];
180	size_t buflen;
181
182	struct addr_pair addrs;
183
184	struct event ev;
185	TAILQ_ENTRY(proxy_request) entry;
186	u_int32_t id;
187};
188
189struct proxy_child {
190	TAILQ_HEAD(, proxy_request) fdrequests;
191	TAILQ_HEAD(, proxy_request) tmrequests;
192	struct event push_ev;
193	struct event pop_ev;
194	struct evbuffer *buf;
195};
196
197struct proxy_child *child = NULL;
198TAILQ_HEAD(, proxy_listener) proxy_listeners;
199
200struct src_addr {
201	TAILQ_ENTRY(src_addr)	entry;
202	struct sockaddr_storage	addr;
203	socklen_t		addrlen;
204};
205TAILQ_HEAD(, src_addr) src_addrs;
206
207void	source_addresses(const char*, int);
208
209int
210main(int argc, char *argv[])
211{
212	extern char *__progname;
213
214	int c;
215	const char *errstr;
216
217	struct src_addr *saddr, *saddr2;
218	struct passwd *pw;
219
220	char *addr = "localhost";
221	char *port = "6969";
222	int family = AF_UNSPEC;
223
224	int pair[2];
225
226	TAILQ_INIT(&src_addrs);
227
228	while ((c = getopt(argc, argv, "46a:dvl:p:w:")) != -1) {
229		switch (c) {
230		case '4':
231			family = AF_INET;
232			break;
233		case '6':
234			family = AF_INET6;
235			break;
236		case 'a':
237			source_addresses(optarg, family);
238			break;
239		case 'd':
240			verbose = debug = 1;
241			break;
242		case 'l':
243			addr = optarg;
244			break;
245		case 'p':
246			port = optarg;
247			break;
248		case 'v':
249			verbose = 1;
250			break;
251		case 'w':
252			transwait.tv_sec = strtonum(optarg, 1, 30, &errstr);
253			if (errstr)
254				errx(1, "wait is %s", errstr);
255			break;
256		default:
257			usage();
258			/* NOTREACHED */
259		}
260	}
261
262	if (geteuid() != 0)
263		lerrx(1, "need root privileges");
264
265	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, PF_UNSPEC, pair)
266	    == -1)
267		lerr(1, "socketpair");
268
269	pw = getpwnam(NOPRIV_USER);
270	if (pw == NULL)
271		lerrx(1, "no %s user", NOPRIV_USER);
272
273	/* Family option may have been specified late. */
274	if (family != AF_UNSPEC)
275		TAILQ_FOREACH_SAFE(saddr, &src_addrs, entry, saddr2)
276			if (saddr->addr.ss_family != family) {
277				TAILQ_REMOVE(&src_addrs, saddr, entry);
278				free(saddr);
279			}
280
281	if (!debug) {
282		if (daemon(1, 0) == -1)
283			lerr(1, "daemon");
284
285		openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON);
286		tzset();
287		logger = &syslogger;
288	}
289
290	switch (fork()) {
291	case -1:
292		lerr(1, "fork");
293
294	case 0:
295		setproctitle("privproc");
296		close(pair[1]);
297		proxy_privproc(pair[0], pw);
298		/* this never returns */
299
300	default:
301		setproctitle("unprivproc");
302		close(pair[0]);
303		break;
304	}
305
306	child = calloc(1, sizeof(*child));
307	if (child == NULL)
308		lerr(1, "alloc(child)");
309
310	child->buf = evbuffer_new();
311	if (child->buf == NULL)
312		lerr(1, "child evbuffer");
313
314	TAILQ_INIT(&child->fdrequests);
315	TAILQ_INIT(&child->tmrequests);
316
317	proxy_listen(addr, port, family);
318
319	/* open /dev/pf */
320	init_filter(NULL, verbose);
321
322	/* revoke privs */
323	if (chroot(CHROOT_DIR) == -1)
324		lerr(1, "chroot %s", CHROOT_DIR);
325
326	if (chdir("/") == -1)
327		lerr(1, "chdir %s", CHROOT_DIR);
328
329	if (setgroups(1, &pw->pw_gid) ||
330	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
331	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
332		err(1, "unable to revoke privs");
333
334	event_init();
335
336	proxy_listener_events();
337
338	event_set(&child->pop_ev, pair[1], EV_READ | EV_PERSIST,
339	    unprivproc_pop, NULL);
340	event_set(&child->push_ev, pair[1], EV_WRITE,
341	    unprivproc_push, NULL);
342
343	event_add(&child->pop_ev, NULL);
344
345	event_dispatch();
346
347	return(0);
348}
349
350void
351source_addresses(const char* name, int family)
352{
353	struct addrinfo hints, *res, *res0;
354	struct src_addr *saddr;
355	int error;
356
357	memset(&hints, 0, sizeof(hints));
358	hints.ai_family = family;
359	hints.ai_socktype = SOCK_DGRAM;
360	hints.ai_flags = AI_PASSIVE;
361	error = getaddrinfo(name, NULL, &hints, &res0);
362	if (error)
363		lerrx(1, "%s: %s", name, gai_strerror(error));
364	for (res = res0; res != NULL; res = res->ai_next) {
365		if ((saddr = calloc(1, sizeof(struct src_addr))) == NULL)
366			lerrx(1, "calloc");
367		memcpy(&(saddr->addr), res->ai_addr, res->ai_addrlen);
368		saddr->addrlen = res->ai_addrlen;
369		TAILQ_INSERT_TAIL(&src_addrs, saddr, entry);
370	}
371	freeaddrinfo(res0);
372}
373
374void
375proxy_privproc(int s, struct passwd *pw)
376{
377	struct privproc p;
378
379	if (chroot(CHROOT_DIR) == -1)
380		lerr(1, "chroot to %s", CHROOT_DIR);
381
382	if (chdir("/") == -1)
383		lerr(1, "chdir to %s", CHROOT_DIR);
384
385	if (setgroups(1, &pw->pw_gid) ||
386	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid))
387		lerr(1, "unable to set group ids");
388
389	if (pledge("stdio inet sendfd", NULL) == -1)
390		err(1, "pledge");
391
392	TAILQ_INIT(&p.replies);
393
394	p.buf = evbuffer_new();
395	if (p.buf == NULL)
396		err(1, "pop evbuffer_new");
397
398	event_init();
399
400	event_set(&p.pop_ev, s, EV_READ | EV_PERSIST, privproc_pop, &p);
401	event_set(&p.push_ev, s, EV_WRITE, privproc_push, &p);
402
403	event_add(&p.pop_ev, NULL);
404
405	event_dispatch();
406}
407
408void
409privproc_pop(int fd, short events, void *arg)
410{
411	struct addr_pair req;
412	struct privproc *p = arg;
413	struct fd_reply *rep;
414	struct src_addr *saddr;
415	int add = 0;
416
417	switch (evbuffer_read(p->buf, fd, sizeof(req))) {
418	case 0:
419		lerrx(1, "unprivproc has gone");
420	case -1:
421		switch (errno) {
422		case EAGAIN:
423		case EINTR:
424			return;
425		default:
426			lerr(1, "privproc_pop read");
427		}
428	default:
429		break;
430	}
431
432	while (EVBUFFER_LENGTH(p->buf) >= sizeof(req)) {
433		evbuffer_remove(p->buf, &req, sizeof(req));
434
435		/* do i really need to check this? */
436		if (req.src.ss_family != req.dst.ss_family)
437			lerrx(1, "family mismatch");
438
439		rep = calloc(1, sizeof(*rep));
440		if (rep == NULL)
441			lerr(1, "reply calloc");
442
443		rep->fd = socket(req.src.ss_family, SOCK_DGRAM | SOCK_NONBLOCK,
444		    IPPROTO_UDP);
445		if (rep->fd == -1)
446			lerr(1, "privproc socket");
447
448		if (setsockopt(rep->fd, SOL_SOCKET, SO_BINDANY,
449		    &on, sizeof(on)) == -1)
450			lerr(1, "privproc setsockopt(BINDANY)");
451
452		if (setsockopt(rep->fd, SOL_SOCKET, SO_REUSEADDR,
453		    &on, sizeof(on)) == -1)
454			lerr(1, "privproc setsockopt(REUSEADDR)");
455
456		if (setsockopt(rep->fd, SOL_SOCKET, SO_REUSEPORT,
457		    &on, sizeof(on)) == -1)
458			lerr(1, "privproc setsockopt(REUSEPORT)");
459
460		TAILQ_FOREACH(saddr, &src_addrs, entry)
461			if (saddr->addr.ss_family == req.src.ss_family)
462				break;
463		if (saddr == NULL) {
464			if (bind(rep->fd, (struct sockaddr *)&req.src,
465			    req.src.ss_len) == -1)
466				lerr(1, "privproc bind");
467		} else {
468			if (bind(rep->fd, (struct sockaddr*)&saddr->addr,
469			    saddr->addrlen) == -1)
470				lerr(1, "privproc bind");
471		}
472
473		if (TAILQ_EMPTY(&p->replies))
474			add = 1;
475
476		TAILQ_INSERT_TAIL(&p->replies, rep, entry);
477	}
478
479	if (add)
480		event_add(&p->push_ev, NULL);
481}
482
483void
484privproc_push(int fd, short events, void *arg)
485{
486	struct privproc *p = arg;
487	struct fd_reply *rep;
488
489	struct msghdr msg;
490	union {
491		struct cmsghdr hdr;
492		char buf[CMSG_SPACE(sizeof(int))];
493	} cmsgbuf;
494	struct cmsghdr *cmsg;
495	struct iovec iov;
496	int result = 0;
497
498	while ((rep = TAILQ_FIRST(&p->replies)) != NULL) {
499		memset(&msg, 0, sizeof(msg));
500
501		msg.msg_control = (caddr_t)&cmsgbuf.buf;
502		msg.msg_controllen = sizeof(cmsgbuf.buf);
503		cmsg = CMSG_FIRSTHDR(&msg);
504		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
505		cmsg->cmsg_level = SOL_SOCKET;
506		cmsg->cmsg_type = SCM_RIGHTS;
507		*(int *)CMSG_DATA(cmsg) = rep->fd;
508
509		iov.iov_base = &result;
510		iov.iov_len = sizeof(int);
511		msg.msg_iov = &iov;
512		msg.msg_iovlen = 1;
513
514		switch (sendmsg(fd, &msg, 0)) {
515		case sizeof(int):
516			break;
517
518		case -1:
519			if (errno == EAGAIN)
520				goto again;
521
522			lerr(1, "privproc sendmsg");
523			/* NOTREACHED */
524
525		default:
526			lerrx(1, "privproc sendmsg weird len");
527		}
528
529		TAILQ_REMOVE(&p->replies, rep, entry);
530		close(rep->fd);
531		free(rep);
532	}
533
534	if (TAILQ_EMPTY(&p->replies))
535		return;
536
537again:
538	event_add(&p->push_ev, NULL);
539}
540
541void
542proxy_listen(const char *addr, const char *port, int family)
543{
544	struct proxy_listener *l;
545
546	struct addrinfo hints, *res, *res0;
547	int error;
548	int s, on = 1;
549	int serrno;
550	const char *cause = NULL;
551
552	memset(&hints, 0, sizeof(hints));
553	hints.ai_family = family;
554	hints.ai_socktype = SOCK_DGRAM;
555	hints.ai_flags = AI_PASSIVE;
556
557	TAILQ_INIT(&proxy_listeners);
558
559	error = getaddrinfo(addr, port, &hints, &res0);
560	if (error)
561		errx(1, "%s:%s: %s", addr, port, gai_strerror(error));
562
563	for (res = res0; res != NULL; res = res->ai_next) {
564		s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK,
565		    res->ai_protocol);
566		if (s == -1) {
567			cause = "socket";
568			continue;
569		}
570
571		if (bind(s, res->ai_addr, res->ai_addrlen) == -1) {
572			cause = "bind";
573			serrno = errno;
574			close(s);
575			errno = serrno;
576			continue;
577		}
578
579		l = calloc(1, sizeof(*l));
580		if (l == NULL)
581			err(1, "listener alloc");
582
583		switch (res->ai_family) {
584		case AF_INET:
585			l->cmsg2dst = proxy_dst4;
586
587			if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR,
588			    &on, sizeof(on)) == -1)
589				errx(1, "setsockopt(IP_RECVDSTADDR)");
590			if (setsockopt(s, IPPROTO_IP, IP_RECVDSTPORT,
591			    &on, sizeof(on)) == -1)
592				errx(1, "setsockopt(IP_RECVDSTPORT)");
593			break;
594		case AF_INET6:
595			l->cmsg2dst = proxy_dst6;
596
597			if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
598			    &on, sizeof(on)) == -1)
599				errx(1, "setsockopt(IPV6_RECVPKTINFO)");
600			if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVDSTPORT,
601			    &on, sizeof(on)) == -1)
602				errx(1, "setsockopt(IPV6_RECVDSTPORT)");
603			break;
604		}
605		l->s = s;
606
607		TAILQ_INSERT_TAIL(&proxy_listeners, l, entry);
608	}
609	freeaddrinfo(res0);
610
611	if (TAILQ_EMPTY(&proxy_listeners))
612		err(1, "%s", cause);
613}
614
615void
616proxy_listener_events(void)
617{
618	struct proxy_listener *l;
619
620	TAILQ_FOREACH(l, &proxy_listeners, entry) {
621		event_set(&l->ev, l->s, EV_READ | EV_PERSIST, proxy_recv, l);
622		event_add(&l->ev, NULL);
623	}
624}
625
626char safety[SEGSIZE_MAX + 4];
627
628int
629proxy_dst4(struct cmsghdr *cmsg, struct sockaddr_storage *ss)
630{
631	struct sockaddr_in *sin = (struct sockaddr_in *)ss;
632
633	if (cmsg->cmsg_level != IPPROTO_IP)
634		return (0);
635
636	switch (cmsg->cmsg_type) {
637	case IP_RECVDSTADDR:
638		memcpy(&sin->sin_addr, CMSG_DATA(cmsg), sizeof(sin->sin_addr));
639		if (sin->sin_addr.s_addr == INADDR_BROADCAST)
640			return (-1);
641		break;
642
643	case IP_RECVDSTPORT:
644		memcpy(&sin->sin_port, CMSG_DATA(cmsg), sizeof(sin->sin_port));
645		break;
646	}
647
648	return (0);
649}
650
651int
652proxy_dst6(struct cmsghdr *cmsg, struct sockaddr_storage *ss)
653{
654	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
655	struct in6_pktinfo *ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg);
656
657	if (cmsg->cmsg_level != IPPROTO_IPV6)
658		return (0);
659
660	switch (cmsg->cmsg_type) {
661	case IPV6_PKTINFO:
662		memcpy(&sin6->sin6_addr, &ipi->ipi6_addr,
663		    sizeof(sin6->sin6_addr));
664		if (IN6_IS_ADDR_LINKLOCAL(&ipi->ipi6_addr))
665		    sin6->sin6_scope_id = ipi->ipi6_ifindex;
666		break;
667	case IPV6_RECVDSTPORT:
668		memcpy(&sin6->sin6_port, CMSG_DATA(cmsg),
669		    sizeof(sin6->sin6_port));
670		break;
671	}
672
673	return (0);
674}
675
676void
677proxy_recv(int fd, short events, void *arg)
678{
679	struct proxy_listener *l = arg;
680
681	union {
682		struct cmsghdr hdr;
683		char buf[CMSG_SPACE(sizeof(struct sockaddr_storage)) +
684		    CMSG_SPACE(sizeof(in_port_t))];
685	} cmsgbuf;
686	struct cmsghdr *cmsg;
687	struct msghdr msg;
688	struct iovec iov;
689	ssize_t n;
690
691	struct proxy_request *r;
692	struct tftphdr *tp;
693
694	r = calloc(1, sizeof(*r));
695	if (r == NULL) {
696		recv(fd, safety, sizeof(safety), 0);
697		return;
698	}
699	r->id = arc4random(); /* XXX unique? */
700
701	bzero(&msg, sizeof(msg));
702	iov.iov_base = r->buf;
703	iov.iov_len = sizeof(r->buf);
704	msg.msg_name = &r->addrs.src;
705	msg.msg_namelen = sizeof(r->addrs.src);
706	msg.msg_iov = &iov;
707	msg.msg_iovlen = 1;
708	msg.msg_control = &cmsgbuf.buf;
709	msg.msg_controllen = sizeof(cmsgbuf.buf);
710
711	n = recvmsg(fd, &msg, 0);
712	if (n == -1) {
713		switch (errno) {
714		case EAGAIN:
715		case EINTR:
716			goto err;
717		default:
718			lerr(1, "recvmsg");
719			/* NOTREACHED */
720		}
721	}
722	r->buflen = n;
723
724	/* check the packet */
725	if (n < 5) {
726		/* not enough to be a real packet */
727		goto err;
728	}
729	tp = (struct tftphdr *)r->buf;
730	switch (ntohs(tp->th_opcode)) {
731	case RRQ:
732	case WRQ:
733		break;
734	default:
735		goto err;
736	}
737
738	r->addrs.dst.ss_family = r->addrs.src.ss_family;
739	r->addrs.dst.ss_len = r->addrs.src.ss_len;
740
741	/* get local address if possible */
742	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
743	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
744		if (l->cmsg2dst(cmsg, &r->addrs.dst) == -1)
745			goto err;
746	}
747
748	if (verbose) {
749		linfo("%s:%d -> %s:%d \"%s %s\"",
750		    sock_ntop((struct sockaddr *)&r->addrs.src),
751		    ntohs(((struct sockaddr_in *)&r->addrs.src)->sin_port),
752		    sock_ntop((struct sockaddr *)&r->addrs.dst),
753		    ntohs(((struct sockaddr_in *)&r->addrs.dst)->sin_port),
754		    opcode(ntohs(tp->th_opcode)), tp->th_stuff);
755		/* XXX tp->th_stuff could be garbage */
756	}
757
758	TAILQ_INSERT_TAIL(&child->fdrequests, r, entry);
759	evbuffer_add(child->buf, &r->addrs, sizeof(r->addrs));
760	event_add(&child->push_ev, NULL);
761
762	return;
763
764err:
765	free(r);
766}
767
768void
769unprivproc_push(int fd, short events, void *arg)
770{
771	if (evbuffer_write(child->buf, fd) == -1)
772		lerr(1, "child evbuffer_write");
773
774	if (EVBUFFER_LENGTH(child->buf))
775		event_add(&child->push_ev, NULL);
776}
777
778void
779unprivproc_pop(int fd, short events, void *arg)
780{
781	struct proxy_request *r;
782
783	struct msghdr msg;
784	union {
785		struct cmsghdr hdr;
786		char buf[CMSG_SPACE(sizeof(int))];
787	} cmsgbuf;
788	struct cmsghdr *cmsg;
789	struct iovec iov;
790	struct src_addr *src_addr;
791	struct sockaddr_storage saddr;
792	socklen_t len;
793	int result;
794	int s;
795
796	len = sizeof(saddr);
797
798	do {
799		memset(&msg, 0, sizeof(msg));
800		iov.iov_base = &result;
801		iov.iov_len = sizeof(int);
802		msg.msg_iov = &iov;
803		msg.msg_iovlen = 1;
804		msg.msg_control = &cmsgbuf.buf;
805		msg.msg_controllen = sizeof(cmsgbuf.buf);
806
807		switch (recvmsg(fd, &msg, 0)) {
808		case sizeof(int):
809			break;
810
811		case -1:
812			switch (errno) {
813			case EAGAIN:
814			case EINTR:
815				return;
816			default:
817				lerr(1, "child recvmsg");
818			}
819			/* NOTREACHED */
820
821		case 0:
822			lerrx(1, "privproc closed connection");
823
824		default:
825			lerrx(1, "child recvmsg was weird");
826			/* NOTREACHED */
827		}
828
829		if (result != 0) {
830			errno = result;
831			lerr(1, "child fdpass fail");
832		}
833
834		cmsg = CMSG_FIRSTHDR(&msg);
835		if (cmsg == NULL)
836			lerrx(1, "%s: no message header", __func__);
837
838		if (cmsg->cmsg_type != SCM_RIGHTS) {
839			lerrx(1, "%s: expected type %d got %d", __func__,
840			    SCM_RIGHTS, cmsg->cmsg_type);
841		}
842
843		s = (*(int *)CMSG_DATA(cmsg));
844
845		r = TAILQ_FIRST(&child->fdrequests);
846		if (r == NULL)
847			lerrx(1, "got fd without a pending request");
848
849		TAILQ_REMOVE(&child->fdrequests, r, entry);
850
851		/* get ready to add rules */
852		if (prepare_commit(r->id) == -1)
853			lerr(1, "%s: prepare_commit", __func__);
854
855		TAILQ_FOREACH(src_addr, &src_addrs, entry)
856			if (src_addr->addr.ss_family == r->addrs.dst.ss_family)
857				break;
858		if (src_addr == NULL) {
859			if (add_filter(r->id, PF_IN, (struct sockaddr *)
860			    &r->addrs.dst, (struct sockaddr *)&r->addrs.src,
861			    ntohs(((struct sockaddr_in *)&r->addrs.src)
862			    ->sin_port), IPPROTO_UDP) == -1)
863				lerr(1, "%s: couldn't add pass in", __func__);
864		} else {
865			if (getsockname(s, (struct sockaddr*)&saddr, &len) == -1)
866				lerr(1, "%s: getsockname", __func__);
867			if (add_rdr(r->id, (struct sockaddr *)&r->addrs.dst,
868			    (struct sockaddr*)&saddr,
869			    ntohs(((struct sockaddr_in *)&saddr)->sin_port),
870			    (struct sockaddr *)&r->addrs.src,
871			    ntohs(((struct sockaddr_in *)&r->addrs.src)->
872			    sin_port), IPPROTO_UDP ) == -1)
873				lerr(1, "%s: couldn't add rdr rule", __func__);
874		}
875
876		if (add_filter(r->id, PF_OUT, (struct sockaddr *)&r->addrs.dst,
877		    (struct sockaddr *)&r->addrs.src,
878		    ntohs(((struct sockaddr_in *)&r->addrs.src)->sin_port),
879		    IPPROTO_UDP) == -1)
880			lerr(1, "%s: couldn't add pass out", __func__);
881
882		if (do_commit() == -1)
883			lerr(1, "%s: couldn't commit rules", __func__);
884
885		/* forward the initial tftp request and start the insanity */
886		if (sendto(s, r->buf, r->buflen, 0,
887		    (struct sockaddr *)&r->addrs.dst,
888		    r->addrs.dst.ss_len) == -1)
889			lerr(1, "%s: unable to send", __func__);
890
891		close(s);
892
893		evtimer_set(&r->ev, unprivproc_timeout, r);
894		evtimer_add(&r->ev, &transwait);
895
896		TAILQ_INSERT_TAIL(&child->tmrequests, r, entry);
897	} while (!TAILQ_EMPTY(&child->fdrequests));
898}
899
900void
901unprivproc_timeout(int fd, short events, void *arg)
902{
903	struct proxy_request *r = arg;
904
905	TAILQ_REMOVE(&child->tmrequests, r, entry);
906
907	/* delete our rdr rule and clean up */
908	prepare_commit(r->id);
909	do_commit();
910
911	free(r);
912}
913
914
915const char *
916opcode(int code)
917{
918	static char str[6];
919
920	switch (code) {
921	case 1:
922		(void)snprintf(str, sizeof(str), "RRQ");
923		break;
924	case 2:
925		(void)snprintf(str, sizeof(str), "WRQ");
926		break;
927	default:
928		(void)snprintf(str, sizeof(str), "(%d)", code);
929		break;
930	}
931
932	return (str);
933}
934
935const char *
936sock_ntop(struct sockaddr *sa)
937{
938	static int n = 0;
939
940	/* Cycle to next buffer. */
941	n = (n + 1) % NTOP_BUFS;
942	ntop_buf[n][0] = '\0';
943
944	if (sa->sa_family == AF_INET) {
945		struct sockaddr_in *sin = (struct sockaddr_in *)sa;
946
947		return (inet_ntop(AF_INET, &sin->sin_addr, ntop_buf[n],
948		    sizeof ntop_buf[0]));
949	}
950
951	if (sa->sa_family == AF_INET6) {
952		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
953
954		return (inet_ntop(AF_INET6, &sin6->sin6_addr, ntop_buf[n],
955		    sizeof ntop_buf[0]));
956	}
957
958	return (NULL);
959}
960
961void
962syslog_vstrerror(int e, int priority, const char *fmt, va_list ap)
963{
964	char *s;
965
966	if (vasprintf(&s, fmt, ap) == -1) {
967		syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror");
968		exit(1);
969	}
970
971	syslog(priority, "%s: %s", s, strerror(e));
972
973	free(s);
974}
975
976void
977syslog_err(int ecode, const char *fmt, ...)
978{
979	va_list ap;
980
981	va_start(ap, fmt);
982	syslog_vstrerror(errno, LOG_CRIT, fmt, ap);
983	va_end(ap);
984
985	exit(ecode);
986}
987
988void
989syslog_errx(int ecode, const char *fmt, ...)
990{
991	va_list ap;
992
993	va_start(ap, fmt);
994	vsyslog(LOG_CRIT, fmt, ap);
995	va_end(ap);
996
997	exit(ecode);
998}
999
1000void
1001syslog_warn(const char *fmt, ...)
1002{
1003	va_list ap;
1004
1005	va_start(ap, fmt);
1006	syslog_vstrerror(errno, LOG_ERR, fmt, ap);
1007	va_end(ap);
1008}
1009
1010void
1011syslog_warnx(const char *fmt, ...)
1012{
1013	va_list ap;
1014
1015	va_start(ap, fmt);
1016	vsyslog(LOG_ERR, fmt, ap);
1017	va_end(ap);
1018}
1019
1020void
1021syslog_info(const char *fmt, ...)
1022{
1023	va_list ap;
1024
1025	va_start(ap, fmt);
1026	vsyslog(LOG_INFO, fmt, ap);
1027	va_end(ap);
1028}
1029
1030void
1031syslog_debug(const char *fmt, ...)
1032{
1033	va_list ap;
1034
1035	if (!debug)
1036		return;
1037
1038	va_start(ap, fmt);
1039	vsyslog(LOG_DEBUG, fmt, ap);
1040	va_end(ap);
1041}
1042