xref: /original-bsd/libexec/tftpd/tftpd.c (revision 29d43723)
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)tftpd.c	5.9 (Berkeley) 10/10/88";
26 #endif /* not lint */
27 
28 /*
29  * Trivial file transfer protocol server.
30  *
31  * This version includes many modifications by Jim Guyton <guyton@rand-unix>
32  */
33 
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/ioctl.h>
37 #include <sys/wait.h>
38 #include <sys/stat.h>
39 
40 #include <netinet/in.h>
41 
42 #include <arpa/tftp.h>
43 
44 #include <signal.h>
45 #include <stdio.h>
46 #include <errno.h>
47 #include <ctype.h>
48 #include <netdb.h>
49 #include <setjmp.h>
50 #include <syslog.h>
51 
52 #define	TIMEOUT		5
53 
54 extern	int errno;
55 struct	sockaddr_in sin = { AF_INET };
56 int	peer;
57 int	rexmtval = TIMEOUT;
58 int	maxtimeout = 5*TIMEOUT;
59 
60 #define	PKTSIZE	SEGSIZE+4
61 char	buf[PKTSIZE];
62 char	ackbuf[PKTSIZE];
63 struct	sockaddr_in from;
64 int	fromlen;
65 
66 #define MAXARG	4
67 char	*dirs[MAXARG+1];
68 
69 main(ac, av)
70 	char **av;
71 {
72 	register struct tftphdr *tp;
73 	register int n = 0;
74 	int on = 1;
75 
76 	ac--; av++;
77 	while (ac-- > 0 && n < MAXARG)
78 		dirs[n++] = *av++;
79 	openlog("tftpd", LOG_PID, LOG_DAEMON);
80 	if (ioctl(0, FIONBIO, &on) < 0) {
81 		syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
82 		exit(1);
83 	}
84 	fromlen = sizeof (from);
85 	n = recvfrom(0, buf, sizeof (buf), 0,
86 	    (caddr_t)&from, &fromlen);
87 	if (n < 0) {
88 		syslog(LOG_ERR, "recvfrom: %m\n");
89 		exit(1);
90 	}
91 	/*
92 	 * Now that we have read the message out of the UDP
93 	 * socket, we fork and exit.  Thus, inetd will go back
94 	 * to listening to the tftp port, and the next request
95 	 * to come in will start up a new instance of tftpd.
96 	 *
97 	 * We do this so that inetd can run tftpd in "wait" mode.
98 	 * The problem with tftpd running in "nowait" mode is that
99 	 * inetd may get one or more successful "selects" on the
100 	 * tftp port before we do our receive, so more than one
101 	 * instance of tftpd may be started up.  Worse, if tftpd
102 	 * break before doing the above "recvfrom", inetd would
103 	 * spawn endless instances, clogging the system.
104 	 */
105 	{
106 		int pid;
107 		int i, j;
108 
109 		for (i = 1; i < 20; i++) {
110 		    pid = fork();
111 		    if (pid < 0) {
112 				sleep(i);
113 				/*
114 				 * flush out to most recently sent request.
115 				 *
116 				 * This may drop some request, but those
117 				 * will be resent by the clients when
118 				 * they timeout.  The positive effect of
119 				 * this flush is to (try to) prevent more
120 				 * than one tftpd being started up to service
121 				 * a single request from a single client.
122 				 */
123 				j = sizeof from;
124 				i = recvfrom(0, buf, sizeof (buf), 0,
125 				    (caddr_t)&from, &j);
126 				if (i > 0) {
127 					n = i;
128 					fromlen = j;
129 				}
130 		    } else {
131 				break;
132 		    }
133 		}
134 		if (pid < 0) {
135 			syslog(LOG_ERR, "fork: %m\n");
136 			exit(1);
137 		} else if (pid != 0) {
138 			exit(0);
139 		}
140 	}
141 	from.sin_family = AF_INET;
142 	alarm(0);
143 	close(0);
144 	close(1);
145 	peer = socket(AF_INET, SOCK_DGRAM, 0);
146 	if (peer < 0) {
147 		syslog(LOG_ERR, "socket: %m\n");
148 		exit(1);
149 	}
150 	if (bind(peer, (caddr_t)&sin, sizeof (sin)) < 0) {
151 		syslog(LOG_ERR, "bind: %m\n");
152 		exit(1);
153 	}
154 	if (connect(peer, (caddr_t)&from, sizeof(from)) < 0) {
155 		syslog(LOG_ERR, "connect: %m\n");
156 		exit(1);
157 	}
158 	tp = (struct tftphdr *)buf;
159 	tp->th_opcode = ntohs(tp->th_opcode);
160 	if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
161 		tftp(tp, n);
162 	exit(1);
163 }
164 
165 int	validate_access();
166 int	sendfile(), recvfile();
167 
168 struct formats {
169 	char	*f_mode;
170 	int	(*f_validate)();
171 	int	(*f_send)();
172 	int	(*f_recv)();
173 	int	f_convert;
174 } formats[] = {
175 	{ "netascii",	validate_access,	sendfile,	recvfile, 1 },
176 	{ "octet",	validate_access,	sendfile,	recvfile, 0 },
177 #ifdef notdef
178 	{ "mail",	validate_user,		sendmail,	recvmail, 1 },
179 #endif
180 	{ 0 }
181 };
182 
183 /*
184  * Handle initial connection protocol.
185  */
186 tftp(tp, size)
187 	struct tftphdr *tp;
188 	int size;
189 {
190 	register char *cp;
191 	int first = 1, ecode;
192 	register struct formats *pf;
193 	char *filename, *mode;
194 
195 	filename = cp = tp->th_stuff;
196 again:
197 	while (cp < buf + size) {
198 		if (*cp == '\0')
199 			break;
200 		cp++;
201 	}
202 	if (*cp != '\0') {
203 		nak(EBADOP);
204 		exit(1);
205 	}
206 	if (first) {
207 		mode = ++cp;
208 		first = 0;
209 		goto again;
210 	}
211 	for (cp = mode; *cp; cp++)
212 		if (isupper(*cp))
213 			*cp = tolower(*cp);
214 	for (pf = formats; pf->f_mode; pf++)
215 		if (strcmp(pf->f_mode, mode) == 0)
216 			break;
217 	if (pf->f_mode == 0) {
218 		nak(EBADOP);
219 		exit(1);
220 	}
221 	ecode = (*pf->f_validate)(filename, tp->th_opcode);
222 	if (ecode) {
223 		nak(ecode);
224 		exit(1);
225 	}
226 	if (tp->th_opcode == WRQ)
227 		(*pf->f_recv)(pf);
228 	else
229 		(*pf->f_send)(pf);
230 	exit(0);
231 }
232 
233 
234 FILE *file;
235 
236 /*
237  * Validate file access.  Since we
238  * have no uid or gid, for now require
239  * file to exist and be publicly
240  * readable/writable.
241  * If we were invoked with arguments
242  * from inetd then the file must also be
243  * in one of the given directory prefixes.
244  * Note also, full path name must be
245  * given as we have no login directory.
246  */
247 validate_access(filename, mode)
248 	char *filename;
249 	int mode;
250 {
251 	struct stat stbuf;
252 	int	fd;
253 	char **dirp = dirs;
254 
255 	if (*filename != '/')
256 		return (EACCESS);
257 	for (; *dirp; dirp++)
258 		if (strncmp(filename, *dirp, strlen(*dirp)) == 0)
259 			break;
260 	if (*dirp==0 && dirp!=dirs)
261 		return (EACCESS);
262 	if (stat(filename, &stbuf) < 0)
263 		return (errno == ENOENT ? ENOTFOUND : EACCESS);
264 	if (mode == RRQ) {
265 		if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
266 			return (EACCESS);
267 	} else {
268 		if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
269 			return (EACCESS);
270 	}
271 	fd = open(filename, mode == RRQ ? 0 : 1);
272 	if (fd < 0)
273 		return (errno + 100);
274 	file = fdopen(fd, (mode == RRQ)? "r":"w");
275 	if (file == NULL) {
276 		return errno+100;
277 	}
278 	return (0);
279 }
280 
281 int	timeout;
282 jmp_buf	timeoutbuf;
283 
284 timer()
285 {
286 
287 	timeout += rexmtval;
288 	if (timeout >= maxtimeout)
289 		exit(1);
290 	longjmp(timeoutbuf, 1);
291 }
292 
293 /*
294  * Send the requested file.
295  */
296 sendfile(pf)
297 	struct formats *pf;
298 {
299 	struct tftphdr *dp, *r_init();
300 	register struct tftphdr *ap;    /* ack packet */
301 	register int block = 1, size, n;
302 
303 	signal(SIGALRM, timer);
304 	dp = r_init();
305 	ap = (struct tftphdr *)ackbuf;
306 	do {
307 		size = readit(file, &dp, pf->f_convert);
308 		if (size < 0) {
309 			nak(errno + 100);
310 			goto abort;
311 		}
312 		dp->th_opcode = htons((u_short)DATA);
313 		dp->th_block = htons((u_short)block);
314 		timeout = 0;
315 		(void) setjmp(timeoutbuf);
316 
317 send_data:
318 		if (send(peer, dp, size + 4, 0) != size + 4) {
319 			syslog(LOG_ERR, "tftpd: write: %m\n");
320 			goto abort;
321 		}
322 		read_ahead(file, pf->f_convert);
323 		for ( ; ; ) {
324 			alarm(rexmtval);        /* read the ack */
325 			n = recv(peer, ackbuf, sizeof (ackbuf), 0);
326 			alarm(0);
327 			if (n < 0) {
328 				syslog(LOG_ERR, "tftpd: read: %m\n");
329 				goto abort;
330 			}
331 			ap->th_opcode = ntohs((u_short)ap->th_opcode);
332 			ap->th_block = ntohs((u_short)ap->th_block);
333 
334 			if (ap->th_opcode == ERROR)
335 				goto abort;
336 
337 			if (ap->th_opcode == ACK) {
338 				if (ap->th_block == block) {
339 					break;
340 				}
341 				/* Re-synchronize with the other side */
342 				(void) synchnet(peer);
343 				if (ap->th_block == (block -1)) {
344 					goto send_data;
345 				}
346 			}
347 
348 		}
349 		block++;
350 	} while (size == SEGSIZE);
351 abort:
352 	(void) fclose(file);
353 }
354 
355 justquit()
356 {
357 	exit(0);
358 }
359 
360 
361 /*
362  * Receive a file.
363  */
364 recvfile(pf)
365 	struct formats *pf;
366 {
367 	struct tftphdr *dp, *w_init();
368 	register struct tftphdr *ap;    /* ack buffer */
369 	register int block = 0, n, size;
370 
371 	signal(SIGALRM, timer);
372 	dp = w_init();
373 	ap = (struct tftphdr *)ackbuf;
374 	do {
375 		timeout = 0;
376 		ap->th_opcode = htons((u_short)ACK);
377 		ap->th_block = htons((u_short)block);
378 		block++;
379 		(void) setjmp(timeoutbuf);
380 send_ack:
381 		if (send(peer, ackbuf, 4, 0) != 4) {
382 			syslog(LOG_ERR, "tftpd: write: %m\n");
383 			goto abort;
384 		}
385 		write_behind(file, pf->f_convert);
386 		for ( ; ; ) {
387 			alarm(rexmtval);
388 			n = recv(peer, dp, PKTSIZE, 0);
389 			alarm(0);
390 			if (n < 0) {            /* really? */
391 				syslog(LOG_ERR, "tftpd: read: %m\n");
392 				goto abort;
393 			}
394 			dp->th_opcode = ntohs((u_short)dp->th_opcode);
395 			dp->th_block = ntohs((u_short)dp->th_block);
396 			if (dp->th_opcode == ERROR)
397 				goto abort;
398 			if (dp->th_opcode == DATA) {
399 				if (dp->th_block == block) {
400 					break;   /* normal */
401 				}
402 				/* Re-synchronize with the other side */
403 				(void) synchnet(peer);
404 				if (dp->th_block == (block-1))
405 					goto send_ack;          /* rexmit */
406 			}
407 		}
408 		/*  size = write(file, dp->th_data, n - 4); */
409 		size = writeit(file, &dp, n - 4, pf->f_convert);
410 		if (size != (n-4)) {                    /* ahem */
411 			if (size < 0) nak(errno + 100);
412 			else nak(ENOSPACE);
413 			goto abort;
414 		}
415 	} while (size == SEGSIZE);
416 	write_behind(file, pf->f_convert);
417 	(void) fclose(file);            /* close data file */
418 
419 	ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */
420 	ap->th_block = htons((u_short)(block));
421 	(void) send(peer, ackbuf, 4, 0);
422 
423 	signal(SIGALRM, justquit);      /* just quit on timeout */
424 	alarm(rexmtval);
425 	n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
426 	alarm(0);
427 	if (n >= 4 &&                   /* if read some data */
428 	    dp->th_opcode == DATA &&    /* and got a data block */
429 	    block == dp->th_block) {	/* then my last ack was lost */
430 		(void) send(peer, ackbuf, 4, 0);     /* resend final ack */
431 	}
432 abort:
433 	return;
434 }
435 
436 struct errmsg {
437 	int	e_code;
438 	char	*e_msg;
439 } errmsgs[] = {
440 	{ EUNDEF,	"Undefined error code" },
441 	{ ENOTFOUND,	"File not found" },
442 	{ EACCESS,	"Access violation" },
443 	{ ENOSPACE,	"Disk full or allocation exceeded" },
444 	{ EBADOP,	"Illegal TFTP operation" },
445 	{ EBADID,	"Unknown transfer ID" },
446 	{ EEXISTS,	"File already exists" },
447 	{ ENOUSER,	"No such user" },
448 	{ -1,		0 }
449 };
450 
451 /*
452  * Send a nak packet (error message).
453  * Error code passed in is one of the
454  * standard TFTP codes, or a UNIX errno
455  * offset by 100.
456  */
457 nak(error)
458 	int error;
459 {
460 	register struct tftphdr *tp;
461 	int length;
462 	register struct errmsg *pe;
463 	extern char *sys_errlist[];
464 
465 	tp = (struct tftphdr *)buf;
466 	tp->th_opcode = htons((u_short)ERROR);
467 	tp->th_code = htons((u_short)error);
468 	for (pe = errmsgs; pe->e_code >= 0; pe++)
469 		if (pe->e_code == error)
470 			break;
471 	if (pe->e_code < 0) {
472 		pe->e_msg = sys_errlist[error - 100];
473 		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
474 	}
475 	strcpy(tp->th_msg, pe->e_msg);
476 	length = strlen(pe->e_msg);
477 	tp->th_msg[length] = '\0';
478 	length += 5;
479 	if (send(peer, buf, length, 0) != length)
480 		syslog(LOG_ERR, "nak: %m\n");
481 }
482