2 * Copyright (c) 1983 Regents of the University of California.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
36 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
37 All rights reserved.\n";
41 /*static char sccsid[] = "from: @(#)tftpd.c 5.13 (Berkeley) 2/26/91";*/
42 /*static char rcsid[] = "$Id: tftpd.c,v 1.3 1993/08/01 18:28:53 mycroft Exp $";*/
46 * Trivial file transfer protocol server.
48 * This version includes many modifications by Jim Guyton <guyton@rand-unix>
51 #include <sys/types.h>
52 #include <sys/ioctl.h>
58 #include <sys/socket.h>
59 #include <netinet/in.h>
74 #warning Please, upgrade kernel, otherwise this tftpd has no advantages.
80 int rexmtval = TIMEOUT;
81 int maxtimeout = 5*TIMEOUT;
83 #define PKTSIZE SEGSIZE+4
88 struct sockaddr_in sin;
89 struct sockaddr_in6 sin6;
96 void tftp(struct tftphdr *tp, int size) __attribute__((noreturn));
98 int validate_access(char *filename, int mode);
102 void sendfile(struct formats *pf);
103 void recvfile(struct formats *pf);
106 int main(int ac, char **av)
108 register struct tftphdr *tp;
112 /* Sanity. If parent forgot to setuid() on us. */
113 if (geteuid() == 0) {
119 while (ac-- > 0 && n < MAXARG)
122 openlog("tftpd", LOG_PID, LOG_DAEMON);
123 if (ioctl(0, FIONBIO, &on) < 0) {
124 syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
127 fromlen = sizeof (from);
128 n = recvfrom(0, buf, sizeof (buf), 0,
129 (struct sockaddr *)&from, &fromlen);
132 syslog(LOG_ERR, "recvfrom: %m\n");
136 * Now that we have read the message out of the UDP
137 * socket, we fork and exit. Thus, inetd will go back
138 * to listening to the tftp port, and the next request
139 * to come in will start up a new instance of tftpd.
141 * We do this so that inetd can run tftpd in "wait" mode.
142 * The problem with tftpd running in "nowait" mode is that
143 * inetd may get one or more successful "selects" on the
144 * tftp port before we do our receive, so more than one
145 * instance of tftpd may be started up. Worse, if tftpd
146 * break before doing the above "recvfrom", inetd would
147 * spawn endless instances, clogging the system.
154 for (i = 1; i < 20; i++) {
159 * flush out to most recently sent request.
161 * This may drop some request, but those
162 * will be resent by the clients when
163 * they timeout. The positive effect of
164 * this flush is to (try to) prevent more
165 * than one tftpd being started up to service
166 * a single request from a single client.
169 i = recvfrom(0, buf, sizeof (buf), 0,
170 (struct sockaddr *)&from, &j);
180 syslog(LOG_ERR, "fork: %m\n");
182 } else if (pid != 0) {
189 peer = socket(from.sa.sa_family, SOCK_DGRAM, 0);
191 syslog(LOG_ERR, "socket: %m\n");
194 if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
195 syslog(LOG_ERR, "connect: %m\n");
198 tp = (struct tftphdr *)buf;
199 tp->th_opcode = ntohs(tp->th_opcode);
200 if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
207 int (*f_validate)(char *filename, int mode);
208 void (*f_send)(struct formats*);
209 void (*f_recv)(struct formats*);
212 { "netascii", validate_access, sendfile, recvfile, 1 },
213 { "octet", validate_access, sendfile, recvfile, 0 },
215 { "mail", validate_user, sendmail, recvmail, 1 },
221 * Handle initial connection protocol.
223 void tftp(struct tftphdr *tp, int size)
226 int first = 1, ecode;
227 register struct formats *pf;
228 char *filename, *mode = NULL;
230 filename = cp = tp->th_stuff;
232 while (cp < buf + size) {
246 for (cp = mode; *cp; cp++)
249 for (pf = formats; pf->f_mode; pf++)
250 if (strcmp(pf->f_mode, mode) == 0)
252 if (pf->f_mode == 0) {
256 ecode = (*pf->f_validate)(filename, tp->th_opcode);
261 if (tp->th_opcode == WRQ)
272 * Validate file access. Since we
273 * have no uid or gid, for now require
274 * file to exist and be publicly
276 * If we were invoked with arguments
277 * from inetd then the file must also be
278 * in one of the given directory prefixes.
279 * Note also, full path name must be
280 * given as we have no login directory.
282 int validate_access(char *filename, int mode)
287 char fnamebuf[1024+512];
289 for (cp = filename; *cp; cp++) {
290 if(*cp == '.' && (cp == filename || strncmp(cp-1, "/../", 4) == 0)) {
291 syslog(LOG_ERR, "bad path %s", filename);
296 if (*filename == '/')
300 syslog(LOG_ERR, "no dirs");
303 snprintf(fnamebuf, sizeof(fnamebuf)-1, "%s/%s", *dirs, filename);
306 if (stat(filename, &stbuf) < 0) {
307 syslog(LOG_ERR, "stat %s : %m", filename);
308 return (errno == ENOENT ? ENOTFOUND : EACCESS);
311 if ((stbuf.st_mode&(S_IREAD >> 6)) == 0) {
312 syslog(LOG_ERR, "not readable %s", filename);
316 if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0) {
317 syslog(LOG_ERR, "not writable %s", filename);
321 fd = open(filename, mode == RRQ ? 0 : 1);
323 syslog(LOG_ERR, "cannot open %s: %m", filename);
324 return (errno + 100);
326 file = fdopen(fd, (mode == RRQ)? "r":"w");
337 void timer(int signo)
341 if (timeout >= maxtimeout)
343 longjmp(timeoutbuf, 1);
347 * Send the requested file.
349 void sendfile(struct formats *pf)
352 register struct tftphdr *ap; /* ack packet */
353 volatile int block = 1;
357 signal(SIGALRM, timer);
359 ap = (struct tftphdr *)ackbuf;
361 size = readit(file, &dp, pf->f_convert);
366 dp->th_opcode = htons((u_short)DATA);
367 dp->th_block = htons((u_short)block);
369 (void) setjmp(timeoutbuf);
372 if (send(peer, dp, size + 4, confirmed) != size + 4) {
373 syslog(LOG_ERR, "tftpd: write: %m\n");
377 read_ahead(file, pf->f_convert);
379 alarm(rexmtval); /* read the ack */
380 n = recv(peer, ackbuf, sizeof (ackbuf), 0);
383 syslog(LOG_ERR, "tftpd: read: %m\n");
386 ap->th_opcode = ntohs((u_short)ap->th_opcode);
387 ap->th_block = ntohs((u_short)ap->th_block);
389 if (ap->th_opcode == ERROR)
392 if (ap->th_opcode == ACK) {
393 if (ap->th_block == block) {
394 confirmed = MSG_CONFIRM;
397 /* Re-synchronize with the other side */
399 if (ap->th_block == (block -1)) {
406 } while (size == SEGSIZE);
411 void justquit(int signo)
420 void recvfile(struct formats *pf)
423 register struct tftphdr *ap; /* ack buffer */
424 volatile int block = 0, n, size;
427 signal(SIGALRM, timer);
429 ap = (struct tftphdr *)ackbuf;
432 ap->th_opcode = htons((u_short)ACK);
433 ap->th_block = htons((u_short)block);
435 (void) setjmp(timeoutbuf);
437 if (send(peer, ackbuf, 4, confirmed) != 4) {
438 syslog(LOG_ERR, "tftpd: write: %m\n");
442 write_behind(file, pf->f_convert);
445 n = recv(peer, dp, PKTSIZE, 0);
447 if (n < 0) { /* really? */
448 syslog(LOG_ERR, "tftpd: read: %m\n");
451 dp->th_opcode = ntohs((u_short)dp->th_opcode);
452 dp->th_block = ntohs((u_short)dp->th_block);
453 if (dp->th_opcode == ERROR)
455 if (dp->th_opcode == DATA) {
456 if (dp->th_block == block) {
457 confirmed = MSG_CONFIRM;
460 /* Re-synchronize with the other side */
461 (void) synchnet(peer);
462 if (dp->th_block == (block-1))
463 goto send_ack; /* rexmit */
466 /* size = write(file, dp->th_data, n - 4); */
467 size = writeit(file, &dp, n - 4, pf->f_convert);
468 if (size != (n-4)) { /* ahem */
469 if (size < 0) nak(errno + 100);
473 } while (size == SEGSIZE);
474 write_behind(file, pf->f_convert);
475 (void) fclose(file); /* close data file */
477 ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */
478 ap->th_block = htons((u_short)(block));
479 (void) send(peer, ackbuf, 4, confirmed);
481 signal(SIGALRM, justquit); /* just quit on timeout */
483 n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
485 if (n >= 4 && /* if read some data */
486 dp->th_opcode == DATA && /* and got a data block */
487 block == dp->th_block) { /* then my last ack was lost */
488 (void) send(peer, ackbuf, 4, 0); /* resend final ack */
498 { EUNDEF, "Undefined error code" },
499 { ENOTFOUND, "File not found" },
500 { EACCESS, "Access violation" },
501 { ENOSPACE, "Disk full or allocation exceeded" },
502 { EBADOP, "Illegal TFTP operation" },
503 { EBADID, "Unknown transfer ID" },
504 { EEXISTS, "File already exists" },
505 { ENOUSER, "No such user" },
510 * Send a nak packet (error message).
511 * Error code passed in is one of the
512 * standard TFTP codes, or a UNIX errno
517 register struct tftphdr *tp;
519 register struct errmsg *pe;
521 tp = (struct tftphdr *)buf;
522 tp->th_opcode = htons((u_short)ERROR);
523 tp->th_code = htons((u_short)error);
524 for (pe = errmsgs; pe->e_code >= 0; pe++)
525 if (pe->e_code == error)
527 if (pe->e_code < 0) {
528 pe->e_msg = strerror(error - 100);
529 tp->th_code = EUNDEF; /* set 'undef' errorcode */
531 strcpy(tp->th_msg, pe->e_msg);
532 length = strlen(pe->e_msg);
533 tp->th_msg[length] = '\0';
535 if (send(peer, buf, length, 0) != length)
536 syslog(LOG_ERR, "nak: %m\n");