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