xref: /openbsd-src/usr.sbin/tftp-proxy/tftp-proxy.c (revision 80b8c34729b274404f4d8b1a49f8f6b5efbf14ca)
1 /* $OpenBSD: tftp-proxy.c,v 1.6 2013/12/19 11:23:29 florian 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 			break;
544 		}
545 		l->s = s;
546 
547 		TAILQ_INSERT_TAIL(&proxy_listeners, l, entry);
548 	}
549 	freeaddrinfo(res0);
550 
551 	if (TAILQ_EMPTY(&proxy_listeners))
552 		err(1, "%s", cause);
553 }
554 
555 void
556 proxy_listener_events(void)
557 {
558 	struct proxy_listener *l;
559 
560 	TAILQ_FOREACH(l, &proxy_listeners, entry) {
561 		event_set(&l->ev, l->s, EV_READ | EV_PERSIST, proxy_recv, l);
562 		event_add(&l->ev, NULL);
563 	}
564 }
565 
566 char safety[SEGSIZE_MAX + 4];
567 
568 int
569 proxy_dst4(struct cmsghdr *cmsg, struct sockaddr_storage *ss)
570 {
571 	struct sockaddr_in *sin = (struct sockaddr_in *)ss;
572 
573 	if (cmsg->cmsg_level != IPPROTO_IP)
574 		return (0);
575 
576 	switch (cmsg->cmsg_type) {
577 	case IP_RECVDSTADDR:
578 		memcpy(&sin->sin_addr, CMSG_DATA(cmsg), sizeof(sin->sin_addr));
579 		if (sin->sin_addr.s_addr == INADDR_BROADCAST)
580 			return (-1);
581 		break;
582 
583 	case IP_RECVDSTPORT:
584 		memcpy(&sin->sin_port, CMSG_DATA(cmsg), sizeof(sin->sin_port));
585 		break;
586 	}
587 
588 	return (0);
589 }
590 
591 int
592 proxy_dst6(struct cmsghdr *cmsg, struct sockaddr_storage *ss)
593 {
594 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
595 	struct in6_pktinfo *ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg);
596 
597 	if (cmsg->cmsg_level != IPPROTO_IPV6)
598 		return (0);
599 
600 	switch (cmsg->cmsg_type) {
601 	case IPV6_PKTINFO:
602 		memcpy(&sin6->sin6_addr, &ipi->ipi6_addr,
603 		    sizeof(sin6->sin6_addr));
604 #ifdef __KAME__
605 		if (IN6_IS_ADDR_LINKLOCAL(&ipi->ipi6_addr))
606 		    sin6->sin6_scope_id = ipi->ipi6_ifindex;
607 #endif
608 		break;
609 
610 	/* XXX PORT */
611 	}
612 
613 	return (0);
614 }
615 
616 void
617 proxy_recv(int fd, short events, void *arg)
618 {
619 	struct proxy_listener *l = arg;
620 
621 	union {
622 		struct cmsghdr hdr;
623 		char buf[CMSG_SPACE(sizeof(struct sockaddr_storage)) +
624 		    CMSG_SPACE(sizeof(in_port_t))];
625 	} cmsgbuf;
626 	struct cmsghdr *cmsg;
627 	struct msghdr msg;
628 	struct iovec iov;
629 	ssize_t n;
630 
631 	struct proxy_request *r;
632 	struct tftphdr *tp;
633 
634 	r = calloc(1, sizeof(*r));
635 	if (r == NULL) {
636 		recv(fd, safety, sizeof(safety), 0);
637 		return;
638 	}
639 	r->id = arc4random(); /* XXX unique? */
640 
641 	bzero(&msg, sizeof(msg));
642 	iov.iov_base = r->buf;
643 	iov.iov_len = sizeof(r->buf);
644 	msg.msg_name = &r->addrs.src;
645 	msg.msg_namelen = sizeof(r->addrs.src);
646 	msg.msg_iov = &iov;
647 	msg.msg_iovlen = 1;
648 	msg.msg_control = &cmsgbuf.buf;
649 	msg.msg_controllen = sizeof(cmsgbuf.buf);
650 
651 	n = recvmsg(fd, &msg, 0);
652 	if (n == -1) {
653 		switch (errno) {
654 		case EAGAIN:
655 		case EINTR:
656 			goto err;
657 		default:
658 			lerr(1, "recvmsg");
659 			/* NOTREACHED */
660 		}
661 	}
662 	r->buflen = n;
663 
664 	/* check the packet */
665 	if (n < 5) {
666 		/* not enough to be a real packet */
667 		goto err;
668 	}
669 	tp = (struct tftphdr *)r->buf;
670 	switch (ntohs(tp->th_opcode)) {
671 	case RRQ:
672 	case WRQ:
673 		break;
674 	default:
675 		goto err;
676 	}
677 
678 	r->addrs.dst.ss_family = r->addrs.src.ss_family;
679 	r->addrs.dst.ss_len = r->addrs.src.ss_len;
680 
681 	/* get local address if possible */
682 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
683 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
684 		if (l->cmsg2dst(cmsg, &r->addrs.dst) == -1)
685 			goto err;
686 	}
687 
688 	if (verbose) {
689 		linfo("%s:%d -> %s:%d \"%s %s\"",
690 		    sock_ntop((struct sockaddr *)&r->addrs.src),
691 		    ntohs(((struct sockaddr_in *)&r->addrs.src)->sin_port),
692 		    sock_ntop((struct sockaddr *)&r->addrs.dst),
693 		    ntohs(((struct sockaddr_in *)&r->addrs.dst)->sin_port),
694 		    opcode(ntohs(tp->th_opcode)), tp->th_stuff);
695 		/* XXX tp->th_stuff could be garbage */
696 	}
697 
698 	TAILQ_INSERT_TAIL(&child->fdrequests, r, entry);
699 	evbuffer_add(child->buf, &r->addrs, sizeof(r->addrs));
700 	event_add(&child->push_ev, NULL);
701 
702 	return;
703 
704 err:
705 	free(r);
706 }
707 
708 void
709 unprivproc_push(int fd, short events, void *arg)
710 {
711 	if (evbuffer_write(child->buf, fd) == -1)
712 		lerr(1, "child evbuffer_write");
713 
714 	if (EVBUFFER_LENGTH(child->buf))
715 		event_add(&child->push_ev, NULL);
716 }
717 
718 void
719 unprivproc_pop(int fd, short events, void *arg)
720 {
721 	struct proxy_request *r;
722 
723 	struct msghdr msg;
724 	union {
725 		struct cmsghdr hdr;
726 		char buf[CMSG_SPACE(sizeof(int))];
727 	} cmsgbuf;
728 	struct cmsghdr *cmsg;
729 	struct iovec iov;
730 	int result;
731 	int s;
732 
733 	do {
734 		memset(&msg, 0, sizeof(msg));
735 		iov.iov_base = &result;
736 		iov.iov_len = sizeof(int);
737 		msg.msg_iov = &iov;
738 		msg.msg_iovlen = 1;
739 		msg.msg_control = &cmsgbuf.buf;
740 		msg.msg_controllen = sizeof(cmsgbuf.buf);
741 
742 		switch (recvmsg(fd, &msg, 0)) {
743 		case sizeof(int):
744 			break;
745 
746 		case -1:
747 			switch (errno) {
748 			case EAGAIN:
749 			case EINTR:
750 				return;
751 			default:
752 				lerr(1, "child recvmsg");
753 			}
754 			/* NOTREACHED */
755 
756 		case 0:
757 			lerrx(1, "privproc closed connection");
758 
759 		default:
760 			lerrx(1, "child recvmsg was weird");
761 			/* NOTREACHED */
762 		}
763 
764 		if (result != 0) {
765 			errno = result;
766 			lerr(1, "child fdpass fail");
767 		}
768 
769 		cmsg = CMSG_FIRSTHDR(&msg);
770 		if (cmsg == NULL)
771 			lerrx(1, "%s: no message header", __func__);
772 
773 		if (cmsg->cmsg_type != SCM_RIGHTS) {
774 			lerrx(1, "%s: expected type %d got %d", __func__,
775 			    SCM_RIGHTS, cmsg->cmsg_type);
776 		}
777 
778 		s = (*(int *)CMSG_DATA(cmsg));
779 
780 		r = TAILQ_FIRST(&child->fdrequests);
781 		if (r == NULL)
782 			lerrx(1, "got fd without a pending request");
783 
784 		TAILQ_REMOVE(&child->fdrequests, r, entry);
785 
786 		/* get ready to add rules */
787 		if (prepare_commit(r->id) == -1)
788 			lerr(1, "%s: prepare_commit", __func__);
789 
790 		if (add_filter(r->id, PF_IN, (struct sockaddr *)&r->addrs.dst,
791 		    (struct sockaddr *)&r->addrs.src,
792 		    ntohs(((struct sockaddr_in *)&r->addrs.src)->sin_port),
793 		    IPPROTO_UDP) == -1)
794 			lerr(1, "%s: couldn't add pass in", __func__);
795 
796 		if (add_filter(r->id, PF_OUT, (struct sockaddr *)&r->addrs.dst,
797 		    (struct sockaddr *)&r->addrs.src,
798 		    ntohs(((struct sockaddr_in *)&r->addrs.src)->sin_port),
799 		    IPPROTO_UDP) == -1)
800 			lerr(1, "%s: couldn't add pass out", __func__);
801 
802 		if (do_commit() == -1)
803 			lerr(1, "%s: couldn't commit rules", __func__);
804 
805 		/* forward the initial tftp request and start the insanity */
806 		if (sendto(s, r->buf, r->buflen, 0,
807 		    (struct sockaddr *)&r->addrs.dst,
808 		    r->addrs.dst.ss_len) == -1)
809 			lerr(1, "%s: unable to send", __func__);
810 
811 		close(s);
812 
813 		evtimer_set(&r->ev, unprivproc_timeout, r);
814 		evtimer_add(&r->ev, &transwait);
815 
816 		TAILQ_INSERT_TAIL(&child->tmrequests, r, entry);
817 	} while (!TAILQ_EMPTY(&child->fdrequests));
818 }
819 
820 void
821 unprivproc_timeout(int fd, short events, void *arg)
822 {
823 	struct proxy_request *r = arg;
824 
825 	TAILQ_REMOVE(&child->tmrequests, r, entry);
826 
827 	/* delete our rdr rule and clean up */
828 	prepare_commit(r->id);
829 	do_commit();
830 
831 	free(r);
832 }
833 
834 
835 const char *
836 opcode(int code)
837 {
838 	static char str[6];
839 
840 	switch (code) {
841 	case 1:
842 		(void)snprintf(str, sizeof(str), "RRQ");
843 		break;
844 	case 2:
845 		(void)snprintf(str, sizeof(str), "WRQ");
846 		break;
847 	default:
848 		(void)snprintf(str, sizeof(str), "(%d)", code);
849 		break;
850 	}
851 
852 	return (str);
853 }
854 
855 const char *
856 sock_ntop(struct sockaddr *sa)
857 {
858 	static int n = 0;
859 
860 	/* Cycle to next buffer. */
861 	n = (n + 1) % NTOP_BUFS;
862 	ntop_buf[n][0] = '\0';
863 
864 	if (sa->sa_family == AF_INET) {
865 		struct sockaddr_in *sin = (struct sockaddr_in *)sa;
866 
867 		return (inet_ntop(AF_INET, &sin->sin_addr, ntop_buf[n],
868 		    sizeof ntop_buf[0]));
869 	}
870 
871 	if (sa->sa_family == AF_INET6) {
872 		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
873 
874 		return (inet_ntop(AF_INET6, &sin6->sin6_addr, ntop_buf[n],
875 		    sizeof ntop_buf[0]));
876 	}
877 
878 	return (NULL);
879 }
880 
881 void
882 syslog_vstrerror(int e, int priority, const char *fmt, va_list ap)
883 {
884 	char *s;
885 
886 	if (vasprintf(&s, fmt, ap) == -1) {
887 		syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror");
888 		exit(1);
889 	}
890 
891 	syslog(priority, "%s: %s", s, strerror(e));
892 
893 	free(s);
894 }
895 
896 void
897 syslog_err(int ecode, const char *fmt, ...)
898 {
899 	va_list ap;
900 
901 	va_start(ap, fmt);
902 	syslog_vstrerror(errno, LOG_EMERG, fmt, ap);
903 	va_end(ap);
904 
905 	exit(ecode);
906 }
907 
908 void
909 syslog_errx(int ecode, const char *fmt, ...)
910 {
911 	va_list ap;
912 
913 	va_start(ap, fmt);
914 	vsyslog(LOG_WARNING, fmt, ap);
915 	va_end(ap);
916 
917 	exit(ecode);
918 }
919 
920 void
921 syslog_warn(const char *fmt, ...)
922 {
923 	va_list ap;
924 
925 	va_start(ap, fmt);
926 	syslog_vstrerror(errno, LOG_WARNING, fmt, ap);
927 	va_end(ap);
928 }
929 
930 void
931 syslog_warnx(const char *fmt, ...)
932 {
933 	va_list ap;
934 
935 	va_start(ap, fmt);
936 	vsyslog(LOG_WARNING, fmt, ap);
937 	va_end(ap);
938 }
939 
940 void
941 syslog_info(const char *fmt, ...)
942 {
943 	va_list ap;
944 
945 	va_start(ap, fmt);
946 	vsyslog(LOG_INFO, fmt, ap);
947 	va_end(ap);
948 }
949 
950