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