xref: /csrg-svn/libexec/tftpd/tftpd.c (revision 26109)
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)tftpd.c	5.4 (Berkeley) 02/07/86";
15 #endif not lint
16 
17 
18 /*
19  * Trivial file transfer protocol server.
20  *
21  * This version includes many modifications by Jim Guyton <guyton@rand-unix>
22  */
23 
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <sys/ioctl.h>
27 #include <sys/wait.h>
28 #include <sys/stat.h>
29 
30 #include <netinet/in.h>
31 
32 #include <arpa/tftp.h>
33 
34 #include <signal.h>
35 #include <stdio.h>
36 #include <errno.h>
37 #include <ctype.h>
38 #include <netdb.h>
39 #include <setjmp.h>
40 #include <syslog.h>
41 
42 #define	TIMEOUT		5
43 
44 extern	int errno;
45 struct	sockaddr_in sin = { AF_INET };
46 int	peer;
47 int	rexmtval = TIMEOUT;
48 int	maxtimeout = 5*TIMEOUT;
49 
50 #define	PKTSIZE	SEGSIZE+4
51 char	buf[PKTSIZE];
52 char	ackbuf[PKTSIZE];
53 struct	sockaddr_in from;
54 int	fromlen;
55 
56 main()
57 {
58 	register struct tftphdr *tp;
59 	register int n;
60 
61 	openlog("tftpd", LOG_PID, LOG_DAEMON);
62 	alarm(10);
63 	fromlen = sizeof (from);
64 	n = recvfrom(0, buf, sizeof (buf), 0,
65 	    (caddr_t)&from, &fromlen);
66 	if (n < 0) {
67 		perror("tftpd: recvfrom");
68 		exit(1);
69 	}
70 	from.sin_family = AF_INET;
71 	alarm(0);
72 	close(0);
73 	close(1);
74 	peer = socket(AF_INET, SOCK_DGRAM, 0);
75 	if (peer < 0) {
76 		syslog(LOG_ERR, "socket: %m");
77 		exit(1);
78 	}
79 	if (bind(peer, (caddr_t)&sin, sizeof (sin)) < 0) {
80 		syslog(LOG_ERR, "bind: %m");
81 		exit(1);
82 	}
83 	if (connect(peer, (caddr_t)&from, sizeof(from)) < 0) {
84 		syslog(LOG_ERR, "connect: %m");
85 		exit(1);
86 	}
87 	tp = (struct tftphdr *)buf;
88 	tp->th_opcode = ntohs(tp->th_opcode);
89 	if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
90 		tftp(tp, n);
91 	exit(1);
92 }
93 
94 int	validate_access();
95 int	sendfile(), recvfile();
96 
97 struct formats {
98 	char	*f_mode;
99 	int	(*f_validate)();
100 	int	(*f_send)();
101 	int	(*f_recv)();
102 	int	f_convert;
103 } formats[] = {
104 	{ "netascii",	validate_access,	sendfile,	recvfile, 1 },
105 	{ "octet",	validate_access,	sendfile,	recvfile, 0 },
106 #ifdef notdef
107 	{ "mail",	validate_user,		sendmail,	recvmail, 1 },
108 #endif
109 	{ 0 }
110 };
111 
112 /*
113  * Handle initial connection protocol.
114  */
115 tftp(tp, size)
116 	struct tftphdr *tp;
117 	int size;
118 {
119 	register char *cp;
120 	int first = 1, ecode;
121 	register struct formats *pf;
122 	char *filename, *mode;
123 
124 	filename = cp = tp->th_stuff;
125 again:
126 	while (cp < buf + size) {
127 		if (*cp == '\0')
128 			break;
129 		cp++;
130 	}
131 	if (*cp != '\0') {
132 		nak(EBADOP);
133 		exit(1);
134 	}
135 	if (first) {
136 		mode = ++cp;
137 		first = 0;
138 		goto again;
139 	}
140 	for (cp = mode; *cp; cp++)
141 		if (isupper(*cp))
142 			*cp = tolower(*cp);
143 	for (pf = formats; pf->f_mode; pf++)
144 		if (strcmp(pf->f_mode, mode) == 0)
145 			break;
146 	if (pf->f_mode == 0) {
147 		nak(EBADOP);
148 		exit(1);
149 	}
150 	ecode = (*pf->f_validate)(filename, tp->th_opcode);
151 	if (ecode) {
152 		nak(ecode);
153 		exit(1);
154 	}
155 	if (tp->th_opcode == WRQ)
156 		(*pf->f_recv)(pf);
157 	else
158 		(*pf->f_send)(pf);
159 	exit(0);
160 }
161 
162 
163 FILE *file;
164 
165 /*
166  * Validate file access.  Since we
167  * have no uid or gid, for now require
168  * file to exist and be publicly
169  * readable/writable.
170  * Note also, full path name must be
171  * given as we have no login directory.
172  */
173 validate_access(filename, mode)
174 	char *filename;
175 	int mode;
176 {
177 	struct stat stbuf;
178 	int	fd;
179 
180 	if (*filename != '/')
181 		return (EACCESS);
182 	if (stat(filename, &stbuf) < 0)
183 		return (errno == ENOENT ? ENOTFOUND : EACCESS);
184 	if (mode == RRQ) {
185 		if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
186 			return (EACCESS);
187 	} else {
188 		if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
189 			return (EACCESS);
190 	}
191 	fd = open(filename, mode == RRQ ? 0 : 1);
192 	if (fd < 0)
193 		return (errno + 100);
194 	file = fdopen(fd, (mode == RRQ)? "r":"w");
195 	if (file == NULL) {
196 		return errno+100;
197 	}
198 	return (0);
199 }
200 
201 int	timeout;
202 jmp_buf	timeoutbuf;
203 
204 timer()
205 {
206 
207 	timeout += rexmtval;
208 	if (timeout >= maxtimeout)
209 		exit(1);
210 	longjmp(timeoutbuf, 1);
211 }
212 
213 /*
214  * Send the requested file.
215  */
216 sendfile(pf)
217 	struct formats *pf;
218 {
219 	struct tftphdr *dp, *r_init();
220 	register struct tftphdr *ap;    /* ack packet */
221 	register int block = 1, size, n;
222 
223 	signal(SIGALRM, timer);
224 	dp = r_init();
225 	ap = (struct tftphdr *)ackbuf;
226 	do {
227 		size = readit(file, &dp, pf->f_convert);
228 		if (size < 0) {
229 			nak(errno + 100);
230 			goto abort;
231 		}
232 		dp->th_opcode = htons((u_short)DATA);
233 		dp->th_block = htons((u_short)block);
234 		timeout = 0;
235 		(void) setjmp(timeoutbuf);
236 
237 send_data:
238 		/* Now, we flush anything pending to be read */
239 		/* This is to try to keep in synch between the two sides */
240 		while (1) {
241 			int i;
242 			char rbuf[PKTSIZE];
243 
244 			(void) ioctl(peer, FIONREAD, &i);
245 			if (i) {
246 				fromlen = sizeof from;
247 				n = recvfrom(peer, rbuf, sizeof (rbuf), 0,
248 					(caddr_t)&from, &fromlen);
249 			} else {
250 				break;
251 			}
252 		}
253 		if (send(peer, dp, size + 4, 0) != size + 4) {
254 			perror("tftpd: write");
255 			goto abort;
256 		}
257 		read_ahead(file, pf->f_convert);
258 		for ( ; ; ) {
259 			alarm(rexmtval);        /* read the ack */
260 			n = recv(peer, ackbuf, sizeof (ackbuf), 0);
261 			alarm(0);
262 			if (n < 0) {
263 				perror("tftpd: read");
264 				goto abort;
265 			}
266 			ap->th_opcode = ntohs((u_short)ap->th_opcode);
267 			ap->th_block = ntohs((u_short)ap->th_block);
268 
269 			if (ap->th_opcode == ERROR)
270 				goto abort;
271 
272 			if (ap->th_opcode == ACK) {
273 				if (ap->th_block == block) {
274 					break;
275 				}
276 				if (ap->th_block == (block -1)) {
277 					goto send_data;
278 				}
279 			}
280 
281 		}
282 		block++;
283 	} while (size == SEGSIZE);
284 abort:
285 	(void) fclose(file);
286 }
287 
288 justquit()
289 {
290 	exit(0);
291 }
292 
293 
294 /*
295  * Receive a file.
296  */
297 recvfile(pf)
298 	struct formats *pf;
299 {
300 	struct tftphdr *dp, *w_init();
301 	register struct tftphdr *ap;    /* ack buffer */
302 	register int block = 0, n, size;
303 
304 	signal(SIGALRM, timer);
305 	dp = w_init();
306 	ap = (struct tftphdr *)ackbuf;
307 	do {
308 		timeout = 0;
309 		ap->th_opcode = htons((u_short)ACK);
310 		ap->th_block = htons((u_short)block);
311 		block++;
312 		(void) setjmp(timeoutbuf);
313 send_ack:
314 		/* Now, we flush anything pending to be read */
315 		/* This is to try to keep in synch between the two sides */
316 		while (1) {
317 			int i;
318 			char rbuf[PKTSIZE];
319 
320 			(void) ioctl(peer, FIONREAD, &i);
321 			if (i) {
322 				fromlen = sizeof from;
323 				n = recvfrom(peer, rbuf, sizeof (rbuf), 0,
324 					(caddr_t)&from, &fromlen);
325 			} else {
326 				break;
327 			}
328 		}
329 		if (send(peer, ackbuf, 4, 0) != 4) {
330 			perror("tftpd: write");
331 			goto abort;
332 		}
333 		write_behind(file, pf->f_convert);
334 		for ( ; ; ) {
335 			alarm(rexmtval);
336 			n = recv(peer, dp, PKTSIZE, 0);
337 			alarm(0);
338 			if (n < 0) {            /* really? */
339 				perror("tftpd: read");
340 				goto abort;
341 			}
342 			dp->th_opcode = ntohs((u_short)dp->th_opcode);
343 			dp->th_block = ntohs((u_short)dp->th_block);
344 			if (dp->th_opcode == ERROR)
345 				goto abort;
346 			if (dp->th_opcode == DATA) {
347 				if (dp->th_block == block) {
348 					break;   /* normal */
349 				}
350 				if (dp->th_block == (block-1))
351 					goto send_ack;          /* rexmit */
352 			}
353 		}
354 		/*  size = write(file, dp->th_data, n - 4); */
355 		size = writeit(file, &dp, n - 4, pf->f_convert);
356 		if (size != (n-4)) {                    /* ahem */
357 			if (size < 0) nak(errno + 100);
358 			else nak(ENOSPACE);
359 			goto abort;
360 		}
361 	} while (size == SEGSIZE);
362 	write_behind(file, pf->f_convert);
363 	(void) fclose(file);            /* close data file */
364 
365 	ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */
366 	ap->th_block = htons((u_short)(block));
367 	(void) send(peer, ackbuf, 4, 0);
368 
369 	signal(SIGALRM, justquit);      /* just quit on timeout */
370 	alarm(rexmtval);
371 	n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
372 	alarm(0);
373 	if (n >= 4 &&                   /* if read some data */
374 	    dp->th_opcode == DATA &&    /* and got a data block */
375 	    block == dp->th_block) {	/* then my last ack was lost */
376 		(void) send(peer, ackbuf, 4, 0);     /* resend final ack */
377 	}
378 abort:
379 	return;
380 }
381 
382 struct errmsg {
383 	int	e_code;
384 	char	*e_msg;
385 } errmsgs[] = {
386 	{ EUNDEF,	"Undefined error code" },
387 	{ ENOTFOUND,	"File not found" },
388 	{ EACCESS,	"Access violation" },
389 	{ ENOSPACE,	"Disk full or allocation exceeded" },
390 	{ EBADOP,	"Illegal TFTP operation" },
391 	{ EBADID,	"Unknown transfer ID" },
392 	{ EEXISTS,	"File already exists" },
393 	{ ENOUSER,	"No such user" },
394 	{ -1,		0 }
395 };
396 
397 /*
398  * Send a nak packet (error message).
399  * Error code passed in is one of the
400  * standard TFTP codes, or a UNIX errno
401  * offset by 100.
402  */
403 nak(error)
404 	int error;
405 {
406 	register struct tftphdr *tp;
407 	int length;
408 	register struct errmsg *pe;
409 	extern char *sys_errlist[];
410 
411 	tp = (struct tftphdr *)buf;
412 	tp->th_opcode = htons((u_short)ERROR);
413 	tp->th_code = htons((u_short)error);
414 	for (pe = errmsgs; pe->e_code >= 0; pe++)
415 		if (pe->e_code == error)
416 			break;
417 	if (pe->e_code < 0) {
418 		pe->e_msg = sys_errlist[error - 100];
419 		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
420 	}
421 	strcpy(tp->th_msg, pe->e_msg);
422 	length = strlen(pe->e_msg);
423 	tp->th_msg[length] = '\0';
424 	length += 5;
425 	if (send(peer, buf, length, 0) != length)
426 		perror("nak");
427 }
428