xref: /csrg-svn/libexec/tftpd/tftpd.c (revision 7772)
1*7772Ssam /*	tftpd.c	4.1	82/08/16	*/
2*7772Ssam 
3*7772Ssam /*
4*7772Ssam  * Trivial file transfer protocol server.
5*7772Ssam  */
6*7772Ssam #include <sys/types.h>
7*7772Ssam #include <net/in.h>
8*7772Ssam #include <sys/socket.h>
9*7772Ssam #include <signal.h>
10*7772Ssam #include <sys/ioctl.h>
11*7772Ssam #include <stat.h>
12*7772Ssam #include <stdio.h>
13*7772Ssam #include <wait.h>
14*7772Ssam #include <errno.h>
15*7772Ssam #include <ctype.h>
16*7772Ssam #include "tftp.h"
17*7772Ssam 
18*7772Ssam #define	DEBUG		1
19*7772Ssam 
20*7772Ssam extern	int errno;
21*7772Ssam struct	sockaddr_in sin = { AF_INET, IPPORT_TFTP };
22*7772Ssam int	f;
23*7772Ssam int	options;
24*7772Ssam char	buf[BUFSIZ];
25*7772Ssam 
26*7772Ssam main(argc, argv)
27*7772Ssam 	char *argv[];
28*7772Ssam {
29*7772Ssam 	union wait status;
30*7772Ssam 	struct sockaddr_in from;
31*7772Ssam 	register struct tftphdr *tp;
32*7772Ssam 	register int n;
33*7772Ssam 
34*7772Ssam #ifndef DEBUG
35*7772Ssam 	if (fork())
36*7772Ssam 		exit(0);
37*7772Ssam 	for (f = 0; f < 10; f++)
38*7772Ssam 		(void) close(f);
39*7772Ssam 	(void) open("/", 0);
40*7772Ssam 	(void) dup2(0, 1);
41*7772Ssam 	(void) dup2(0, 2);
42*7772Ssam 	{ int t = open("/dev/tty", 2);
43*7772Ssam 	  if (t >= 0) {
44*7772Ssam 		ioctl(t, TIOCNOTTY, (char *)0);
45*7772Ssam 		(void) close(t);
46*7772Ssam 	  }
47*7772Ssam 	}
48*7772Ssam #endif
49*7772Ssam #if vax || pdp11
50*7772Ssam 	sin.sin_port = htons(sin.sin_port);
51*7772Ssam #endif
52*7772Ssam 	argc--, argv++;
53*7772Ssam 	if (argc > 0 && !strcmp(argv[0], "-d"))
54*7772Ssam 		options |= SO_DEBUG;
55*7772Ssam 	for (;;) {
56*7772Ssam 		errno = 0;
57*7772Ssam 		f = socket(SOCK_DGRAM, 0, &sin, options);
58*7772Ssam 		if (f < 0) {
59*7772Ssam 			perror("socket");
60*7772Ssam 			sleep(5);
61*7772Ssam 			continue;
62*7772Ssam 		}
63*7772Ssam again:
64*7772Ssam 		n = receive(f, &from, buf, sizeof (buf));
65*7772Ssam 		if (n <= 0) {
66*7772Ssam 			if (n < 0)
67*7772Ssam 				perror("receive");
68*7772Ssam 			goto again;
69*7772Ssam 		}
70*7772Ssam 		tp = (struct tftphdr *)buf;
71*7772Ssam #if vax || pdp11
72*7772Ssam 		tp->th_opcode = ntohs(tp->th_opcode);
73*7772Ssam #endif
74*7772Ssam 		if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
75*7772Ssam 			if (fork() == 0)
76*7772Ssam 				tftp(&from, tp, n);
77*7772Ssam 		(void) close(f);
78*7772Ssam #ifdef notdef
79*7772Ssam 		while (wait3(status, WNOHANG, 0) > 0)
80*7772Ssam #else
81*7772Ssam 		while (wait3(status, 0, 0) > 0)
82*7772Ssam 			continue;
83*7772Ssam 	}
84*7772Ssam }
85*7772Ssam 
86*7772Ssam int	validate_access();
87*7772Ssam int	sendfile(), recvfile();
88*7772Ssam 
89*7772Ssam struct formats {
90*7772Ssam 	char	*f_mode;
91*7772Ssam 	int	(*f_validate)();
92*7772Ssam 	int	(*f_send)();
93*7772Ssam 	int	(*f_recv)();
94*7772Ssam } formats[] = {
95*7772Ssam 	{ "netascii",	validate_access,	sendfile,	recvfile },
96*7772Ssam 	{ "octet",	validate_access,	sendfile,	recvfile },
97*7772Ssam #ifdef notdef
98*7772Ssam 	{ "mail",	validate_user,		sendmail,	recvmail },
99*7772Ssam #endif
100*7772Ssam 	{ 0 }
101*7772Ssam };
102*7772Ssam 
103*7772Ssam int	fd;			/* file being transferred */
104*7772Ssam 
105*7772Ssam /*
106*7772Ssam  * Handle initial connection protocol.
107*7772Ssam  */
108*7772Ssam tftp(client, tp, size)
109*7772Ssam 	struct sockaddr_in *client;
110*7772Ssam 	struct tftphdr *tp;
111*7772Ssam 	int size;
112*7772Ssam {
113*7772Ssam 	register char *cp;
114*7772Ssam 	int first = 1, ecode;
115*7772Ssam 	register struct formats *pf;
116*7772Ssam 	char *filename, *mode;
117*7772Ssam 
118*7772Ssam 	if (connect(f, client) < 0) {
119*7772Ssam 		perror("connect");
120*7772Ssam 		exit(1);
121*7772Ssam 	}
122*7772Ssam 	filename = cp = tp->th_stuff;
123*7772Ssam again:
124*7772Ssam 	while (cp < buf + size) {
125*7772Ssam 		if (*cp == '\0')
126*7772Ssam 			break;
127*7772Ssam 		cp++;
128*7772Ssam 	}
129*7772Ssam 	if (*cp != '\0') {
130*7772Ssam 		nak(EBADOP);
131*7772Ssam 		exit(1);
132*7772Ssam 	}
133*7772Ssam 	if (first) {
134*7772Ssam 		mode = ++cp;
135*7772Ssam 		first = 0;
136*7772Ssam 		goto again;
137*7772Ssam 	}
138*7772Ssam 	for (cp = mode; *cp; cp++)
139*7772Ssam 		if (isupper(*cp))
140*7772Ssam 			*cp = tolower(*cp);
141*7772Ssam 	for (pf = formats; pf->f_mode; pf++)
142*7772Ssam 		if (strcmp(pf->f_mode, mode) == 0)
143*7772Ssam 			break;
144*7772Ssam 	if (pf->f_mode == 0) {
145*7772Ssam 		nak(EBADOP);
146*7772Ssam 		exit(1);
147*7772Ssam 	}
148*7772Ssam 	ecode = (*pf->f_validate)(filename, client, tp->th_opcode);
149*7772Ssam 	if (ecode) {
150*7772Ssam 		nak(ecode);
151*7772Ssam 		exit(1);
152*7772Ssam 	}
153*7772Ssam 	if (tp->th_opcode == WRQ)
154*7772Ssam 		(*pf->f_recv)(pf);
155*7772Ssam 	else
156*7772Ssam 		(*pf->f_send)(pf);
157*7772Ssam 	exit(0);
158*7772Ssam }
159*7772Ssam 
160*7772Ssam /*
161*7772Ssam  * Validate file access.  Since we
162*7772Ssam  * have no uid or gid, for now require
163*7772Ssam  * file to exist and be publicly
164*7772Ssam  * readable/writable.
165*7772Ssam  * Note also, full path name must be
166*7772Ssam  * given as we have no login directory.
167*7772Ssam  */
168*7772Ssam validate_access(file, client, mode)
169*7772Ssam 	char *file;
170*7772Ssam 	struct sockaddr_in *client;
171*7772Ssam 	int mode;
172*7772Ssam {
173*7772Ssam 	struct stat stbuf;
174*7772Ssam 
175*7772Ssam 	if (*file != '/')
176*7772Ssam 		return (EACCESS);
177*7772Ssam 	if (stat(file, &stbuf) < 0)
178*7772Ssam 		return (errno == ENOENT ? ENOTFOUND : EACCESS);
179*7772Ssam 	if (mode == RRQ) {
180*7772Ssam 		if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
181*7772Ssam 			return (EACCESS);
182*7772Ssam 	} else {
183*7772Ssam 		if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
184*7772Ssam 			return (EACCESS);
185*7772Ssam 	}
186*7772Ssam 	fd = open(file, mode == RRQ ? 0 : 1);
187*7772Ssam 	if (fd < 0)
188*7772Ssam 		return (errno + 100);
189*7772Ssam 	return (0);
190*7772Ssam }
191*7772Ssam 
192*7772Ssam int timeout;
193*7772Ssam 
194*7772Ssam timer()
195*7772Ssam {
196*7772Ssam 	timeout += TIMEOUT;
197*7772Ssam 	if (timeout >= MAXTIMEOUT)
198*7772Ssam 		exit(1);
199*7772Ssam 	alarm(TIMEOUT);
200*7772Ssam }
201*7772Ssam 
202*7772Ssam /*
203*7772Ssam  * Send the requested file.
204*7772Ssam  */
205*7772Ssam sendfile(pf)
206*7772Ssam 	struct format *pf;
207*7772Ssam {
208*7772Ssam 	register struct tftphdr *tp;
209*7772Ssam 	register int block = 1, size, n;
210*7772Ssam 
211*7772Ssam 	sigset(SIGALRM, timer);
212*7772Ssam 	tp = (struct tftphdr *)buf;
213*7772Ssam 	do {
214*7772Ssam 		size = read(fd, tp->th_data, SEGSIZE);
215*7772Ssam 		if (size < 0) {
216*7772Ssam 			nak(errno + 100);
217*7772Ssam 			break;
218*7772Ssam 		}
219*7772Ssam 		tp->th_opcode = htons((u_short)DATA);
220*7772Ssam 		tp->th_block = htons((u_short)block);
221*7772Ssam 		timeout = 0;
222*7772Ssam 		alarm(TIMEOUT);
223*7772Ssam rexmt:
224*7772Ssam 		if (write(f, buf, size + 4) != size + 4) {
225*7772Ssam 			perror("send");
226*7772Ssam 			break;
227*7772Ssam 		}
228*7772Ssam again:
229*7772Ssam 		n = read(f, buf, sizeof (buf));
230*7772Ssam 		if (n <= 0) {
231*7772Ssam 			if (n == 0)
232*7772Ssam 				goto again;
233*7772Ssam 			if (errno == EINTR)
234*7772Ssam 				goto rexmt;
235*7772Ssam 			alarm(0);
236*7772Ssam 			perror("receive");
237*7772Ssam 			break;
238*7772Ssam 		}
239*7772Ssam 		alarm(0);
240*7772Ssam #if vax || pdp11
241*7772Ssam 		tp->th_opcode = ntohs(tp->th_opcode);
242*7772Ssam 		tp->th_block = ntohs(tp->th_block);
243*7772Ssam #endif
244*7772Ssam 		if (tp->th_opcode == ERROR)
245*7772Ssam 			break;
246*7772Ssam 		if (tp->th_opcode != ACK || tp->th_block != block)
247*7772Ssam 			goto again;
248*7772Ssam 		block++;
249*7772Ssam 	} while (size == SEGSIZE);
250*7772Ssam 	(void) close(fd);
251*7772Ssam }
252*7772Ssam 
253*7772Ssam /*
254*7772Ssam  * Receive a file.
255*7772Ssam  */
256*7772Ssam recvfile(pf)
257*7772Ssam 	struct format *pf;
258*7772Ssam {
259*7772Ssam 	register struct tftphdr *tp;
260*7772Ssam 	register int block = 0, n, size;
261*7772Ssam 
262*7772Ssam 	sigset(SIGALRM, timer);
263*7772Ssam 	tp = (struct tftphdr *)buf;
264*7772Ssam 	do {
265*7772Ssam 		timeout = 0;
266*7772Ssam 		alarm(TIMEOUT);
267*7772Ssam 		tp->th_opcode = htons((u_short)ACK);
268*7772Ssam 		tp->th_block = htons((u_short)block);
269*7772Ssam 		block++;
270*7772Ssam rexmt:
271*7772Ssam 		if (write(f, buf, 4) != 4) {
272*7772Ssam 			perror("ack");
273*7772Ssam 			break;
274*7772Ssam 		}
275*7772Ssam again:
276*7772Ssam 		n = read(f, buf, sizeof (buf));
277*7772Ssam 		if (n <= 0) {
278*7772Ssam 			if (n == 0)
279*7772Ssam 				goto again;
280*7772Ssam 			if (errno == EINTR)
281*7772Ssam 				goto rexmt;
282*7772Ssam 			alarm(0);
283*7772Ssam 			perror("receive");
284*7772Ssam 			break;
285*7772Ssam 		}
286*7772Ssam 		alarm(0);
287*7772Ssam #if vax || pdp11
288*7772Ssam 		tp->th_opcode = ntohs(tp->th_opcode);
289*7772Ssam 		tp->th_block = ntohs(tp->th_block);
290*7772Ssam #endif
291*7772Ssam 		if (tp->th_opcode == ERROR)
292*7772Ssam 			break;
293*7772Ssam 		if (tp->th_opcode != DATA || block != tp->th_block)
294*7772Ssam 			goto again;
295*7772Ssam 		size = write(fd, tp->th_data, n - 4);
296*7772Ssam 		if (size < 0) {
297*7772Ssam 			nak(errno + 100);
298*7772Ssam 			break;
299*7772Ssam 		}
300*7772Ssam 	} while (size == SEGSIZE);
301*7772Ssam 	tp->th_opcode = htons((u_short)ACK);
302*7772Ssam 	tp->th_block = htons((u_short)(block));
303*7772Ssam 	(void) write(f, buf, 4);
304*7772Ssam 	(void) close(fd);
305*7772Ssam }
306*7772Ssam 
307*7772Ssam struct errmsg {
308*7772Ssam 	int	e_code;
309*7772Ssam 	char	*e_msg;
310*7772Ssam } errmsgs[] = {
311*7772Ssam 	{ EUNDEF,	"Undefined error code" },
312*7772Ssam 	{ ENOTFOUND,	"File not found" },
313*7772Ssam 	{ EACCESS,	"Access violation" },
314*7772Ssam 	{ ENOSPACE,	"Disk full or allocation exceeded" },
315*7772Ssam 	{ EBADOP,	"Illegal TFTP operation" },
316*7772Ssam 	{ EBADID,	"Unknown transfer ID" },
317*7772Ssam 	{ EEXISTS,	"File already exists" },
318*7772Ssam 	{ ENOUSER,	"No such user" },
319*7772Ssam 	{ -1,		0 }
320*7772Ssam };
321*7772Ssam 
322*7772Ssam /*
323*7772Ssam  * Send a nak packet (error message).
324*7772Ssam  * Error code passed in is one of the
325*7772Ssam  * standard TFTP codes, or a UNIX errno
326*7772Ssam  * offset by 100.
327*7772Ssam  */
328*7772Ssam nak(error)
329*7772Ssam 	int error;
330*7772Ssam {
331*7772Ssam 	register struct tftphdr *tp;
332*7772Ssam 	int length;
333*7772Ssam 	register struct errmsg *pe;
334*7772Ssam 	extern char *sys_errlist[];
335*7772Ssam 
336*7772Ssam 	tp = (struct tftphdr *)buf;
337*7772Ssam 	tp->th_opcode = htons((u_short)ERROR);
338*7772Ssam 	tp->th_code = htons((u_short)error);
339*7772Ssam 	for (pe = errmsgs; pe->e_code >= 0; pe++)
340*7772Ssam 		if (pe->e_code == error)
341*7772Ssam 			break;
342*7772Ssam 	if (pe->e_code < 0)
343*7772Ssam 		pe->e_msg = sys_errlist[error - 100];
344*7772Ssam 	strcpy(tp->th_msg, pe->e_msg);
345*7772Ssam 	length = strlen(pe->e_msg);
346*7772Ssam 	tp->th_msg[length] = '\0';
347*7772Ssam 	length += 5;
348*7772Ssam 	if (write(f, buf, length) != length)
349*7772Ssam 		perror("nak");
350*7772Ssam }
351