xref: /openbsd-src/usr.sbin/tftpd/tftpd.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: tftpd.c,v 1.8 2012/07/13 02:31:46 gsoares Exp $	*/
2 
3 /*
4  * Copyright (c) 2012 David Gwynne <dlg@uq.edu.au>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * Copyright (c) 1983 Regents of the University of California.
21  * All rights reserved.
22  *
23  * Redistribution and use in source and binary forms, with or without
24  * modification, are permitted provided that the following conditions
25  * are met:
26  * 1. Redistributions of source code must retain the above copyright
27  *    notice, this list of conditions and the following disclaimer.
28  * 2. Redistributions in binary form must reproduce the above copyright
29  *    notice, this list of conditions and the following disclaimer in the
30  *    documentation and/or other materials provided with the distribution.
31  * 3. Neither the name of the University nor the names of its contributors
32  *    may be used to endorse or promote products derived from this software
33  *    without specific prior written permission.
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
36  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
39  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
41  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45  * SUCH DAMAGE.
46  */
47 
48 /*
49  * Trivial file transfer protocol server.
50  *
51  * This version is based on src/libexec/tftpd which includes many
52  * modifications by Jim Guyton <guyton@rand-unix>.
53  *
54  * It was restructured to be a persistent event driven daemon
55  * supporting concurrent connections by dlg for use at the University
56  * of Queensland in the Faculty of Engineering Architecture and
57  * Information Technology.
58  */
59 
60 #include <sys/ioctl.h>
61 #include <sys/param.h>
62 #include <sys/types.h>
63 #include <sys/queue.h>
64 #include <sys/socket.h>
65 #include <sys/stat.h>
66 #include <sys/uio.h>
67 #include <sys/un.h>
68 
69 #include <netinet/in.h>
70 #include <arpa/inet.h>
71 #include <arpa/tftp.h>
72 #include <netdb.h>
73 
74 #include <err.h>
75 #include <ctype.h>
76 #include <errno.h>
77 #include <event.h>
78 #include <fcntl.h>
79 #include <poll.h>
80 #include <pwd.h>
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <stdarg.h>
85 #include <syslog.h>
86 #include <unistd.h>
87 #include <vis.h>
88 
89 #define TIMEOUT		5		/* packet rexmt timeout */
90 #define TIMEOUT_MIN	1		/* minimal packet rexmt timeout */
91 #define TIMEOUT_MAX	255		/* maximal packet rexmt timeout */
92 
93 #define RETRIES		5
94 
95 struct formats;
96 
97 enum opt_enum {
98 	OPT_TSIZE = 0,
99 	OPT_TIMEOUT,
100 	OPT_BLKSIZE,
101 	NOPT
102 };
103 
104 static char *opt_names[] = {
105 	"tsize",
106 	"timeout",
107 	"blksize"
108 };
109 
110 struct opt_client {
111 	char *o_request;
112 	long long o_reply;
113 };
114 
115 
116 struct tftp_server {
117 	struct event ev;
118 	TAILQ_ENTRY(tftp_server) entry;
119 	int s;
120 };
121 
122 TAILQ_HEAD(, tftp_server) tftp_servers;
123 
124 struct tftp_client {
125 	char buf[SEGSIZE_MAX + 4];
126 	struct event sev;
127 	struct sockaddr_storage ss;
128 
129 	struct timeval tv;
130 
131 	TAILQ_ENTRY(tftp_client) entry;
132 
133 	struct opt_client *options;
134 
135 	size_t segment_size;
136 	size_t packet_size;
137 	size_t buflen;
138 
139 	FILE *file;
140 	int (*fgetc)(struct tftp_client *);
141 	int (*fputc)(struct tftp_client *, int);
142 
143 	u_int retries;
144 	u_int16_t block;
145 
146 	int opcode;
147 	int newline;
148 
149 	int sock;
150 };
151 
152 __dead void	usage(void);
153 const char	*getip(void *);
154 
155 void		rewrite_connect(const char *);
156 void		rewrite_events(void);
157 void		rewrite_map(struct tftp_client *, const char *);
158 void		rewrite_req(int, short, void *);
159 void		rewrite_res(int, short, void *);
160 
161 int		tftpd_listen(const char *, const char *, int);
162 void		tftpd_events(void);
163 void		tftpd_recv(int, short, void *);
164 int		retry(struct tftp_client *);
165 int		tftp_flush(struct tftp_client *);
166 void		tftp_end(struct tftp_client *);
167 
168 void		tftp(struct tftp_client *, struct tftphdr *, size_t);
169 void		tftp_open(struct tftp_client *, const char *);
170 void		nak(struct tftp_client *, int);
171 void		oack(struct tftp_client *);
172 void		oack_done(int, short, void *);
173 
174 void		sendfile(struct tftp_client *);
175 void		recvfile(struct tftp_client *);
176 int		fget_octet(struct tftp_client *);
177 int		fput_octet(struct tftp_client *, int);
178 int		fget_netascii(struct tftp_client *);
179 int		fput_netascii(struct tftp_client *, int);
180 void		file_read(struct tftp_client *);
181 int		tftp_wrq_ack_packet(struct tftp_client *);
182 void		tftp_rrq_ack(int, short, void *);
183 void		tftp_wrq_ack(struct tftp_client *client);
184 void		tftp_wrq(int, short, void *);
185 void		tftp_wrq_end(int, short, void *);
186 
187 int		parse_options(struct tftp_client *, char *, size_t,
188 		    struct opt_client *);
189 int		validate_access(struct tftp_client *, const char *);
190 
191 struct formats {
192 	const char	*f_mode;
193 	int (*f_getc)(struct tftp_client *);
194 	int (*f_putc)(struct tftp_client *, int);
195 } formats[] = {
196 	{ "octet",	fget_octet,	fput_octet },
197 	{ "netascii",	fget_netascii,	fput_netascii },
198 	{ NULL,		NULL }
199 };
200 
201 struct errmsg {
202 	int		 e_code;
203 	const char	*e_msg;
204 } errmsgs[] = {
205 	{ EUNDEF,	"Undefined error code" },
206 	{ ENOTFOUND,	"File not found" },
207 	{ EACCESS,	"Access violation" },
208 	{ ENOSPACE,	"Disk full or allocation exceeded" },
209 	{ EBADOP,	"Illegal TFTP operation" },
210 	{ EBADID,	"Unknown transfer ID" },
211 	{ EEXISTS,	"File already exists" },
212 	{ ENOUSER,	"No such user" },
213 	{ EOPTNEG,	"Option negotiation failed" },
214 	{ -1,		NULL }
215 };
216 
217 struct loggers {
218 	void (*err)(int, const char *, ...);
219 	void (*errx)(int, const char *, ...);
220 	void (*warn)(const char *, ...);
221 	void (*warnx)(const char *, ...);
222 	void (*info)(const char *, ...);
223 };
224 
225 const struct loggers conslogger = {
226 	err,
227 	errx,
228 	warn,
229 	warnx,
230 	warnx
231 };
232 
233 void	syslog_err(int, const char *, ...);
234 void	syslog_errx(int, const char *, ...);
235 void	syslog_warn(const char *, ...);
236 void	syslog_warnx(const char *, ...);
237 void	syslog_info(const char *, ...);
238 void	syslog_vstrerror(int, int, const char *, va_list);
239 
240 const struct loggers syslogger = {
241 	syslog_err,
242 	syslog_errx,
243 	syslog_warn,
244 	syslog_warnx,
245 	syslog_info,
246 };
247 
248 const struct loggers *logger = &conslogger;
249 
250 #define lerr(_e, _f...) logger->err((_e), _f)
251 #define lerrx(_e, _f...) logger->errx((_e), _f)
252 #define lwarn(_f...) logger->warn(_f)
253 #define lwarnx(_f...) logger->warnx(_f)
254 #define linfo(_f...) logger->info(_f)
255 
256 __dead void
257 usage(void)
258 {
259 	extern char *__progname;
260 	fprintf(stderr, "usage: %s [-46cdv] [-l address] [-p port] [-r socket]"
261 	    " directory\n", __progname);
262 	exit(1);
263 }
264 
265 int		  cancreate = 0;
266 int		  verbose = 0;
267 
268 int
269 main(int argc, char *argv[])
270 {
271 	extern char *__progname;
272 	int debug = 0;
273 
274 	int		 c;
275 	struct passwd	*pw;
276 
277 	char *dir = NULL;
278 	char *rewrite = NULL;
279 
280 	char *addr = NULL;
281 	char *port = "tftp";
282 	int family = AF_UNSPEC;
283 
284 	while ((c = getopt(argc, argv, "46cdl:p:r:v")) != -1) {
285 		switch (c) {
286 		case '4':
287 			family = AF_INET;
288 			break;
289 		case '6':
290 			family = AF_INET6;
291 			break;
292 		case 'c':
293 			cancreate = 1;
294 			break;
295 		case 'd':
296 			verbose = debug = 1;
297 			break;
298 		case 'l':
299 			addr = optarg;
300 			break;
301 		case 'p':
302 			port = optarg;
303 			break;
304 		case 'r':
305 			rewrite = optarg;
306 			break;
307 		case 'v':
308 			verbose = 1;
309 			break;
310 		default:
311 			usage();
312 			/* NOTREACHED */
313 		}
314 	}
315 
316 	argc -= optind;
317 	argv += optind;
318 
319 	if (argc != 1)
320 		usage();
321 
322 	dir = argv[0];
323 
324 	if (geteuid() != 0)
325 		errx(1, "need root privileges");
326 
327 	pw = getpwnam("_tftpd");
328 	if (pw == NULL)
329 		err(1, "no _tftpd user");
330 
331 	if (!debug) {
332 		openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON);
333 		tzset();
334 		logger = &syslogger;
335 	}
336 
337 	if (rewrite != NULL)
338 		rewrite_connect(rewrite);
339 
340 	tftpd_listen(addr, port, family);
341 
342 	if (chroot(dir))
343 		err(1, "chroot %s", dir);
344 	if (chdir("/"))
345 		err(1, "chdir %s", dir);
346 
347 	/* drop privs */
348 	if (setgroups(1, &pw->pw_gid) ||
349 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
350 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
351 		errx(1, "can't drop privileges");
352 
353 	if (!debug && daemon(1, 0) == -1)
354 		err(1, "unable to daemonize");
355 
356 	event_init();
357 
358 	if (rewrite != NULL)
359 		rewrite_events();
360 
361 	tftpd_events();
362 
363 	event_dispatch();
364 
365 	exit(0);
366 }
367 
368 struct rewritemap {
369 	struct event wrev;
370 	struct event rdev;
371 	struct evbuffer *wrbuf;
372 	struct evbuffer *rdbuf;
373 
374 	TAILQ_HEAD(, tftp_client) clients;
375 
376 	int s;
377 };
378 
379 struct rewritemap *rwmap = NULL;
380 
381 void
382 rewrite_connect(const char *path)
383 {
384 	int s;
385 	struct sockaddr_un remote;
386 	size_t len;
387 	int on = 1;
388 
389 	rwmap = malloc(sizeof(*rwmap));
390 	if (rwmap == NULL)
391 		err(1, "rewrite event malloc");
392 
393 	rwmap->wrbuf = evbuffer_new();
394 	if (rwmap->wrbuf == NULL)
395 		err(1, "rewrite wrbuf");
396 
397 	rwmap->rdbuf = evbuffer_new();
398 	if (rwmap->rdbuf == NULL)
399 		err(1, "rewrite rdbuf");
400 
401 	TAILQ_INIT(&rwmap->clients);
402 
403 	s = socket(AF_UNIX, SOCK_STREAM, 0);
404 	if (s == -1)
405 		err(1, "rewrite socket");
406 
407 	remote.sun_family = AF_UNIX;
408 	len = strlcpy(remote.sun_path, path, sizeof(remote.sun_path));
409 	if (len >= sizeof(remote.sun_path))
410 		errx(1, "rewrite socket path is too long");
411 
412 	len += sizeof(remote.sun_family) + 1;
413 	if (connect(s, (struct sockaddr *)&remote, len) == -1)
414 		err(1, "%s", path);
415 
416 	if (ioctl(s, FIONBIO, &on) < 0)
417 		err(1, "rewrite ioctl(FIONBIO)");
418 
419 	rwmap->s = s;
420 }
421 
422 void
423 rewrite_events(void)
424 {
425 	event_set(&rwmap->wrev, rwmap->s, EV_WRITE, rewrite_req, NULL);
426 	event_set(&rwmap->rdev, rwmap->s, EV_READ | EV_PERSIST, rewrite_res, NULL);
427 	event_add(&rwmap->rdev, NULL);
428 }
429 
430 void
431 rewrite_map(struct tftp_client *client, const char *filename)
432 {
433 	char nicebuf[MAXPATHLEN];
434 
435 	(void)strnvis(nicebuf, filename, MAXPATHLEN, VIS_SAFE|VIS_OCTAL);
436 
437 	if (evbuffer_add_printf(rwmap->wrbuf, "%s %s %s\n", getip(&client->ss),
438 	    client->opcode == WRQ ? "write" : "read", nicebuf) == -1)
439 		lerr(1, "rwmap printf");
440 
441 	TAILQ_INSERT_TAIL(&rwmap->clients, client, entry);
442 
443 	event_add(&rwmap->wrev, NULL);
444 }
445 
446 void
447 rewrite_req(int fd, short events, void *arg)
448 {
449 	if (evbuffer_write(rwmap->wrbuf, fd) == -1)
450 		lerr(1, "rwmap read");
451 
452 	if (EVBUFFER_LENGTH(rwmap->wrbuf))
453 		event_add(&rwmap->wrev, NULL);
454 }
455 
456 void
457 rewrite_res(int fd, short events, void *arg)
458 {
459 	struct tftp_client *client;
460 	char *filename;
461 	size_t len;
462 
463 	if (evbuffer_read(rwmap->rdbuf, fd, MAXPATHLEN) == -1)
464 		lerr(1, "rwmap read");
465 
466 	while ((filename = evbuffer_readln(rwmap->rdbuf, &len,
467 	    EVBUFFER_EOL_LF)) != NULL) {
468 		client = TAILQ_FIRST(&rwmap->clients);
469 		if (client == NULL)
470 			lerrx(1, "unexpected rwmap reply");
471 
472 		TAILQ_REMOVE(&rwmap->clients, client, entry);
473 
474 		tftp_open(client, filename);
475 
476 		free(filename);
477 	};
478 }
479 
480 int
481 tftpd_listen(const char *addr, const char *port, int family)
482 {
483 	struct tftp_server *server;
484 
485 	struct addrinfo hints, *res, *res0;
486 	int error;
487 	int s;
488 
489 	int saved_errno;
490 	const char *cause = NULL;
491 
492 	int on = 1;
493 
494 	memset(&hints, 0, sizeof(hints));
495 	hints.ai_family = family;
496 	hints.ai_socktype = SOCK_DGRAM;
497 	hints.ai_flags = AI_PASSIVE;
498 
499 	TAILQ_INIT(&tftp_servers);
500 
501 	error = getaddrinfo(addr, port, &hints, &res0);
502 	if (error) {
503 		errx(1, "%s:%s: %s", addr ? addr : "*", port,
504 		    gai_strerror(error));
505 	}
506 
507 	for (res = res0; res != NULL; res = res->ai_next) {
508 		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
509 		if (s == -1) {
510 			cause = "socket";
511 			continue;
512 		}
513 
514 		if (bind(s, res->ai_addr, res->ai_addrlen) == -1) {
515 			cause = "bind";
516 			saved_errno = errno;
517 			close(s);
518 			errno = saved_errno;
519 			continue;
520 		}
521 
522 		if (ioctl(s, FIONBIO, &on) < 0)
523 			err(1, "ioctl(FIONBIO)");
524 
525 		switch (res->ai_family) {
526 		case AF_INET:
527 			if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR,
528 			    &on, sizeof(on)) == -1)
529 				errx(1, "setsockopt(IP_RECVDSTADDR)");
530 			break;
531 		case AF_INET6:
532 			if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
533 			    &on, sizeof(on)) == -1)
534 				errx(1, "setsockopt(IPV6_RECVPKTINFO)");
535 			break;
536 		}
537 
538 		server = malloc(sizeof(*server));
539 		if (server == NULL)
540 			err(1, "malloc");
541 
542 		server->s = s;
543 		TAILQ_INSERT_TAIL(&tftp_servers, server, entry);
544 	}
545 
546 	if (TAILQ_EMPTY(&tftp_servers))
547 		err(1, "%s", cause);
548 
549 	return (0);
550 }
551 
552 void
553 tftpd_events(void)
554 {
555 	struct tftp_server *server;
556 	TAILQ_FOREACH(server, &tftp_servers, entry) {
557 		event_set(&server->ev, server->s, EV_READ | EV_PERSIST,
558 		    tftpd_recv, server);
559 		event_add(&server->ev, NULL);
560 	}
561 }
562 
563 struct tftp_client *
564 client_alloc()
565 {
566 	struct tftp_client *client;
567 
568 	client = calloc(sizeof(*client), 1);
569 	if (client == NULL)
570 		return (NULL);
571 
572 	client->segment_size = SEGSIZE;
573 	client->packet_size = SEGSIZE + 4;
574 
575 	client->tv.tv_sec = TIMEOUT;
576 	client->tv.tv_usec = 0;
577 
578 	client->sock = -1;
579 	client->file = NULL;
580 	client->newline = 0;
581 
582 	return (client);
583 }
584 
585 void
586 client_free(struct tftp_client *client)
587 {
588 	if (client->options != NULL)
589 		free(client->options);
590 
591 	if (client->file != NULL)
592 		fclose(client->file);
593 
594 	close(client->sock);
595 
596 	free(client);
597 }
598 
599 void
600 tftpd_recv(int fd, short events, void *arg)
601 {
602 	union {
603 		struct cmsghdr hdr;
604 		char	buf[CMSG_SPACE(sizeof(struct sockaddr_storage))];
605 	} cmsgbuf;
606 	struct cmsghdr *cmsg;
607 	struct msghdr msg;
608 	struct iovec iov;
609 
610 	ssize_t n;
611 	struct sockaddr_storage s_in;
612 	int dobind = 1;
613 	int on = 1;
614 
615 	struct tftphdr *tp;
616 
617 	struct tftp_client *client;
618 
619 	client = client_alloc();
620 	if (client == NULL) {
621 		char *buf = alloca(SEGSIZE_MAX + 4);
622 		/* no memory! flush this request... */
623 		recv(fd, buf, SEGSIZE_MAX + 4, 0);
624 		/* dont care if it fails */
625 		return;
626 	}
627 
628 	bzero(&msg, sizeof(msg));
629 	iov.iov_base = client->buf;
630 	iov.iov_len = client->packet_size;
631 	msg.msg_name = &client->ss;
632 	msg.msg_namelen = sizeof(client->ss);
633 	msg.msg_iov = &iov;
634 	msg.msg_iovlen = 1;
635 	msg.msg_control = &cmsgbuf.buf;
636 	msg.msg_controllen = sizeof(cmsgbuf.buf);
637 
638 	n = recvmsg(fd, &msg, 0);
639 	if (n == -1) {
640 		lwarn("recvmsg");
641 		goto err;
642 	}
643 	if (n < 4)
644 		goto err;
645 
646 	client->sock = socket(client->ss.ss_family, SOCK_DGRAM, 0);
647 	if (client->sock == -1) {
648 		lwarn("socket");
649 		goto err;
650 	}
651 	memset(&s_in, 0, sizeof(s_in));
652 	s_in.ss_family = client->ss.ss_family;
653 	s_in.ss_len = client->ss.ss_len;
654 
655 	/* get local address if possible */
656 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
657 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
658 		if (cmsg->cmsg_level == IPPROTO_IP &&
659 		    cmsg->cmsg_type == IP_RECVDSTADDR) {
660 			memcpy(&((struct sockaddr_in *)&s_in)->sin_addr,
661 			    CMSG_DATA(cmsg), sizeof(struct in_addr));
662 			if (((struct sockaddr_in *)&s_in)->sin_addr.s_addr ==
663 			    INADDR_BROADCAST)
664 				dobind = 0;
665 			break;
666 		}
667 		if (cmsg->cmsg_level == IPPROTO_IPV6 &&
668 		    cmsg->cmsg_type == IPV6_PKTINFO) {
669 			struct in6_pktinfo *ipi;
670 
671 			ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg);
672 			memcpy(&((struct sockaddr_in6 *)&s_in)->sin6_addr,
673 			    &ipi->ipi6_addr, sizeof(struct in6_addr));
674 #ifdef __KAME__
675 			if (IN6_IS_ADDR_LINKLOCAL(&ipi->ipi6_addr))
676 				((struct sockaddr_in6 *)&s_in)->sin6_scope_id =
677 				    ipi->ipi6_ifindex;
678 #endif
679 			break;
680 		}
681 	}
682 
683 	if (dobind) {
684 		setsockopt(client->sock, SOL_SOCKET, SO_REUSEADDR,
685 		    &on, sizeof(on));
686 		setsockopt(client->sock, SOL_SOCKET, SO_REUSEPORT,
687 		    &on, sizeof(on));
688 
689 		if (bind(client->sock, (struct sockaddr *)&s_in,
690 		    s_in.ss_len) < 0) {
691 			lwarn("bind to %s", getip(&s_in));
692 			goto err;
693 		}
694 	}
695 	if (connect(client->sock, (struct sockaddr *)&client->ss,
696 	    client->ss.ss_len) == -1) {
697 		lwarn("connect to %s", getip(&client->ss));
698 		goto err;
699 	}
700 
701 	if (ioctl(client->sock, FIONBIO, &on) < 0)
702 		err(1, "client ioctl(FIONBIO)");
703 
704 	tp = (struct tftphdr *)client->buf;
705 	client->opcode = ntohs(tp->th_opcode);
706 	if (client->opcode != RRQ && client->opcode != WRQ) {
707 		/* bad request */
708 		goto err;
709 	}
710 
711 	tftp(client, tp, n);
712 
713 	return;
714 
715 err:
716 	client_free(client);
717 }
718 
719 int
720 parse_options(struct tftp_client *client, char *cp, size_t size,
721     struct opt_client *options)
722 {
723 	char *option;
724 	char *ccp;
725 	int has_options = 0;
726 	int i;
727 
728 	while (++cp < client->buf + size) {
729 		for (i = 2, ccp = cp; i > 0; ccp++) {
730 			if (ccp >= client->buf + size) {
731 				/*
732 				 * Don't reject the request, just stop trying
733 				 * to parse the option and get on with it.
734 				 * Some Apple OpenFirmware versions have
735 				 * trailing garbage on the end of otherwise
736 				 * valid requests.
737 				 */
738 				return (has_options);
739 			} else if (*ccp == '\0')
740 				i--;
741 		}
742 
743 		for (option = cp; *cp; cp++)
744 			*cp = tolower(*cp);
745 
746 		for (i = 0; i < NOPT; i++) {
747 			if (strcmp(option, opt_names[i]) == 0) {
748 				options[i].o_request = ++cp;
749 				has_options = 1;
750 			}
751 		}
752 		cp = ccp - 1;
753 	}
754 
755 	return (has_options);
756 }
757 
758 /*
759  * Handle initial connection protocol.
760  */
761 void
762 tftp(struct tftp_client *client, struct tftphdr *tp, size_t size)
763 {
764 	struct opt_client *options;
765 
766 	char		*cp;
767 	int		 i, first = 1, ecode, to;
768 	struct formats	*pf;
769 	char		*mode = NULL;
770 	char		 filename[MAXPATHLEN];
771 	const char	*errstr;
772 
773 	if (size < 5) {
774 		ecode = EBADOP;
775 		goto error;
776 	}
777 
778 	cp = tp->th_stuff;
779 again:
780 	while (cp < client->buf + size) {
781 		if (*cp == '\0')
782 			break;
783 		cp++;
784 	}
785 	if (*cp != '\0') {
786 		ecode = EBADOP;
787 		goto error;
788 	}
789 	i = cp - tp->th_stuff;
790 	if (i >= sizeof(filename)) {
791 		ecode = EBADOP;
792 		goto error;
793 	}
794 	memcpy(filename, tp->th_stuff, i);
795 	filename[i] = '\0';
796 	if (first) {
797 		mode = ++cp;
798 		first = 0;
799 		goto again;
800 	}
801 	for (cp = mode; *cp; cp++)
802 		*cp = tolower(*cp);
803 
804 	for (pf = formats; pf->f_mode; pf++) {
805 		if (strcmp(pf->f_mode, mode) == 0)
806 			break;
807 	}
808 	if (pf->f_mode == 0) {
809 		ecode = EBADOP;
810 		goto error;
811 	}
812 	client->fgetc = pf->f_getc;
813 	client->fputc = pf->f_putc;
814 
815 	client->options = options = calloc(sizeof(*client->options), NOPT);
816 	if (options == NULL) {
817 		ecode = 100 + ENOMEM;
818 		goto error;
819 	}
820 
821 	if (parse_options(client, cp, size, options)) {
822 		if (options[OPT_TIMEOUT].o_request != NULL) {
823 			to = strtonum(options[OPT_TIMEOUT].o_request,
824 			    TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
825 			if (errstr) {
826 				ecode = EBADOP;
827 				goto error;
828 			}
829 			options[OPT_TIMEOUT].o_reply = client->tv.tv_sec = to;
830 		}
831 
832 		if (options[OPT_BLKSIZE].o_request) {
833 			client->segment_size = strtonum(
834 			    options[OPT_BLKSIZE].o_request,
835 			    SEGSIZE_MIN, SEGSIZE_MAX, &errstr);
836 			if (errstr) {
837 				ecode = EBADOP;
838 				goto error;
839 			}
840 			client->packet_size = client->segment_size + 4;
841 			options[OPT_BLKSIZE].o_reply = client->segment_size;
842 		}
843 	} else {
844 		free(options);
845 		client->options = NULL;
846 	}
847 
848 	if (verbose) {
849 		char nicebuf[MAXPATHLEN];
850 
851 		(void)strnvis(nicebuf, filename, MAXPATHLEN,
852 		    VIS_SAFE|VIS_OCTAL);
853 
854 		linfo("%s: %s request for '%s'", getip(&client->ss),
855 		    client->opcode == WRQ ? "write" : "read", nicebuf);
856 	}
857 
858 	if (rwmap != NULL)
859 		rewrite_map(client, filename);
860 	else
861 		tftp_open(client, filename);
862 
863 	return;
864 
865 error:
866 	nak(client, ecode);
867 }
868 
869 void
870 tftp_open(struct tftp_client *client, const char *filename)
871 {
872 	int ecode;
873 
874 	ecode = validate_access(client, filename);
875 	if (ecode)
876 		goto error;
877 
878 	if (client->options) {
879 		oack(client);
880 
881 		free(client->options);
882 		client->options = NULL;
883 	} else if (client->opcode == WRQ) {
884 		recvfile(client);
885 	} else
886 		sendfile(client);
887 
888 	return;
889 
890 error:
891 	nak(client, ecode);
892 }
893 
894 /*
895  * Validate file access.  Since we
896  * have no uid or gid, for now require
897  * file to exist and be publicly
898  * readable/writable.
899  * If we were invoked with arguments
900  * from inetd then the file must also be
901  * in one of the given directory prefixes.
902  * Note also, full path name must be
903  * given as we have no login directory.
904  */
905 int
906 validate_access(struct tftp_client *client, const char *filename)
907 {
908 	int		 mode = client->opcode;
909 	struct opt_client *options = client->options;
910 	struct stat	 stbuf;
911 	int		 fd, wmode;
912 	const char	*errstr;
913 
914 	/*
915 	 * We use a different permissions scheme if `cancreate' is
916 	 * set.
917 	 */
918 	wmode = O_TRUNC;
919 	if (stat(filename, &stbuf) < 0) {
920 		if (!cancreate)
921 			return (errno == ENOENT ? ENOTFOUND : EACCESS);
922 		else {
923 			if ((errno == ENOENT) && (mode != RRQ))
924 				wmode |= O_CREAT;
925 			else
926 				return (EACCESS);
927 		}
928 	} else {
929 		if (mode == RRQ) {
930 			if ((stbuf.st_mode & (S_IREAD >> 6)) == 0)
931 				return (EACCESS);
932 		} else {
933 			if ((stbuf.st_mode & (S_IWRITE >> 6)) == 0)
934 				return (EACCESS);
935 		}
936 	}
937 
938 	if (options != NULL && options[OPT_TSIZE].o_request) {
939 		if (mode == RRQ)
940 			options[OPT_TSIZE].o_reply = stbuf.st_size;
941 		else {
942 			/* allows writes of 65535 blocks * SEGSIZE_MAX bytes */
943 			options[OPT_TSIZE].o_reply =
944 			    strtonum(options[OPT_TSIZE].o_request,
945 			    1, 65535LL * SEGSIZE_MAX, &errstr);
946 			if (errstr)
947 				return (EOPTNEG);
948 		}
949 	}
950 	fd = open(filename, mode == RRQ ? O_RDONLY : (O_WRONLY|wmode), 0666);
951 	if (fd < 0)
952 		return (errno + 100);
953 	/*
954 	 * If the file was created, set default permissions.
955 	 */
956 	if ((wmode & O_CREAT) && fchmod(fd, 0666) < 0) {
957 		int serrno = errno;
958 
959 		close(fd);
960 		unlink(filename);
961 
962 		return (serrno + 100);
963 	}
964 	client->file = fdopen(fd, mode == RRQ ? "r" : "w");
965 	if (client->file == NULL) {
966 		close(fd);
967 		return (errno + 100);
968 	}
969 
970 	return (0);
971 }
972 
973 int
974 fget_octet(struct tftp_client *client)
975 {
976 	return (getc(client->file));
977 }
978 
979 int
980 fput_octet(struct tftp_client *client, int c)
981 {
982 	return (putc(c, client->file));
983 }
984 
985 int
986 fget_netascii(struct tftp_client *client)
987 {
988 	int c = -1;
989 
990 	switch (client->newline) {
991 	case 0:
992 		c = getc(client->file);
993 		if (c == EOF)
994 			break;
995 
996 		if (c == '\n' || c == '\r') {
997 			client->newline = c;
998 			c = '\r';
999 		}
1000 		break;
1001 	case '\n':
1002 		client->newline = 0;
1003 		c = '\n';
1004 		break;
1005 	case '\r':
1006 		client->newline = 0;
1007 		c = '\0';
1008 		break;
1009 	}
1010 
1011 	return (c);
1012 }
1013 
1014 int
1015 fput_netascii(struct tftp_client *client, int c)
1016 {
1017 	if (client->newline == '\r') {
1018 		client->newline = 0;
1019 
1020 		if (c == '\0')
1021 			c = '\r';
1022 
1023 	} else if (c == '\r') {
1024 		client->newline = c;
1025 		return (c);
1026 	}
1027 
1028 	return (putc(c, client->file));
1029 }
1030 
1031 void
1032 sendfile(struct tftp_client *client)
1033 {
1034 	event_set(&client->sev, client->sock, EV_READ, tftp_rrq_ack, client);
1035 	client->block = 1;
1036 
1037 	file_read(client);
1038 }
1039 
1040 void
1041 file_read(struct tftp_client *client)
1042 {
1043 	u_int8_t *buf;
1044 	struct tftphdr *dp;
1045 	int i;
1046 	int c;
1047 
1048 	dp = (struct tftphdr *)client->buf;
1049 	dp->th_opcode = htons((u_short)DATA);
1050 	dp->th_block = htons(client->block);
1051 	buf = (u_int8_t *)dp->th_data;
1052 
1053 	for (i = 0; i < client->segment_size; i++) {
1054 		c = client->fgetc(client);
1055 		if (c == EOF) {
1056 			if (ferror(client->file)) {
1057 				nak(client, 100 + EIO);
1058 				return;
1059 			}
1060 
1061 			break;
1062 		}
1063 		buf[i] = c;
1064 	}
1065 
1066 	client->buflen = i + 4;
1067 	client->retries = RETRIES;
1068 
1069 	if (send(client->sock, client->buf, client->buflen, 0) == -1) {
1070 		lwarn("send(block)");
1071 		client_free(client);
1072 		return;
1073 	}
1074 
1075 	event_add(&client->sev, &client->tv);
1076 }
1077 
1078 void
1079 tftp_rrq_ack(int fd, short events, void *arg)
1080 {
1081 	struct tftp_client *client = arg;
1082 	struct tftphdr *ap; /* ack packet */
1083 	char rbuf[SEGSIZE_MIN];
1084 	ssize_t n;
1085 
1086 	if (events & EV_TIMEOUT) {
1087 		if (retry(client) == -1) {
1088 			lwarn("%s: retry", getip(&client->ss));
1089 			goto done;
1090 		}
1091 
1092 		return;
1093 	}
1094 
1095 	n = recv(fd, rbuf, sizeof(rbuf), 0);
1096 	if (n == -1) {
1097 		switch (errno) {
1098 		case EINTR:
1099 		case EAGAIN:
1100 			event_add(&client->sev, &client->tv);
1101 			return;
1102 
1103 		default:
1104 			lwarn("%s: recv", getip(&client->ss));
1105 			goto done;
1106 		}
1107 	}
1108 
1109 	ap = (struct tftphdr *)rbuf;
1110 	ap->th_opcode = ntohs((u_short)ap->th_opcode);
1111 	ap->th_block = ntohs((u_short)ap->th_block);
1112 
1113 	switch (ap->th_opcode) {
1114 	case ERROR:
1115 		goto done;
1116 	case ACK:
1117 		break;
1118 	default:
1119 		goto retry;
1120 	}
1121 
1122 	if (ap->th_block != client->block) {
1123 		if (tftp_flush(client) == -1) {
1124 			lwarnx("%s: flush", getip(&client->ss));
1125 			goto done;
1126 		}
1127 
1128 		if (ap->th_block != (client->block - 1))
1129 			goto done;
1130 
1131 		goto retry;
1132 	}
1133 
1134 	if (client->buflen != client->packet_size) {
1135 		/* this was the last packet in the stream */
1136 		goto done;
1137 	}
1138 
1139 	client->block++;
1140 	file_read(client);
1141 	return;
1142 
1143 retry:
1144 	event_add(&client->sev, &client->tv);
1145 	return;
1146 
1147 done:
1148 	client_free(client);
1149 }
1150 
1151 int
1152 tftp_flush(struct tftp_client *client)
1153 {
1154 	char rbuf[SEGSIZE_MIN];
1155 	ssize_t n;
1156 
1157 	for (;;) {
1158 		n = recv(client->sock, rbuf, sizeof(rbuf), 0);
1159 		if (n == -1) {
1160 			switch (errno) {
1161 			case EAGAIN:
1162 				return (0);
1163 
1164 			case EINTR:
1165 				break;
1166 
1167 			default:
1168 				return (-1);
1169 			}
1170 		}
1171 	}
1172 }
1173 
1174 void
1175 recvfile(struct tftp_client *client)
1176 {
1177 	event_set(&client->sev, client->sock, EV_READ, tftp_wrq, client);
1178 	tftp_wrq_ack(client);
1179 }
1180 
1181 int
1182 tftp_wrq_ack_packet(struct tftp_client *client)
1183 {
1184 	struct tftphdr *ap; /* ack packet */
1185 
1186 	ap = (struct tftphdr *)client->buf;
1187 	ap->th_opcode = htons((u_short)ACK);
1188 	ap->th_block = htons(client->block);
1189 
1190 	client->buflen = 4;
1191 	client->retries = RETRIES;
1192 
1193 	return (send(client->sock, client->buf, client->buflen, 0) != 4);
1194 }
1195 
1196 void
1197 tftp_wrq_ack(struct tftp_client *client)
1198 {
1199 	if (tftp_wrq_ack_packet(client) != 0) {
1200 		lwarn("tftp wrq ack");
1201 		client_free(client);
1202 		return;
1203 	}
1204 
1205 	client->block++;
1206 	event_add(&client->sev, &client->tv);
1207 }
1208 
1209 void
1210 tftp_wrq(int fd, short events, void *arg)
1211 {
1212 	char wbuf[SEGSIZE_MAX + 4];
1213 	struct tftp_client *client = arg;
1214 	struct tftphdr *dp;
1215 	ssize_t n;
1216 	int i;
1217 
1218 	if (events & EV_TIMEOUT) {
1219 		if (retry(client) == -1) {
1220 			lwarn("%s", getip(&client->ss));
1221 			goto done;
1222 		}
1223 
1224 		return;
1225 	}
1226 
1227 	n = recv(fd, wbuf, client->packet_size, 0);
1228 	if (n == -1) {
1229 		switch (errno) {
1230 		case EINTR:
1231 		case EAGAIN:
1232 			goto retry;
1233 
1234 		default:
1235 			lwarn("tftp_wrq recv");
1236 			goto done;
1237 		}
1238 	}
1239 
1240 	if (n < 4)
1241 		goto done;
1242 
1243 	dp = (struct tftphdr *)wbuf;
1244 	dp->th_opcode = ntohs((u_short)dp->th_opcode);
1245 	dp->th_block = ntohs((u_short)dp->th_block);
1246 
1247 	switch (dp->th_opcode) {
1248 	case ERROR:
1249 		goto done;
1250 	case DATA:
1251 		break;
1252 	default:
1253 		goto retry;
1254 	}
1255 
1256 	if (dp->th_block != client->block) {
1257 		if (tftp_flush(client) == -1) {
1258 			lwarnx("%s: flush", getip(&client->ss));
1259 			goto done;
1260 		}
1261 
1262 		if (dp->th_block != (client->block - 1))
1263 			goto done;
1264 
1265 		goto retry;
1266 	}
1267 
1268 	for (i = 4; i < n; i++) {
1269 		if (client->fputc(client, wbuf[i]) == EOF) {
1270 			lwarn("tftp wrq");
1271 			goto done;
1272 		}
1273 	}
1274 
1275 	if (n < client->packet_size) {
1276 		tftp_wrq_ack_packet(client);
1277 		event_set(&client->sev, client->sock, EV_READ,
1278 		    tftp_wrq_end, client);
1279 		event_add(&client->sev, &client->tv);
1280 		return;
1281 	}
1282 
1283 	tftp_wrq_ack(client);
1284 	return;
1285 
1286 retry:
1287 	event_add(&client->sev, &client->tv);
1288 	return;
1289 done:
1290 	client_free(client);
1291 }
1292 
1293 void
1294 tftp_wrq_end(int fd, short events, void *arg)
1295 {
1296 	char wbuf[SEGSIZE_MAX + 4];
1297 	struct tftp_client *client = arg;
1298 	struct tftphdr *dp;
1299 	ssize_t n;
1300 
1301 	if (events & EV_TIMEOUT) {
1302 		/* this was the last packet, we can clean up */
1303 		goto done;
1304 	}
1305 
1306 	n = recv(fd, wbuf, client->packet_size, 0);
1307 	if (n == -1) {
1308 		switch (errno) {
1309 		case EINTR:
1310 		case EAGAIN:
1311 			goto retry;
1312 
1313 		default:
1314 			lwarn("tftp_wrq_end recv");
1315 			goto done;
1316 		}
1317 	}
1318 
1319 	if (n < 4)
1320 		goto done;
1321 
1322 	dp = (struct tftphdr *)wbuf;
1323 	dp->th_opcode = ntohs((u_short)dp->th_opcode);
1324 	dp->th_block = ntohs((u_short)dp->th_block);
1325 
1326 	switch (dp->th_opcode) {
1327 	case ERROR:
1328 		goto done;
1329 	case DATA:
1330 		break;
1331 	default:
1332 		goto retry;
1333 	}
1334 
1335 	if (dp->th_block != client->block)
1336 		goto done;
1337 
1338 retry:
1339 	if (retry(client) == -1) {
1340 		lwarn("%s", getip(&client->ss));
1341 		goto done;
1342 	}
1343 	return;
1344 done:
1345 	client_free(client);
1346 	return;
1347 }
1348 
1349 
1350 /*
1351  * Send a nak packet (error message).
1352  * Error code passed in is one of the
1353  * standard TFTP codes, or a UNIX errno
1354  * offset by 100.
1355  */
1356 void
1357 nak(struct tftp_client *client, int error)
1358 {
1359 	struct tftphdr	*tp;
1360 	struct errmsg	*pe;
1361 	size_t		 length;
1362 
1363 	tp = (struct tftphdr *)client->buf;
1364 	tp->th_opcode = htons((u_short)ERROR);
1365 	tp->th_code = htons((u_short)error);
1366 
1367 	for (pe = errmsgs; pe->e_code >= 0; pe++) {
1368 		if (pe->e_code == error)
1369 			break;
1370 	}
1371 	if (pe->e_code < 0) {
1372 		pe->e_msg = strerror(error - 100);
1373 		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
1374 	}
1375 
1376 	length = strlcpy(tp->th_msg, pe->e_msg, client->packet_size - 5) + 5;
1377 	if (length > client->packet_size)
1378 		length = client->packet_size;
1379 
1380 	if (send(client->sock, client->buf, length, 0) != length)
1381 		lwarn("nak");
1382 
1383 	client_free(client);
1384 }
1385 
1386 /*
1387  * Send an oack packet (option acknowledgement).
1388  */
1389 void
1390 oack(struct tftp_client *client)
1391 {
1392 	struct opt_client *options = client->options;
1393 	struct tftphdr *tp;
1394 	char *bp;
1395 	int i, n, size;
1396 
1397 	tp = (struct tftphdr *)client->buf;
1398 	bp = (char *)tp->th_stuff;
1399 	size = client->packet_size - 2;
1400 
1401 	tp->th_opcode = htons((u_short)OACK);
1402 	for (i = 0; i < NOPT; i++) {
1403 		if (options[i].o_request == NULL)
1404 			continue;
1405 
1406 		n = snprintf(bp, size, "%s%c%lld", opt_names[i], '\0',
1407 		    options[i].o_reply);
1408 		if (n == -1 || n >= size) {
1409 			lwarn("oack: no buffer space");
1410 			goto error;
1411 		}
1412 
1413 		bp += n + 1;
1414 		size -= n + 1;
1415 		if (size < 0) {
1416 			lwarn("oack: no buffer space");
1417 			goto error;
1418 		}
1419 	}
1420 
1421 	client->buflen = bp - client->buf;
1422 	client->retries = RETRIES;
1423 
1424 	if (send(client->sock, client->buf, client->buflen, 0) == -1) {
1425 		lwarn("oack");
1426 		goto error;
1427 	}
1428 
1429 	/* no client ACK for write requests with options */
1430 	if (client->opcode == WRQ) {
1431 		client->block = 1;
1432 		event_set(&client->sev, client->sock, EV_READ,
1433 		    tftp_wrq, client);
1434 	} else
1435 		event_set(&client->sev, client->sock, EV_READ,
1436 		    oack_done, client);
1437 
1438 	event_add(&client->sev, &client->tv);
1439 	return;
1440 
1441 error:
1442 	client_free(client);
1443 }
1444 
1445 int
1446 retry(struct tftp_client *client)
1447 {
1448 	if (--client->retries == 0) {
1449 		errno = ETIMEDOUT;
1450 		return (-1);
1451 	}
1452 
1453 	if (send(client->sock, client->buf, client->buflen, 0) == -1)
1454 		return (-1);
1455 
1456 	event_add(&client->sev, &client->tv);
1457 
1458 	return (0);
1459 }
1460 
1461 void
1462 oack_done(int fd, short events, void *arg)
1463 {
1464 	struct tftp_client *client = arg;
1465 	struct tftphdr *ap;
1466 	ssize_t n;
1467 
1468 	if (events & EV_TIMEOUT) {
1469 		if (retry(client) == -1) {
1470 			lwarn("%s", getip(&client->ss));
1471 			goto done;
1472 		}
1473 
1474 		return;
1475 	}
1476 
1477 	n = recv(client->sock, client->buf, client->packet_size, 0);
1478 	if (n == -1) {
1479 		switch (errno) {
1480 		case EINTR:
1481 		case EAGAIN:
1482 			event_add(&client->sev, &client->tv);
1483 			return;
1484 
1485 		default:
1486 			lwarn("%s: recv", getip(&client->ss));
1487 			goto done;
1488 		}
1489 	}
1490 
1491 	if (n < 4)
1492 		goto done;
1493 
1494 	ap = (struct tftphdr *)client->buf;
1495 	ap->th_opcode = ntohs((u_short)ap->th_opcode);
1496 	ap->th_block = ntohs((u_short)ap->th_block);
1497 
1498 	if (ap->th_opcode != ACK || ap->th_block != 0)
1499 		goto done;
1500 
1501 	sendfile(client);
1502 	return;
1503 
1504 done:
1505 	client_free(client);
1506 }
1507 
1508 const char *
1509 getip(void *s)
1510 {
1511 	struct sockaddr *sa = s;
1512 	static char hbuf[NI_MAXHOST];
1513 
1514 	if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf),
1515 	    NULL, 0, NI_NUMERICHOST))
1516 		strlcpy(hbuf, "0.0.0.0", sizeof(hbuf));
1517 
1518 	return(hbuf);
1519 }
1520 
1521 void
1522 syslog_vstrerror(int e, int priority, const char *fmt, va_list ap)
1523 {
1524 	char *s;
1525 
1526 	if (vasprintf(&s, fmt, ap) == -1) {
1527 		syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror");
1528 		exit(1);
1529 	}
1530 
1531 	syslog(priority, "%s: %s", s, strerror(e));
1532 
1533 	free(s);
1534 }
1535 
1536 void
1537 syslog_err(int ecode, const char *fmt, ...)
1538 {
1539 	va_list ap;
1540 
1541 	va_start(ap, fmt);
1542 	syslog_vstrerror(errno, LOG_EMERG, fmt, ap);
1543 	va_end(ap);
1544 
1545 	exit(ecode);
1546 }
1547 
1548 void
1549 syslog_errx(int ecode, const char *fmt, ...)
1550 {
1551 	va_list ap;
1552 
1553 	va_start(ap, fmt);
1554 	vsyslog(LOG_WARNING, fmt, ap);
1555 	va_end(ap);
1556 
1557 	exit(ecode);
1558 }
1559 
1560 void
1561 syslog_warn(const char *fmt, ...)
1562 {
1563 	va_list ap;
1564 
1565 	va_start(ap, fmt);
1566 	syslog_vstrerror(errno, LOG_WARNING, fmt, ap);
1567 	va_end(ap);
1568 }
1569 
1570 void
1571 syslog_warnx(const char *fmt, ...)
1572 {
1573 	va_list ap;
1574 
1575 	va_start(ap, fmt);
1576 	vsyslog(LOG_WARNING, fmt, ap);
1577 	va_end(ap);
1578 }
1579 
1580 void
1581 syslog_info(const char *fmt, ...)
1582 {
1583 	va_list ap;
1584 
1585 	va_start(ap, fmt);
1586 	vsyslog(LOG_INFO, fmt, ap);
1587 	va_end(ap);
1588 }
1589 
1590