tftpd.c revision 130839
1/*
2 * Copyright (c) 1983, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1983, 1993\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41#if 0
42static char sccsid[] = "@(#)tftpd.c	8.1 (Berkeley) 6/4/93";
43#endif
44static const char rcsid[] =
45  "$FreeBSD: head/libexec/tftpd/tftpd.c 130839 2004-06-21 10:47:12Z brian $";
46#endif /* not lint */
47
48/*
49 * Trivial file transfer protocol server.
50 *
51 * This version includes many modifications by Jim Guyton
52 * <guyton@rand-unix>.
53 */
54
55#include <sys/param.h>
56#include <sys/ioctl.h>
57#include <sys/stat.h>
58#include <sys/socket.h>
59#include <sys/types.h>
60#include <sys/time.h>
61
62#include <netinet/in.h>
63#include <arpa/tftp.h>
64#include <arpa/inet.h>
65
66#include <ctype.h>
67#include <errno.h>
68#include <fcntl.h>
69#include <libutil.h>
70#include <netdb.h>
71#include <pwd.h>
72#include <setjmp.h>
73#include <signal.h>
74#include <stdio.h>
75#include <stdlib.h>
76#include <string.h>
77#include <syslog.h>
78#include <unistd.h>
79
80#include "tftpsubs.h"
81
82#define	TIMEOUT		5
83#define	MAX_TIMEOUTS	5
84
85int	peer;
86int	rexmtval = TIMEOUT;
87int	max_rexmtval = 2*TIMEOUT;
88
89#define	PKTSIZE	SEGSIZE+4
90char	buf[PKTSIZE];
91char	ackbuf[PKTSIZE];
92struct	sockaddr_storage from;
93int	fromlen;
94
95void	tftp(struct tftphdr *, int);
96static void unmappedaddr(struct sockaddr_in6 *);
97
98/*
99 * Null-terminated directory prefix list for absolute pathname requests and
100 * search list for relative pathname requests.
101 *
102 * MAXDIRS should be at least as large as the number of arguments that
103 * inetd allows (currently 20).
104 */
105#define MAXDIRS	20
106static struct dirlist {
107	const char	*name;
108	int	len;
109} dirs[MAXDIRS+1];
110static int	suppress_naks;
111static int	logging;
112static int	ipchroot;
113static int	create_new = 0;
114static mode_t	mask = S_IWGRP|S_IWOTH;
115
116static const char *errtomsg(int);
117static void  nak(int);
118static void  oack(void);
119
120static void  timer(int);
121static void  justquit(int);
122
123int
124main(int argc, char *argv[])
125{
126	struct tftphdr *tp;
127	int n;
128	int ch, on;
129	struct sockaddr_storage me;
130	int len;
131	char *chroot_dir = NULL;
132	struct passwd *nobody;
133	const char *chuser = "nobody";
134
135	tzset();			/* syslog in localtime */
136
137	openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
138	while ((ch = getopt(argc, argv, "cClns:u:Uw")) != -1) {
139		switch (ch) {
140		case 'c':
141			ipchroot = 1;
142			break;
143		case 'C':
144			ipchroot = 2;
145			break;
146		case 'l':
147			logging = 1;
148			break;
149		case 'n':
150			suppress_naks = 1;
151			break;
152		case 's':
153			chroot_dir = optarg;
154			break;
155		case 'u':
156			chuser = optarg;
157			break;
158		case 'U':
159			mask = strtol(optarg, NULL, 0);
160			break;
161		case 'w':
162			create_new = 1;
163			break;
164		default:
165			syslog(LOG_WARNING, "ignoring unknown option -%c", ch);
166		}
167	}
168	if (optind < argc) {
169		struct dirlist *dirp;
170
171		/* Get list of directory prefixes. Skip relative pathnames. */
172		for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS];
173		     optind++) {
174			if (argv[optind][0] == '/') {
175				dirp->name = argv[optind];
176				dirp->len  = strlen(dirp->name);
177				dirp++;
178			}
179		}
180	}
181	else if (chroot_dir) {
182		dirs->name = "/";
183		dirs->len = 1;
184	}
185	if (ipchroot > 0 && chroot_dir == NULL) {
186		syslog(LOG_ERR, "-c requires -s");
187		exit(1);
188	}
189
190	umask(mask);
191
192	on = 1;
193	if (ioctl(0, FIONBIO, &on) < 0) {
194		syslog(LOG_ERR, "ioctl(FIONBIO): %m");
195		exit(1);
196	}
197	fromlen = sizeof (from);
198	n = recvfrom(0, buf, sizeof (buf), 0,
199	    (struct sockaddr *)&from, &fromlen);
200	if (n < 0) {
201		syslog(LOG_ERR, "recvfrom: %m");
202		exit(1);
203	}
204	/*
205	 * Now that we have read the message out of the UDP
206	 * socket, we fork and exit.  Thus, inetd will go back
207	 * to listening to the tftp port, and the next request
208	 * to come in will start up a new instance of tftpd.
209	 *
210	 * We do this so that inetd can run tftpd in "wait" mode.
211	 * The problem with tftpd running in "nowait" mode is that
212	 * inetd may get one or more successful "selects" on the
213	 * tftp port before we do our receive, so more than one
214	 * instance of tftpd may be started up.  Worse, if tftpd
215	 * break before doing the above "recvfrom", inetd would
216	 * spawn endless instances, clogging the system.
217	 */
218	{
219		int pid;
220		int i, j;
221
222		for (i = 1; i < 20; i++) {
223		    pid = fork();
224		    if (pid < 0) {
225				sleep(i);
226				/*
227				 * flush out to most recently sent request.
228				 *
229				 * This may drop some request, but those
230				 * will be resent by the clients when
231				 * they timeout.  The positive effect of
232				 * this flush is to (try to) prevent more
233				 * than one tftpd being started up to service
234				 * a single request from a single client.
235				 */
236				j = sizeof from;
237				i = recvfrom(0, buf, sizeof (buf), 0,
238				    (struct sockaddr *)&from, &j);
239				if (i > 0) {
240					n = i;
241					fromlen = j;
242				}
243		    } else {
244				break;
245		    }
246		}
247		if (pid < 0) {
248			syslog(LOG_ERR, "fork: %m");
249			exit(1);
250		} else if (pid != 0) {
251			exit(0);
252		}
253	}
254
255	/*
256	 * Since we exit here, we should do that only after the above
257	 * recvfrom to keep inetd from constantly forking should there
258	 * be a problem.  See the above comment about system clogging.
259	 */
260	if (chroot_dir) {
261		if (ipchroot > 0) {
262			char *tempchroot;
263			struct stat sb;
264			int statret;
265			struct sockaddr_storage ss;
266			char hbuf[NI_MAXHOST];
267
268			memcpy(&ss, &from, from.ss_len);
269			unmappedaddr((struct sockaddr_in6 *)&ss);
270			getnameinfo((struct sockaddr *)&ss, ss.ss_len,
271				    hbuf, sizeof(hbuf), NULL, 0,
272				    NI_NUMERICHOST | NI_WITHSCOPEID);
273			asprintf(&tempchroot, "%s/%s", chroot_dir, hbuf);
274			if (ipchroot == 2)
275				statret = stat(tempchroot, &sb);
276			if (ipchroot == 1 ||
277			    (statret == 0 && (sb.st_mode & S_IFDIR)))
278				chroot_dir = tempchroot;
279		}
280		/* Must get this before chroot because /etc might go away */
281		if ((nobody = getpwnam(chuser)) == NULL) {
282			syslog(LOG_ERR, "%s: no such user", chuser);
283			exit(1);
284		}
285		if (chroot(chroot_dir)) {
286			syslog(LOG_ERR, "chroot: %s: %m", chroot_dir);
287			exit(1);
288		}
289		chdir( "/" );
290		setuid(nobody->pw_uid);
291		setgroups(1, &nobody->pw_gid);
292	}
293
294	len = sizeof(me);
295	if (getsockname(0, (struct sockaddr *)&me, &len) == 0) {
296		switch (me.ss_family) {
297		case AF_INET:
298			((struct sockaddr_in *)&me)->sin_port = 0;
299			break;
300		case AF_INET6:
301			((struct sockaddr_in6 *)&me)->sin6_port = 0;
302			break;
303		default:
304			/* unsupported */
305			break;
306		}
307	} else {
308		memset(&me, 0, sizeof(me));
309		me.ss_family = from.ss_family;
310		me.ss_len = from.ss_len;
311	}
312	alarm(0);
313	close(0);
314	close(1);
315	peer = socket(from.ss_family, SOCK_DGRAM, 0);
316	if (peer < 0) {
317		syslog(LOG_ERR, "socket: %m");
318		exit(1);
319	}
320	if (bind(peer, (struct sockaddr *)&me, me.ss_len) < 0) {
321		syslog(LOG_ERR, "bind: %m");
322		exit(1);
323	}
324	if (connect(peer, (struct sockaddr *)&from, from.ss_len) < 0) {
325		syslog(LOG_ERR, "connect: %m");
326		exit(1);
327	}
328	tp = (struct tftphdr *)buf;
329	tp->th_opcode = ntohs(tp->th_opcode);
330	if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
331		tftp(tp, n);
332	exit(1);
333}
334
335static void
336reduce_path(char *fn)
337{
338	char *slash, *ptr;
339
340	/* Reduce all "/+./" to "/" (just in case we've got "/./../" later */
341	while ((slash = strstr(fn, "/./")) != NULL) {
342		for (ptr = slash; ptr > fn && ptr[-1] == '/'; ptr--)
343			;
344		slash += 2;
345		while (*slash)
346			*++ptr = *++slash;
347	}
348
349	/* Now reduce all "/something/+../" to "/" */
350	while ((slash = strstr(fn, "/../")) != NULL) {
351		if (slash == fn)
352			break;
353		for (ptr = slash; ptr > fn && ptr[-1] == '/'; ptr--)
354			;
355		for (ptr--; ptr >= fn; ptr--)
356			if (*ptr == '/')
357				break;
358		if (ptr < fn)
359			break;
360		slash += 3;
361		while (*slash)
362			*++ptr = *++slash;
363	}
364}
365
366struct formats;
367int	validate_access(char **, int);
368void	xmitfile(struct formats *);
369void	recvfile(struct formats *);
370
371struct formats {
372	const char	*f_mode;
373	int	(*f_validate)(char **, int);
374	void	(*f_send)(struct formats *);
375	void	(*f_recv)(struct formats *);
376	int	f_convert;
377} formats[] = {
378	{ "netascii",	validate_access,	xmitfile,	recvfile, 1 },
379	{ "octet",	validate_access,	xmitfile,	recvfile, 0 },
380#ifdef notdef
381	{ "mail",	validate_user,		sendmail,	recvmail, 1 },
382#endif
383	{ 0,		NULL,			NULL,		NULL,	  0 }
384};
385
386struct options {
387	const char	*o_type;
388	char	*o_request;
389	int	o_reply;	/* turn into union if need be */
390} options[] = {
391	{ "tsize",	NULL, 0 },		/* OPT_TSIZE */
392	{ "timeout",	NULL, 0 },		/* OPT_TIMEOUT */
393	{ NULL,		NULL, 0 }
394};
395
396enum opt_enum {
397	OPT_TSIZE = 0,
398	OPT_TIMEOUT,
399};
400
401/*
402 * Handle initial connection protocol.
403 */
404void
405tftp(struct tftphdr *tp, int size)
406{
407	char *cp;
408	int i, first = 1, has_options = 0, ecode;
409	struct formats *pf;
410	char *filename, *mode, *option, *ccp;
411	char fnbuf[PATH_MAX], resolved_fnbuf[PATH_MAX];
412
413	cp = tp->th_stuff;
414again:
415	while (cp < buf + size) {
416		if (*cp == '\0')
417			break;
418		cp++;
419	}
420	if (*cp != '\0') {
421		nak(EBADOP);
422		exit(1);
423	}
424	i = cp - tp->th_stuff;
425	if (i >= sizeof(fnbuf)) {
426		nak(EBADOP);
427		exit(1);
428	}
429	memcpy(fnbuf, tp->th_stuff, i);
430	fnbuf[i] = '\0';
431	reduce_path(fnbuf);
432	filename = fnbuf;
433	if (first) {
434		mode = ++cp;
435		first = 0;
436		goto again;
437	}
438	for (cp = mode; *cp; cp++)
439		if (isupper(*cp))
440			*cp = tolower(*cp);
441	for (pf = formats; pf->f_mode; pf++)
442		if (strcmp(pf->f_mode, mode) == 0)
443			break;
444	if (pf->f_mode == 0) {
445		nak(EBADOP);
446		exit(1);
447	}
448	while (++cp < buf + size) {
449		for (i = 2, ccp = cp; i > 0; ccp++) {
450			if (ccp >= buf + size) {
451				/*
452				 * Don't reject the request, just stop trying
453				 * to parse the option and get on with it.
454				 * Some Apple OpenFirmware versions have
455				 * trailing garbage on the end of otherwise
456				 * valid requests.
457				 */
458				goto option_fail;
459			} else if (*ccp == '\0')
460				i--;
461		}
462		for (option = cp; *cp; cp++)
463			if (isupper(*cp))
464				*cp = tolower(*cp);
465		for (i = 0; options[i].o_type != NULL; i++)
466			if (strcmp(option, options[i].o_type) == 0) {
467				options[i].o_request = ++cp;
468				has_options = 1;
469			}
470		cp = ccp-1;
471	}
472
473option_fail:
474	if (options[OPT_TIMEOUT].o_request) {
475		int to = atoi(options[OPT_TIMEOUT].o_request);
476		if (to < 1 || to > 255) {
477			nak(EBADOP);
478			exit(1);
479		}
480		else if (to <= max_rexmtval)
481			options[OPT_TIMEOUT].o_reply = rexmtval = to;
482		else
483			options[OPT_TIMEOUT].o_request = NULL;
484	}
485
486	ecode = (*pf->f_validate)(&filename, tp->th_opcode);
487	if (has_options && ecode == 0)
488		oack();
489	if (logging) {
490		char hbuf[NI_MAXHOST];
491
492		getnameinfo((struct sockaddr *)&from, from.ss_len,
493			    hbuf, sizeof(hbuf), NULL, 0,
494			    NI_WITHSCOPEID);
495		syslog(LOG_INFO, "%s: %s request for %s: %s", hbuf,
496			tp->th_opcode == WRQ ? "write" : "read",
497			filename, errtomsg(ecode));
498	}
499	if (ecode) {
500		/*
501		 * Avoid storms of naks to a RRQ broadcast for a relative
502		 * bootfile pathname from a diskless Sun.
503		 */
504		if (suppress_naks && *filename != '/' && ecode == ENOTFOUND)
505			exit(0);
506		nak(ecode);
507		exit(1);
508	}
509	if (tp->th_opcode == WRQ)
510		(*pf->f_recv)(pf);
511	else
512		(*pf->f_send)(pf);
513	exit(0);
514}
515
516
517FILE *file;
518
519/*
520 * Validate file access.  Since we
521 * have no uid or gid, for now require
522 * file to exist and be publicly
523 * readable/writable.
524 * If we were invoked with arguments
525 * from inetd then the file must also be
526 * in one of the given directory prefixes.
527 * Note also, full path name must be
528 * given as we have no login directory.
529 */
530int
531validate_access(char **filep, int mode)
532{
533	struct stat stbuf;
534	int	fd;
535	struct dirlist *dirp;
536	static char pathname[MAXPATHLEN];
537	char *filename = *filep;
538
539	/*
540	 * Prevent tricksters from getting around the directory restrictions
541	 */
542	if (strstr(filename, "/../"))
543		return (EACCESS);
544
545	if (*filename == '/') {
546		/*
547		 * Allow the request if it's in one of the approved locations.
548		 * Special case: check the null prefix ("/") by looking
549		 * for length = 1 and relying on the arg. processing that
550		 * it's a /.
551		 */
552		for (dirp = dirs; dirp->name != NULL; dirp++) {
553			if (dirp->len == 1 ||
554			    (!strncmp(filename, dirp->name, dirp->len) &&
555			     filename[dirp->len] == '/'))
556				    break;
557		}
558		/* If directory list is empty, allow access to any file */
559		if (dirp->name == NULL && dirp != dirs)
560			return (EACCESS);
561		if (stat(filename, &stbuf) < 0)
562			return (errno == ENOENT ? ENOTFOUND : EACCESS);
563		if ((stbuf.st_mode & S_IFMT) != S_IFREG)
564			return (ENOTFOUND);
565		if (mode == RRQ) {
566			if ((stbuf.st_mode & S_IROTH) == 0)
567				return (EACCESS);
568		} else {
569			if ((stbuf.st_mode & S_IWOTH) == 0)
570				return (EACCESS);
571		}
572	} else {
573		int err;
574
575		/*
576		 * Relative file name: search the approved locations for it.
577		 * Don't allow write requests that avoid directory
578		 * restrictions.
579		 */
580
581		if (!strncmp(filename, "../", 3))
582			return (EACCESS);
583
584		/*
585		 * If the file exists in one of the directories and isn't
586		 * readable, continue looking. However, change the error code
587		 * to give an indication that the file exists.
588		 */
589		err = ENOTFOUND;
590		for (dirp = dirs; dirp->name != NULL; dirp++) {
591			snprintf(pathname, sizeof(pathname), "%s/%s",
592				dirp->name, filename);
593			if (stat(pathname, &stbuf) == 0 &&
594			    (stbuf.st_mode & S_IFMT) == S_IFREG) {
595				if ((stbuf.st_mode & S_IROTH) != 0) {
596					break;
597				}
598				err = EACCESS;
599			}
600		}
601		if (dirp->name != NULL)
602			*filep = filename = pathname;
603		else if (mode == RRQ)
604			return (err);
605	}
606	if (options[OPT_TSIZE].o_request) {
607		if (mode == RRQ)
608			options[OPT_TSIZE].o_reply = stbuf.st_size;
609		else
610			/* XXX Allows writes of all sizes. */
611			options[OPT_TSIZE].o_reply =
612				atoi(options[OPT_TSIZE].o_request);
613	}
614	if (mode == RRQ)
615		fd = open(filename, O_RDONLY);
616	else {
617		if (create_new)
618			fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0666);
619		else
620			fd = open(filename, O_WRONLY|O_TRUNC);
621	}
622	if (fd < 0)
623		return (errno + 100);
624	file = fdopen(fd, (mode == RRQ)? "r":"w");
625	if (file == NULL) {
626		close(fd);
627		return (errno + 100);
628	}
629	return (0);
630}
631
632int	timeouts;
633jmp_buf	timeoutbuf;
634
635void
636timer(int sig __unused)
637{
638	if (++timeouts > MAX_TIMEOUTS)
639		exit(1);
640	longjmp(timeoutbuf, 1);
641}
642
643/*
644 * Send the requested file.
645 */
646void
647xmitfile(struct formats *pf)
648{
649	struct tftphdr *dp;
650	struct tftphdr *ap;    /* ack packet */
651	int size, n;
652	volatile unsigned short block;
653
654	signal(SIGALRM, timer);
655	dp = r_init();
656	ap = (struct tftphdr *)ackbuf;
657	block = 1;
658	do {
659		size = readit(file, &dp, pf->f_convert);
660		if (size < 0) {
661			nak(errno + 100);
662			goto abort;
663		}
664		dp->th_opcode = htons((u_short)DATA);
665		dp->th_block = htons((u_short)block);
666		timeouts = 0;
667		(void)setjmp(timeoutbuf);
668
669send_data:
670		{
671			int i, t = 1;
672			for (i = 0; ; i++){
673				if (send(peer, dp, size + 4, 0) != size + 4) {
674					sleep(t);
675					t = (t < 32) ? t<< 1 : t;
676					if (i >= 12) {
677						syslog(LOG_ERR, "write: %m");
678						goto abort;
679					}
680				}
681				break;
682			}
683		}
684		read_ahead(file, pf->f_convert);
685		for ( ; ; ) {
686			alarm(rexmtval);        /* read the ack */
687			n = recv(peer, ackbuf, sizeof (ackbuf), 0);
688			alarm(0);
689			if (n < 0) {
690				syslog(LOG_ERR, "read: %m");
691				goto abort;
692			}
693			ap->th_opcode = ntohs((u_short)ap->th_opcode);
694			ap->th_block = ntohs((u_short)ap->th_block);
695
696			if (ap->th_opcode == ERROR)
697				goto abort;
698
699			if (ap->th_opcode == ACK) {
700				if (ap->th_block == block)
701					break;
702				/* Re-synchronize with the other side */
703				(void) synchnet(peer);
704				if (ap->th_block == (block -1))
705					goto send_data;
706			}
707
708		}
709		block++;
710	} while (size == SEGSIZE);
711abort:
712	(void) fclose(file);
713}
714
715void
716justquit(int sig __unused)
717{
718	exit(0);
719}
720
721
722/*
723 * Receive a file.
724 */
725void
726recvfile(struct formats *pf)
727{
728	struct tftphdr *dp;
729	struct tftphdr *ap;    /* ack buffer */
730	int n, size;
731	volatile unsigned short block;
732
733	signal(SIGALRM, timer);
734	dp = w_init();
735	ap = (struct tftphdr *)ackbuf;
736	block = 0;
737	do {
738		timeouts = 0;
739		ap->th_opcode = htons((u_short)ACK);
740		ap->th_block = htons((u_short)block);
741		block++;
742		(void) setjmp(timeoutbuf);
743send_ack:
744		if (send(peer, ackbuf, 4, 0) != 4) {
745			syslog(LOG_ERR, "write: %m");
746			goto abort;
747		}
748		write_behind(file, pf->f_convert);
749		for ( ; ; ) {
750			alarm(rexmtval);
751			n = recv(peer, dp, PKTSIZE, 0);
752			alarm(0);
753			if (n < 0) {            /* really? */
754				syslog(LOG_ERR, "read: %m");
755				goto abort;
756			}
757			dp->th_opcode = ntohs((u_short)dp->th_opcode);
758			dp->th_block = ntohs((u_short)dp->th_block);
759			if (dp->th_opcode == ERROR)
760				goto abort;
761			if (dp->th_opcode == DATA) {
762				if (dp->th_block == block) {
763					break;   /* normal */
764				}
765				/* Re-synchronize with the other side */
766				(void) synchnet(peer);
767				if (dp->th_block == (block-1))
768					goto send_ack;          /* rexmit */
769			}
770		}
771		/*  size = write(file, dp->th_data, n - 4); */
772		size = writeit(file, &dp, n - 4, pf->f_convert);
773		if (size != (n-4)) {                    /* ahem */
774			if (size < 0) nak(errno + 100);
775			else nak(ENOSPACE);
776			goto abort;
777		}
778	} while (size == SEGSIZE);
779	write_behind(file, pf->f_convert);
780	(void) fclose(file);            /* close data file */
781
782	ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */
783	ap->th_block = htons((u_short)(block));
784	(void) send(peer, ackbuf, 4, 0);
785
786	signal(SIGALRM, justquit);      /* just quit on timeout */
787	alarm(rexmtval);
788	n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
789	alarm(0);
790	if (n >= 4 &&                   /* if read some data */
791	    dp->th_opcode == DATA &&    /* and got a data block */
792	    block == dp->th_block) {	/* then my last ack was lost */
793		(void) send(peer, ackbuf, 4, 0);     /* resend final ack */
794	}
795abort:
796	return;
797}
798
799struct errmsg {
800	int	e_code;
801	const char	*e_msg;
802} errmsgs[] = {
803	{ EUNDEF,	"Undefined error code" },
804	{ ENOTFOUND,	"File not found" },
805	{ EACCESS,	"Access violation" },
806	{ ENOSPACE,	"Disk full or allocation exceeded" },
807	{ EBADOP,	"Illegal TFTP operation" },
808	{ EBADID,	"Unknown transfer ID" },
809	{ EEXISTS,	"File already exists" },
810	{ ENOUSER,	"No such user" },
811	{ EOPTNEG,	"Option negotiation" },
812	{ -1,		0 }
813};
814
815static const char *
816errtomsg(int error)
817{
818	static char ebuf[20];
819	struct errmsg *pe;
820	if (error == 0)
821		return "success";
822	for (pe = errmsgs; pe->e_code >= 0; pe++)
823		if (pe->e_code == error)
824			return pe->e_msg;
825	snprintf(ebuf, sizeof(buf), "error %d", error);
826	return ebuf;
827}
828
829/*
830 * Send a nak packet (error message).
831 * Error code passed in is one of the
832 * standard TFTP codes, or a UNIX errno
833 * offset by 100.
834 */
835static void
836nak(int error)
837{
838	struct tftphdr *tp;
839	int length;
840	struct errmsg *pe;
841
842	tp = (struct tftphdr *)buf;
843	tp->th_opcode = htons((u_short)ERROR);
844	tp->th_code = htons((u_short)error);
845	for (pe = errmsgs; pe->e_code >= 0; pe++)
846		if (pe->e_code == error)
847			break;
848	if (pe->e_code < 0) {
849		pe->e_msg = strerror(error - 100);
850		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
851	}
852	strcpy(tp->th_msg, pe->e_msg);
853	length = strlen(pe->e_msg);
854	tp->th_msg[length] = '\0';
855	length += 5;
856	if (send(peer, buf, length, 0) != length)
857		syslog(LOG_ERR, "nak: %m");
858}
859
860/* translate IPv4 mapped IPv6 address to IPv4 address */
861static void
862unmappedaddr(struct sockaddr_in6 *sin6)
863{
864	struct sockaddr_in *sin4;
865	u_int32_t addr;
866	int port;
867
868	if (sin6->sin6_family != AF_INET6 ||
869	    !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
870		return;
871	sin4 = (struct sockaddr_in *)sin6;
872	addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
873	port = sin6->sin6_port;
874	memset(sin4, 0, sizeof(struct sockaddr_in));
875	sin4->sin_addr.s_addr = addr;
876	sin4->sin_port = port;
877	sin4->sin_family = AF_INET;
878	sin4->sin_len = sizeof(struct sockaddr_in);
879}
880
881/*
882 * Send an oack packet (option acknowledgement).
883 */
884static void
885oack(void)
886{
887	struct tftphdr *tp, *ap;
888	int size, i, n;
889	char *bp;
890
891	tp = (struct tftphdr *)buf;
892	bp = buf + 2;
893	size = sizeof(buf) - 2;
894	tp->th_opcode = htons((u_short)OACK);
895	for (i = 0; options[i].o_type != NULL; i++) {
896		if (options[i].o_request) {
897			n = snprintf(bp, size, "%s%c%d", options[i].o_type,
898				     0, options[i].o_reply);
899			bp += n+1;
900			size -= n+1;
901			if (size < 0) {
902				syslog(LOG_ERR, "oack: buffer overflow");
903				exit(1);
904			}
905		}
906	}
907	size = bp - buf;
908	ap = (struct tftphdr *)ackbuf;
909	signal(SIGALRM, timer);
910	timeouts = 0;
911
912	(void)setjmp(timeoutbuf);
913	if (send(peer, buf, size, 0) != size) {
914		syslog(LOG_INFO, "oack: %m");
915		exit(1);
916	}
917
918	for (;;) {
919		alarm(rexmtval);
920		n = recv(peer, ackbuf, sizeof (ackbuf), 0);
921		alarm(0);
922		if (n < 0) {
923			syslog(LOG_ERR, "recv: %m");
924			exit(1);
925		}
926		ap->th_opcode = ntohs((u_short)ap->th_opcode);
927		ap->th_block = ntohs((u_short)ap->th_block);
928		if (ap->th_opcode == ERROR)
929			exit(1);
930		if (ap->th_opcode == ACK && ap->th_block == 0)
931			break;
932	}
933}
934