tftpd.c revision 24193
167754Smsmith/*
267754Smsmith * Copyright (c) 1983, 1993
367754Smsmith *	The Regents of the University of California.  All rights reserved.
470243Smsmith *
567754Smsmith * Redistribution and use in source and binary forms, with or without
667754Smsmith * modification, are permitted provided that the following conditions
767754Smsmith * are met:
867754Smsmith * 1. Redistributions of source code must retain the above copyright
967754Smsmith *    notice, this list of conditions and the following disclaimer.
1067754Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1167754Smsmith *    notice, this list of conditions and the following disclaimer in the
12202771Sjkim *    documentation and/or other materials provided with the distribution.
1370243Smsmith * 3. All advertising materials mentioning features or use of this software
1467754Smsmith *    must display the following acknowledgement:
1567754Smsmith *	This product includes software developed by the University of
1667754Smsmith *	California, Berkeley and its contributors.
1767754Smsmith * 4. Neither the name of the University nor the names of its contributors
1867754Smsmith *    may be used to endorse or promote products derived from this software
1967754Smsmith *    without specific prior written permission.
2067754Smsmith *
2167754Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2267754Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2367754Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2467754Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2567754Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2667754Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2767754Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2867754Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2967754Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3067754Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3167754Smsmith * SUCH DAMAGE.
3267754Smsmith *
3367754Smsmith *	$Id: tftpd.c,v 1.7 1997/02/22 14:22:36 peter Exp $
3467754Smsmith */
3567754Smsmith
3667754Smsmith#ifndef lint
3767754Smsmithstatic char copyright[] =
3867754Smsmith"@(#) Copyright (c) 1983, 1993\n\
3967754Smsmith	The Regents of the University of California.  All rights reserved.\n";
4067754Smsmith#endif /* not lint */
4167754Smsmith
4267754Smsmith#ifndef lint
4367754Smsmithstatic char sccsid[] = "@(#)tftpd.c	8.1 (Berkeley) 6/4/93";
4467754Smsmith#endif /* not lint */
4567754Smsmith
4667754Smsmith/*
4767754Smsmith * Trivial file transfer protocol server.
4867754Smsmith *
4967754Smsmith * This version includes many modifications by Jim Guyton
5067754Smsmith * <guyton@rand-unix>.
5167754Smsmith */
5267754Smsmith
5367754Smsmith#include <sys/param.h>
5467754Smsmith#include <sys/ioctl.h>
5567754Smsmith#include <sys/stat.h>
5667754Smsmith#include <sys/socket.h>
5767754Smsmith#include <sys/types.h>
5867754Smsmith
5967754Smsmith#include <netinet/in.h>
6067754Smsmith#include <arpa/tftp.h>
6167754Smsmith#include <arpa/inet.h>
6267754Smsmith
6367754Smsmith#include <ctype.h>
6467754Smsmith#include <errno.h>
6567754Smsmith#include <fcntl.h>
6667754Smsmith#include <netdb.h>
6767754Smsmith#include <setjmp.h>
6867754Smsmith#include <signal.h>
6967754Smsmith#include <stdio.h>
7067754Smsmith#include <stdlib.h>
7167754Smsmith#include <string.h>
7267754Smsmith#include <syslog.h>
7367754Smsmith#include <unistd.h>
7467754Smsmith#include <pwd.h>
7567754Smsmith
7667754Smsmith#include "tftpsubs.h"
7767754Smsmith
7867754Smsmith#define	TIMEOUT		5
7967754Smsmith
8067754Smsmithint	peer;
8167754Smsmithint	rexmtval = TIMEOUT;
8267754Smsmithint	maxtimeout = 5*TIMEOUT;
8367754Smsmith
8467754Smsmith#define	PKTSIZE	SEGSIZE+4
8567754Smsmithchar	buf[PKTSIZE];
8667754Smsmithchar	ackbuf[PKTSIZE];
8767754Smsmithstruct	sockaddr_in from;
8867754Smsmithint	fromlen;
8967754Smsmith
9067754Smsmithvoid	tftp __P((struct tftphdr *, int));
9167754Smsmith
9267754Smsmith/*
9367754Smsmith * Null-terminated directory prefix list for absolute pathname requests and
9467754Smsmith * search list for relative pathname requests.
9567754Smsmith *
9667754Smsmith * MAXDIRS should be at least as large as the number of arguments that
9767754Smsmith * inetd allows (currently 20).
9867754Smsmith */
9967754Smsmith#define MAXDIRS	20
10067754Smsmithstatic struct dirlist {
10167754Smsmith	char	*name;
10267754Smsmith	int	len;
10367754Smsmith} dirs[MAXDIRS+1];
10467754Smsmithstatic int	suppress_naks;
10567754Smsmithstatic int	logging;
10667754Smsmith
10767754Smsmithstatic char *errtomsg __P((int));
10867754Smsmithstatic void  nak __P((int));
10967754Smsmithstatic char *verifyhost __P((struct sockaddr_in *));
11067754Smsmith
11167754Smsmithint
11267754Smsmithmain(argc, argv)
11367754Smsmith	int argc;
11467754Smsmith	char *argv[];
11567754Smsmith{
11667754Smsmith	register struct tftphdr *tp;
11767754Smsmith	register int n;
11867754Smsmith	int ch, on;
11967754Smsmith	struct sockaddr_in sin;
12067754Smsmith	char *chroot_dir = NULL;
121193267Sjkim	struct passwd *nobody;
122193267Sjkim
123206117Sjkim	openlog("tftpd", LOG_PID, LOG_FTP);
124193267Sjkim	while ((ch = getopt(argc, argv, "lns:")) != EOF) {
125193341Sjkim		switch (ch) {
126193341Sjkim		case 'l':
12767754Smsmith			logging = 1;
128193267Sjkim			break;
129193267Sjkim		case 'n':
130193267Sjkim			suppress_naks = 1;
131193267Sjkim			break;
132193267Sjkim		case 's':
13377424Smsmith			chroot_dir = optarg;
134193267Sjkim			break;
135193267Sjkim		default:
136193267Sjkim			syslog(LOG_WARNING, "ignoring unknown option -%c", ch);
137193267Sjkim		}
138193267Sjkim	}
139193267Sjkim	if (optind < argc) {
140193267Sjkim		struct dirlist *dirp;
141193267Sjkim
142193267Sjkim		/* Get list of directory prefixes. Skip relative pathnames. */
143193267Sjkim		for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS];
144193267Sjkim		     optind++) {
145193267Sjkim			if (argv[optind][0] == '/') {
146193267Sjkim				dirp->name = argv[optind];
147193267Sjkim				dirp->len  = strlen(dirp->name);
148204773Sjkim				dirp++;
149206117Sjkim			}
150193267Sjkim		}
151193267Sjkim	}
152151937Sjkim	else if (chroot_dir) {
15367754Smsmith		dirs->name = "/";
15467754Smsmith		dirs->len = 1;
15567754Smsmith	}
156167802Sjkim
157167802Sjkim	on = 1;
158167802Sjkim	if (ioctl(0, FIONBIO, &on) < 0) {
159167802Sjkim		syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
160167802Sjkim		exit(1);
161167802Sjkim	}
16267754Smsmith	fromlen = sizeof (from);
16367754Smsmith	n = recvfrom(0, buf, sizeof (buf), 0,
16467754Smsmith	    (struct sockaddr *)&from, &fromlen);
16567754Smsmith	if (n < 0) {
16667754Smsmith		syslog(LOG_ERR, "recvfrom: %m\n");
16767754Smsmith		exit(1);
16867754Smsmith	}
16967754Smsmith	/*
170100966Siwasaki	 * Now that we have read the message out of the UDP
171100966Siwasaki	 * socket, we fork and exit.  Thus, inetd will go back
172100966Siwasaki	 * to listening to the tftp port, and the next request
173100966Siwasaki	 * to come in will start up a new instance of tftpd.
17467754Smsmith	 *
17567754Smsmith	 * We do this so that inetd can run tftpd in "wait" mode.
17667754Smsmith	 * The problem with tftpd running in "nowait" mode is that
17767754Smsmith	 * inetd may get one or more successful "selects" on the
17880062Smsmith	 * tftp port before we do our receive, so more than one
17980062Smsmith	 * instance of tftpd may be started up.  Worse, if tftpd
18080062Smsmith	 * break before doing the above "recvfrom", inetd would
18180062Smsmith	 * spawn endless instances, clogging the system.
18267754Smsmith	 */
18367754Smsmith	{
18467754Smsmith		int pid;
18567754Smsmith		int i, j;
18667754Smsmith
18767754Smsmith		for (i = 1; i < 20; i++) {
18867754Smsmith		    pid = fork();
18967754Smsmith		    if (pid < 0) {
19080062Smsmith				sleep(i);
19167754Smsmith				/*
19267754Smsmith				 * flush out to most recently sent request.
193193267Sjkim				 *
194193267Sjkim				 * This may drop some request, but those
195193267Sjkim				 * will be resent by the clients when
196193267Sjkim				 * they timeout.  The positive effect of
19780062Smsmith				 * this flush is to (try to) prevent more
19867754Smsmith				 * than one tftpd being started up to service
19980062Smsmith				 * a single request from a single client.
20067754Smsmith				 */
20187031Smsmith				j = sizeof from;
20287031Smsmith				i = recvfrom(0, buf, sizeof (buf), 0,
20387031Smsmith				    (struct sockaddr *)&from, &j);
20467754Smsmith				if (i > 0) {
20587031Smsmith					n = i;
20667754Smsmith					fromlen = j;
207151937Sjkim				}
20877424Smsmith		    } else {
20977424Smsmith				break;
21077424Smsmith		    }
21177424Smsmith		}
21277424Smsmith		if (pid < 0) {
21377424Smsmith			syslog(LOG_ERR, "fork: %m\n");
21477424Smsmith			exit(1);
21577424Smsmith		} else if (pid != 0) {
21677424Smsmith			exit(0);
21777424Smsmith		}
21877424Smsmith	}
21977424Smsmith
22077424Smsmith	/*
22177424Smsmith	 * Since we exit here, we should do that only after the above
22277424Smsmith	 * recvfrom to keep inetd from constantly forking should there
22367754Smsmith	 * be a problem.  See the above comment about system clogging.
22467754Smsmith	 */
22567754Smsmith	if (chroot_dir) {
226167802Sjkim		/* Must get this before chroot because /etc might go away */
227167802Sjkim		if ((nobody = getpwnam("nobody")) == NULL) {
228167802Sjkim			syslog(LOG_ERR, "nobody: no such user");
229167802Sjkim			exit(1);
23067754Smsmith		}
231193267Sjkim		if (chroot(chroot_dir)) {
23267754Smsmith			syslog(LOG_ERR, "chroot: %s: %m", chroot_dir);
23367754Smsmith			exit(1);
23467754Smsmith		}
23580062Smsmith		chdir( "/" );
23667754Smsmith		setuid(nobody->pw_uid);
23767754Smsmith	}
23867754Smsmith
239167802Sjkim	from.sin_family = AF_INET;
240193267Sjkim	alarm(0);
24167754Smsmith	close(0);
24267754Smsmith	close(1);
24367754Smsmith	peer = socket(AF_INET, SOCK_DGRAM, 0);
24467754Smsmith	if (peer < 0) {
245167802Sjkim		syslog(LOG_ERR, "socket: %m\n");
246193267Sjkim		exit(1);
247167802Sjkim	}
24867754Smsmith	memset(&sin, 0, sizeof(sin));
24978986Smsmith	sin.sin_family = AF_INET;
250167802Sjkim	if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
251193267Sjkim		syslog(LOG_ERR, "bind: %m\n");
252167802Sjkim		exit(1);
25367754Smsmith	}
254193267Sjkim	if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
255193267Sjkim		syslog(LOG_ERR, "connect: %m\n");
256193267Sjkim		exit(1);
257193267Sjkim	}
25878986Smsmith	tp = (struct tftphdr *)buf;
259193267Sjkim	tp->th_opcode = ntohs(tp->th_opcode);
260193267Sjkim	if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
261193267Sjkim		tftp(tp, n);
262193267Sjkim	exit(1);
263193267Sjkim}
26467754Smsmith
26567754Smsmithstruct formats;
26667754Smsmithint	validate_access __P((char **, int));
26767754Smsmithvoid	sendfile __P((struct formats *));
26867754Smsmithvoid	recvfile __P((struct formats *));
26967754Smsmith
27067754Smsmithstruct formats {
27167754Smsmith	char	*f_mode;
272199337Sjkim	int	(*f_validate) __P((char **, int));
273199337Sjkim	void	(*f_send) __P((struct formats *));
27467754Smsmith	void	(*f_recv) __P((struct formats *));
275114237Snjl	int	f_convert;
27667754Smsmith} formats[] = {
27767754Smsmith	{ "netascii",	validate_access,	sendfile,	recvfile, 1 },
27867754Smsmith	{ "octet",	validate_access,	sendfile,	recvfile, 0 },
279114237Snjl#ifdef notdef
28077424Smsmith	{ "mail",	validate_user,		sendmail,	recvmail, 1 },
28167754Smsmith#endif
28267754Smsmith	{ 0 }
28367754Smsmith};
28467754Smsmith
28567754Smsmith/*
28667754Smsmith * Handle initial connection protocol.
28767754Smsmith */
28867754Smsmithvoid
28967754Smsmithtftp(tp, size)
29067754Smsmith	struct tftphdr *tp;
29167754Smsmith	int size;
29267754Smsmith{
29367754Smsmith	register char *cp;
29467754Smsmith	int first = 1, ecode;
29567754Smsmith	register struct formats *pf;
29687031Smsmith	char *filename, *mode;
29787031Smsmith
29887031Smsmith	filename = cp = tp->th_stuff;
29987031Smsmithagain:
30087031Smsmith	while (cp < buf + size) {
30167754Smsmith		if (*cp == '\0')
30287031Smsmith			break;
30387031Smsmith		cp++;
30487031Smsmith	}
30587031Smsmith	if (*cp != '\0') {
30687031Smsmith		nak(EBADOP);
30787031Smsmith		exit(1);
30887031Smsmith	}
30987031Smsmith	if (first) {
31087031Smsmith		mode = ++cp;
31187031Smsmith		first = 0;
31287031Smsmith		goto again;
313151937Sjkim	}
314151937Sjkim	for (cp = mode; *cp; cp++)
315151937Sjkim		if (isupper(*cp))
316151937Sjkim			*cp = tolower(*cp);
317151937Sjkim	for (pf = formats; pf->f_mode; pf++)
318151937Sjkim		if (strcmp(pf->f_mode, mode) == 0)
31987031Smsmith			break;
320151937Sjkim	if (pf->f_mode == 0) {
32167754Smsmith		nak(EBADOP);
32267754Smsmith		exit(1);
32367754Smsmith	}
32467754Smsmith	ecode = (*pf->f_validate)(&filename, tp->th_opcode);
32567754Smsmith	if (logging) {
32667754Smsmith		syslog(LOG_INFO, "%s: %s request for %s: %s",
32767754Smsmith			verifyhost(&from),
32867754Smsmith			tp->th_opcode == WRQ ? "write" : "read",
32967754Smsmith			filename, errtomsg(ecode));
33067754Smsmith	}
33167754Smsmith	if (ecode) {
33299679Siwasaki		/*
33399679Siwasaki		 * Avoid storms of naks to a RRQ broadcast for a relative
33499679Siwasaki		 * bootfile pathname from a diskless Sun.
33599679Siwasaki		 */
33699679Siwasaki		if (suppress_naks && *filename != '/' && ecode == ENOTFOUND)
33799679Siwasaki			exit(0);
33899679Siwasaki		nak(ecode);
33999679Siwasaki		exit(1);
34067754Smsmith	}
341117521Snjl	if (tp->th_opcode == WRQ)
342197104Sjkim		(*pf->f_recv)(pf);
34367754Smsmith	else
34467754Smsmith		(*pf->f_send)(pf);
345193267Sjkim	exit(0);
346193267Sjkim}
347193267Sjkim
348193267Sjkim
34967754SmsmithFILE *file;
35067754Smsmith
35167754Smsmith/*
35267754Smsmith * Validate file access.  Since we
35367754Smsmith * have no uid or gid, for now require
35467754Smsmith * file to exist and be publicly
35567754Smsmith * readable/writable.
35667754Smsmith * If we were invoked with arguments
35767754Smsmith * from inetd then the file must also be
35867754Smsmith * in one of the given directory prefixes.
35967754Smsmith * Note also, full path name must be
36067754Smsmith * given as we have no login directory.
36167754Smsmith */
36267754Smsmithint
36367754Smsmithvalidate_access(filep, mode)
36467754Smsmith	char **filep;
36567754Smsmith	int mode;
36667754Smsmith{
367193267Sjkim	struct stat stbuf;
36867754Smsmith	int	fd;
36967754Smsmith	struct dirlist *dirp;
370193267Sjkim	static char pathname[MAXPATHLEN];
371193267Sjkim	char *filename = *filep;
372193267Sjkim
373193267Sjkim	/*
374193267Sjkim	 * Prevent tricksters from getting around the directory restrictions
37567754Smsmith	 */
37667754Smsmith	if (strstr(filename, "/../"))
37777424Smsmith		return (EACCESS);
37867754Smsmith
37967754Smsmith	if (*filename == '/') {
38067754Smsmith		/*
38167754Smsmith		 * Allow the request if it's in one of the approved locations.
38267754Smsmith		 * Special case: check the null prefix ("/") by looking
38377424Smsmith		 * for length = 1 and relying on the arg. processing that
38467754Smsmith		 * it's a /.
38567754Smsmith		 */
38667754Smsmith		for (dirp = dirs; dirp->name != NULL; dirp++) {
38767754Smsmith			if (dirp->len == 1 ||
38867754Smsmith			    (!strncmp(filename, dirp->name, dirp->len) &&
38977424Smsmith			     filename[dirp->len] == '/'))
39067754Smsmith				    break;
39167754Smsmith		}
39267754Smsmith		/* If directory list is empty, allow access to any file */
39367754Smsmith		if (dirp->name == NULL && dirp != dirs)
39467754Smsmith			return (EACCESS);
39567754Smsmith		if (stat(filename, &stbuf) < 0)
39677424Smsmith			return (errno == ENOENT ? ENOTFOUND : EACCESS);
39767754Smsmith		if ((stbuf.st_mode & S_IFMT) != S_IFREG)
39867754Smsmith			return (ENOTFOUND);
39967754Smsmith		if (mode == RRQ) {
40067754Smsmith			if ((stbuf.st_mode & S_IROTH) == 0)
40177424Smsmith				return (EACCESS);
40277424Smsmith		} else {
40377424Smsmith			if ((stbuf.st_mode & S_IWOTH) == 0)
40467754Smsmith				return (EACCESS);
40567754Smsmith		}
40667754Smsmith	} else {
40767754Smsmith		int err;
40867754Smsmith
40977424Smsmith		/*
41077424Smsmith		 * Relative file name: search the approved locations for it.
41167754Smsmith		 * Don't allow write requests that avoid directory
41267754Smsmith		 * restrictions.
41367754Smsmith		 */
414117521Snjl
41567754Smsmith		if (!strncmp(filename, "../", 3))
41667754Smsmith			return (EACCESS);
417129684Snjl
41867754Smsmith		/*
41967754Smsmith		 * If the file exists in one of the directories and isn't
42067754Smsmith		 * readable, continue looking. However, change the error code
421193267Sjkim		 * to give an indication that the file exists.
422193267Sjkim		 */
423193267Sjkim		err = ENOTFOUND;
424193267Sjkim		for (dirp = dirs; dirp->name != NULL; dirp++) {
425193267Sjkim			snprintf(pathname, sizeof(pathname), "%s/%s",
426193267Sjkim				dirp->name, filename);
427138287Smarks			if (stat(pathname, &stbuf) == 0 &&
428138287Smarks			    (stbuf.st_mode & S_IFMT) == S_IFREG) {
429138287Smarks				if ((stbuf.st_mode & S_IROTH) != 0) {
430138287Smarks					break;
431138287Smarks				}
432138287Smarks				err = EACCESS;
433138287Smarks			}
434138287Smarks		}
43567754Smsmith		if (dirp->name == NULL)
436107325Siwasaki			return (err);
43791116Smsmith		*filep = filename = pathname;
43867754Smsmith	}
43967754Smsmith	fd = open(filename, mode == RRQ ? O_RDONLY : O_WRONLY|O_TRUNC);
44067754Smsmith	if (fd < 0)
44191116Smsmith		return (errno + 100);
44267754Smsmith	file = fdopen(fd, (mode == RRQ)? "r":"w");
44367754Smsmith	if (file == NULL) {
44467754Smsmith		return errno+100;
445117521Snjl	}
44684491Smsmith	return (0);
44767754Smsmith}
44867754Smsmith
44967754Smsmithint	timeout;
450117521Snjljmp_buf	timeoutbuf;
45184491Smsmith
45267754Smsmithvoid
45367754Smsmithtimer()
45467754Smsmith{
455117521Snjl
45667754Smsmith	timeout += rexmtval;
45767754Smsmith	if (timeout >= maxtimeout)
45867754Smsmith		exit(1);
459117521Snjl	longjmp(timeoutbuf, 1);
46067754Smsmith}
46167754Smsmith
462193267Sjkim/*
463193267Sjkim * Send the requested file.
464193267Sjkim */
465193267Sjkimvoid
466117521Snjlsendfile(pf)
467206117Sjkim	struct formats *pf;
468129684Snjl{
469129684Snjl	struct tftphdr *dp, *r_init();
470206117Sjkim	register struct tftphdr *ap;    /* ack packet */
471129684Snjl	register int size, n;
472129684Snjl	volatile int block;
473117521Snjl
474117521Snjl	signal(SIGALRM, timer);
475117521Snjl	dp = r_init();
476206117Sjkim	ap = (struct tftphdr *)ackbuf;
477117521Snjl	block = 1;
478117521Snjl	do {
479117521Snjl		size = readit(file, &dp, pf->f_convert);
480117521Snjl		if (size < 0) {
481117521Snjl			nak(errno + 100);
482206117Sjkim			goto abort;
483117521Snjl		}
484117521Snjl		dp->th_opcode = htons((u_short)DATA);
485117521Snjl		dp->th_block = htons((u_short)block);
486117521Snjl		timeout = 0;
487206117Sjkim		(void)setjmp(timeoutbuf);
488117521Snjl
489117521Snjlsend_data:
490117521Snjl		if (send(peer, dp, size + 4, 0) != size + 4) {
491117521Snjl			syslog(LOG_ERR, "tftpd: write: %m\n");
492117521Snjl			goto abort;
493117521Snjl		}
494117521Snjl		read_ahead(file, pf->f_convert);
495117521Snjl		for ( ; ; ) {
496193267Sjkim			alarm(rexmtval);        /* read the ack */
497193267Sjkim			n = recv(peer, ackbuf, sizeof (ackbuf), 0);
498193267Sjkim			alarm(0);
499193267Sjkim			if (n < 0) {
500193267Sjkim				syslog(LOG_ERR, "tftpd: read: %m\n");
501193267Sjkim				goto abort;
502193267Sjkim			}
503193267Sjkim			ap->th_opcode = ntohs((u_short)ap->th_opcode);
504193267Sjkim			ap->th_block = ntohs((u_short)ap->th_block);
505193267Sjkim
506193267Sjkim			if (ap->th_opcode == ERROR)
507193267Sjkim				goto abort;
508193267Sjkim
509117521Snjl			if (ap->th_opcode == ACK) {
510117521Snjl				if (ap->th_block == block)
511117521Snjl					break;
512117521Snjl				/* Re-synchronize with the other side */
513151937Sjkim				(void) synchnet(peer);
514117521Snjl				if (ap->th_block == (block -1))
515117521Snjl					goto send_data;
516117521Snjl			}
517117521Snjl
518117521Snjl		}
519117521Snjl		block++;
52067754Smsmith	} while (size == SEGSIZE);
52167754Smsmithabort:
52267754Smsmith	(void) fclose(file);
523114237Snjl}
524114237Snjl
525114237Snjlvoid
526114237Snjljustquit()
527114237Snjl{
528167802Sjkim	exit(0);
529167802Sjkim}
530167802Sjkim
531167802Sjkim
532167802Sjkim/*
533167802Sjkim * Receive a file.
534114237Snjl */
53567754Smsmithvoid
53667754Smsmithrecvfile(pf)
53767754Smsmith	struct formats *pf;
53867754Smsmith{
53967754Smsmith	struct tftphdr *dp, *w_init();
54067754Smsmith	register struct tftphdr *ap;    /* ack buffer */
54167754Smsmith	register int n, size;
54267754Smsmith	volatile int block;
54367754Smsmith
54467754Smsmith	signal(SIGALRM, timer);
54567754Smsmith	dp = w_init();
546114237Snjl	ap = (struct tftphdr *)ackbuf;
547167802Sjkim	block = 0;
548167802Sjkim	do {
549167802Sjkim		timeout = 0;
550167802Sjkim		ap->th_opcode = htons((u_short)ACK);
551114237Snjl		ap->th_block = htons((u_short)block);
552114237Snjl		block++;
55367754Smsmith		(void) setjmp(timeoutbuf);
55467754Smsmithsend_ack:
55567754Smsmith		if (send(peer, ackbuf, 4, 0) != 4) {
55667754Smsmith			syslog(LOG_ERR, "tftpd: write: %m\n");
55767754Smsmith			goto abort;
55867754Smsmith		}
55967754Smsmith		write_behind(file, pf->f_convert);
56067754Smsmith		for ( ; ; ) {
56167754Smsmith			alarm(rexmtval);
562114237Snjl			n = recv(peer, dp, PKTSIZE, 0);
563114237Snjl			alarm(0);
564114237Snjl			if (n < 0) {            /* really? */
565114237Snjl				syslog(LOG_ERR, "tftpd: read: %m\n");
56667754Smsmith				goto abort;
567193267Sjkim			}
56867754Smsmith			dp->th_opcode = ntohs((u_short)dp->th_opcode);
56967754Smsmith			dp->th_block = ntohs((u_short)dp->th_block);
57067754Smsmith			if (dp->th_opcode == ERROR)
57167754Smsmith				goto abort;
572193267Sjkim			if (dp->th_opcode == DATA) {
573193267Sjkim				if (dp->th_block == block) {
574193267Sjkim					break;   /* normal */
575193267Sjkim				}
576193267Sjkim				/* Re-synchronize with the other side */
577197104Sjkim				(void) synchnet(peer);
578193267Sjkim				if (dp->th_block == (block-1))
579193267Sjkim					goto send_ack;          /* rexmit */
580193267Sjkim			}
581193267Sjkim		}
582197104Sjkim		/*  size = write(file, dp->th_data, n - 4); */
583193267Sjkim		size = writeit(file, &dp, n - 4, pf->f_convert);
584193267Sjkim		if (size != (n-4)) {                    /* ahem */
585193267Sjkim			if (size < 0) nak(errno + 100);
586193267Sjkim			else nak(ENOSPACE);
58799679Siwasaki			goto abort;
588167802Sjkim		}
58999679Siwasaki	} while (size == SEGSIZE);
59099679Siwasaki	write_behind(file, pf->f_convert);
591193267Sjkim	(void) fclose(file);            /* close data file */
59299679Siwasaki
593167802Sjkim	ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */
59499679Siwasaki	ap->th_block = htons((u_short)(block));
59599679Siwasaki	(void) send(peer, ackbuf, 4, 0);
59699679Siwasaki
59799679Siwasaki	signal(SIGALRM, justquit);      /* just quit on timeout */
59899679Siwasaki	alarm(rexmtval);
59999679Siwasaki	n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
60087031Smsmith	alarm(0);
60167754Smsmith	if (n >= 4 &&                   /* if read some data */
60287031Smsmith	    dp->th_opcode == DATA &&    /* and got a data block */
60399679Siwasaki	    block == dp->th_block) {	/* then my last ack was lost */
60487031Smsmith		(void) send(peer, ackbuf, 4, 0);     /* resend final ack */
60587031Smsmith	}
60671867Smsmithabort:
60799679Siwasaki	return;
60871867Smsmith}
60971867Smsmith
610114237Snjlstruct errmsg {
611114237Snjl	int	e_code;
612114237Snjl	char	*e_msg;
613114237Snjl} errmsgs[] = {
61482367Smsmith	{ EUNDEF,	"Undefined error code" },
615193267Sjkim	{ ENOTFOUND,	"File not found" },
616193267Sjkim	{ EACCESS,	"Access violation" },
617193267Sjkim	{ ENOSPACE,	"Disk full or allocation exceeded" },
618193267Sjkim	{ EBADOP,	"Illegal TFTP operation" },
619193267Sjkim	{ EBADID,	"Unknown transfer ID" },
62067754Smsmith	{ EEXISTS,	"File already exists" },
621193267Sjkim	{ ENOUSER,	"No such user" },
622193267Sjkim	{ -1,		0 }
623193267Sjkim};
624193267Sjkim
625193267Sjkimstatic char *
62699679Siwasakierrtomsg(error)
627193267Sjkim	int error;
628193267Sjkim{
629193267Sjkim	static char buf[20];
630193267Sjkim	register struct errmsg *pe;
631193267Sjkim	if (error == 0)
632193267Sjkim		return "success";
633193267Sjkim	for (pe = errmsgs; pe->e_code >= 0; pe++)
634193267Sjkim		if (pe->e_code == error)
635193267Sjkim			return pe->e_msg;
636193267Sjkim	snprintf(buf, sizeof(buf), "error %d", error);
637193267Sjkim	return buf;
638193267Sjkim}
639193267Sjkim
640193267Sjkim/*
641193267Sjkim * Send a nak packet (error message).
642193267Sjkim * Error code passed in is one of the
643193267Sjkim * standard TFTP codes, or a UNIX errno
644193267Sjkim * offset by 100.
645193267Sjkim */
646193267Sjkimstatic void
647193267Sjkimnak(error)
648193267Sjkim	int error;
649193267Sjkim{
650193267Sjkim	register struct tftphdr *tp;
651193267Sjkim	int length;
652193267Sjkim	register struct errmsg *pe;
653193267Sjkim
654193267Sjkim	tp = (struct tftphdr *)buf;
655193267Sjkim	tp->th_opcode = htons((u_short)ERROR);
656193267Sjkim	tp->th_code = htons((u_short)error);
657193267Sjkim	for (pe = errmsgs; pe->e_code >= 0; pe++)
658193267Sjkim		if (pe->e_code == error)
659193267Sjkim			break;
660193267Sjkim	if (pe->e_code < 0) {
661193267Sjkim		pe->e_msg = strerror(error - 100);
662193267Sjkim		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
663193267Sjkim	}
664193267Sjkim	strcpy(tp->th_msg, pe->e_msg);
665193267Sjkim	length = strlen(pe->e_msg);
666193267Sjkim	tp->th_msg[length] = '\0';
667193267Sjkim	length += 5;
668193267Sjkim	if (send(peer, buf, length, 0) != length)
669193267Sjkim		syslog(LOG_ERR, "nak: %m\n");
670193267Sjkim}
671193267Sjkim
672193267Sjkimstatic char *
673193267Sjkimverifyhost(fromp)
674193267Sjkim	struct sockaddr_in *fromp;
675193267Sjkim{
676193267Sjkim	struct hostent *hp;
677193267Sjkim
678193267Sjkim	hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (fromp->sin_addr),
679193267Sjkim			    fromp->sin_family);
680193267Sjkim	if (hp)
681193267Sjkim		return hp->h_name;
682193267Sjkim	else
683193267Sjkim		return inet_ntoa(fromp->sin_addr);
684193267Sjkim}
685193267Sjkim