1313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti/*
2313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * Copyright (c) 1983 Regents of the University of California.
3313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * All rights reserved.
4313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti *
5313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * Redistribution and use in source and binary forms, with or without
6313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * modification, are permitted provided that the following conditions
7313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * are met:
8313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * 1. Redistributions of source code must retain the above copyright
9313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti *    notice, this list of conditions and the following disclaimer.
10313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * 2. Redistributions in binary form must reproduce the above copyright
11313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti *    notice, this list of conditions and the following disclaimer in the
12313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti *    documentation and/or other materials provided with the distribution.
13313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * 3. All advertising materials mentioning features or use of this software
14313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti *    must display the following acknowledgement:
15313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti *	This product includes software developed by the University of
16313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti *	California, Berkeley and its contributors.
17313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * 4. Neither the name of the University nor the names of its contributors
18313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti *    may be used to endorse or promote products derived from this software
19313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti *    without specific prior written permission.
20313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti *
21313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * SUCH DAMAGE.
32313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti */
33313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
34313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#ifndef lint
35313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittichar copyright[] =
36313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti"@(#) Copyright (c) 1983 Regents of the University of California.\n\
37313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti All rights reserved.\n";
38313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#endif /* not lint */
39313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
40313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#ifndef lint
41313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti/*static char sccsid[] = "from: @(#)tftpd.c	5.13 (Berkeley) 2/26/91";*/
42313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti/*static char rcsid[] = "$Id: tftpd.c,v 1.3 1993/08/01 18:28:53 mycroft Exp $";*/
43313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#endif /* not lint */
44313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
45313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti/*
46313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * Trivial file transfer protocol server.
47313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti *
48313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * This version includes many modifications by Jim Guyton <guyton@rand-unix>
49313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti */
50313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
51313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <sys/types.h>
52313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <sys/ioctl.h>
53313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <sys/stat.h>
54313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <unistd.h>
55313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <signal.h>
56313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <fcntl.h>
57313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
58313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <sys/socket.h>
59313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <netinet/in.h>
60313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <netdb.h>
61313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
62313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <setjmp.h>
63313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <syslog.h>
64313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <stdio.h>
65313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <errno.h>
66313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <ctype.h>
67313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <string.h>
68313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include <stdlib.h>
69313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
70313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#include "tftp.h"
71313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
72313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#ifndef MSG_CONFIRM
73313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#define MSG_CONFIRM 0
74313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#warning Please, upgrade kernel, otherwise this tftpd has no advantages.
75313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#endif
76313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
77313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#define	TIMEOUT		5
78313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
79313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittiint	peer;
80313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittiint	rexmtval = TIMEOUT;
81313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittiint	maxtimeout = 5*TIMEOUT;
82313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
83313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#define	PKTSIZE	SEGSIZE+4
84313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittichar	buf[PKTSIZE];
85313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittichar	ackbuf[PKTSIZE];
86313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittiunion {
87313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	struct	sockaddr     sa;
88313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	struct	sockaddr_in  sin;
89313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	struct	sockaddr_in6 sin6;
90313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti} from;
91313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittisocklen_t	fromlen;
92313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
93313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#define MAXARG	1
94313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittichar	*dirs[MAXARG+1];
95313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
96313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittivoid tftp(struct tftphdr *tp, int size) __attribute__((noreturn));
97313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittivoid nak(int error);
98313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittiint validate_access(char *filename, int mode);
99313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
100313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittistruct formats;
101313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
102313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittivoid sendfile(struct formats *pf);
103313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittivoid recvfile(struct formats *pf);
104313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
105313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
106313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittiint main(int ac, char **av)
107313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti{
108313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	register struct tftphdr *tp;
109313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	register int n = 0;
110313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	int on = 1;
111313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
112313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	/* Sanity. If parent forgot to setuid() on us. */
113313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (geteuid() == 0) {
114313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		setgid(65534);
115313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		setuid(65534);
116313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
117313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
118313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	ac--; av++;
119313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	while (ac-- > 0 && n < MAXARG)
120313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		dirs[n++] = *av++;
121313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
122313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	openlog("tftpd", LOG_PID, LOG_DAEMON);
123313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (ioctl(0, FIONBIO, &on) < 0) {
124313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
125313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		exit(1);
126313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
127313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	fromlen = sizeof (from);
128313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	n = recvfrom(0, buf, sizeof (buf), 0,
129313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	    (struct sockaddr *)&from, &fromlen);
130313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (n < 0) {
131313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		if (errno != EAGAIN)
132313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			syslog(LOG_ERR, "recvfrom: %m\n");
133313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		exit(1);
134313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
135313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	/*
136313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	 * Now that we have read the message out of the UDP
137313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	 * socket, we fork and exit.  Thus, inetd will go back
138313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	 * to listening to the tftp port, and the next request
139313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	 * to come in will start up a new instance of tftpd.
140313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	 *
141313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	 * We do this so that inetd can run tftpd in "wait" mode.
142313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	 * The problem with tftpd running in "nowait" mode is that
143313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	 * inetd may get one or more successful "selects" on the
144313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	 * tftp port before we do our receive, so more than one
145313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	 * instance of tftpd may be started up.  Worse, if tftpd
146313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	 * break before doing the above "recvfrom", inetd would
147313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	 * spawn endless instances, clogging the system.
148313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	 */
149313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	{
150313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		int pid;
151313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		int i;
152313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		socklen_t j;
153313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
154313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		for (i = 1; i < 20; i++) {
155313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		    pid = fork();
156313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		    if (pid < 0) {
157313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				sleep(i);
158313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				/*
159313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				 * flush out to most recently sent request.
160313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				 *
161313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				 * This may drop some request, but those
162313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				 * will be resent by the clients when
163313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				 * they timeout.  The positive effect of
164313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				 * this flush is to (try to) prevent more
165313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				 * than one tftpd being started up to service
166313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				 * a single request from a single client.
167313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				 */
168313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				j = sizeof from;
169313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				i = recvfrom(0, buf, sizeof (buf), 0,
170313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				    (struct sockaddr *)&from, &j);
171313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				if (i > 0) {
172313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti					n = i;
173313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti					fromlen = j;
174313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				}
175313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		    } else {
176313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				break;
177313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		    }
178313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		}
179313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		if (pid < 0) {
180313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			syslog(LOG_ERR, "fork: %m\n");
181313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			exit(1);
182313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		} else if (pid != 0) {
183313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			exit(0);
184313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		}
185313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
186313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	alarm(0);
187313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	close(0);
188313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	close(1);
189313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	peer = socket(from.sa.sa_family, SOCK_DGRAM, 0);
190313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (peer < 0) {
191313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		syslog(LOG_ERR, "socket: %m\n");
192313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		exit(1);
193313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
194313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
195313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		syslog(LOG_ERR, "connect: %m\n");
196313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		exit(1);
197313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
198313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	tp = (struct tftphdr *)buf;
199313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	tp->th_opcode = ntohs(tp->th_opcode);
200313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
201313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		tftp(tp, n);
202313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	exit(1);
203313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti}
204313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
205313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittistruct formats {
206313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	char	*f_mode;
207313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	int	(*f_validate)(char *filename, int mode);
208313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	void	(*f_send)(struct formats*);
209313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	void	(*f_recv)(struct formats*);
210313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	int	f_convert;
211313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti} formats[] = {
212313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	{ "netascii",	validate_access,	sendfile,	recvfile, 1 },
213313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	{ "octet",	validate_access,	sendfile,	recvfile, 0 },
214313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#ifdef notdef
215313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	{ "mail",	validate_user,		sendmail,	recvmail, 1 },
216313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti#endif
217313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	{ 0 }
218313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti};
219313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
220313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti/*
221313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * Handle initial connection protocol.
222313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti */
223313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittivoid tftp(struct tftphdr *tp, int size)
224313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti{
225313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	register char *cp;
226313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	int first = 1, ecode;
227313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	register struct formats *pf;
228313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	char *filename, *mode = NULL;
229313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
230313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	filename = cp = tp->th_stuff;
231313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittiagain:
232313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	while (cp < buf + size) {
233313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		if (*cp == '\0')
234313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			break;
235313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		cp++;
236313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
237313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (*cp != '\0') {
238313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		nak(EBADOP);
239313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		exit(1);
240313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
241313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (first) {
242313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		mode = ++cp;
243313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		first = 0;
244313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		goto again;
245313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
246313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	for (cp = mode; *cp; cp++)
247313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		if (isupper(*cp))
248313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			*cp = tolower(*cp);
249313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	for (pf = formats; pf->f_mode; pf++)
250313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		if (strcmp(pf->f_mode, mode) == 0)
251313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			break;
252313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (pf->f_mode == 0) {
253313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		nak(EBADOP);
254313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		exit(1);
255313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
256313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	ecode = (*pf->f_validate)(filename, tp->th_opcode);
257313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (ecode) {
258313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		nak(ecode);
259313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		exit(1);
260313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
261313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (tp->th_opcode == WRQ)
262313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		(*pf->f_recv)(pf);
263313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	else
264313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		(*pf->f_send)(pf);
265313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	exit(0);
266313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti}
267313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
268313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
269313379eb6b9da55f7371adef39a92153a0707d4aLorenzo ColittiFILE *file;
270313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
271313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti/*
272313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * Validate file access.  Since we
273313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * have no uid or gid, for now require
274313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * file to exist and be publicly
275313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * readable/writable.
276313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * If we were invoked with arguments
277313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * from inetd then the file must also be
278313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * in one of the given directory prefixes.
279313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * Note also, full path name must be
280313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * given as we have no login directory.
281313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti */
282313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittiint validate_access(char *filename, int mode)
283313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti{
284313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	struct stat stbuf;
285313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	int    fd;
286313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	char  *cp;
287313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	char   fnamebuf[1024+512];
288313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
289313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	for (cp = filename; *cp; cp++) {
290313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		if(*cp == '.' && (cp == filename || strncmp(cp-1, "/../", 4) == 0)) {
291313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			syslog(LOG_ERR, "bad path %s", filename);
292313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			return(EACCESS);
293313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		}
294313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
295313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
296313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (*filename == '/')
297313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		filename++;
298313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
299313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (!*dirs) {
300313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		syslog(LOG_ERR, "no dirs");
301313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		return EACCESS;
302313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
303313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	snprintf(fnamebuf, sizeof(fnamebuf)-1, "%s/%s", *dirs, filename);
304313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	filename = fnamebuf;
305313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
306313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (stat(filename, &stbuf) < 0) {
307313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		syslog(LOG_ERR, "stat %s : %m", filename);
308313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		return (errno == ENOENT ? ENOTFOUND : EACCESS);
309313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
310313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (mode == RRQ) {
311313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		if ((stbuf.st_mode&(S_IREAD >> 6)) == 0) {
312313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			syslog(LOG_ERR, "not readable %s", filename);
313313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			return (EACCESS);
314313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		}
315313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	} else {
316313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0) {
317313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			syslog(LOG_ERR, "not writable %s", filename);
318313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			return (EACCESS);
319313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		}
320313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
321313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	fd = open(filename, mode == RRQ ? 0 : 1);
322313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (fd < 0) {
323313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		syslog(LOG_ERR, "cannot open %s: %m", filename);
324313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		return (errno + 100);
325313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
326313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	file = fdopen(fd, (mode == RRQ)? "r":"w");
327313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (file == NULL) {
328313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		return errno+100;
329313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
330313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	return (0);
331313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti}
332313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
333313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittiint	confirmed;
334313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittiint	timeout;
335313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittijmp_buf	timeoutbuf;
336313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
337313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittivoid timer(int signo)
338313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti{
339313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	confirmed = 0;
340313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	timeout += rexmtval;
341313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (timeout >= maxtimeout)
342313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		exit(1);
343313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	longjmp(timeoutbuf, 1);
344313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti}
345313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
346313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti/*
347313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * Send the requested file.
348313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti */
349313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittivoid sendfile(struct formats *pf)
350313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti{
351313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	struct tftphdr *dp;
352313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	register struct tftphdr *ap;    /* ack packet */
353313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	volatile int block = 1;
354313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	int size, n;
355313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
356313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	confirmed = 0;
357313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	signal(SIGALRM, timer);
358313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	dp = r_init();
359313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	ap = (struct tftphdr *)ackbuf;
360313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	do {
361313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		size = readit(file, &dp, pf->f_convert);
362313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		if (size < 0) {
363313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			nak(errno + 100);
364313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			goto abort;
365313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		}
366313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		dp->th_opcode = htons((u_short)DATA);
367313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		dp->th_block = htons((u_short)block);
368313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		timeout = 0;
369313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		(void) setjmp(timeoutbuf);
370313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
371313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittisend_data:
372313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		if (send(peer, dp, size + 4, confirmed) != size + 4) {
373313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			syslog(LOG_ERR, "tftpd: write: %m\n");
374313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			goto abort;
375313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		}
376313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		confirmed = 0;
377313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		read_ahead(file, pf->f_convert);
378313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		for ( ; ; ) {
379313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			alarm(rexmtval);        /* read the ack */
380313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			n = recv(peer, ackbuf, sizeof (ackbuf), 0);
381313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			alarm(0);
382313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			if (n < 0) {
383313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				syslog(LOG_ERR, "tftpd: read: %m\n");
384313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				goto abort;
385313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			}
386313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			ap->th_opcode = ntohs((u_short)ap->th_opcode);
387313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			ap->th_block = ntohs((u_short)ap->th_block);
388313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
389313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			if (ap->th_opcode == ERROR)
390313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				goto abort;
391313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
392313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			if (ap->th_opcode == ACK) {
393313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				if (ap->th_block == block) {
394313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti					confirmed = MSG_CONFIRM;
395313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti					break;
396313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				}
397313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				/* Re-synchronize with the other side */
398313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				synchnet(peer);
399313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				if (ap->th_block == (block -1)) {
400313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti					goto send_data;
401313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				}
402313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			}
403313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
404313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		}
405313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		block++;
406313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	} while (size == SEGSIZE);
407313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittiabort:
408313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	(void) fclose(file);
409313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti}
410313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
411313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittivoid justquit(int signo)
412313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti{
413313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	exit(0);
414313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti}
415313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
416313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
417313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti/*
418313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * Receive a file.
419313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti */
420313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittivoid recvfile(struct formats *pf)
421313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti{
422313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	struct tftphdr *dp;
423313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	register struct tftphdr *ap;    /* ack buffer */
424313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	volatile int block = 0, n, size;
425313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
426313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	confirmed = 0;
427313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	signal(SIGALRM, timer);
428313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	dp = w_init();
429313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	ap = (struct tftphdr *)ackbuf;
430313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	do {
431313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		timeout = 0;
432313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		ap->th_opcode = htons((u_short)ACK);
433313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		ap->th_block = htons((u_short)block);
434313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		block++;
435313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		(void) setjmp(timeoutbuf);
436313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittisend_ack:
437313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		if (send(peer, ackbuf, 4, confirmed) != 4) {
438313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			syslog(LOG_ERR, "tftpd: write: %m\n");
439313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			goto abort;
440313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		}
441313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		confirmed = 0;
442313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		write_behind(file, pf->f_convert);
443313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		for ( ; ; ) {
444313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			alarm(rexmtval);
445313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			n = recv(peer, dp, PKTSIZE, 0);
446313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			alarm(0);
447313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			if (n < 0) {            /* really? */
448313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				syslog(LOG_ERR, "tftpd: read: %m\n");
449313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				goto abort;
450313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			}
451313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			dp->th_opcode = ntohs((u_short)dp->th_opcode);
452313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			dp->th_block = ntohs((u_short)dp->th_block);
453313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			if (dp->th_opcode == ERROR)
454313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				goto abort;
455313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			if (dp->th_opcode == DATA) {
456313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				if (dp->th_block == block) {
457313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti					confirmed = MSG_CONFIRM;
458313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti					break;   /* normal */
459313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				}
460313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				/* Re-synchronize with the other side */
461313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				(void) synchnet(peer);
462313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti				if (dp->th_block == (block-1))
463313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti					goto send_ack;          /* rexmit */
464313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			}
465313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		}
466313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		/*  size = write(file, dp->th_data, n - 4); */
467313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		size = writeit(file, &dp, n - 4, pf->f_convert);
468313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		if (size != (n-4)) {                    /* ahem */
469313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			if (size < 0) nak(errno + 100);
470313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			else nak(ENOSPACE);
471313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			goto abort;
472313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		}
473313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	} while (size == SEGSIZE);
474313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	write_behind(file, pf->f_convert);
475313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	(void) fclose(file);            /* close data file */
476313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
477313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */
478313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	ap->th_block = htons((u_short)(block));
479313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	(void) send(peer, ackbuf, 4, confirmed);
480313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
481313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	signal(SIGALRM, justquit);      /* just quit on timeout */
482313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	alarm(rexmtval);
483313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
484313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	alarm(0);
485313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (n >= 4 &&                   /* if read some data */
486313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	    dp->th_opcode == DATA &&    /* and got a data block */
487313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	    block == dp->th_block) {	/* then my last ack was lost */
488313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		(void) send(peer, ackbuf, 4, 0);     /* resend final ack */
489313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
490313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittiabort:
491313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	return;
492313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti}
493313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
494313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittistruct errmsg {
495313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	int	e_code;
496313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	char	*e_msg;
497313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti} errmsgs[] = {
498313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	{ EUNDEF,	"Undefined error code" },
499313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	{ ENOTFOUND,	"File not found" },
500313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	{ EACCESS,	"Access violation" },
501313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	{ ENOSPACE,	"Disk full or allocation exceeded" },
502313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	{ EBADOP,	"Illegal TFTP operation" },
503313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	{ EBADID,	"Unknown transfer ID" },
504313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	{ EEXISTS,	"File already exists" },
505313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	{ ENOUSER,	"No such user" },
506313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	{ -1,		0 }
507313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti};
508313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
509313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti/*
510313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * Send a nak packet (error message).
511313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * Error code passed in is one of the
512313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * standard TFTP codes, or a UNIX errno
513313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti * offset by 100.
514313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti */
515313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colittivoid nak(int error)
516313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti{
517313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	register struct tftphdr *tp;
518313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	int length;
519313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	register struct errmsg *pe;
520313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti
521313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	tp = (struct tftphdr *)buf;
522313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	tp->th_opcode = htons((u_short)ERROR);
523313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	tp->th_code = htons((u_short)error);
524313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	for (pe = errmsgs; pe->e_code >= 0; pe++)
525313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		if (pe->e_code == error)
526313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti			break;
527313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (pe->e_code < 0) {
528313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		pe->e_msg = strerror(error - 100);
529313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
530313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	}
531313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	strcpy(tp->th_msg, pe->e_msg);
532313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	length = strlen(pe->e_msg);
533313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	tp->th_msg[length] = '\0';
534313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	length += 5;
535313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti	if (send(peer, buf, length, 0) != length)
536313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti		syslog(LOG_ERR, "nak: %m\n");
537313379eb6b9da55f7371adef39a92153a0707d4aLorenzo Colitti}
538