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