]>
git.saurik.com Git - apple/network_cmds.git/blob - tftpd.tproj/tftpd.c
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
25 * Copyright (c) 1983, 1993
26 * The Regents of the University of California. All rights reserved.
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 * 3. All advertising materials mentioning features or use of this software
37 * must display the following acknowledgement:
38 * This product includes software developed by the University of
39 * California, Berkeley and its contributors.
40 * 4. Neither the name of the University nor the names of its contributors
41 * may be used to endorse or promote products derived from this software
42 * without specific prior written permission.
44 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 static char copyright
[] =
59 "@(#) Copyright (c) 1983, 1993\n\
60 The Regents of the University of California. All rights reserved.\n";
64 static char sccsid
[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93";
68 * Trivial file transfer protocol server.
70 * This version includes many modifications by Jim Guyton
74 #include <sys/param.h>
75 #include <sys/ioctl.h>
77 #include <sys/socket.h>
79 #include <netinet/in.h>
80 #include <arpa/tftp.h>
81 #include <arpa/inet.h>
100 int rexmtval
= TIMEOUT
;
101 int maxtimeout
= 5*TIMEOUT
;
103 #define PKTSIZE SEGSIZE+4
105 char ackbuf
[PKTSIZE
];
106 struct sockaddr_in from
;
109 void tftp
__P((struct tftphdr
*, int));
112 * Null-terminated directory prefix list for absolute pathname requests and
113 * search list for relative pathname requests.
115 * MAXDIRS should be at least as large as the number of arguments that
116 * inetd allows (currently 20).
119 static struct dirlist
{
123 static int suppress_naks
;
126 static char *errtomsg
__P((int));
127 static void nak
__P((int));
128 static char *verifyhost
__P((struct sockaddr_in
*));
135 register struct tftphdr
*tp
;
138 struct sockaddr_in sin
;
140 openlog("tftpd", LOG_PID
, LOG_FTP
);
141 while ((ch
= getopt(argc
, argv
, "ln")) != EOF
) {
150 syslog(LOG_WARNING
, "ignoring unknown option -%c", ch
);
154 struct dirlist
*dirp
;
156 /* Get list of directory prefixes. Skip relative pathnames. */
157 for (dirp
= dirs
; optind
< argc
&& dirp
< &dirs
[MAXDIRS
];
159 if (argv
[optind
][0] == '/') {
160 dirp
->name
= argv
[optind
];
161 dirp
->len
= strlen(dirp
->name
);
168 if (ioctl(0, FIONBIO
, &on
) < 0) {
169 syslog(LOG_ERR
, "ioctl(FIONBIO): %m\n");
172 fromlen
= sizeof (from
);
173 n
= recvfrom(0, buf
, sizeof (buf
), 0,
174 (struct sockaddr
*)&from
, &fromlen
);
176 syslog(LOG_ERR
, "recvfrom: %m\n");
180 * Now that we have read the message out of the UDP
181 * socket, we fork and exit. Thus, inetd will go back
182 * to listening to the tftp port, and the next request
183 * to come in will start up a new instance of tftpd.
185 * We do this so that inetd can run tftpd in "wait" mode.
186 * The problem with tftpd running in "nowait" mode is that
187 * inetd may get one or more successful "selects" on the
188 * tftp port before we do our receive, so more than one
189 * instance of tftpd may be started up. Worse, if tftpd
190 * break before doing the above "recvfrom", inetd would
191 * spawn endless instances, clogging the system.
197 for (i
= 1; i
< 20; i
++) {
202 * flush out to most recently sent request.
204 * This may drop some request, but those
205 * will be resent by the clients when
206 * they timeout. The positive effect of
207 * this flush is to (try to) prevent more
208 * than one tftpd being started up to service
209 * a single request from a single client.
212 i
= recvfrom(0, buf
, sizeof (buf
), 0,
213 (struct sockaddr
*)&from
, &j
);
223 syslog(LOG_ERR
, "fork: %m\n");
225 } else if (pid
!= 0) {
229 from
.sin_family
= AF_INET
;
233 peer
= socket(AF_INET
, SOCK_DGRAM
, 0);
235 syslog(LOG_ERR
, "socket: %m\n");
238 memset(&sin
, 0, sizeof(sin
));
239 sin
.sin_family
= AF_INET
;
240 if (bind(peer
, (struct sockaddr
*)&sin
, sizeof (sin
)) < 0) {
241 syslog(LOG_ERR
, "bind: %m\n");
244 if (connect(peer
, (struct sockaddr
*)&from
, sizeof(from
)) < 0) {
245 syslog(LOG_ERR
, "connect: %m\n");
248 tp
= (struct tftphdr
*)buf
;
249 tp
->th_opcode
= ntohs(tp
->th_opcode
);
250 if (tp
->th_opcode
== RRQ
|| tp
->th_opcode
== WRQ
)
256 int validate_access
__P((char **, int));
257 void tftp_sendfile
__P((struct formats
*));
258 void recvfile
__P((struct formats
*));
262 int (*f_validate
) __P((char **, int));
263 void (*f_send
) __P((struct formats
*));
264 void (*f_recv
) __P((struct formats
*));
267 { "netascii", validate_access
, tftp_sendfile
, recvfile
, 1 },
268 { "octet", validate_access
, tftp_sendfile
, recvfile
, 0 },
270 { "mail", validate_user
, sendmail
, recvmail
, 1 },
276 * Handle initial connection protocol.
284 int first
= 1, ecode
;
285 register struct formats
*pf
;
286 char *filename
, *mode
;
288 filename
= cp
= tp
->th_stuff
;
290 while (cp
< buf
+ size
) {
304 for (cp
= mode
; *cp
; cp
++)
307 for (pf
= formats
; pf
->f_mode
; pf
++)
308 if (strcmp(pf
->f_mode
, mode
) == 0)
310 if (pf
->f_mode
== 0) {
314 ecode
= (*pf
->f_validate
)(&filename
, tp
->th_opcode
);
316 syslog(LOG_INFO
, "%s: %s request for %s: %s",
318 tp
->th_opcode
== WRQ
? "write" : "read",
319 filename
, errtomsg(ecode
));
323 * Avoid storms of naks to a RRQ broadcast for a relative
324 * bootfile pathname from a diskless Sun.
326 if (suppress_naks
&& *filename
!= '/' && ecode
== ENOTFOUND
)
331 if (tp
->th_opcode
== WRQ
)
342 * Validate file access. Since we
343 * have no uid or gid, for now require
344 * file to exist and be publicly
346 * If we were invoked with arguments
347 * from inetd then the file must also be
348 * in one of the given directory prefixes.
349 * Note also, full path name must be
350 * given as we have no login directory.
353 validate_access(filep
, mode
)
359 struct dirlist
*dirp
;
360 static char pathname
[MAXPATHLEN
];
361 char *filename
= *filep
;
364 * Prevent tricksters from getting around the directory restrictions
366 if (strstr(filename
, "/../"))
369 if (*filename
== '/') {
371 * Allow the request if it's in one of the approved locations.
372 * Special case: check the null prefix ("/") by looking
373 * for length = 1 and relying on the arg. processing that
376 for (dirp
= dirs
; dirp
->name
!= NULL
; dirp
++) {
377 if (dirp
->len
== 1 ||
378 (!strncmp(filename
, dirp
->name
, dirp
->len
) &&
379 filename
[dirp
->len
] == '/'))
382 /* If directory list is empty, allow access to any file */
383 if (dirp
->name
== NULL
&& dirp
!= dirs
)
385 if (stat(filename
, &stbuf
) < 0)
386 return (errno
== ENOENT
? ENOTFOUND
: EACCESS
);
387 if ((stbuf
.st_mode
& S_IFMT
) != S_IFREG
)
390 if ((stbuf
.st_mode
& S_IROTH
) == 0)
393 if ((stbuf
.st_mode
& S_IWOTH
) == 0)
400 * Relative file name: search the approved locations for it.
401 * Don't allow write requests or ones that avoid directory
405 if (mode
!= RRQ
|| !strncmp(filename
, "../", 3))
409 * If the file exists in one of the directories and isn't
410 * readable, continue looking. However, change the error code
411 * to give an indication that the file exists.
414 for (dirp
= dirs
; dirp
->name
!= NULL
; dirp
++) {
415 sprintf(pathname
, "%s/%s", dirp
->name
, filename
);
416 if (stat(pathname
, &stbuf
) == 0 &&
417 (stbuf
.st_mode
& S_IFMT
) == S_IFREG
) {
418 if ((stbuf
.st_mode
& S_IROTH
) != 0) {
424 if (dirp
->name
== NULL
)
426 *filep
= filename
= pathname
;
428 fd
= open(filename
, mode
== RRQ
? 0 : 1);
430 return (errno
+ 100);
431 file
= fdopen(fd
, (mode
== RRQ
)? "r":"w");
446 if (timeout
>= maxtimeout
)
448 longjmp(timeoutbuf
, 1);
452 * Send the requested file.
458 struct tftphdr
*dp
, *r_init();
459 register struct tftphdr
*ap
; /* ack packet */
460 register int size
, n
;
463 signal(SIGALRM
, timer
);
465 ap
= (struct tftphdr
*)ackbuf
;
468 size
= readit(file
, &dp
, pf
->f_convert
);
473 dp
->th_opcode
= htons((u_short
)DATA
);
474 dp
->th_block
= htons((u_short
)block
);
476 (void)setjmp(timeoutbuf
);
479 if (send(peer
, dp
, size
+ 4, 0) != size
+ 4) {
480 syslog(LOG_ERR
, "tftpd: write: %m\n");
483 read_ahead(file
, pf
->f_convert
);
485 alarm(rexmtval
); /* read the ack */
486 n
= recv(peer
, ackbuf
, sizeof (ackbuf
), 0);
489 syslog(LOG_ERR
, "tftpd: read: %m\n");
492 ap
->th_opcode
= ntohs((u_short
)ap
->th_opcode
);
493 ap
->th_block
= ntohs((u_short
)ap
->th_block
);
495 if (ap
->th_opcode
== ERROR
)
498 if (ap
->th_opcode
== ACK
) {
499 if (ap
->th_block
== block
)
501 /* Re-synchronize with the other side */
502 (void) synchnet(peer
);
503 if (ap
->th_block
== (block
-1))
509 } while (size
== SEGSIZE
);
528 struct tftphdr
*dp
, *w_init();
529 register struct tftphdr
*ap
; /* ack buffer */
530 register int n
, size
;
533 signal(SIGALRM
, timer
);
535 ap
= (struct tftphdr
*)ackbuf
;
539 ap
->th_opcode
= htons((u_short
)ACK
);
540 ap
->th_block
= htons((u_short
)block
);
542 (void) setjmp(timeoutbuf
);
544 if (send(peer
, ackbuf
, 4, 0) != 4) {
545 syslog(LOG_ERR
, "tftpd: write: %m\n");
548 write_behind(file
, pf
->f_convert
);
551 n
= recv(peer
, dp
, PKTSIZE
, 0);
553 if (n
< 0) { /* really? */
554 syslog(LOG_ERR
, "tftpd: read: %m\n");
557 dp
->th_opcode
= ntohs((u_short
)dp
->th_opcode
);
558 dp
->th_block
= ntohs((u_short
)dp
->th_block
);
559 if (dp
->th_opcode
== ERROR
)
561 if (dp
->th_opcode
== DATA
) {
562 if (dp
->th_block
== block
) {
565 /* Re-synchronize with the other side */
566 (void) synchnet(peer
);
567 if (dp
->th_block
== (block
-1))
568 goto send_ack
; /* rexmit */
571 /* size = write(file, dp->th_data, n - 4); */
572 size
= writeit(file
, &dp
, n
- 4, pf
->f_convert
);
573 if (size
!= (n
-4)) { /* ahem */
574 if (size
< 0) nak(errno
+ 100);
578 } while (size
== SEGSIZE
);
579 write_behind(file
, pf
->f_convert
);
580 (void) fclose(file
); /* close data file */
582 ap
->th_opcode
= htons((u_short
)ACK
); /* send the "final" ack */
583 ap
->th_block
= htons((u_short
)(block
));
584 (void) send(peer
, ackbuf
, 4, 0);
586 signal(SIGALRM
, justquit
); /* just quit on timeout */
588 n
= recv(peer
, buf
, sizeof (buf
), 0); /* normally times out and quits */
590 if (n
>= 4 && /* if read some data */
591 dp
->th_opcode
== DATA
&& /* and got a data block */
592 block
== dp
->th_block
) { /* then my last ack was lost */
593 (void) send(peer
, ackbuf
, 4, 0); /* resend final ack */
603 { EUNDEF
, "Undefined error code" },
604 { ENOTFOUND
, "File not found" },
605 { EACCESS
, "Access violation" },
606 { ENOSPACE
, "Disk full or allocation exceeded" },
607 { EBADOP
, "Illegal TFTP operation" },
608 { EBADID
, "Unknown transfer ID" },
609 { EEXISTS
, "File already exists" },
610 { ENOUSER
, "No such user" },
619 register struct errmsg
*pe
;
622 for (pe
= errmsgs
; pe
->e_code
>= 0; pe
++)
623 if (pe
->e_code
== error
)
625 sprintf(buf
, "error %d", error
);
630 * Send a nak packet (error message).
631 * Error code passed in is one of the
632 * standard TFTP codes, or a UNIX errno
639 register struct tftphdr
*tp
;
641 register struct errmsg
*pe
;
643 tp
= (struct tftphdr
*)buf
;
644 tp
->th_opcode
= htons((u_short
)ERROR
);
645 tp
->th_code
= htons((u_short
)error
);
646 for (pe
= errmsgs
; pe
->e_code
>= 0; pe
++)
647 if (pe
->e_code
== error
)
649 if (pe
->e_code
< 0) {
650 pe
->e_msg
= strerror(error
- 100);
651 tp
->th_code
= EUNDEF
; /* set 'undef' errorcode */
653 strcpy(tp
->th_msg
, pe
->e_msg
);
654 length
= strlen(pe
->e_msg
);
655 tp
->th_msg
[length
] = '\0';
657 if (send(peer
, buf
, length
, 0) != length
)
658 syslog(LOG_ERR
, "nak: %m\n");
663 struct sockaddr_in
*fromp
;
667 hp
= gethostbyaddr((char *)&fromp
->sin_addr
, sizeof (fromp
->sin_addr
),
672 return inet_ntoa(fromp
->sin_addr
);