122499Sdist /* 236304Skarels * Copyright (c) 1985, 1988 Regents of the University of California. 333738Sbostic * All rights reserved. 433738Sbostic * 5*42666Sbostic * %sccs.include.redist.c% 622499Sdist */ 722499Sdist 810275Ssam #ifndef lint 922499Sdist char copyright[] = 1036304Skarels "@(#) Copyright (c) 1985, 1988 Regents of the University of California.\n\ 1122499Sdist All rights reserved.\n"; 1233738Sbostic #endif /* not lint */ 1310275Ssam 1422499Sdist #ifndef lint 15*42666Sbostic static char sccsid[] = "@(#)ftpd.c 5.36 (Berkeley) 06/01/90"; 1633738Sbostic #endif /* not lint */ 1722499Sdist 1810275Ssam /* 1910275Ssam * FTP server. 2010275Ssam */ 2110303Ssam #include <sys/param.h> 2210275Ssam #include <sys/stat.h> 2310275Ssam #include <sys/ioctl.h> 2410275Ssam #include <sys/socket.h> 2513247Ssam #include <sys/file.h> 2613595Ssam #include <sys/wait.h> 2736620Srick #include <sys/dir.h> 2810275Ssam 2910275Ssam #include <netinet/in.h> 3010275Ssam 3136933Skarels #define FTP_NAMES 3213034Ssam #include <arpa/ftp.h> 3313211Sroot #include <arpa/inet.h> 3426044Sminshall #include <arpa/telnet.h> 3513034Ssam 3636933Skarels #include <ctype.h> 3710275Ssam #include <stdio.h> 3810275Ssam #include <signal.h> 3910275Ssam #include <pwd.h> 4010275Ssam #include <setjmp.h> 4110275Ssam #include <netdb.h> 4210423Ssam #include <errno.h> 4342031Sbostic #include <string.h> 4426493Sminshall #include <syslog.h> 4536435Sbostic #include <varargs.h> 4637459Skarels #include "pathnames.h" 4710275Ssam 4810695Ssam /* 4910695Ssam * File containing login names 5010695Ssam * NOT to be used on this machine. 5110695Ssam * Commonly used to disallow uucp. 5210695Ssam */ 5310275Ssam extern int errno; 5410275Ssam extern char *crypt(); 5510275Ssam extern char version[]; 5610275Ssam extern char *home; /* pointer to home directory for glob */ 5736276Sbostic extern FILE *ftpd_popen(), *fopen(), *freopen(); 5836304Skarels extern int ftpd_pclose(), fclose(); 5926044Sminshall extern char *getline(); 6026044Sminshall extern char cbuf[]; 6137459Skarels extern off_t restart_point; 6210275Ssam 6310275Ssam struct sockaddr_in ctrl_addr; 6410275Ssam struct sockaddr_in data_source; 6510275Ssam struct sockaddr_in data_dest; 6610275Ssam struct sockaddr_in his_addr; 6736933Skarels struct sockaddr_in pasv_addr; 6810275Ssam 6910275Ssam int data; 7026044Sminshall jmp_buf errcatch, urgcatch; 7110275Ssam int logged_in; 7210275Ssam struct passwd *pw; 7310275Ssam int debug; 7426493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */ 7536933Skarels int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 7611757Ssam int logging; 7710275Ssam int guest; 7810275Ssam int type; 7910275Ssam int form; 8010275Ssam int stru; /* avoid C keyword */ 8110275Ssam int mode; 8210321Ssam int usedefault = 1; /* for data transfers */ 8336304Skarels int pdata = -1; /* for passive mode */ 8426044Sminshall int transflag; 8536933Skarels off_t file_size; 8636933Skarels off_t byte_count; 8736933Skarels #if !defined(CMASK) || CMASK == 0 8836933Skarels #undef CMASK 8936933Skarels #define CMASK 027 9036933Skarels #endif 9136933Skarels int defumask = CMASK; /* default umask value */ 9226044Sminshall char tmpline[7]; 9336276Sbostic char hostname[MAXHOSTNAMELEN]; 9436276Sbostic char remotehost[MAXHOSTNAMELEN]; 9510275Ssam 9611653Ssam /* 9711653Ssam * Timeout intervals for retrying connections 9811653Ssam * to hosts that don't accept PORT cmds. This 9911653Ssam * is a kludge, but given the problems with TCP... 10011653Ssam */ 10111653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 10211653Ssam #define SWAITINT 5 /* interval between retries */ 10311653Ssam 10411653Ssam int swaitmax = SWAITMAX; 10511653Ssam int swaitint = SWAITINT; 10611653Ssam 10710275Ssam int lostconn(); 10826044Sminshall int myoob(); 10910275Ssam FILE *getdatasock(), *dataconn(); 11010275Ssam 11136620Srick #ifdef SETPROCTITLE 11236620Srick char **Argv = NULL; /* pointer to argument vector */ 11336620Srick char *LastArgv = NULL; /* end of argv */ 11436933Skarels char proctitle[BUFSIZ]; /* initial part of title */ 11536620Srick #endif /* SETPROCTITLE */ 11636620Srick 11736620Srick main(argc, argv, envp) 11810275Ssam int argc; 11910275Ssam char *argv[]; 12036620Srick char **envp; 12110275Ssam { 12227750Sminshall int addrlen, on = 1; 12310275Ssam char *cp; 12410275Ssam 12516339Skarels addrlen = sizeof (his_addr); 12636304Skarels if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 12726493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 12810275Ssam exit(1); 12910275Ssam } 13016339Skarels addrlen = sizeof (ctrl_addr); 13136304Skarels if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 13226493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 13316339Skarels exit(1); 13416339Skarels } 13516339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 13610275Ssam debug = 0; 13726493Sminshall openlog("ftpd", LOG_PID, LOG_DAEMON); 13836620Srick #ifdef SETPROCTITLE 13936620Srick /* 14036620Srick * Save start and extent of argv for setproctitle. 14136620Srick */ 14236620Srick Argv = argv; 14336620Srick while (*envp) 14436620Srick envp++; 14536620Srick LastArgv = envp[-1] + strlen(envp[-1]); 14636620Srick #endif /* SETPROCTITLE */ 14736620Srick 14810275Ssam argc--, argv++; 14910275Ssam while (argc > 0 && *argv[0] == '-') { 15010275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 15110275Ssam 15211653Ssam case 'v': 15311653Ssam debug = 1; 15411653Ssam break; 15511653Ssam 15610275Ssam case 'd': 15710275Ssam debug = 1; 15810275Ssam break; 15910275Ssam 16011757Ssam case 'l': 16111757Ssam logging = 1; 16211757Ssam break; 16311757Ssam 16411653Ssam case 't': 16511653Ssam timeout = atoi(++cp); 16636933Skarels if (maxtimeout < timeout) 16736933Skarels maxtimeout = timeout; 16811653Ssam goto nextopt; 16911653Ssam 17036933Skarels case 'T': 17136933Skarels maxtimeout = atoi(++cp); 17236933Skarels if (timeout > maxtimeout) 17336933Skarels timeout = maxtimeout; 17436933Skarels goto nextopt; 17536933Skarels 17636933Skarels case 'u': 17736933Skarels { 17836933Skarels int val = 0; 17936933Skarels 18036933Skarels while (*++cp && *cp >= '0' && *cp <= '9') 18136933Skarels val = val*8 + *cp - '0'; 18236933Skarels if (*cp) 18336933Skarels fprintf(stderr, "ftpd: Bad value for -u\n"); 18436933Skarels else 18536933Skarels defumask = val; 18636933Skarels goto nextopt; 18736933Skarels } 18836933Skarels 18910275Ssam default: 19016339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 19116339Skarels *cp); 19210275Ssam break; 19310275Ssam } 19411653Ssam nextopt: 19510275Ssam argc--, argv++; 19610275Ssam } 19737459Skarels (void) freopen(_PATH_DEVNULL, "w", stderr); 19826493Sminshall (void) signal(SIGPIPE, lostconn); 19926493Sminshall (void) signal(SIGCHLD, SIG_IGN); 20035691Sbostic if ((int)signal(SIGURG, myoob) < 0) 20126493Sminshall syslog(LOG_ERR, "signal: %m"); 20235691Sbostic 20338134Srick /* Try to handle urgent data inline */ 20427750Sminshall #ifdef SO_OOBINLINE 20536276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 20627750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 20736276Sbostic #endif 20838134Srick 20936933Skarels #ifdef F_SETOWN 21036304Skarels if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 21136304Skarels syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 21236933Skarels #endif 21316760Slepreau dolog(&his_addr); 21416339Skarels /* 21516339Skarels * Set up default state 21616339Skarels */ 21716339Skarels data = -1; 21816339Skarels type = TYPE_A; 21916339Skarels form = FORM_N; 22016339Skarels stru = STRU_F; 22116339Skarels mode = MODE_S; 22226044Sminshall tmpline[0] = '\0'; 22326493Sminshall (void) gethostname(hostname, sizeof (hostname)); 22436276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version); 22536304Skarels (void) setjmp(errcatch); 22636304Skarels for (;;) 22726493Sminshall (void) yyparse(); 22836620Srick /* NOTREACHED */ 22910275Ssam } 23010419Ssam 23110275Ssam lostconn() 23210275Ssam { 23310275Ssam 23414089Ssam if (debug) 23526493Sminshall syslog(LOG_DEBUG, "lost connection"); 23614089Ssam dologout(-1); 23710275Ssam } 23810275Ssam 23935672Sbostic static char ttyline[20]; 24035672Sbostic 24136185Sbostic /* 24236185Sbostic * Helper function for sgetpwnam(). 24336185Sbostic */ 24436185Sbostic char * 24536185Sbostic sgetsave(s) 24636185Sbostic char *s; 24736185Sbostic { 24836185Sbostic char *malloc(); 24936185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 25036933Skarels 25136185Sbostic if (new == NULL) { 25236620Srick perror_reply(421, "Local resource failure: malloc"); 25336185Sbostic dologout(1); 25436620Srick /* NOTREACHED */ 25536185Sbostic } 25636185Sbostic (void) strcpy(new, s); 25736185Sbostic return (new); 25836185Sbostic } 25936185Sbostic 26036185Sbostic /* 26136185Sbostic * Save the result of a getpwnam. Used for USER command, since 26236185Sbostic * the data returned must not be clobbered by any other command 26336185Sbostic * (e.g., globbing). 26436185Sbostic */ 26536185Sbostic struct passwd * 26636185Sbostic sgetpwnam(name) 26736185Sbostic char *name; 26836185Sbostic { 26936185Sbostic static struct passwd save; 27036185Sbostic register struct passwd *p; 27136185Sbostic char *sgetsave(); 27236185Sbostic 27336185Sbostic if ((p = getpwnam(name)) == NULL) 27436185Sbostic return (p); 27536185Sbostic if (save.pw_name) { 27636185Sbostic free(save.pw_name); 27736185Sbostic free(save.pw_passwd); 27836185Sbostic free(save.pw_gecos); 27936185Sbostic free(save.pw_dir); 28036185Sbostic free(save.pw_shell); 28136185Sbostic } 28236185Sbostic save = *p; 28336185Sbostic save.pw_name = sgetsave(p->pw_name); 28436185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 28536185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 28636185Sbostic save.pw_dir = sgetsave(p->pw_dir); 28736185Sbostic save.pw_shell = sgetsave(p->pw_shell); 28836185Sbostic return (&save); 28936185Sbostic } 29036185Sbostic 29136304Skarels int login_attempts; /* number of failed login attempts */ 29236304Skarels int askpasswd; /* had user command, ask for passwd */ 29336304Skarels 29436304Skarels /* 29536304Skarels * USER command. 29642327Sbostic * Sets global passwd pointer pw if named account exists and is acceptable; 29742327Sbostic * sets askpasswd if a PASS command is expected. If logged in previously, 29842327Sbostic * need to reset state. If name is "ftp" or "anonymous", the name is not in 29942327Sbostic * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 30042327Sbostic * If account doesn't exist, ask for passwd anyway. Otherwise, check user 30142327Sbostic * requesting login privileges. Disallow anyone who does not have a standard 30242327Sbostic * shell as returned by getusershell(). Disallow anyone mentioned in the file 30342327Sbostic * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 30436304Skarels */ 30536304Skarels user(name) 30636304Skarels char *name; 30736304Skarels { 30836304Skarels register char *cp; 30936304Skarels char *shell; 31040183Smckusick char *getusershell(); 31136304Skarels 31236304Skarels if (logged_in) { 31336304Skarels if (guest) { 31436304Skarels reply(530, "Can't change user from guest login."); 31536304Skarels return; 31636304Skarels } 31736304Skarels end_login(); 31836304Skarels } 31936304Skarels 32036304Skarels guest = 0; 32136304Skarels if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 32240183Smckusick if (checkuser("ftp") || checkuser("anonymous")) 32340155Smckusick reply(530, "User %s access denied.", name); 32440155Smckusick else if ((pw = sgetpwnam("ftp")) != NULL) { 32536304Skarels guest = 1; 32636304Skarels askpasswd = 1; 32736304Skarels reply(331, "Guest login ok, send ident as password."); 32836933Skarels } else 32936304Skarels reply(530, "User %s unknown.", name); 33036304Skarels return; 33136304Skarels } 33236304Skarels if (pw = sgetpwnam(name)) { 33336304Skarels if ((shell = pw->pw_shell) == NULL || *shell == 0) 33437459Skarels shell = _PATH_BSHELL; 33536304Skarels while ((cp = getusershell()) != NULL) 33636304Skarels if (strcmp(cp, shell) == 0) 33736304Skarels break; 33836304Skarels endusershell(); 33940183Smckusick if (cp == NULL || checkuser(name)) { 34036304Skarels reply(530, "User %s access denied.", name); 34136933Skarels if (logging) 34236933Skarels syslog(LOG_NOTICE, 34336933Skarels "FTP LOGIN REFUSED FROM %s, %s", 34436933Skarels remotehost, name); 34536304Skarels pw = (struct passwd *) NULL; 34636304Skarels return; 34736304Skarels } 34836304Skarels } 34936304Skarels reply(331, "Password required for %s.", name); 35036304Skarels askpasswd = 1; 35136304Skarels /* 35236304Skarels * Delay before reading passwd after first failed 35336304Skarels * attempt to slow down passwd-guessing programs. 35436304Skarels */ 35536304Skarels if (login_attempts) 35636304Skarels sleep((unsigned) login_attempts); 35736304Skarels } 35836304Skarels 35936304Skarels /* 36040183Smckusick * Check if a user is in the file _PATH_FTPUSERS 36140183Smckusick */ 36240183Smckusick checkuser(name) 36340183Smckusick char *name; 36440183Smckusick { 36542327Sbostic register FILE *fd; 36642327Sbostic register char *p; 36742327Sbostic char line[BUFSIZ]; 36840183Smckusick 36940183Smckusick if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { 37042327Sbostic while (fgets(line, sizeof(line), fd) != NULL) 37142327Sbostic if ((p = index(line, '\n')) != NULL) { 37242327Sbostic *p = '\0'; 37342327Sbostic if (line[0] == '#') 37442327Sbostic continue; 37542327Sbostic if (strcmp(line, name) == 0) 37642327Sbostic return (1); 37742327Sbostic } 37840183Smckusick (void) fclose(fd); 37940183Smckusick } 38040183Smckusick return (0); 38140183Smckusick } 38240183Smckusick 38340183Smckusick /* 38436304Skarels * Terminate login as previous user, if any, resetting state; 38536304Skarels * used when USER command is given or login fails. 38636304Skarels */ 38736304Skarels end_login() 38836304Skarels { 38936304Skarels 39036304Skarels (void) seteuid((uid_t)0); 39136304Skarels if (logged_in) 39236304Skarels logwtmp(ttyline, "", ""); 39336304Skarels pw = NULL; 39436304Skarels logged_in = 0; 39536304Skarels guest = 0; 39636304Skarels } 39736304Skarels 39810275Ssam pass(passwd) 39910275Ssam char *passwd; 40010275Ssam { 40136304Skarels char *xpasswd, *salt; 40210275Ssam 40336304Skarels if (logged_in || askpasswd == 0) { 40410275Ssam reply(503, "Login with USER first."); 40510275Ssam return; 40610275Ssam } 40736304Skarels askpasswd = 0; 40810275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 40936304Skarels if (pw == NULL) 41036304Skarels salt = "xx"; 41136304Skarels else 41236304Skarels salt = pw->pw_passwd; 41336304Skarels xpasswd = crypt(passwd, salt); 41416760Slepreau /* The strcmp does not catch null passwords! */ 41536304Skarels if (pw == NULL || *pw->pw_passwd == '\0' || 41636304Skarels strcmp(xpasswd, pw->pw_passwd)) { 41710275Ssam reply(530, "Login incorrect."); 41810275Ssam pw = NULL; 41936304Skarels if (login_attempts++ >= 5) { 42036933Skarels syslog(LOG_NOTICE, 42136304Skarels "repeated login failures from %s", 42236304Skarels remotehost); 42336304Skarels exit(0); 42436304Skarels } 42510275Ssam return; 42610275Ssam } 42710275Ssam } 42836304Skarels login_attempts = 0; /* this time successful */ 42936304Skarels (void) setegid((gid_t)pw->pw_gid); 43036304Skarels (void) initgroups(pw->pw_name, pw->pw_gid); 43116033Sralph 43236192Sbostic /* open wtmp before chroot */ 43336192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 43436192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 43536192Sbostic logged_in = 1; 43636192Sbostic 43736446Sbostic if (guest) { 43836620Srick /* 43936933Skarels * We MUST do a chdir() after the chroot. Otherwise 44036933Skarels * the old current directory will be accessible as "." 44136933Skarels * outside the new root! 44236620Srick */ 44336620Srick if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 44436446Sbostic reply(550, "Can't set guest privileges."); 44536446Sbostic goto bad; 44636446Sbostic } 44736933Skarels } else if (chdir(pw->pw_dir) < 0) { 44836620Srick if (chdir("/") < 0) { 44936446Sbostic reply(530, "User %s: can't change directory to %s.", 45036446Sbostic pw->pw_name, pw->pw_dir); 45136446Sbostic goto bad; 45236933Skarels } else 45336446Sbostic lreply(230, "No directory! Logging in with home=/"); 45436620Srick } 45536304Skarels if (seteuid((uid_t)pw->pw_uid) < 0) { 45636304Skarels reply(550, "Can't set uid."); 45736304Skarels goto bad; 45836304Skarels } 45936550Sbostic if (guest) { 46036192Sbostic reply(230, "Guest login ok, access restrictions apply."); 46136933Skarels #ifdef SETPROCTITLE 46236933Skarels sprintf(proctitle, "%s: anonymous/%.*s", remotehost, 46336933Skarels sizeof(proctitle) - sizeof(remotehost) - 46436933Skarels sizeof(": anonymous/"), passwd); 46536933Skarels setproctitle(proctitle); 46636933Skarels #endif /* SETPROCTITLE */ 46736933Skarels if (logging) 46836933Skarels syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 46936933Skarels remotehost, passwd); 47036933Skarels } else { 47110275Ssam reply(230, "User %s logged in.", pw->pw_name); 47236933Skarels #ifdef SETPROCTITLE 47336933Skarels sprintf(proctitle, "%s: %s", remotehost, pw->pw_name); 47436933Skarels setproctitle(proctitle); 47536933Skarels #endif /* SETPROCTITLE */ 47636933Skarels if (logging) 47736933Skarels syslog(LOG_INFO, "FTP LOGIN FROM %s, %s", 47836933Skarels remotehost, pw->pw_name); 47936550Sbostic } 48010303Ssam home = pw->pw_dir; /* home dir for globbing */ 48136933Skarels (void) umask(defumask); 48210303Ssam return; 48310303Ssam bad: 48436304Skarels /* Forget all about it... */ 48536304Skarels end_login(); 48610275Ssam } 48710275Ssam 48810275Ssam retrieve(cmd, name) 48910275Ssam char *cmd, *name; 49010275Ssam { 49110275Ssam FILE *fin, *dout; 49210275Ssam struct stat st; 49336620Srick int (*closefunc)(); 49410275Ssam 49536557Sbostic if (cmd == 0) { 49636446Sbostic fin = fopen(name, "r"), closefunc = fclose; 49736557Sbostic st.st_size = 0; 49836557Sbostic } else { 49910275Ssam char line[BUFSIZ]; 50010275Ssam 50126493Sminshall (void) sprintf(line, cmd, name), name = line; 50236304Skarels fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 50336557Sbostic st.st_size = -1; 50436933Skarels st.st_blksize = BUFSIZ; 50510275Ssam } 50610275Ssam if (fin == NULL) { 50713152Ssam if (errno != 0) 50836304Skarels perror_reply(550, name); 50910275Ssam return; 51010275Ssam } 51110275Ssam if (cmd == 0 && 51236933Skarels (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 51310275Ssam reply(550, "%s: not a plain file.", name); 51410275Ssam goto done; 51510275Ssam } 51637459Skarels if (restart_point) { 51737459Skarels if (type == TYPE_A) { 51837459Skarels register int i, n, c; 51937459Skarels 52037459Skarels n = restart_point; 52137459Skarels i = 0; 52237459Skarels while (i++ < n) { 52337459Skarels if ((c=getc(fin)) == EOF) { 52437459Skarels perror_reply(550, name); 52537459Skarels goto done; 52637459Skarels } 52737459Skarels if (c == '\n') 52837459Skarels i++; 52937459Skarels } 53037459Skarels } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 53137459Skarels perror_reply(550, name); 53237459Skarels goto done; 53337459Skarels } 53437459Skarels } 53510275Ssam dout = dataconn(name, st.st_size, "w"); 53610275Ssam if (dout == NULL) 53710275Ssam goto done; 53836620Srick send_data(fin, dout, st.st_blksize); 53926493Sminshall (void) fclose(dout); 54026044Sminshall data = -1; 54126044Sminshall pdata = -1; 54210275Ssam done: 54310275Ssam (*closefunc)(fin); 54410275Ssam } 54510275Ssam 54636304Skarels store(name, mode, unique) 54710275Ssam char *name, *mode; 54836304Skarels int unique; 54910275Ssam { 55010275Ssam FILE *fout, *din; 55136446Sbostic struct stat st; 55236620Srick int (*closefunc)(); 55336304Skarels char *gunique(); 55410275Ssam 55536446Sbostic if (unique && stat(name, &st) == 0 && 55636446Sbostic (name = gunique(name)) == NULL) 55736446Sbostic return; 55810303Ssam 55937459Skarels if (restart_point) 56037459Skarels mode = "r+w"; 56136620Srick fout = fopen(name, mode); 56236620Srick closefunc = fclose; 56310275Ssam if (fout == NULL) { 56436304Skarels perror_reply(553, name); 56510275Ssam return; 56610275Ssam } 56737459Skarels if (restart_point) { 56837459Skarels if (type == TYPE_A) { 56937459Skarels register int i, n, c; 57037459Skarels 57137459Skarels n = restart_point; 57237459Skarels i = 0; 57337459Skarels while (i++ < n) { 57437459Skarels if ((c=getc(fout)) == EOF) { 57537459Skarels perror_reply(550, name); 57637459Skarels goto done; 57737459Skarels } 57837459Skarels if (c == '\n') 57937459Skarels i++; 58037459Skarels } 58137459Skarels /* 58237459Skarels * We must do this seek to "current" position 58337459Skarels * because we are changing from reading to 58437459Skarels * writing. 58537459Skarels */ 58637459Skarels if (fseek(fout, 0L, L_INCR) < 0) { 58737459Skarels perror_reply(550, name); 58837459Skarels goto done; 58937459Skarels } 59037459Skarels } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 59137459Skarels perror_reply(550, name); 59237459Skarels goto done; 59337459Skarels } 59437459Skarels } 59536304Skarels din = dataconn(name, (off_t)-1, "r"); 59610275Ssam if (din == NULL) 59710275Ssam goto done; 59836620Srick if (receive_data(din, fout) == 0) { 59936620Srick if (unique) 60036304Skarels reply(226, "Transfer complete (unique file name:%s).", 60136933Skarels name); 60236304Skarels else 60336304Skarels reply(226, "Transfer complete."); 60426044Sminshall } 60526493Sminshall (void) fclose(din); 60626044Sminshall data = -1; 60726044Sminshall pdata = -1; 60810275Ssam done: 60910275Ssam (*closefunc)(fout); 61010275Ssam } 61110275Ssam 61210275Ssam FILE * 61310275Ssam getdatasock(mode) 61410275Ssam char *mode; 61510275Ssam { 61637459Skarels int s, on = 1, tries; 61710275Ssam 61810275Ssam if (data >= 0) 61910275Ssam return (fdopen(data, mode)); 62013247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 62110602Ssam if (s < 0) 62210275Ssam return (NULL); 62336304Skarels (void) seteuid((uid_t)0); 62437459Skarels if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 62537459Skarels (char *) &on, sizeof (on)) < 0) 62610602Ssam goto bad; 62713152Ssam /* anchor socket to avoid multi-homing problems */ 62813152Ssam data_source.sin_family = AF_INET; 62913152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 63037459Skarels for (tries = 1; ; tries++) { 63137459Skarels if (bind(s, (struct sockaddr *)&data_source, 63237459Skarels sizeof (data_source)) >= 0) 63337459Skarels break; 63437459Skarels if (errno != EADDRINUSE || tries > 10) 63537459Skarels goto bad; 63637459Skarels sleep(tries); 63737459Skarels } 63836304Skarels (void) seteuid((uid_t)pw->pw_uid); 63910275Ssam return (fdopen(s, mode)); 64010602Ssam bad: 64136304Skarels (void) seteuid((uid_t)pw->pw_uid); 64226493Sminshall (void) close(s); 64310602Ssam return (NULL); 64410275Ssam } 64510275Ssam 64610275Ssam FILE * 64710275Ssam dataconn(name, size, mode) 64810275Ssam char *name; 64911653Ssam off_t size; 65010275Ssam char *mode; 65110275Ssam { 65210275Ssam char sizebuf[32]; 65310275Ssam FILE *file; 65411653Ssam int retry = 0; 65510275Ssam 65636933Skarels file_size = size; 65736933Skarels byte_count = 0; 65836304Skarels if (size != (off_t) -1) 65926493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 66010275Ssam else 66110275Ssam (void) strcpy(sizebuf, ""); 66236304Skarels if (pdata >= 0) { 66326044Sminshall struct sockaddr_in from; 66426044Sminshall int s, fromlen = sizeof(from); 66526044Sminshall 66636304Skarels s = accept(pdata, (struct sockaddr *)&from, &fromlen); 66726044Sminshall if (s < 0) { 66826044Sminshall reply(425, "Can't open data connection."); 66926044Sminshall (void) close(pdata); 67026044Sminshall pdata = -1; 67126044Sminshall return(NULL); 67226044Sminshall } 67326044Sminshall (void) close(pdata); 67426044Sminshall pdata = s; 67536235Skarels reply(150, "Opening %s mode data connection for %s%s.", 67636235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 67726044Sminshall return(fdopen(pdata, mode)); 67826044Sminshall } 67910275Ssam if (data >= 0) { 68010275Ssam reply(125, "Using existing data connection for %s%s.", 68110275Ssam name, sizebuf); 68210321Ssam usedefault = 1; 68310275Ssam return (fdopen(data, mode)); 68410275Ssam } 68510566Ssam if (usedefault) 68610422Ssam data_dest = his_addr; 68710422Ssam usedefault = 1; 68810275Ssam file = getdatasock(mode); 68910275Ssam if (file == NULL) { 69010275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 69113247Ssam inet_ntoa(data_source.sin_addr), 69242412Sbostic ntohs(data_source.sin_port), strerror(errno)); 69310275Ssam return (NULL); 69410275Ssam } 69510275Ssam data = fileno(file); 69636304Skarels while (connect(data, (struct sockaddr *)&data_dest, 69736304Skarels sizeof (data_dest)) < 0) { 69811653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 69926493Sminshall sleep((unsigned) swaitint); 70011653Ssam retry += swaitint; 70111653Ssam continue; 70211653Ssam } 70336304Skarels perror_reply(425, "Can't build data connection"); 70410275Ssam (void) fclose(file); 70510275Ssam data = -1; 70610275Ssam return (NULL); 70710275Ssam } 70836235Skarels reply(150, "Opening %s mode data connection for %s%s.", 70936235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 71010275Ssam return (file); 71110275Ssam } 71210275Ssam 71310275Ssam /* 71410275Ssam * Tranfer the contents of "instr" to 71510275Ssam * "outstr" peer using the appropriate 71636933Skarels * encapsulation of the data subject 71710275Ssam * to Mode, Structure, and Type. 71810275Ssam * 71910275Ssam * NB: Form isn't handled. 72010275Ssam */ 72136446Sbostic send_data(instr, outstr, blksize) 72210275Ssam FILE *instr, *outstr; 72336446Sbostic off_t blksize; 72410275Ssam { 72536446Sbostic register int c, cnt; 72636446Sbostic register char *buf; 72736446Sbostic int netfd, filefd; 72810275Ssam 72926044Sminshall transflag++; 73026044Sminshall if (setjmp(urgcatch)) { 73126044Sminshall transflag = 0; 73236620Srick return; 73326044Sminshall } 73410275Ssam switch (type) { 73510275Ssam 73610275Ssam case TYPE_A: 73710275Ssam while ((c = getc(instr)) != EOF) { 73836933Skarels byte_count++; 73911220Ssam if (c == '\n') { 74036933Skarels if (ferror(outstr)) 74136620Srick goto data_err; 74227750Sminshall (void) putc('\r', outstr); 74311220Ssam } 74427750Sminshall (void) putc(c, outstr); 74510275Ssam } 74636933Skarels fflush(outstr); 74726044Sminshall transflag = 0; 74836933Skarels if (ferror(instr)) 74936933Skarels goto file_err; 75036933Skarels if (ferror(outstr)) 75136620Srick goto data_err; 75236620Srick reply(226, "Transfer complete."); 75336620Srick return; 75436446Sbostic 75510275Ssam case TYPE_I: 75610275Ssam case TYPE_L: 75736446Sbostic if ((buf = malloc((u_int)blksize)) == NULL) { 75836446Sbostic transflag = 0; 75936933Skarels perror_reply(451, "Local resource failure: malloc"); 76036933Skarels return; 76136446Sbostic } 76210275Ssam netfd = fileno(outstr); 76310275Ssam filefd = fileno(instr); 76436933Skarels while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 76536620Srick write(netfd, buf, cnt) == cnt) 76636933Skarels byte_count += cnt; 76726044Sminshall transflag = 0; 76836446Sbostic (void)free(buf); 76936933Skarels if (cnt != 0) { 77036933Skarels if (cnt < 0) 77136933Skarels goto file_err; 77236620Srick goto data_err; 77336933Skarels } 77436620Srick reply(226, "Transfer complete."); 77536620Srick return; 77636620Srick default: 77736620Srick transflag = 0; 77836620Srick reply(550, "Unimplemented TYPE %d in send_data", type); 77936620Srick return; 78010275Ssam } 78136620Srick 78236620Srick data_err: 78326044Sminshall transflag = 0; 78436933Skarels perror_reply(426, "Data connection"); 78536933Skarels return; 78636933Skarels 78736933Skarels file_err: 78836933Skarels transflag = 0; 78936933Skarels perror_reply(551, "Error on input file"); 79010275Ssam } 79110275Ssam 79210275Ssam /* 79310275Ssam * Transfer data from peer to 79410275Ssam * "outstr" using the appropriate 79510275Ssam * encapulation of the data subject 79610275Ssam * to Mode, Structure, and Type. 79710275Ssam * 79810275Ssam * N.B.: Form isn't handled. 79910275Ssam */ 80010275Ssam receive_data(instr, outstr) 80110275Ssam FILE *instr, *outstr; 80210275Ssam { 80310275Ssam register int c; 80438134Srick int cnt, bare_lfs = 0; 80510275Ssam char buf[BUFSIZ]; 80610275Ssam 80726044Sminshall transflag++; 80826044Sminshall if (setjmp(urgcatch)) { 80926044Sminshall transflag = 0; 81036933Skarels return (-1); 81126044Sminshall } 81210275Ssam switch (type) { 81310275Ssam 81410275Ssam case TYPE_I: 81510275Ssam case TYPE_L: 81626044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 81736620Srick if (write(fileno(outstr), buf, cnt) != cnt) 81836933Skarels goto file_err; 81936933Skarels byte_count += cnt; 82026044Sminshall } 82136933Skarels if (cnt < 0) 82236620Srick goto data_err; 82326044Sminshall transflag = 0; 82436933Skarels return (0); 82510275Ssam 82610275Ssam case TYPE_E: 82727106Smckusick reply(553, "TYPE E not implemented."); 82826044Sminshall transflag = 0; 82927106Smckusick return (-1); 83010275Ssam 83110275Ssam case TYPE_A: 83210275Ssam while ((c = getc(instr)) != EOF) { 83336933Skarels byte_count++; 83438134Srick if (c == '\n') 83538134Srick bare_lfs++; 83627750Sminshall while (c == '\r') { 83736933Skarels if (ferror(outstr)) 83836620Srick goto data_err; 83936933Skarels if ((c = getc(instr)) != '\n') { 84027750Sminshall (void) putc ('\r', outstr); 84136933Skarels if (c == '\0' || c == EOF) 84236933Skarels goto contin2; 84336933Skarels } 84410275Ssam } 84536933Skarels (void) putc(c, outstr); 84636933Skarels contin2: ; 84710275Ssam } 84836620Srick fflush(outstr); 84936933Skarels if (ferror(instr)) 85036620Srick goto data_err; 85136933Skarels if (ferror(outstr)) 85236933Skarels goto file_err; 85326044Sminshall transflag = 0; 85438134Srick if (bare_lfs) { 85538134Srick lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs); 85638134Srick printf(" File may not have transferred correctly.\r\n"); 85738134Srick } 85810275Ssam return (0); 85936620Srick default: 86036620Srick reply(550, "Unimplemented TYPE %d in receive_data", type); 86136620Srick transflag = 0; 86236933Skarels return (-1); 86310275Ssam } 86436620Srick 86536620Srick data_err: 86626044Sminshall transflag = 0; 86736933Skarels perror_reply(426, "Data Connection"); 86836933Skarels return (-1); 86936933Skarels 87036933Skarels file_err: 87136933Skarels transflag = 0; 87236933Skarels perror_reply(452, "Error writing file"); 87336933Skarels return (-1); 87410275Ssam } 87510275Ssam 87636933Skarels statfilecmd(filename) 87736933Skarels char *filename; 87836933Skarels { 87936933Skarels char line[BUFSIZ]; 88036933Skarels FILE *fin; 88136933Skarels int c; 88236933Skarels 88336933Skarels (void) sprintf(line, "/bin/ls -lgA %s", filename); 88436933Skarels fin = ftpd_popen(line, "r"); 88536933Skarels lreply(211, "status of %s:", filename); 88636933Skarels while ((c = getc(fin)) != EOF) { 88736933Skarels if (c == '\n') { 88836933Skarels if (ferror(stdout)){ 88936933Skarels perror_reply(421, "control connection"); 89036933Skarels (void) ftpd_pclose(fin); 89136933Skarels dologout(1); 89236933Skarels /* NOTREACHED */ 89336933Skarels } 89436933Skarels if (ferror(fin)) { 89536933Skarels perror_reply(551, filename); 89636933Skarels (void) ftpd_pclose(fin); 89736933Skarels return; 89836933Skarels } 89936933Skarels (void) putc('\r', stdout); 90036933Skarels } 90136933Skarels (void) putc(c, stdout); 90236933Skarels } 90336933Skarels (void) ftpd_pclose(fin); 90436933Skarels reply(211, "End of Status"); 90536933Skarels } 90636933Skarels 90736933Skarels statcmd() 90836933Skarels { 90936933Skarels struct sockaddr_in *sin; 91036933Skarels u_char *a, *p; 91136933Skarels 91236933Skarels lreply(211, "%s FTP server status:", hostname, version); 91336933Skarels printf(" %s\r\n", version); 91436933Skarels printf(" Connected to %s", remotehost); 91538134Srick if (!isdigit(remotehost[0])) 91636933Skarels printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 91736933Skarels printf("\r\n"); 91836933Skarels if (logged_in) { 91936933Skarels if (guest) 92036933Skarels printf(" Logged in anonymously\r\n"); 92136933Skarels else 92236933Skarels printf(" Logged in as %s\r\n", pw->pw_name); 92336933Skarels } else if (askpasswd) 92436933Skarels printf(" Waiting for password\r\n"); 92536933Skarels else 92636933Skarels printf(" Waiting for user name\r\n"); 92736933Skarels printf(" TYPE: %s", typenames[type]); 92836933Skarels if (type == TYPE_A || type == TYPE_E) 92936933Skarels printf(", FORM: %s", formnames[form]); 93036933Skarels if (type == TYPE_L) 93136933Skarels #if NBBY == 8 93236933Skarels printf(" %d", NBBY); 93336933Skarels #else 93436933Skarels printf(" %d", bytesize); /* need definition! */ 93536933Skarels #endif 93636933Skarels printf("; STRUcture: %s; transfer MODE: %s\r\n", 93736933Skarels strunames[stru], modenames[mode]); 93836933Skarels if (data != -1) 93936933Skarels printf(" Data connection open\r\n"); 94036933Skarels else if (pdata != -1) { 94136933Skarels printf(" in Passive mode"); 94236933Skarels sin = &pasv_addr; 94336933Skarels goto printaddr; 94436933Skarels } else if (usedefault == 0) { 94536933Skarels printf(" PORT"); 94636933Skarels sin = &data_dest; 94736933Skarels printaddr: 94836933Skarels a = (u_char *) &sin->sin_addr; 94936933Skarels p = (u_char *) &sin->sin_port; 95036933Skarels #define UC(b) (((int) b) & 0xff) 95136933Skarels printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 95236933Skarels UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 95336933Skarels #undef UC 95436933Skarels } else 95536933Skarels printf(" No data connection\r\n"); 95636933Skarels reply(211, "End of status"); 95736933Skarels } 95836933Skarels 95910275Ssam fatal(s) 96010275Ssam char *s; 96110275Ssam { 96210275Ssam reply(451, "Error in server: %s\n", s); 96310275Ssam reply(221, "Closing connection due to server error."); 96413247Ssam dologout(0); 96536620Srick /* NOTREACHED */ 96610275Ssam } 96710275Ssam 96836446Sbostic /* VARARGS2 */ 96936446Sbostic reply(n, fmt, p0, p1, p2, p3, p4, p5) 97010275Ssam int n; 97136446Sbostic char *fmt; 97210275Ssam { 97310275Ssam printf("%d ", n); 97436446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 97510275Ssam printf("\r\n"); 97636435Sbostic (void)fflush(stdout); 97710275Ssam if (debug) { 97826493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 97936446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 98010275Ssam } 98136620Srick } 98210275Ssam 98336446Sbostic /* VARARGS2 */ 98436446Sbostic lreply(n, fmt, p0, p1, p2, p3, p4, p5) 98510275Ssam int n; 98636446Sbostic char *fmt; 98710275Ssam { 98836446Sbostic printf("%d- ", n); 98936446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 99036446Sbostic printf("\r\n"); 99136435Sbostic (void)fflush(stdout); 99236446Sbostic if (debug) { 99336446Sbostic syslog(LOG_DEBUG, "<--- %d- ", n); 99436446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 99536446Sbostic } 99610275Ssam } 99710275Ssam 99810275Ssam ack(s) 99910275Ssam char *s; 100010275Ssam { 100127106Smckusick reply(250, "%s command successful.", s); 100210275Ssam } 100310275Ssam 100410275Ssam nack(s) 100510275Ssam char *s; 100610275Ssam { 100710275Ssam reply(502, "%s command not implemented.", s); 100810275Ssam } 100910275Ssam 101036304Skarels /* ARGSUSED */ 101126493Sminshall yyerror(s) 101226493Sminshall char *s; 101310275Ssam { 101426044Sminshall char *cp; 101526044Sminshall 101636551Sbostic if (cp = index(cbuf,'\n')) 101736551Sbostic *cp = '\0'; 101836933Skarels reply(500, "'%s': command not understood.", cbuf); 101910275Ssam } 102010275Ssam 102110275Ssam delete(name) 102210275Ssam char *name; 102310275Ssam { 102410275Ssam struct stat st; 102510275Ssam 102610275Ssam if (stat(name, &st) < 0) { 102736304Skarels perror_reply(550, name); 102810275Ssam return; 102910275Ssam } 103010275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 103110275Ssam if (rmdir(name) < 0) { 103236304Skarels perror_reply(550, name); 103310275Ssam return; 103410275Ssam } 103510275Ssam goto done; 103610275Ssam } 103710275Ssam if (unlink(name) < 0) { 103836304Skarels perror_reply(550, name); 103910275Ssam return; 104010275Ssam } 104110275Ssam done: 104210275Ssam ack("DELE"); 104310275Ssam } 104410275Ssam 104510275Ssam cwd(path) 104610275Ssam char *path; 104710275Ssam { 104836620Srick if (chdir(path) < 0) 104936304Skarels perror_reply(550, path); 105036620Srick else 105136620Srick ack("CWD"); 105210275Ssam } 105310275Ssam 105410303Ssam makedir(name) 105510275Ssam char *name; 105610275Ssam { 105736276Sbostic if (mkdir(name, 0777) < 0) 105836304Skarels perror_reply(550, name); 105936276Sbostic else 106036276Sbostic reply(257, "MKD command successful."); 106110275Ssam } 106210275Ssam 106310303Ssam removedir(name) 106410275Ssam char *name; 106510275Ssam { 106636620Srick if (rmdir(name) < 0) 106736304Skarels perror_reply(550, name); 106836620Srick else 106936620Srick ack("RMD"); 107010275Ssam } 107110275Ssam 107210303Ssam pwd() 107310275Ssam { 107410303Ssam char path[MAXPATHLEN + 1]; 107536304Skarels extern char *getwd(); 107610275Ssam 107736620Srick if (getwd(path) == (char *)NULL) 107827106Smckusick reply(550, "%s.", path); 107936620Srick else 108036620Srick reply(257, "\"%s\" is current directory.", path); 108110275Ssam } 108210275Ssam 108310275Ssam char * 108410275Ssam renamefrom(name) 108510275Ssam char *name; 108610275Ssam { 108710275Ssam struct stat st; 108810275Ssam 108910275Ssam if (stat(name, &st) < 0) { 109036304Skarels perror_reply(550, name); 109110275Ssam return ((char *)0); 109210275Ssam } 109310303Ssam reply(350, "File exists, ready for destination name"); 109410275Ssam return (name); 109510275Ssam } 109610275Ssam 109710275Ssam renamecmd(from, to) 109810275Ssam char *from, *to; 109910275Ssam { 110036620Srick if (rename(from, to) < 0) 110136304Skarels perror_reply(550, "rename"); 110236620Srick else 110336620Srick ack("RNTO"); 110410275Ssam } 110510275Ssam 110610275Ssam dolog(sin) 110710275Ssam struct sockaddr_in *sin; 110810275Ssam { 110936304Skarels struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 111010275Ssam sizeof (struct in_addr), AF_INET); 111136304Skarels time_t t, time(); 111226493Sminshall extern char *ctime(); 111310275Ssam 111436304Skarels if (hp) 111526493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 111636304Skarels else 111726493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 111813247Ssam sizeof (remotehost)); 111936620Srick #ifdef SETPROCTITLE 112036933Skarels sprintf(proctitle, "%s: connected", remotehost); 112136933Skarels setproctitle(proctitle); 112236620Srick #endif /* SETPROCTITLE */ 112336933Skarels 112436933Skarels if (logging) { 112536933Skarels t = time((time_t *) 0); 112636933Skarels syslog(LOG_INFO, "connection from %s at %s", 112736933Skarels remotehost, ctime(&t)); 112836933Skarels } 112910275Ssam } 113010695Ssam 113110695Ssam /* 113213247Ssam * Record logout in wtmp file 113313247Ssam * and exit with supplied status. 113413247Ssam */ 113513247Ssam dologout(status) 113613247Ssam int status; 113713247Ssam { 113817580Ssam if (logged_in) { 113936304Skarels (void) seteuid((uid_t)0); 114035672Sbostic logwtmp(ttyline, "", ""); 114113247Ssam } 114214436Ssam /* beware of flushing buffers after a SIGPIPE */ 114314436Ssam _exit(status); 114413247Ssam } 114513247Ssam 114626044Sminshall myoob() 114726044Sminshall { 114827750Sminshall char *cp; 114926044Sminshall 115027750Sminshall /* only process if transfer occurring */ 115136304Skarels if (!transflag) 115226044Sminshall return; 115327750Sminshall cp = tmpline; 115427750Sminshall if (getline(cp, 7, stdin) == NULL) { 115536304Skarels reply(221, "You could at least say goodbye."); 115627750Sminshall dologout(0); 115726044Sminshall } 115826044Sminshall upper(cp); 115936933Skarels if (strcmp(cp, "ABOR\r\n") == 0) { 116036933Skarels tmpline[0] = '\0'; 116136933Skarels reply(426, "Transfer aborted. Data connection closed."); 116236933Skarels reply(226, "Abort successful"); 116336933Skarels longjmp(urgcatch, 1); 116436933Skarels } 116536933Skarels if (strcmp(cp, "STAT\r\n") == 0) { 116636933Skarels if (file_size != (off_t) -1) 116736933Skarels reply(213, "Status: %lu of %lu bytes transferred", 116836933Skarels byte_count, file_size); 116936933Skarels else 117036933Skarels reply(213, "Status: %lu bytes transferred", byte_count); 117136933Skarels } 117226044Sminshall } 117326044Sminshall 117427106Smckusick /* 117536620Srick * Note: a response of 425 is not mentioned as a possible response to 117636620Srick * the PASV command in RFC959. However, it has been blessed as 117736620Srick * a legitimate response by Jon Postel in a telephone conversation 117836620Srick * with Rick Adams on 25 Jan 89. 117927106Smckusick */ 118026044Sminshall passive() 118126044Sminshall { 118226044Sminshall int len; 118326044Sminshall register char *p, *a; 118426044Sminshall 118526044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 118626044Sminshall if (pdata < 0) { 118736620Srick perror_reply(425, "Can't open passive connection"); 118826044Sminshall return; 118926044Sminshall } 119036933Skarels pasv_addr = ctrl_addr; 119136933Skarels pasv_addr.sin_port = 0; 119236304Skarels (void) seteuid((uid_t)0); 119336933Skarels if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { 119436304Skarels (void) seteuid((uid_t)pw->pw_uid); 119536620Srick goto pasv_error; 119626044Sminshall } 119736304Skarels (void) seteuid((uid_t)pw->pw_uid); 119836933Skarels len = sizeof(pasv_addr); 119936933Skarels if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 120036620Srick goto pasv_error; 120136620Srick if (listen(pdata, 1) < 0) 120236620Srick goto pasv_error; 120336933Skarels a = (char *) &pasv_addr.sin_addr; 120436933Skarels p = (char *) &pasv_addr.sin_port; 120526044Sminshall 120626044Sminshall #define UC(b) (((int) b) & 0xff) 120726044Sminshall 120826044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 120926044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 121036620Srick return; 121136620Srick 121236620Srick pasv_error: 121336620Srick (void) close(pdata); 121436620Srick pdata = -1; 121536620Srick perror_reply(425, "Can't open passive connection"); 121636620Srick return; 121726044Sminshall } 121826044Sminshall 121936304Skarels /* 122036304Skarels * Generate unique name for file with basename "local". 122136304Skarels * The file named "local" is already known to exist. 122236304Skarels * Generates failure reply on error. 122336304Skarels */ 122426044Sminshall char * 122526044Sminshall gunique(local) 122626044Sminshall char *local; 122726044Sminshall { 122826044Sminshall static char new[MAXPATHLEN]; 122936304Skarels struct stat st; 123026044Sminshall char *cp = rindex(local, '/'); 123136933Skarels int count = 0; 123226044Sminshall 123336304Skarels if (cp) 123426044Sminshall *cp = '\0'; 123536620Srick if (stat(cp ? local : ".", &st) < 0) { 123636933Skarels perror_reply(553, cp ? local : "."); 123726044Sminshall return((char *) 0); 123826044Sminshall } 123936620Srick if (cp) 124036620Srick *cp = '/'; 124126044Sminshall (void) strcpy(new, local); 124226044Sminshall cp = new + strlen(new); 124326044Sminshall *cp++ = '.'; 124436304Skarels for (count = 1; count < 100; count++) { 124536304Skarels (void) sprintf(cp, "%d", count); 124636304Skarels if (stat(new, &st) < 0) 124736304Skarels return(new); 124826044Sminshall } 124936304Skarels reply(452, "Unique file name cannot be created."); 125036304Skarels return((char *) 0); 125126044Sminshall } 125236304Skarels 125336304Skarels /* 125436304Skarels * Format and send reply containing system error number. 125536304Skarels */ 125636304Skarels perror_reply(code, string) 125736304Skarels int code; 125836304Skarels char *string; 125936304Skarels { 126042412Sbostic reply(code, "%s: %s.", string, strerror(errno)); 126136304Skarels } 126236620Srick 126336620Srick static char *onefile[] = { 126436620Srick "", 126536620Srick 0 126636620Srick }; 126736620Srick 126836620Srick send_file_list(whichfiles) 126936620Srick char *whichfiles; 127036620Srick { 127136620Srick struct stat st; 127236620Srick DIR *dirp = NULL; 127336620Srick struct direct *dir; 127436620Srick FILE *dout = NULL; 127536620Srick register char **dirlist, *dirname; 127637459Skarels int simple = 0; 127736620Srick char *strpbrk(); 127836620Srick 127936620Srick if (strpbrk(whichfiles, "~{[*?") != NULL) { 128036620Srick extern char **glob(), *globerr; 128136933Skarels 128236620Srick globerr = NULL; 128336620Srick dirlist = glob(whichfiles); 128436620Srick if (globerr != NULL) { 128536620Srick reply(550, globerr); 128636620Srick return; 128736620Srick } else if (dirlist == NULL) { 128836620Srick errno = ENOENT; 128936620Srick perror_reply(550, whichfiles); 129036620Srick return; 129136620Srick } 129236620Srick } else { 129336620Srick onefile[0] = whichfiles; 129436620Srick dirlist = onefile; 129537459Skarels simple = 1; 129636620Srick } 129736933Skarels 129836933Skarels if (setjmp(urgcatch)) { 129936933Skarels transflag = 0; 130036933Skarels return; 130136933Skarels } 130236620Srick while (dirname = *dirlist++) { 130336620Srick if (stat(dirname, &st) < 0) { 130436933Skarels /* 130536933Skarels * If user typed "ls -l", etc, and the client 130636933Skarels * used NLST, do what the user meant. 130736933Skarels */ 130836933Skarels if (dirname[0] == '-' && *dirlist == NULL && 130936933Skarels transflag == 0) { 131036933Skarels retrieve("/bin/ls %s", dirname); 131136933Skarels return; 131236933Skarels } 131336620Srick perror_reply(550, whichfiles); 131436620Srick if (dout != NULL) { 131536620Srick (void) fclose(dout); 131636933Skarels transflag = 0; 131736620Srick data = -1; 131836620Srick pdata = -1; 131936620Srick } 132036620Srick return; 132136620Srick } 132236933Skarels 132336620Srick if ((st.st_mode&S_IFMT) == S_IFREG) { 132436620Srick if (dout == NULL) { 132537459Skarels dout = dataconn("file list", (off_t)-1, "w"); 132636620Srick if (dout == NULL) 132736620Srick return; 132836933Skarels transflag++; 132936620Srick } 133038158Srick fprintf(dout, "%s%s\n", dirname, 133138158Srick type == TYPE_A ? "\r" : ""); 133236933Skarels byte_count += strlen(dirname) + 1; 133336620Srick continue; 133436933Skarels } else if ((st.st_mode&S_IFMT) != S_IFDIR) 133536620Srick continue; 133636620Srick 133736620Srick if ((dirp = opendir(dirname)) == NULL) 133836620Srick continue; 133936620Srick 134036620Srick while ((dir = readdir(dirp)) != NULL) { 134136933Skarels char nbuf[MAXPATHLEN]; 134236620Srick 134336620Srick if (dir->d_name[0] == '.' && dir->d_namlen == 1) 134436620Srick continue; 134536933Skarels if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 134636933Skarels dir->d_namlen == 2) 134736620Srick continue; 134836620Srick 134936933Skarels sprintf(nbuf, "%s/%s", dirname, dir->d_name); 135036933Skarels 135136620Srick /* 135236933Skarels * We have to do a stat to insure it's 135336933Skarels * not a directory or special file. 135436620Srick */ 135537459Skarels if (simple || (stat(nbuf, &st) == 0 && 135637459Skarels (st.st_mode&S_IFMT) == S_IFREG)) { 135736620Srick if (dout == NULL) { 135837459Skarels dout = dataconn("file list", (off_t)-1, 135936620Srick "w"); 136036620Srick if (dout == NULL) 136136620Srick return; 136236933Skarels transflag++; 136336620Srick } 136436620Srick if (nbuf[0] == '.' && nbuf[1] == '/') 136538158Srick fprintf(dout, "%s%s\n", &nbuf[2], 136638158Srick type == TYPE_A ? "\r" : ""); 136736620Srick else 136838158Srick fprintf(dout, "%s%s\n", nbuf, 136938158Srick type == TYPE_A ? "\r" : ""); 137036933Skarels byte_count += strlen(nbuf) + 1; 137136620Srick } 137236620Srick } 137336620Srick (void) closedir(dirp); 137436620Srick } 137536620Srick 137636933Skarels if (dout == NULL) 137736933Skarels reply(550, "No files found."); 137836933Skarels else if (ferror(dout) != 0) 137936933Skarels perror_reply(550, "Data connection"); 138036933Skarels else 138136620Srick reply(226, "Transfer complete."); 138236620Srick 138336933Skarels transflag = 0; 138436933Skarels if (dout != NULL) 138536620Srick (void) fclose(dout); 138636620Srick data = -1; 138736620Srick pdata = -1; 138836620Srick } 138936620Srick 139036620Srick #ifdef SETPROCTITLE 139136620Srick /* 139236620Srick * clobber argv so ps will show what we're doing. 139336620Srick * (stolen from sendmail) 139436620Srick * warning, since this is usually started from inetd.conf, it 139536620Srick * often doesn't have much of an environment or arglist to overwrite. 139636620Srick */ 139736620Srick 139836620Srick /*VARARGS1*/ 139936620Srick setproctitle(fmt, a, b, c) 140036620Srick char *fmt; 140136620Srick { 140236620Srick register char *p, *bp, ch; 140336620Srick register int i; 140436620Srick char buf[BUFSIZ]; 140536620Srick 140636620Srick (void) sprintf(buf, fmt, a, b, c); 140736620Srick 140836620Srick /* make ps print our process name */ 140936620Srick p = Argv[0]; 141036620Srick *p++ = '-'; 141136620Srick 141236620Srick i = strlen(buf); 141336620Srick if (i > LastArgv - p - 2) { 141436620Srick i = LastArgv - p - 2; 141536620Srick buf[i] = '\0'; 141636620Srick } 141736620Srick bp = buf; 141836620Srick while (ch = *bp++) 141936620Srick if (ch != '\n' && ch != '\r') 142036620Srick *p++ = ch; 142136620Srick while (p < LastArgv) 142236620Srick *p++ = ' '; 142336620Srick } 142436620Srick #endif /* SETPROCTITLE */ 1425