xref: /csrg-svn/libexec/tftpd/tftpd.c (revision 26108)
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.3 (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 		if (send(peer, dp, size + 4, 0) != size + 4) {
238 			perror("tftpd: write");
239 			goto abort;
240 		}
241 		read_ahead(file, pf->f_convert);
242 		do {
243 			alarm(rexmtval);        /* read the ack */
244 			n = recv(peer, ackbuf, sizeof (ackbuf), 0);
245 			alarm(0);
246 			if (n < 0) {
247 				perror("tftpd: read");
248 				goto abort;
249 			}
250 			ap->th_opcode = ntohs((u_short)ap->th_opcode);
251 			ap->th_block = ntohs((u_short)ap->th_block);
252 
253 			if (ap->th_opcode == ERROR)
254 				goto abort;
255 
256 		}  while (ap->th_opcode != ACK || ap->th_block != block);
257 		block++;
258 	} while (size == SEGSIZE);
259 abort:
260 	(void) fclose(file);
261 }
262 
263 justquit()
264 {
265 	exit(0);
266 }
267 
268 
269 /*
270  * Receive a file.
271  */
272 recvfile(pf)
273 	struct formats *pf;
274 {
275 	struct tftphdr *dp, *w_init();
276 	register struct tftphdr *ap;    /* ack buffer */
277 	register int block = 0, n, size;
278 
279 	signal(SIGALRM, timer);
280 	dp = w_init();
281 	ap = (struct tftphdr *)ackbuf;
282 	do {
283 		timeout = 0;
284 		ap->th_opcode = htons((u_short)ACK);
285 		ap->th_block = htons((u_short)block);
286 		block++;
287 		(void) setjmp(timeoutbuf);
288 send_ack:
289 		if (send(peer, ackbuf, 4, 0) != 4) {
290 			perror("tftpd: write");
291 			goto abort;
292 		}
293 		write_behind(file, pf->f_convert);
294 		for ( ; ; ) {
295 			alarm(rexmtval);
296 			n = recv(peer, dp, PKTSIZE, 0);
297 			alarm(0);
298 			if (n < 0) {            /* really? */
299 				perror("tftpd: read");
300 				goto abort;
301 			}
302 			dp->th_opcode = ntohs((u_short)dp->th_opcode);
303 			dp->th_block = ntohs((u_short)dp->th_block);
304 			if (dp->th_opcode == ERROR)
305 				goto abort;
306 			if (dp->th_opcode == DATA) {
307 				if (dp->th_block == block) {
308 					break;   /* normal */
309 				}
310 				if (dp->th_block == (block-1))
311 					goto send_ack;          /* rexmit */
312 			}
313 		}
314 		/*  size = write(file, dp->th_data, n - 4); */
315 		size = writeit(file, &dp, n - 4, pf->f_convert);
316 		if (size != (n-4)) {                    /* ahem */
317 			if (size < 0) nak(errno + 100);
318 			else nak(ENOSPACE);
319 			goto abort;
320 		}
321 	} while (size == SEGSIZE);
322 	write_behind(file, pf->f_convert);
323 	(void) fclose(file);            /* close data file */
324 
325 	ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */
326 	ap->th_block = htons((u_short)(block));
327 	(void) send(peer, ackbuf, 4, 0);
328 
329 	signal(SIGALRM, justquit);      /* just quit on timeout */
330 	alarm(rexmtval);
331 	n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
332 	alarm(0);
333 	if (n >= 4 &&                   /* if read some data */
334 	    dp->th_opcode == DATA &&    /* and got a data block */
335 	    block == dp->th_block) {	/* then my last ack was lost */
336 		(void) send(peer, ackbuf, 4, 0);     /* resend final ack */
337 	}
338 abort:
339 	return;
340 }
341 
342 struct errmsg {
343 	int	e_code;
344 	char	*e_msg;
345 } errmsgs[] = {
346 	{ EUNDEF,	"Undefined error code" },
347 	{ ENOTFOUND,	"File not found" },
348 	{ EACCESS,	"Access violation" },
349 	{ ENOSPACE,	"Disk full or allocation exceeded" },
350 	{ EBADOP,	"Illegal TFTP operation" },
351 	{ EBADID,	"Unknown transfer ID" },
352 	{ EEXISTS,	"File already exists" },
353 	{ ENOUSER,	"No such user" },
354 	{ -1,		0 }
355 };
356 
357 /*
358  * Send a nak packet (error message).
359  * Error code passed in is one of the
360  * standard TFTP codes, or a UNIX errno
361  * offset by 100.
362  */
363 nak(error)
364 	int error;
365 {
366 	register struct tftphdr *tp;
367 	int length;
368 	register struct errmsg *pe;
369 	extern char *sys_errlist[];
370 
371 	tp = (struct tftphdr *)buf;
372 	tp->th_opcode = htons((u_short)ERROR);
373 	tp->th_code = htons((u_short)error);
374 	for (pe = errmsgs; pe->e_code >= 0; pe++)
375 		if (pe->e_code == error)
376 			break;
377 	if (pe->e_code < 0) {
378 		pe->e_msg = sys_errlist[error - 100];
379 		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
380 	}
381 	strcpy(tp->th_msg, pe->e_msg);
382 	length = strlen(pe->e_msg);
383 	tp->th_msg[length] = '\0';
384 	length += 5;
385 	if (send(peer, buf, length, 0) != length)
386 		perror("nak");
387 }
388