]>
git.saurik.com Git - apple/network_cmds.git/blob - ftpd.tproj/ftpd.c
4071f026dd4ddb7f5e1f539cd596a9d4820e5823
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) 1985, 1988, 1990, 1992, 1993, 1994
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) 1985, 1988, 1990, 1992, 1993, 1994\n\
60 The Regents of the University of California. All rights reserved.\n";
64 static char sccsid
[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
67 /* XXX this is to get around a compiler bug with long long's on ppc */
73 #include <sys/param.h>
75 #include <sys/ioctl.h>
76 #include <sys/socket.h>
79 #include <netinet/in.h>
80 #include <netinet/in_systm.h>
81 #include <netinet/ip.h>
85 #include <arpa/inet.h>
86 #include <arpa/telnet.h>
106 #include "pathnames.h"
115 static char version
[] = "Version 6.00";
117 extern off_t restart_point
;
120 struct sockaddr_in ctrl_addr
;
121 struct sockaddr_in data_source
;
122 struct sockaddr_in data_dest
;
123 struct sockaddr_in his_addr
;
124 struct sockaddr_in pasv_addr
;
127 jmp_buf errcatch
, urgcatch
;
131 int timeout
= 900; /* timeout after 15 minutes of inactivity */
132 int maxtimeout
= 7200;/* don't allow idle time to be set beyond 2 hours */
137 int stru
; /* avoid C keyword */
139 int usedefault
= 1; /* for data transfers */
140 int pdata
= -1; /* for passive mode */
141 sig_atomic_t transflag
;
144 #if !defined(CMASK) || CMASK == 0
148 int defumask
= CMASK
; /* default umask value */
150 char hostname
[MAXHOSTNAMELEN
];
151 char remotehost
[MAXHOSTNAMELEN
];
154 * Timeout intervals for retrying connections
155 * to hosts that don't accept PORT cmds. This
156 * is a kludge, but given the problems with TCP...
158 #define SWAITMAX 90 /* wait at most 90 seconds */
159 #define SWAITINT 5 /* interval between retries */
161 int swaitmax
= SWAITMAX
;
162 int swaitint
= SWAITINT
;
165 char **Argv
= NULL
; /* pointer to argument vector */
166 char *LastArgv
= NULL
; /* end of argv */
167 char proctitle
[LINE_MAX
]; /* initial part of title */
168 #endif /* SETPROCTITLE */
170 #define LOGCMD(cmd, file) \
172 syslog(LOG_INFO,"%s %s%s", cmd, \
173 *(file) == '/' ? "" : curdir(), file);
174 #define LOGCMD2(cmd, file1, file2) \
176 syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
177 *(file1) == '/' ? "" : curdir(), file1, \
178 *(file2) == '/' ? "" : curdir(), file2);
179 #define LOGBYTES(cmd, file, cnt) \
181 if (cnt == (off_t)-1) \
182 syslog(LOG_INFO,"%s %s%s", cmd, \
183 *(file) == '/' ? "" : curdir(), file); \
185 syslog(LOG_INFO, "%s %s%s = %qd bytes", \
186 cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
189 static void ack
__P((char *));
190 static void myoob
__P((int));
191 static int checkuser
__P((char *));
192 static FILE *dataconn
__P((char *, off_t
, char *));
193 static void dolog
__P((struct sockaddr_in
*));
194 static char *curdir
__P((void));
195 static void end_login
__P((void));
196 static FILE *getdatasock
__P((char *));
197 static char *gunique
__P((char *));
198 static void lostconn
__P((int));
199 static int receive_data
__P((FILE *, FILE *));
200 static void send_data
__P((FILE *, FILE *, off_t
));
201 static struct passwd
*
202 sgetpwnam
__P((char *));
203 static char *sgetsave
__P((char *));
208 static char path
[MAXPATHLEN
+1+1]; /* path + '/' + '\0' */
210 if (getcwd(path
, sizeof(path
)-2) == NULL
)
212 if (path
[1] != '\0') /* special case for root dir. */
214 /* For guest account, skip / since it's chrooted */
215 return (guest
? path
+1 : path
);
219 main(argc
, argv
, envp
)
224 int addrlen
, ch
, on
= 1, tos
;
225 char *cp
, line
[LINE_MAX
];
229 * LOG_NDELAY sets up the logging connection immediately,
230 * necessary for anonymous ftp's that chroot and can't do it later.
232 openlog("ftpd", LOG_PID
| LOG_NDELAY
, LOG_FTP
);
233 addrlen
= sizeof(his_addr
);
234 if (getpeername(0, (struct sockaddr
*)&his_addr
, &addrlen
) < 0) {
235 syslog(LOG_ERR
, "getpeername (%s): %m",argv
[0]);
238 addrlen
= sizeof(ctrl_addr
);
239 if (getsockname(0, (struct sockaddr
*)&ctrl_addr
, &addrlen
) < 0) {
240 syslog(LOG_ERR
, "getsockname (%s): %m",argv
[0]);
244 tos
= IPTOS_LOWDELAY
;
245 if (setsockopt(0, IPPROTO_IP
, IP_TOS
, (char *)&tos
, sizeof(int)) < 0)
246 syslog(LOG_WARNING
, "setsockopt (IP_TOS): %m");
248 data_source
.sin_port
= htons(ntohs(ctrl_addr
.sin_port
) - 1);
252 * Save start and extent of argv for setproctitle.
257 LastArgv
= envp
[-1] + strlen(envp
[-1]);
258 #endif /* SETPROCTITLE */
260 while ((ch
= getopt(argc
, argv
, "dlt:T:u:v")) != -1) {
267 logging
++; /* > 1 == extra logging */
271 timeout
= atoi(optarg
);
272 if (maxtimeout
< timeout
)
273 maxtimeout
= timeout
;
277 maxtimeout
= atoi(optarg
);
278 if (timeout
> maxtimeout
)
279 timeout
= maxtimeout
;
286 val
= strtol(optarg
, &optarg
, 8);
287 if (*optarg
!= '\0' || val
< 0)
288 warnx("bad value for -u");
299 warnx("unknown flag -%c ignored", optopt
);
303 (void) freopen(_PATH_DEVNULL
, "w", stderr
);
304 (void) signal(SIGPIPE
, lostconn
);
305 (void) signal(SIGCHLD
, SIG_IGN
);
306 if ((int)signal(SIGURG
, myoob
) < 0)
307 syslog(LOG_ERR
, "signal: %m");
309 /* Try to handle urgent data inline */
311 if (setsockopt(0, SOL_SOCKET
, SO_OOBINLINE
, (char *)&on
, sizeof(on
)) < 0)
312 syslog(LOG_ERR
, "setsockopt: %m");
316 if (fcntl(fileno(stdin
), F_SETOWN
, getpid()) == -1)
317 syslog(LOG_ERR
, "fcntl F_SETOWN: %m");
321 * Set up default state
330 /* If logins are disabled, print out the message. */
331 if ((fd
= fopen(_PATH_NOLOGIN
,"r")) != NULL
) {
332 while (fgets(line
, sizeof(line
), fd
) != NULL
) {
333 if ((cp
= strchr(line
, '\n')) != NULL
)
335 lreply(530, "%s", line
);
337 (void) fflush(stdout
);
339 reply(530, "System not available.");
342 if ((fd
= fopen(_PATH_FTPWELCOME
, "r")) != NULL
) {
343 while (fgets(line
, sizeof(line
), fd
) != NULL
) {
344 if ((cp
= strchr(line
, '\n')) != NULL
)
346 lreply(220, "%s", line
);
348 (void) fflush(stdout
);
350 /* reply(220,) must follow */
352 (void) gethostname(hostname
, sizeof(hostname
));
353 reply(220, "%s FTP server (%s) ready.", hostname
, version
);
354 (void) setjmp(errcatch
);
366 syslog(LOG_DEBUG
, "lost connection");
370 static char ttyline
[20];
373 * Helper function for sgetpwnam().
379 char *new = malloc((unsigned) strlen(s
) + 1);
382 perror_reply(421, "Local resource failure: malloc");
386 (void) strcpy(new, s
);
391 * Save the result of a getpwnam. Used for USER command, since
392 * the data returned must not be clobbered by any other command
395 static struct passwd
*
399 static struct passwd save
;
402 if ((p
= getpwnam(name
)) == NULL
)
406 free(save
.pw_passwd
);
412 save
.pw_name
= sgetsave(p
->pw_name
);
413 save
.pw_passwd
= sgetsave(p
->pw_passwd
);
414 save
.pw_gecos
= sgetsave(p
->pw_gecos
);
415 save
.pw_dir
= sgetsave(p
->pw_dir
);
416 save
.pw_shell
= sgetsave(p
->pw_shell
);
420 static int login_attempts
; /* number of failed login attempts */
421 static int askpasswd
; /* had user command, ask for passwd */
422 static char curname
[10]; /* current USER name */
426 * Sets global passwd pointer pw if named account exists and is acceptable;
427 * sets askpasswd if a PASS command is expected. If logged in previously,
428 * need to reset state. If name is "ftp" or "anonymous", the name is not in
429 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
430 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
431 * requesting login privileges. Disallow anyone who does not have a standard
432 * shell as returned by getusershell(). Disallow anyone mentioned in the file
433 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
443 reply(530, "Can't change user from guest login.");
450 if (strcmp(name
, "ftp") == 0 || strcmp(name
, "anonymous") == 0) {
451 if (checkuser("ftp") || checkuser("anonymous"))
452 reply(530, "User %s access denied.", name
);
453 else if ((pw
= sgetpwnam("ftp")) != NULL
) {
457 "Guest login ok, type your name as password.");
459 reply(530, "User %s unknown.", name
);
460 if (!askpasswd
&& logging
)
462 "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost
);
465 if (pw
= sgetpwnam(name
)) {
466 if ((shell
= pw
->pw_shell
) == NULL
|| *shell
== 0)
467 shell
= _PATH_BSHELL
;
468 while ((cp
= getusershell()) != NULL
)
469 if (strcmp(cp
, shell
) == 0)
473 if (cp
== NULL
|| checkuser(name
)) {
474 reply(530, "User %s access denied.", name
);
477 "FTP LOGIN REFUSED FROM %s, %s",
479 pw
= (struct passwd
*) NULL
;
484 strncpy(curname
, name
, sizeof(curname
)-1);
485 reply(331, "Password required for %s.", name
);
488 * Delay before reading passwd after first failed
489 * attempt to slow down passwd-guessing programs.
492 sleep((unsigned) login_attempts
);
496 * Check if a user is in the file _PATH_FTPUSERS
504 char *p
, line
[BUFSIZ
];
506 if ((fd
= fopen(_PATH_FTPUSERS
, "r")) != NULL
) {
507 while (fgets(line
, sizeof(line
), fd
) != NULL
)
508 if ((p
= strchr(line
, '\n')) != NULL
) {
512 if (strcmp(line
, name
) == 0) {
523 * Terminate login as previous user, if any, resetting state;
524 * used when USER command is given or login fails.
530 (void) seteuid((uid_t
)0);
532 logwtmp(ttyline
, "", "");
542 char *salt
, *xpasswd
;
545 if (logged_in
|| askpasswd
== 0) {
546 reply(503, "Login with USER first.");
550 if (!guest
) { /* "ftp" is only account allowed no password */
554 salt
= pw
->pw_passwd
;
555 xpasswd
= crypt(passwd
, salt
);
556 /* The strcmp does not catch null passwords! */
557 if (pw
== NULL
|| *pw
->pw_passwd
== '\0' ||
558 strcmp(xpasswd
, pw
->pw_passwd
)) {
559 reply(530, "Login incorrect.");
562 "FTP LOGIN FAILED FROM %s, %s",
563 remotehost
, curname
);
565 if (login_attempts
++ >= 5) {
567 "repeated login failures from %s",
574 login_attempts
= 0; /* this time successful */
575 if (setegid((gid_t
)pw
->pw_gid
) < 0) {
576 reply(550, "Can't set gid.");
579 (void) initgroups(pw
->pw_name
, pw
->pw_gid
);
581 /* open wtmp before chroot */
582 (void)sprintf(ttyline
, "ftp%d", getpid());
583 logwtmp(ttyline
, pw
->pw_name
, remotehost
);
588 * We MUST do a chdir() after the chroot. Otherwise
589 * the old current directory will be accessible as "."
590 * outside the new root!
592 if (chroot(pw
->pw_dir
) < 0 || chdir("/") < 0) {
593 reply(550, "Can't set guest privileges.");
596 } else if (chdir(pw
->pw_dir
) < 0) {
597 if (chdir("/") < 0) {
598 reply(530, "User %s: can't change directory to %s.",
599 pw
->pw_name
, pw
->pw_dir
);
602 lreply(230, "No directory! Logging in with home=/");
604 if (seteuid((uid_t
)pw
->pw_uid
) < 0) {
605 reply(550, "Can't set uid.");
609 * Display a login message, if it exists.
610 * N.B. reply(230,) must follow the message.
612 if ((fd
= fopen(_PATH_FTPLOGINMESG
, "r")) != NULL
) {
613 char *cp
, line
[LINE_MAX
];
615 while (fgets(line
, sizeof(line
), fd
) != NULL
) {
616 if ((cp
= strchr(line
, '\n')) != NULL
)
618 lreply(230, "%s", line
);
620 (void) fflush(stdout
);
624 reply(230, "Guest login ok, access restrictions apply.");
626 snprintf(proctitle
, sizeof(proctitle
),
627 "%s: anonymous/%.*s", remotehost
,
628 sizeof(proctitle
) - sizeof(remotehost
) -
629 sizeof(": anonymous/"), passwd
);
630 setproctitle("%s", proctitle
);
631 #endif /* SETPROCTITLE */
633 syslog(LOG_INFO
, "ANONYMOUS FTP LOGIN FROM %s, %s",
636 reply(230, "User %s logged in.", pw
->pw_name
);
638 snprintf(proctitle
, sizeof(proctitle
),
639 "%s: %s", remotehost
, pw
->pw_name
);
640 setproctitle("%s", proctitle
);
641 #endif /* SETPROCTITLE */
643 syslog(LOG_INFO
, "FTP LOGIN FROM %s as %s",
644 remotehost
, pw
->pw_name
);
646 (void) umask(defumask
);
649 /* Forget all about it... */
659 int (*closefunc
) __P((FILE *));
662 fin
= fopen(name
, "r"), closefunc
= fclose
;
667 (void) sprintf(line
, cmd
, name
), name
= line
;
668 fin
= ftpd_popen(line
, "r"), closefunc
= ftpd_pclose
;
670 st
.st_blksize
= BUFSIZ
;
674 perror_reply(550, name
);
682 if (cmd
== 0 && (fstat(fileno(fin
), &st
) < 0 || !S_ISREG(st
.st_mode
))) {
683 reply(550, "%s: not a plain file.", name
);
687 if (type
== TYPE_A
) {
694 if ((c
=getc(fin
)) == EOF
) {
695 perror_reply(550, name
);
701 } else if (lseek(fileno(fin
), restart_point
, L_SET
) < 0) {
702 perror_reply(550, name
);
706 dout
= dataconn(name
, st
.st_size
, "w");
709 send_data(fin
, dout
, st
.st_blksize
);
715 LOGBYTES("get", name
, byte_count
);
720 store(name
, mode
, unique
)
726 int (*closefunc
) __P((FILE *));
728 if (unique
&& stat(name
, &st
) == 0 &&
729 (name
= gunique(name
)) == NULL
) {
730 LOGCMD(*mode
== 'w' ? "put" : "append", name
);
736 fout
= fopen(name
, mode
);
739 perror_reply(553, name
);
740 LOGCMD(*mode
== 'w' ? "put" : "append", name
);
745 if (type
== TYPE_A
) {
752 if ((c
=getc(fout
)) == EOF
) {
753 perror_reply(550, name
);
760 * We must do this seek to "current" position
761 * because we are changing from reading to
764 if (fseek(fout
, 0L, L_INCR
) < 0) {
765 perror_reply(550, name
);
768 } else if (lseek(fileno(fout
), restart_point
, L_SET
) < 0) {
769 perror_reply(550, name
);
773 din
= dataconn(name
, (off_t
)-1, "r");
776 if (receive_data(din
, fout
) == 0) {
778 reply(226, "Transfer complete (unique file name:%s).",
781 reply(226, "Transfer complete.");
787 LOGBYTES(*mode
== 'w' ? "put" : "append", name
, byte_count
);
795 int on
= 1, s
, t
, tries
;
798 return (fdopen(data
, mode
));
799 (void) seteuid((uid_t
)0);
800 s
= socket(AF_INET
, SOCK_STREAM
, 0);
803 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
,
804 (char *) &on
, sizeof(on
)) < 0)
806 /* anchor socket to avoid multi-homing problems */
807 data_source
.sin_family
= AF_INET
;
808 data_source
.sin_addr
= ctrl_addr
.sin_addr
;
809 for (tries
= 1; ; tries
++) {
810 if (bind(s
, (struct sockaddr
*)&data_source
,
811 sizeof(data_source
)) >= 0)
813 if (errno
!= EADDRINUSE
|| tries
> 10)
817 (void) seteuid((uid_t
)pw
->pw_uid
);
819 on
= IPTOS_THROUGHPUT
;
820 if (setsockopt(s
, IPPROTO_IP
, IP_TOS
, (char *)&on
, sizeof(int)) < 0)
821 syslog(LOG_WARNING
, "setsockopt (IP_TOS): %m");
823 return (fdopen(s
, mode
));
825 /* Return the real value of errno (close may change it) */
827 (void) seteuid((uid_t
)pw
->pw_uid
);
834 dataconn(name
, size
, mode
)
845 if (size
!= (off_t
) -1)
846 (void) sprintf(sizebuf
, " (%qd bytes)", size
);
848 (void) strcpy(sizebuf
, "");
850 struct sockaddr_in from
;
851 int s
, fromlen
= sizeof(from
);
853 s
= accept(pdata
, (struct sockaddr
*)&from
, &fromlen
);
855 reply(425, "Can't open data connection.");
863 tos
= IPTOS_LOWDELAY
;
864 (void) setsockopt(s
, IPPROTO_IP
, IP_TOS
, (char *)&tos
,
867 reply(150, "Opening %s mode data connection for '%s'%s.",
868 type
== TYPE_A
? "ASCII" : "BINARY", name
, sizebuf
);
869 return (fdopen(pdata
, mode
));
872 reply(125, "Using existing data connection for '%s'%s.",
875 return (fdopen(data
, mode
));
878 data_dest
= his_addr
;
880 file
= getdatasock(mode
);
882 reply(425, "Can't create data socket (%s,%d): %s.",
883 inet_ntoa(data_source
.sin_addr
),
884 ntohs(data_source
.sin_port
), strerror(errno
));
888 while (connect(data
, (struct sockaddr
*)&data_dest
,
889 sizeof(data_dest
)) < 0) {
890 if (errno
== EADDRINUSE
&& retry
< swaitmax
) {
891 sleep((unsigned) swaitint
);
895 perror_reply(425, "Can't build data connection");
900 reply(150, "Opening %s mode data connection for '%s'%s.",
901 type
== TYPE_A
? "ASCII" : "BINARY", name
, sizebuf
);
906 * Tranfer the contents of "instr" to "outstr" peer using the appropriate
907 * encapsulation of the data subject * to Mode, Structure, and Type.
909 * NB: Form isn't handled.
912 send_data(instr
, outstr
, blksize
)
913 FILE *instr
, *outstr
;
916 int c
, cnt
, filefd
, netfd
;
920 if (setjmp(urgcatch
)) {
927 while ((c
= getc(instr
)) != EOF
) {
932 (void) putc('\r', outstr
);
934 (void) putc(c
, outstr
);
942 reply(226, "Transfer complete.");
947 if ((buf
= malloc((u_int
)blksize
)) == NULL
) {
949 perror_reply(451, "Local resource failure: malloc");
952 netfd
= fileno(outstr
);
953 filefd
= fileno(instr
);
954 while ((cnt
= read(filefd
, buf
, (u_int
)blksize
)) > 0 &&
955 write(netfd
, buf
, cnt
) == cnt
)
964 reply(226, "Transfer complete.");
968 reply(550, "Unimplemented TYPE %d in send_data", type
);
974 perror_reply(426, "Data connection");
979 perror_reply(551, "Error on input file");
983 * Transfer data from peer to "outstr" using the appropriate encapulation of
984 * the data subject to Mode, Structure, and Type.
986 * N.B.: Form isn't handled.
989 receive_data(instr
, outstr
)
990 FILE *instr
, *outstr
;
993 int cnt
, bare_lfs
= 0;
997 if (setjmp(urgcatch
)) {
1005 while ((cnt
= read(fileno(instr
), buf
, sizeof(buf
))) > 0) {
1006 if (write(fileno(outstr
), buf
, cnt
) != cnt
)
1016 reply(553, "TYPE E not implemented.");
1021 while ((c
= getc(instr
)) != EOF
) {
1028 if ((c
= getc(instr
)) != '\n') {
1029 (void) putc ('\r', outstr
);
1030 if (c
== '\0' || c
== EOF
)
1034 (void) putc(c
, outstr
);
1045 "WARNING! %d bare linefeeds received in ASCII mode",
1047 (void)printf(" File may not have transferred correctly.\r\n");
1051 reply(550, "Unimplemented TYPE %d in receive_data", type
);
1058 perror_reply(426, "Data Connection");
1063 perror_reply(452, "Error writing file");
1068 statfilecmd(filename
)
1073 char line
[LINE_MAX
];
1075 (void)snprintf(line
, sizeof(line
), "/bin/ls -lgA %s", filename
);
1076 fin
= ftpd_popen(line
, "r");
1077 lreply(211, "status of %s:", filename
);
1078 while ((c
= getc(fin
)) != EOF
) {
1080 if (ferror(stdout
)){
1081 perror_reply(421, "control connection");
1082 (void) ftpd_pclose(fin
);
1087 perror_reply(551, filename
);
1088 (void) ftpd_pclose(fin
);
1091 (void) putc('\r', stdout
);
1093 (void) putc(c
, stdout
);
1095 (void) ftpd_pclose(fin
);
1096 reply(211, "End of Status");
1102 struct sockaddr_in
*sin
;
1105 lreply(211, "%s FTP server status:", hostname
, version
);
1106 printf(" %s\r\n", version
);
1107 printf(" Connected to %s", remotehost
);
1108 if (!isdigit(remotehost
[0]))
1109 printf(" (%s)", inet_ntoa(his_addr
.sin_addr
));
1113 printf(" Logged in anonymously\r\n");
1115 printf(" Logged in as %s\r\n", pw
->pw_name
);
1116 } else if (askpasswd
)
1117 printf(" Waiting for password\r\n");
1119 printf(" Waiting for user name\r\n");
1120 printf(" TYPE: %s", typenames
[type
]);
1121 if (type
== TYPE_A
|| type
== TYPE_E
)
1122 printf(", FORM: %s", formnames
[form
]);
1125 printf(" %d", NBBY
);
1127 printf(" %d", bytesize
); /* need definition! */
1129 printf("; STRUcture: %s; transfer MODE: %s\r\n",
1130 strunames
[stru
], modenames
[mode
]);
1132 printf(" Data connection open\r\n");
1133 else if (pdata
!= -1) {
1134 printf(" in Passive mode");
1137 } else if (usedefault
== 0) {
1141 a
= (u_char
*) &sin
->sin_addr
;
1142 p
= (u_char
*) &sin
->sin_port
;
1143 #define UC(b) (((int) b) & 0xff)
1144 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a
[0]),
1145 UC(a
[1]), UC(a
[2]), UC(a
[3]), UC(p
[0]), UC(p
[1]));
1148 printf(" No data connection\r\n");
1149 reply(211, "End of status");
1157 reply(451, "Error in server: %s\n", s
);
1158 reply(221, "Closing connection due to server error.");
1165 reply(int n
, const char *fmt
, ...)
1167 reply(n
, fmt
, va_alist
)
1179 (void)printf("%d ", n
);
1180 (void)vprintf(fmt
, ap
);
1181 (void)printf("\r\n");
1182 (void)fflush(stdout
);
1184 syslog(LOG_DEBUG
, "<--- %d ", n
);
1185 vsyslog(LOG_DEBUG
, fmt
, ap
);
1191 lreply(int n
, const char *fmt
, ...)
1193 lreply(n
, fmt
, va_alist
)
1205 (void)printf("%d- ", n
);
1206 (void)vprintf(fmt
, ap
);
1207 (void)printf("\r\n");
1208 (void)fflush(stdout
);
1210 syslog(LOG_DEBUG
, "<--- %d- ", n
);
1211 vsyslog(LOG_DEBUG
, fmt
, ap
);
1220 reply(250, "%s command successful.", s
);
1228 reply(502, "%s command not implemented.", s
);
1238 if (cp
= strchr(cbuf
,'\n'))
1240 reply(500, "'%s': command not understood.", cbuf
);
1249 LOGCMD("delete", name
);
1250 if (stat(name
, &st
) < 0) {
1251 perror_reply(550, name
);
1254 if ((st
.st_mode
&S_IFMT
) == S_IFDIR
) {
1255 if (rmdir(name
) < 0) {
1256 perror_reply(550, name
);
1261 if (unlink(name
) < 0) {
1262 perror_reply(550, name
);
1274 if (chdir(path
) < 0)
1275 perror_reply(550, path
);
1285 LOGCMD("mkdir", name
);
1286 if (mkdir(name
, 0777) < 0)
1287 perror_reply(550, name
);
1289 reply(257, "MKD command successful.");
1297 LOGCMD("rmdir", name
);
1298 if (rmdir(name
) < 0)
1299 perror_reply(550, name
);
1307 char path
[MAXPATHLEN
];
1309 if (getcwd(path
, sizeof(path
)) == (char *)NULL
)
1310 reply(550, "%s.", path
);
1312 reply(257, "\"%s\" is current directory.", path
);
1321 if (stat(name
, &st
) < 0) {
1322 perror_reply(550, name
);
1325 reply(350, "File exists, ready for destination name");
1334 LOGCMD2("rename", from
, to
);
1335 if (rename(from
, to
) < 0)
1336 perror_reply(550, "rename");
1343 struct sockaddr_in
*sin
;
1345 struct hostent
*hp
= gethostbyaddr((char *)&sin
->sin_addr
,
1346 sizeof(struct in_addr
), AF_INET
);
1349 (void) strncpy(remotehost
, hp
->h_name
, sizeof(remotehost
));
1351 (void) strncpy(remotehost
, inet_ntoa(sin
->sin_addr
),
1352 sizeof(remotehost
));
1354 snprintf(proctitle
, sizeof(proctitle
), "%s: connected", remotehost
);
1355 setproctitle("%s", proctitle
);
1356 #endif /* SETPROCTITLE */
1359 syslog(LOG_INFO
, "connection from %s", remotehost
);
1363 * Record logout in wtmp file
1364 * and exit with supplied status.
1371 * Prevent reception of SIGURG from resulting in a resumption
1372 * back to the main program loop.
1377 (void) seteuid((uid_t
)0);
1378 logwtmp(ttyline
, "", "");
1380 /* beware of flushing buffers after a SIGPIPE */
1390 /* only process if transfer occurring */
1394 if (getline(cp
, 7, stdin
) == NULL
) {
1395 reply(221, "You could at least say goodbye.");
1399 if (strcmp(cp
, "ABOR\r\n") == 0) {
1401 reply(426, "Transfer aborted. Data connection closed.");
1402 reply(226, "Abort successful");
1403 longjmp(urgcatch
, 1);
1405 if (strcmp(cp
, "STAT\r\n") == 0) {
1406 if (file_size
!= (off_t
) -1)
1407 reply(213, "Status: %qd of %qd bytes transferred",
1408 byte_count
, file_size
);
1410 reply(213, "Status: %qd bytes transferred", byte_count
);
1415 * Note: a response of 425 is not mentioned as a possible response to
1416 * the PASV command in RFC959. However, it has been blessed as
1417 * a legitimate response by Jon Postel in a telephone conversation
1418 * with Rick Adams on 25 Jan 89.
1426 pdata
= socket(AF_INET
, SOCK_STREAM
, 0);
1428 perror_reply(425, "Can't open passive connection");
1431 pasv_addr
= ctrl_addr
;
1432 pasv_addr
.sin_port
= 0;
1433 (void) seteuid((uid_t
)0);
1434 if (bind(pdata
, (struct sockaddr
*)&pasv_addr
, sizeof(pasv_addr
)) < 0) {
1435 (void) seteuid((uid_t
)pw
->pw_uid
);
1438 (void) seteuid((uid_t
)pw
->pw_uid
);
1439 len
= sizeof(pasv_addr
);
1440 if (getsockname(pdata
, (struct sockaddr
*) &pasv_addr
, &len
) < 0)
1442 if (listen(pdata
, 1) < 0)
1444 a
= (char *) &pasv_addr
.sin_addr
;
1445 p
= (char *) &pasv_addr
.sin_port
;
1447 #define UC(b) (((int) b) & 0xff)
1449 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a
[0]),
1450 UC(a
[1]), UC(a
[2]), UC(a
[3]), UC(p
[0]), UC(p
[1]));
1454 (void) close(pdata
);
1456 perror_reply(425, "Can't open passive connection");
1461 * Generate unique name for file with basename "local".
1462 * The file named "local" is already known to exist.
1463 * Generates failure reply on error.
1469 static char new[MAXPATHLEN
];
1474 cp
= strrchr(local
, '/');
1477 if (stat(cp
? local
: ".", &st
) < 0) {
1478 perror_reply(553, cp
? local
: ".");
1479 return ((char *) 0);
1483 (void) strcpy(new, local
);
1484 cp
= new + strlen(new);
1486 for (count
= 1; count
< 100; count
++) {
1487 (void)sprintf(cp
, "%d", count
);
1488 if (stat(new, &st
) < 0)
1491 reply(452, "Unique file name cannot be created.");
1496 * Format and send reply containing system error number.
1499 perror_reply(code
, string
)
1504 reply(code
, "%s: %s.", string
, strerror(errno
));
1507 static char *onefile
[] = {
1513 send_file_list(whichf
)
1520 char **dirlist
, *dirname
;
1525 if (strpbrk(whichf
, "~{[*?") != NULL
) {
1526 int flags
= GLOB_BRACE
|GLOB_NOCHECK
|GLOB_QUOTE
|GLOB_TILDE
;
1528 memset(&gl
, 0, sizeof(gl
));
1530 if (glob(whichf
, flags
, 0, &gl
)) {
1531 reply(550, "not found");
1533 } else if (gl
.gl_pathc
== 0) {
1535 perror_reply(550, whichf
);
1538 dirlist
= gl
.gl_pathv
;
1540 onefile
[0] = whichf
;
1545 if (setjmp(urgcatch
)) {
1549 while (dirname
= *dirlist
++) {
1550 if (stat(dirname
, &st
) < 0) {
1552 * If user typed "ls -l", etc, and the client
1553 * used NLST, do what the user meant.
1555 if (dirname
[0] == '-' && *dirlist
== NULL
&&
1557 retrieve("/bin/ls %s", dirname
);
1560 perror_reply(550, whichf
);
1562 (void) fclose(dout
);
1570 if (S_ISREG(st
.st_mode
)) {
1572 dout
= dataconn("file list", (off_t
)-1, "w");
1577 fprintf(dout
, "%s%s\n", dirname
,
1578 type
== TYPE_A
? "\r" : "");
1579 byte_count
+= strlen(dirname
) + 1;
1581 } else if (!S_ISDIR(st
.st_mode
))
1584 if ((dirp
= opendir(dirname
)) == NULL
)
1587 while ((dir
= readdir(dirp
)) != NULL
) {
1588 char nbuf
[MAXPATHLEN
];
1590 if (dir
->d_name
[0] == '.' && dir
->d_namlen
== 1)
1592 if (dir
->d_name
[0] == '.' && dir
->d_name
[1] == '.' &&
1596 sprintf(nbuf
, "%s/%s", dirname
, dir
->d_name
);
1599 * We have to do a stat to insure it's
1600 * not a directory or special file.
1602 if (simple
|| (stat(nbuf
, &st
) == 0 &&
1603 S_ISREG(st
.st_mode
))) {
1605 dout
= dataconn("file list", (off_t
)-1,
1611 if (nbuf
[0] == '.' && nbuf
[1] == '/')
1612 fprintf(dout
, "%s%s\n", &nbuf
[2],
1613 type
== TYPE_A
? "\r" : "");
1615 fprintf(dout
, "%s%s\n", nbuf
,
1616 type
== TYPE_A
? "\r" : "");
1617 byte_count
+= strlen(nbuf
) + 1;
1620 (void) closedir(dirp
);
1624 reply(550, "No files found.");
1625 else if (ferror(dout
) != 0)
1626 perror_reply(550, "Data connection");
1628 reply(226, "Transfer complete.");
1632 (void) fclose(dout
);
1644 * Clobber argv so ps will show what we're doing. (Stolen from sendmail.)
1645 * Warning, since this is usually started from inetd.conf, it often doesn't
1646 * have much of an environment or arglist to overwrite.
1650 setproctitle(const char *fmt
, ...)
1652 setproctitle(fmt
, va_alist
)
1667 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1669 /* make ps print our process name */
1674 if (i
> LastArgv
- p
- 2) {
1675 i
= LastArgv
- p
- 2;
1680 if (ch
!= '\n' && ch
!= '\r')
1682 while (p
< LastArgv
)
1685 #endif /* SETPROCTITLE */
1686 #pragma CC_OPT_RESTORE