xref: /freebsd/libexec/tftpd/tftpd.c (revision a35d88931c87cfe6bd38f01d7bad22140b3b38f3)
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
35 static 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
42 static char sccsid[] = "@(#)tftpd.c	8.1 (Berkeley) 6/4/93";
43 #endif
44 static const char rcsid[] =
45   "$FreeBSD$";
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 
85 int	peer;
86 int	rexmtval = TIMEOUT;
87 int	max_rexmtval = 2*TIMEOUT;
88 
89 #define	PKTSIZE	SEGSIZE+4
90 char	buf[PKTSIZE];
91 char	ackbuf[PKTSIZE];
92 struct	sockaddr_storage from;
93 int	fromlen;
94 
95 void	tftp(struct tftphdr *, int);
96 static 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
106 static struct dirlist {
107 	const char	*name;
108 	int	len;
109 } dirs[MAXDIRS+1];
110 static int	suppress_naks;
111 static int	logging;
112 static int	ipchroot;
113 static int	create_new = 0;
114 static mode_t	mask = S_IWGRP|S_IWOTH;
115 
116 static const char *errtomsg(int);
117 static void  nak(int);
118 static void  oack(void);
119 
120 static void  timer(int);
121 static void  justquit(int);
122 
123 int
124 main(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 		setgroups(1, &nobody->pw_gid);
291 		setuid(nobody->pw_uid);
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 
335 static void
336 reduce_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 
366 struct formats;
367 int	validate_access(char **, int);
368 void	xmitfile(struct formats *);
369 void	recvfile(struct formats *);
370 
371 struct 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 
386 struct 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 
396 enum opt_enum {
397 	OPT_TSIZE = 0,
398 	OPT_TIMEOUT,
399 };
400 
401 /*
402  * Handle initial connection protocol.
403  */
404 void
405 tftp(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;
414 again:
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 Open Firmware 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 
473 option_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 
517 FILE *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  */
530 int
531 validate_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 
632 int	timeouts;
633 jmp_buf	timeoutbuf;
634 
635 void
636 timer(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  */
646 void
647 xmitfile(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 
669 send_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);
711 abort:
712 	(void) fclose(file);
713 }
714 
715 void
716 justquit(int sig __unused)
717 {
718 	exit(0);
719 }
720 
721 
722 /*
723  * Receive a file.
724  */
725 void
726 recvfile(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);
743 send_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 	}
795 abort:
796 	return;
797 }
798 
799 struct 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 
815 static const char *
816 errtomsg(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  */
835 static void
836 nak(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 */
861 static void
862 unmappedaddr(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  */
884 static void
885 oack(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