122499Sdist /* 236304Skarels * Copyright (c) 1985, 1988 Regents of the University of California. 333738Sbostic * All rights reserved. 433738Sbostic * 533738Sbostic * Redistribution and use in source and binary forms are permitted 634769Sbostic * provided that the above copyright notice and this paragraph are 734769Sbostic * duplicated in all such forms and that any documentation, 834769Sbostic * advertising materials, and other materials related to such 934769Sbostic * distribution and use acknowledge that the software was developed 1034769Sbostic * by the University of California, Berkeley. The name of the 1134769Sbostic * University may not be used to endorse or promote products derived 1234769Sbostic * from this software without specific prior written permission. 1334769Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434769Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1536933Skarels * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1622499Sdist */ 1722499Sdist 1810275Ssam #ifndef lint 1922499Sdist char copyright[] = 2036304Skarels "@(#) Copyright (c) 1985, 1988 Regents of the University of California.\n\ 2122499Sdist All rights reserved.\n"; 2233738Sbostic #endif /* not lint */ 2310275Ssam 2422499Sdist #ifndef lint 25*42412Sbostic static char sccsid[] = "@(#)ftpd.c 5.35 (Berkeley) 05/28/90"; 2633738Sbostic #endif /* not lint */ 2722499Sdist 2810275Ssam /* 2910275Ssam * FTP server. 3010275Ssam */ 3110303Ssam #include <sys/param.h> 3210275Ssam #include <sys/stat.h> 3310275Ssam #include <sys/ioctl.h> 3410275Ssam #include <sys/socket.h> 3513247Ssam #include <sys/file.h> 3613595Ssam #include <sys/wait.h> 3736620Srick #include <sys/dir.h> 3810275Ssam 3910275Ssam #include <netinet/in.h> 4010275Ssam 4136933Skarels #define FTP_NAMES 4213034Ssam #include <arpa/ftp.h> 4313211Sroot #include <arpa/inet.h> 4426044Sminshall #include <arpa/telnet.h> 4513034Ssam 4636933Skarels #include <ctype.h> 4710275Ssam #include <stdio.h> 4810275Ssam #include <signal.h> 4910275Ssam #include <pwd.h> 5010275Ssam #include <setjmp.h> 5110275Ssam #include <netdb.h> 5210423Ssam #include <errno.h> 5342031Sbostic #include <string.h> 5426493Sminshall #include <syslog.h> 5536435Sbostic #include <varargs.h> 5637459Skarels #include "pathnames.h" 5710275Ssam 5810695Ssam /* 5910695Ssam * File containing login names 6010695Ssam * NOT to be used on this machine. 6110695Ssam * Commonly used to disallow uucp. 6210695Ssam */ 6310275Ssam extern int errno; 6410275Ssam extern char *crypt(); 6510275Ssam extern char version[]; 6610275Ssam extern char *home; /* pointer to home directory for glob */ 6736276Sbostic extern FILE *ftpd_popen(), *fopen(), *freopen(); 6836304Skarels extern int ftpd_pclose(), fclose(); 6926044Sminshall extern char *getline(); 7026044Sminshall extern char cbuf[]; 7137459Skarels extern off_t restart_point; 7210275Ssam 7310275Ssam struct sockaddr_in ctrl_addr; 7410275Ssam struct sockaddr_in data_source; 7510275Ssam struct sockaddr_in data_dest; 7610275Ssam struct sockaddr_in his_addr; 7736933Skarels struct sockaddr_in pasv_addr; 7810275Ssam 7910275Ssam int data; 8026044Sminshall jmp_buf errcatch, urgcatch; 8110275Ssam int logged_in; 8210275Ssam struct passwd *pw; 8310275Ssam int debug; 8426493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */ 8536933Skarels int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 8611757Ssam int logging; 8710275Ssam int guest; 8810275Ssam int type; 8910275Ssam int form; 9010275Ssam int stru; /* avoid C keyword */ 9110275Ssam int mode; 9210321Ssam int usedefault = 1; /* for data transfers */ 9336304Skarels int pdata = -1; /* for passive mode */ 9426044Sminshall int transflag; 9536933Skarels off_t file_size; 9636933Skarels off_t byte_count; 9736933Skarels #if !defined(CMASK) || CMASK == 0 9836933Skarels #undef CMASK 9936933Skarels #define CMASK 027 10036933Skarels #endif 10136933Skarels int defumask = CMASK; /* default umask value */ 10226044Sminshall char tmpline[7]; 10336276Sbostic char hostname[MAXHOSTNAMELEN]; 10436276Sbostic char remotehost[MAXHOSTNAMELEN]; 10510275Ssam 10611653Ssam /* 10711653Ssam * Timeout intervals for retrying connections 10811653Ssam * to hosts that don't accept PORT cmds. This 10911653Ssam * is a kludge, but given the problems with TCP... 11011653Ssam */ 11111653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 11211653Ssam #define SWAITINT 5 /* interval between retries */ 11311653Ssam 11411653Ssam int swaitmax = SWAITMAX; 11511653Ssam int swaitint = SWAITINT; 11611653Ssam 11710275Ssam int lostconn(); 11826044Sminshall int myoob(); 11910275Ssam FILE *getdatasock(), *dataconn(); 12010275Ssam 12136620Srick #ifdef SETPROCTITLE 12236620Srick char **Argv = NULL; /* pointer to argument vector */ 12336620Srick char *LastArgv = NULL; /* end of argv */ 12436933Skarels char proctitle[BUFSIZ]; /* initial part of title */ 12536620Srick #endif /* SETPROCTITLE */ 12636620Srick 12736620Srick main(argc, argv, envp) 12810275Ssam int argc; 12910275Ssam char *argv[]; 13036620Srick char **envp; 13110275Ssam { 13227750Sminshall int addrlen, on = 1; 13310275Ssam char *cp; 13410275Ssam 13516339Skarels addrlen = sizeof (his_addr); 13636304Skarels if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 13726493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 13810275Ssam exit(1); 13910275Ssam } 14016339Skarels addrlen = sizeof (ctrl_addr); 14136304Skarels if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 14226493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 14316339Skarels exit(1); 14416339Skarels } 14516339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 14610275Ssam debug = 0; 14726493Sminshall openlog("ftpd", LOG_PID, LOG_DAEMON); 14836620Srick #ifdef SETPROCTITLE 14936620Srick /* 15036620Srick * Save start and extent of argv for setproctitle. 15136620Srick */ 15236620Srick Argv = argv; 15336620Srick while (*envp) 15436620Srick envp++; 15536620Srick LastArgv = envp[-1] + strlen(envp[-1]); 15636620Srick #endif /* SETPROCTITLE */ 15736620Srick 15810275Ssam argc--, argv++; 15910275Ssam while (argc > 0 && *argv[0] == '-') { 16010275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 16110275Ssam 16211653Ssam case 'v': 16311653Ssam debug = 1; 16411653Ssam break; 16511653Ssam 16610275Ssam case 'd': 16710275Ssam debug = 1; 16810275Ssam break; 16910275Ssam 17011757Ssam case 'l': 17111757Ssam logging = 1; 17211757Ssam break; 17311757Ssam 17411653Ssam case 't': 17511653Ssam timeout = atoi(++cp); 17636933Skarels if (maxtimeout < timeout) 17736933Skarels maxtimeout = timeout; 17811653Ssam goto nextopt; 17911653Ssam 18036933Skarels case 'T': 18136933Skarels maxtimeout = atoi(++cp); 18236933Skarels if (timeout > maxtimeout) 18336933Skarels timeout = maxtimeout; 18436933Skarels goto nextopt; 18536933Skarels 18636933Skarels case 'u': 18736933Skarels { 18836933Skarels int val = 0; 18936933Skarels 19036933Skarels while (*++cp && *cp >= '0' && *cp <= '9') 19136933Skarels val = val*8 + *cp - '0'; 19236933Skarels if (*cp) 19336933Skarels fprintf(stderr, "ftpd: Bad value for -u\n"); 19436933Skarels else 19536933Skarels defumask = val; 19636933Skarels goto nextopt; 19736933Skarels } 19836933Skarels 19910275Ssam default: 20016339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 20116339Skarels *cp); 20210275Ssam break; 20310275Ssam } 20411653Ssam nextopt: 20510275Ssam argc--, argv++; 20610275Ssam } 20737459Skarels (void) freopen(_PATH_DEVNULL, "w", stderr); 20826493Sminshall (void) signal(SIGPIPE, lostconn); 20926493Sminshall (void) signal(SIGCHLD, SIG_IGN); 21035691Sbostic if ((int)signal(SIGURG, myoob) < 0) 21126493Sminshall syslog(LOG_ERR, "signal: %m"); 21235691Sbostic 21338134Srick /* Try to handle urgent data inline */ 21427750Sminshall #ifdef SO_OOBINLINE 21536276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 21627750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 21736276Sbostic #endif 21838134Srick 21936933Skarels #ifdef F_SETOWN 22036304Skarels if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 22136304Skarels syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 22236933Skarels #endif 22316760Slepreau dolog(&his_addr); 22416339Skarels /* 22516339Skarels * Set up default state 22616339Skarels */ 22716339Skarels data = -1; 22816339Skarels type = TYPE_A; 22916339Skarels form = FORM_N; 23016339Skarels stru = STRU_F; 23116339Skarels mode = MODE_S; 23226044Sminshall tmpline[0] = '\0'; 23326493Sminshall (void) gethostname(hostname, sizeof (hostname)); 23436276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version); 23536304Skarels (void) setjmp(errcatch); 23636304Skarels for (;;) 23726493Sminshall (void) yyparse(); 23836620Srick /* NOTREACHED */ 23910275Ssam } 24010419Ssam 24110275Ssam lostconn() 24210275Ssam { 24310275Ssam 24414089Ssam if (debug) 24526493Sminshall syslog(LOG_DEBUG, "lost connection"); 24614089Ssam dologout(-1); 24710275Ssam } 24810275Ssam 24935672Sbostic static char ttyline[20]; 25035672Sbostic 25136185Sbostic /* 25236185Sbostic * Helper function for sgetpwnam(). 25336185Sbostic */ 25436185Sbostic char * 25536185Sbostic sgetsave(s) 25636185Sbostic char *s; 25736185Sbostic { 25836185Sbostic char *malloc(); 25936185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 26036933Skarels 26136185Sbostic if (new == NULL) { 26236620Srick perror_reply(421, "Local resource failure: malloc"); 26336185Sbostic dologout(1); 26436620Srick /* NOTREACHED */ 26536185Sbostic } 26636185Sbostic (void) strcpy(new, s); 26736185Sbostic return (new); 26836185Sbostic } 26936185Sbostic 27036185Sbostic /* 27136185Sbostic * Save the result of a getpwnam. Used for USER command, since 27236185Sbostic * the data returned must not be clobbered by any other command 27336185Sbostic * (e.g., globbing). 27436185Sbostic */ 27536185Sbostic struct passwd * 27636185Sbostic sgetpwnam(name) 27736185Sbostic char *name; 27836185Sbostic { 27936185Sbostic static struct passwd save; 28036185Sbostic register struct passwd *p; 28136185Sbostic char *sgetsave(); 28236185Sbostic 28336185Sbostic if ((p = getpwnam(name)) == NULL) 28436185Sbostic return (p); 28536185Sbostic if (save.pw_name) { 28636185Sbostic free(save.pw_name); 28736185Sbostic free(save.pw_passwd); 28836185Sbostic free(save.pw_gecos); 28936185Sbostic free(save.pw_dir); 29036185Sbostic free(save.pw_shell); 29136185Sbostic } 29236185Sbostic save = *p; 29336185Sbostic save.pw_name = sgetsave(p->pw_name); 29436185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 29536185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 29636185Sbostic save.pw_dir = sgetsave(p->pw_dir); 29736185Sbostic save.pw_shell = sgetsave(p->pw_shell); 29836185Sbostic return (&save); 29936185Sbostic } 30036185Sbostic 30136304Skarels int login_attempts; /* number of failed login attempts */ 30236304Skarels int askpasswd; /* had user command, ask for passwd */ 30336304Skarels 30436304Skarels /* 30536304Skarels * USER command. 30642327Sbostic * Sets global passwd pointer pw if named account exists and is acceptable; 30742327Sbostic * sets askpasswd if a PASS command is expected. If logged in previously, 30842327Sbostic * need to reset state. If name is "ftp" or "anonymous", the name is not in 30942327Sbostic * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 31042327Sbostic * If account doesn't exist, ask for passwd anyway. Otherwise, check user 31142327Sbostic * requesting login privileges. Disallow anyone who does not have a standard 31242327Sbostic * shell as returned by getusershell(). Disallow anyone mentioned in the file 31342327Sbostic * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 31436304Skarels */ 31536304Skarels user(name) 31636304Skarels char *name; 31736304Skarels { 31836304Skarels register char *cp; 31936304Skarels char *shell; 32040183Smckusick char *getusershell(); 32136304Skarels 32236304Skarels if (logged_in) { 32336304Skarels if (guest) { 32436304Skarels reply(530, "Can't change user from guest login."); 32536304Skarels return; 32636304Skarels } 32736304Skarels end_login(); 32836304Skarels } 32936304Skarels 33036304Skarels guest = 0; 33136304Skarels if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 33240183Smckusick if (checkuser("ftp") || checkuser("anonymous")) 33340155Smckusick reply(530, "User %s access denied.", name); 33440155Smckusick else if ((pw = sgetpwnam("ftp")) != NULL) { 33536304Skarels guest = 1; 33636304Skarels askpasswd = 1; 33736304Skarels reply(331, "Guest login ok, send ident as password."); 33836933Skarels } else 33936304Skarels reply(530, "User %s unknown.", name); 34036304Skarels return; 34136304Skarels } 34236304Skarels if (pw = sgetpwnam(name)) { 34336304Skarels if ((shell = pw->pw_shell) == NULL || *shell == 0) 34437459Skarels shell = _PATH_BSHELL; 34536304Skarels while ((cp = getusershell()) != NULL) 34636304Skarels if (strcmp(cp, shell) == 0) 34736304Skarels break; 34836304Skarels endusershell(); 34940183Smckusick if (cp == NULL || checkuser(name)) { 35036304Skarels reply(530, "User %s access denied.", name); 35136933Skarels if (logging) 35236933Skarels syslog(LOG_NOTICE, 35336933Skarels "FTP LOGIN REFUSED FROM %s, %s", 35436933Skarels remotehost, name); 35536304Skarels pw = (struct passwd *) NULL; 35636304Skarels return; 35736304Skarels } 35836304Skarels } 35936304Skarels reply(331, "Password required for %s.", name); 36036304Skarels askpasswd = 1; 36136304Skarels /* 36236304Skarels * Delay before reading passwd after first failed 36336304Skarels * attempt to slow down passwd-guessing programs. 36436304Skarels */ 36536304Skarels if (login_attempts) 36636304Skarels sleep((unsigned) login_attempts); 36736304Skarels } 36836304Skarels 36936304Skarels /* 37040183Smckusick * Check if a user is in the file _PATH_FTPUSERS 37140183Smckusick */ 37240183Smckusick checkuser(name) 37340183Smckusick char *name; 37440183Smckusick { 37542327Sbostic register FILE *fd; 37642327Sbostic register char *p; 37742327Sbostic char line[BUFSIZ]; 37840183Smckusick 37940183Smckusick if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { 38042327Sbostic while (fgets(line, sizeof(line), fd) != NULL) 38142327Sbostic if ((p = index(line, '\n')) != NULL) { 38242327Sbostic *p = '\0'; 38342327Sbostic if (line[0] == '#') 38442327Sbostic continue; 38542327Sbostic if (strcmp(line, name) == 0) 38642327Sbostic return (1); 38742327Sbostic } 38840183Smckusick (void) fclose(fd); 38940183Smckusick } 39040183Smckusick return (0); 39140183Smckusick } 39240183Smckusick 39340183Smckusick /* 39436304Skarels * Terminate login as previous user, if any, resetting state; 39536304Skarels * used when USER command is given or login fails. 39636304Skarels */ 39736304Skarels end_login() 39836304Skarels { 39936304Skarels 40036304Skarels (void) seteuid((uid_t)0); 40136304Skarels if (logged_in) 40236304Skarels logwtmp(ttyline, "", ""); 40336304Skarels pw = NULL; 40436304Skarels logged_in = 0; 40536304Skarels guest = 0; 40636304Skarels } 40736304Skarels 40810275Ssam pass(passwd) 40910275Ssam char *passwd; 41010275Ssam { 41136304Skarels char *xpasswd, *salt; 41210275Ssam 41336304Skarels if (logged_in || askpasswd == 0) { 41410275Ssam reply(503, "Login with USER first."); 41510275Ssam return; 41610275Ssam } 41736304Skarels askpasswd = 0; 41810275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 41936304Skarels if (pw == NULL) 42036304Skarels salt = "xx"; 42136304Skarels else 42236304Skarels salt = pw->pw_passwd; 42336304Skarels xpasswd = crypt(passwd, salt); 42416760Slepreau /* The strcmp does not catch null passwords! */ 42536304Skarels if (pw == NULL || *pw->pw_passwd == '\0' || 42636304Skarels strcmp(xpasswd, pw->pw_passwd)) { 42710275Ssam reply(530, "Login incorrect."); 42810275Ssam pw = NULL; 42936304Skarels if (login_attempts++ >= 5) { 43036933Skarels syslog(LOG_NOTICE, 43136304Skarels "repeated login failures from %s", 43236304Skarels remotehost); 43336304Skarels exit(0); 43436304Skarels } 43510275Ssam return; 43610275Ssam } 43710275Ssam } 43836304Skarels login_attempts = 0; /* this time successful */ 43936304Skarels (void) setegid((gid_t)pw->pw_gid); 44036304Skarels (void) initgroups(pw->pw_name, pw->pw_gid); 44116033Sralph 44236192Sbostic /* open wtmp before chroot */ 44336192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 44436192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 44536192Sbostic logged_in = 1; 44636192Sbostic 44736446Sbostic if (guest) { 44836620Srick /* 44936933Skarels * We MUST do a chdir() after the chroot. Otherwise 45036933Skarels * the old current directory will be accessible as "." 45136933Skarels * outside the new root! 45236620Srick */ 45336620Srick if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 45436446Sbostic reply(550, "Can't set guest privileges."); 45536446Sbostic goto bad; 45636446Sbostic } 45736933Skarels } else if (chdir(pw->pw_dir) < 0) { 45836620Srick if (chdir("/") < 0) { 45936446Sbostic reply(530, "User %s: can't change directory to %s.", 46036446Sbostic pw->pw_name, pw->pw_dir); 46136446Sbostic goto bad; 46236933Skarels } else 46336446Sbostic lreply(230, "No directory! Logging in with home=/"); 46436620Srick } 46536304Skarels if (seteuid((uid_t)pw->pw_uid) < 0) { 46636304Skarels reply(550, "Can't set uid."); 46736304Skarels goto bad; 46836304Skarels } 46936550Sbostic if (guest) { 47036192Sbostic reply(230, "Guest login ok, access restrictions apply."); 47136933Skarels #ifdef SETPROCTITLE 47236933Skarels sprintf(proctitle, "%s: anonymous/%.*s", remotehost, 47336933Skarels sizeof(proctitle) - sizeof(remotehost) - 47436933Skarels sizeof(": anonymous/"), passwd); 47536933Skarels setproctitle(proctitle); 47636933Skarels #endif /* SETPROCTITLE */ 47736933Skarels if (logging) 47836933Skarels syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 47936933Skarels remotehost, passwd); 48036933Skarels } else { 48110275Ssam reply(230, "User %s logged in.", pw->pw_name); 48236933Skarels #ifdef SETPROCTITLE 48336933Skarels sprintf(proctitle, "%s: %s", remotehost, pw->pw_name); 48436933Skarels setproctitle(proctitle); 48536933Skarels #endif /* SETPROCTITLE */ 48636933Skarels if (logging) 48736933Skarels syslog(LOG_INFO, "FTP LOGIN FROM %s, %s", 48836933Skarels remotehost, pw->pw_name); 48936550Sbostic } 49010303Ssam home = pw->pw_dir; /* home dir for globbing */ 49136933Skarels (void) umask(defumask); 49210303Ssam return; 49310303Ssam bad: 49436304Skarels /* Forget all about it... */ 49536304Skarels end_login(); 49610275Ssam } 49710275Ssam 49810275Ssam retrieve(cmd, name) 49910275Ssam char *cmd, *name; 50010275Ssam { 50110275Ssam FILE *fin, *dout; 50210275Ssam struct stat st; 50336620Srick int (*closefunc)(); 50410275Ssam 50536557Sbostic if (cmd == 0) { 50636446Sbostic fin = fopen(name, "r"), closefunc = fclose; 50736557Sbostic st.st_size = 0; 50836557Sbostic } else { 50910275Ssam char line[BUFSIZ]; 51010275Ssam 51126493Sminshall (void) sprintf(line, cmd, name), name = line; 51236304Skarels fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 51336557Sbostic st.st_size = -1; 51436933Skarels st.st_blksize = BUFSIZ; 51510275Ssam } 51610275Ssam if (fin == NULL) { 51713152Ssam if (errno != 0) 51836304Skarels perror_reply(550, name); 51910275Ssam return; 52010275Ssam } 52110275Ssam if (cmd == 0 && 52236933Skarels (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 52310275Ssam reply(550, "%s: not a plain file.", name); 52410275Ssam goto done; 52510275Ssam } 52637459Skarels if (restart_point) { 52737459Skarels if (type == TYPE_A) { 52837459Skarels register int i, n, c; 52937459Skarels 53037459Skarels n = restart_point; 53137459Skarels i = 0; 53237459Skarels while (i++ < n) { 53337459Skarels if ((c=getc(fin)) == EOF) { 53437459Skarels perror_reply(550, name); 53537459Skarels goto done; 53637459Skarels } 53737459Skarels if (c == '\n') 53837459Skarels i++; 53937459Skarels } 54037459Skarels } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 54137459Skarels perror_reply(550, name); 54237459Skarels goto done; 54337459Skarels } 54437459Skarels } 54510275Ssam dout = dataconn(name, st.st_size, "w"); 54610275Ssam if (dout == NULL) 54710275Ssam goto done; 54836620Srick send_data(fin, dout, st.st_blksize); 54926493Sminshall (void) fclose(dout); 55026044Sminshall data = -1; 55126044Sminshall pdata = -1; 55210275Ssam done: 55310275Ssam (*closefunc)(fin); 55410275Ssam } 55510275Ssam 55636304Skarels store(name, mode, unique) 55710275Ssam char *name, *mode; 55836304Skarels int unique; 55910275Ssam { 56010275Ssam FILE *fout, *din; 56136446Sbostic struct stat st; 56236620Srick int (*closefunc)(); 56336304Skarels char *gunique(); 56410275Ssam 56536446Sbostic if (unique && stat(name, &st) == 0 && 56636446Sbostic (name = gunique(name)) == NULL) 56736446Sbostic return; 56810303Ssam 56937459Skarels if (restart_point) 57037459Skarels mode = "r+w"; 57136620Srick fout = fopen(name, mode); 57236620Srick closefunc = fclose; 57310275Ssam if (fout == NULL) { 57436304Skarels perror_reply(553, name); 57510275Ssam return; 57610275Ssam } 57737459Skarels if (restart_point) { 57837459Skarels if (type == TYPE_A) { 57937459Skarels register int i, n, c; 58037459Skarels 58137459Skarels n = restart_point; 58237459Skarels i = 0; 58337459Skarels while (i++ < n) { 58437459Skarels if ((c=getc(fout)) == EOF) { 58537459Skarels perror_reply(550, name); 58637459Skarels goto done; 58737459Skarels } 58837459Skarels if (c == '\n') 58937459Skarels i++; 59037459Skarels } 59137459Skarels /* 59237459Skarels * We must do this seek to "current" position 59337459Skarels * because we are changing from reading to 59437459Skarels * writing. 59537459Skarels */ 59637459Skarels if (fseek(fout, 0L, L_INCR) < 0) { 59737459Skarels perror_reply(550, name); 59837459Skarels goto done; 59937459Skarels } 60037459Skarels } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 60137459Skarels perror_reply(550, name); 60237459Skarels goto done; 60337459Skarels } 60437459Skarels } 60536304Skarels din = dataconn(name, (off_t)-1, "r"); 60610275Ssam if (din == NULL) 60710275Ssam goto done; 60836620Srick if (receive_data(din, fout) == 0) { 60936620Srick if (unique) 61036304Skarels reply(226, "Transfer complete (unique file name:%s).", 61136933Skarels name); 61236304Skarels else 61336304Skarels reply(226, "Transfer complete."); 61426044Sminshall } 61526493Sminshall (void) fclose(din); 61626044Sminshall data = -1; 61726044Sminshall pdata = -1; 61810275Ssam done: 61910275Ssam (*closefunc)(fout); 62010275Ssam } 62110275Ssam 62210275Ssam FILE * 62310275Ssam getdatasock(mode) 62410275Ssam char *mode; 62510275Ssam { 62637459Skarels int s, on = 1, tries; 62710275Ssam 62810275Ssam if (data >= 0) 62910275Ssam return (fdopen(data, mode)); 63013247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 63110602Ssam if (s < 0) 63210275Ssam return (NULL); 63336304Skarels (void) seteuid((uid_t)0); 63437459Skarels if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 63537459Skarels (char *) &on, sizeof (on)) < 0) 63610602Ssam goto bad; 63713152Ssam /* anchor socket to avoid multi-homing problems */ 63813152Ssam data_source.sin_family = AF_INET; 63913152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 64037459Skarels for (tries = 1; ; tries++) { 64137459Skarels if (bind(s, (struct sockaddr *)&data_source, 64237459Skarels sizeof (data_source)) >= 0) 64337459Skarels break; 64437459Skarels if (errno != EADDRINUSE || tries > 10) 64537459Skarels goto bad; 64637459Skarels sleep(tries); 64737459Skarels } 64836304Skarels (void) seteuid((uid_t)pw->pw_uid); 64910275Ssam return (fdopen(s, mode)); 65010602Ssam bad: 65136304Skarels (void) seteuid((uid_t)pw->pw_uid); 65226493Sminshall (void) close(s); 65310602Ssam return (NULL); 65410275Ssam } 65510275Ssam 65610275Ssam FILE * 65710275Ssam dataconn(name, size, mode) 65810275Ssam char *name; 65911653Ssam off_t size; 66010275Ssam char *mode; 66110275Ssam { 66210275Ssam char sizebuf[32]; 66310275Ssam FILE *file; 66411653Ssam int retry = 0; 66510275Ssam 66636933Skarels file_size = size; 66736933Skarels byte_count = 0; 66836304Skarels if (size != (off_t) -1) 66926493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 67010275Ssam else 67110275Ssam (void) strcpy(sizebuf, ""); 67236304Skarels if (pdata >= 0) { 67326044Sminshall struct sockaddr_in from; 67426044Sminshall int s, fromlen = sizeof(from); 67526044Sminshall 67636304Skarels s = accept(pdata, (struct sockaddr *)&from, &fromlen); 67726044Sminshall if (s < 0) { 67826044Sminshall reply(425, "Can't open data connection."); 67926044Sminshall (void) close(pdata); 68026044Sminshall pdata = -1; 68126044Sminshall return(NULL); 68226044Sminshall } 68326044Sminshall (void) close(pdata); 68426044Sminshall pdata = s; 68536235Skarels reply(150, "Opening %s mode data connection for %s%s.", 68636235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 68726044Sminshall return(fdopen(pdata, mode)); 68826044Sminshall } 68910275Ssam if (data >= 0) { 69010275Ssam reply(125, "Using existing data connection for %s%s.", 69110275Ssam name, sizebuf); 69210321Ssam usedefault = 1; 69310275Ssam return (fdopen(data, mode)); 69410275Ssam } 69510566Ssam if (usedefault) 69610422Ssam data_dest = his_addr; 69710422Ssam usedefault = 1; 69810275Ssam file = getdatasock(mode); 69910275Ssam if (file == NULL) { 70010275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 70113247Ssam inet_ntoa(data_source.sin_addr), 702*42412Sbostic ntohs(data_source.sin_port), strerror(errno)); 70310275Ssam return (NULL); 70410275Ssam } 70510275Ssam data = fileno(file); 70636304Skarels while (connect(data, (struct sockaddr *)&data_dest, 70736304Skarels sizeof (data_dest)) < 0) { 70811653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 70926493Sminshall sleep((unsigned) swaitint); 71011653Ssam retry += swaitint; 71111653Ssam continue; 71211653Ssam } 71336304Skarels perror_reply(425, "Can't build data connection"); 71410275Ssam (void) fclose(file); 71510275Ssam data = -1; 71610275Ssam return (NULL); 71710275Ssam } 71836235Skarels reply(150, "Opening %s mode data connection for %s%s.", 71936235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 72010275Ssam return (file); 72110275Ssam } 72210275Ssam 72310275Ssam /* 72410275Ssam * Tranfer the contents of "instr" to 72510275Ssam * "outstr" peer using the appropriate 72636933Skarels * encapsulation of the data subject 72710275Ssam * to Mode, Structure, and Type. 72810275Ssam * 72910275Ssam * NB: Form isn't handled. 73010275Ssam */ 73136446Sbostic send_data(instr, outstr, blksize) 73210275Ssam FILE *instr, *outstr; 73336446Sbostic off_t blksize; 73410275Ssam { 73536446Sbostic register int c, cnt; 73636446Sbostic register char *buf; 73736446Sbostic int netfd, filefd; 73810275Ssam 73926044Sminshall transflag++; 74026044Sminshall if (setjmp(urgcatch)) { 74126044Sminshall transflag = 0; 74236620Srick return; 74326044Sminshall } 74410275Ssam switch (type) { 74510275Ssam 74610275Ssam case TYPE_A: 74710275Ssam while ((c = getc(instr)) != EOF) { 74836933Skarels byte_count++; 74911220Ssam if (c == '\n') { 75036933Skarels if (ferror(outstr)) 75136620Srick goto data_err; 75227750Sminshall (void) putc('\r', outstr); 75311220Ssam } 75427750Sminshall (void) putc(c, outstr); 75510275Ssam } 75636933Skarels fflush(outstr); 75726044Sminshall transflag = 0; 75836933Skarels if (ferror(instr)) 75936933Skarels goto file_err; 76036933Skarels if (ferror(outstr)) 76136620Srick goto data_err; 76236620Srick reply(226, "Transfer complete."); 76336620Srick return; 76436446Sbostic 76510275Ssam case TYPE_I: 76610275Ssam case TYPE_L: 76736446Sbostic if ((buf = malloc((u_int)blksize)) == NULL) { 76836446Sbostic transflag = 0; 76936933Skarels perror_reply(451, "Local resource failure: malloc"); 77036933Skarels return; 77136446Sbostic } 77210275Ssam netfd = fileno(outstr); 77310275Ssam filefd = fileno(instr); 77436933Skarels while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 77536620Srick write(netfd, buf, cnt) == cnt) 77636933Skarels byte_count += cnt; 77726044Sminshall transflag = 0; 77836446Sbostic (void)free(buf); 77936933Skarels if (cnt != 0) { 78036933Skarels if (cnt < 0) 78136933Skarels goto file_err; 78236620Srick goto data_err; 78336933Skarels } 78436620Srick reply(226, "Transfer complete."); 78536620Srick return; 78636620Srick default: 78736620Srick transflag = 0; 78836620Srick reply(550, "Unimplemented TYPE %d in send_data", type); 78936620Srick return; 79010275Ssam } 79136620Srick 79236620Srick data_err: 79326044Sminshall transflag = 0; 79436933Skarels perror_reply(426, "Data connection"); 79536933Skarels return; 79636933Skarels 79736933Skarels file_err: 79836933Skarels transflag = 0; 79936933Skarels perror_reply(551, "Error on input file"); 80010275Ssam } 80110275Ssam 80210275Ssam /* 80310275Ssam * Transfer data from peer to 80410275Ssam * "outstr" using the appropriate 80510275Ssam * encapulation of the data subject 80610275Ssam * to Mode, Structure, and Type. 80710275Ssam * 80810275Ssam * N.B.: Form isn't handled. 80910275Ssam */ 81010275Ssam receive_data(instr, outstr) 81110275Ssam FILE *instr, *outstr; 81210275Ssam { 81310275Ssam register int c; 81438134Srick int cnt, bare_lfs = 0; 81510275Ssam char buf[BUFSIZ]; 81610275Ssam 81726044Sminshall transflag++; 81826044Sminshall if (setjmp(urgcatch)) { 81926044Sminshall transflag = 0; 82036933Skarels return (-1); 82126044Sminshall } 82210275Ssam switch (type) { 82310275Ssam 82410275Ssam case TYPE_I: 82510275Ssam case TYPE_L: 82626044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 82736620Srick if (write(fileno(outstr), buf, cnt) != cnt) 82836933Skarels goto file_err; 82936933Skarels byte_count += cnt; 83026044Sminshall } 83136933Skarels if (cnt < 0) 83236620Srick goto data_err; 83326044Sminshall transflag = 0; 83436933Skarels return (0); 83510275Ssam 83610275Ssam case TYPE_E: 83727106Smckusick reply(553, "TYPE E not implemented."); 83826044Sminshall transflag = 0; 83927106Smckusick return (-1); 84010275Ssam 84110275Ssam case TYPE_A: 84210275Ssam while ((c = getc(instr)) != EOF) { 84336933Skarels byte_count++; 84438134Srick if (c == '\n') 84538134Srick bare_lfs++; 84627750Sminshall while (c == '\r') { 84736933Skarels if (ferror(outstr)) 84836620Srick goto data_err; 84936933Skarels if ((c = getc(instr)) != '\n') { 85027750Sminshall (void) putc ('\r', outstr); 85136933Skarels if (c == '\0' || c == EOF) 85236933Skarels goto contin2; 85336933Skarels } 85410275Ssam } 85536933Skarels (void) putc(c, outstr); 85636933Skarels contin2: ; 85710275Ssam } 85836620Srick fflush(outstr); 85936933Skarels if (ferror(instr)) 86036620Srick goto data_err; 86136933Skarels if (ferror(outstr)) 86236933Skarels goto file_err; 86326044Sminshall transflag = 0; 86438134Srick if (bare_lfs) { 86538134Srick lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs); 86638134Srick printf(" File may not have transferred correctly.\r\n"); 86738134Srick } 86810275Ssam return (0); 86936620Srick default: 87036620Srick reply(550, "Unimplemented TYPE %d in receive_data", type); 87136620Srick transflag = 0; 87236933Skarels return (-1); 87310275Ssam } 87436620Srick 87536620Srick data_err: 87626044Sminshall transflag = 0; 87736933Skarels perror_reply(426, "Data Connection"); 87836933Skarels return (-1); 87936933Skarels 88036933Skarels file_err: 88136933Skarels transflag = 0; 88236933Skarels perror_reply(452, "Error writing file"); 88336933Skarels return (-1); 88410275Ssam } 88510275Ssam 88636933Skarels statfilecmd(filename) 88736933Skarels char *filename; 88836933Skarels { 88936933Skarels char line[BUFSIZ]; 89036933Skarels FILE *fin; 89136933Skarels int c; 89236933Skarels 89336933Skarels (void) sprintf(line, "/bin/ls -lgA %s", filename); 89436933Skarels fin = ftpd_popen(line, "r"); 89536933Skarels lreply(211, "status of %s:", filename); 89636933Skarels while ((c = getc(fin)) != EOF) { 89736933Skarels if (c == '\n') { 89836933Skarels if (ferror(stdout)){ 89936933Skarels perror_reply(421, "control connection"); 90036933Skarels (void) ftpd_pclose(fin); 90136933Skarels dologout(1); 90236933Skarels /* NOTREACHED */ 90336933Skarels } 90436933Skarels if (ferror(fin)) { 90536933Skarels perror_reply(551, filename); 90636933Skarels (void) ftpd_pclose(fin); 90736933Skarels return; 90836933Skarels } 90936933Skarels (void) putc('\r', stdout); 91036933Skarels } 91136933Skarels (void) putc(c, stdout); 91236933Skarels } 91336933Skarels (void) ftpd_pclose(fin); 91436933Skarels reply(211, "End of Status"); 91536933Skarels } 91636933Skarels 91736933Skarels statcmd() 91836933Skarels { 91936933Skarels struct sockaddr_in *sin; 92036933Skarels u_char *a, *p; 92136933Skarels 92236933Skarels lreply(211, "%s FTP server status:", hostname, version); 92336933Skarels printf(" %s\r\n", version); 92436933Skarels printf(" Connected to %s", remotehost); 92538134Srick if (!isdigit(remotehost[0])) 92636933Skarels printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 92736933Skarels printf("\r\n"); 92836933Skarels if (logged_in) { 92936933Skarels if (guest) 93036933Skarels printf(" Logged in anonymously\r\n"); 93136933Skarels else 93236933Skarels printf(" Logged in as %s\r\n", pw->pw_name); 93336933Skarels } else if (askpasswd) 93436933Skarels printf(" Waiting for password\r\n"); 93536933Skarels else 93636933Skarels printf(" Waiting for user name\r\n"); 93736933Skarels printf(" TYPE: %s", typenames[type]); 93836933Skarels if (type == TYPE_A || type == TYPE_E) 93936933Skarels printf(", FORM: %s", formnames[form]); 94036933Skarels if (type == TYPE_L) 94136933Skarels #if NBBY == 8 94236933Skarels printf(" %d", NBBY); 94336933Skarels #else 94436933Skarels printf(" %d", bytesize); /* need definition! */ 94536933Skarels #endif 94636933Skarels printf("; STRUcture: %s; transfer MODE: %s\r\n", 94736933Skarels strunames[stru], modenames[mode]); 94836933Skarels if (data != -1) 94936933Skarels printf(" Data connection open\r\n"); 95036933Skarels else if (pdata != -1) { 95136933Skarels printf(" in Passive mode"); 95236933Skarels sin = &pasv_addr; 95336933Skarels goto printaddr; 95436933Skarels } else if (usedefault == 0) { 95536933Skarels printf(" PORT"); 95636933Skarels sin = &data_dest; 95736933Skarels printaddr: 95836933Skarels a = (u_char *) &sin->sin_addr; 95936933Skarels p = (u_char *) &sin->sin_port; 96036933Skarels #define UC(b) (((int) b) & 0xff) 96136933Skarels printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 96236933Skarels UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 96336933Skarels #undef UC 96436933Skarels } else 96536933Skarels printf(" No data connection\r\n"); 96636933Skarels reply(211, "End of status"); 96736933Skarels } 96836933Skarels 96910275Ssam fatal(s) 97010275Ssam char *s; 97110275Ssam { 97210275Ssam reply(451, "Error in server: %s\n", s); 97310275Ssam reply(221, "Closing connection due to server error."); 97413247Ssam dologout(0); 97536620Srick /* NOTREACHED */ 97610275Ssam } 97710275Ssam 97836446Sbostic /* VARARGS2 */ 97936446Sbostic reply(n, fmt, p0, p1, p2, p3, p4, p5) 98010275Ssam int n; 98136446Sbostic char *fmt; 98210275Ssam { 98310275Ssam printf("%d ", n); 98436446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 98510275Ssam printf("\r\n"); 98636435Sbostic (void)fflush(stdout); 98710275Ssam if (debug) { 98826493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 98936446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 99010275Ssam } 99136620Srick } 99210275Ssam 99336446Sbostic /* VARARGS2 */ 99436446Sbostic lreply(n, fmt, p0, p1, p2, p3, p4, p5) 99510275Ssam int n; 99636446Sbostic char *fmt; 99710275Ssam { 99836446Sbostic printf("%d- ", n); 99936446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 100036446Sbostic printf("\r\n"); 100136435Sbostic (void)fflush(stdout); 100236446Sbostic if (debug) { 100336446Sbostic syslog(LOG_DEBUG, "<--- %d- ", n); 100436446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 100536446Sbostic } 100610275Ssam } 100710275Ssam 100810275Ssam ack(s) 100910275Ssam char *s; 101010275Ssam { 101127106Smckusick reply(250, "%s command successful.", s); 101210275Ssam } 101310275Ssam 101410275Ssam nack(s) 101510275Ssam char *s; 101610275Ssam { 101710275Ssam reply(502, "%s command not implemented.", s); 101810275Ssam } 101910275Ssam 102036304Skarels /* ARGSUSED */ 102126493Sminshall yyerror(s) 102226493Sminshall char *s; 102310275Ssam { 102426044Sminshall char *cp; 102526044Sminshall 102636551Sbostic if (cp = index(cbuf,'\n')) 102736551Sbostic *cp = '\0'; 102836933Skarels reply(500, "'%s': command not understood.", cbuf); 102910275Ssam } 103010275Ssam 103110275Ssam delete(name) 103210275Ssam char *name; 103310275Ssam { 103410275Ssam struct stat st; 103510275Ssam 103610275Ssam if (stat(name, &st) < 0) { 103736304Skarels perror_reply(550, name); 103810275Ssam return; 103910275Ssam } 104010275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 104110275Ssam if (rmdir(name) < 0) { 104236304Skarels perror_reply(550, name); 104310275Ssam return; 104410275Ssam } 104510275Ssam goto done; 104610275Ssam } 104710275Ssam if (unlink(name) < 0) { 104836304Skarels perror_reply(550, name); 104910275Ssam return; 105010275Ssam } 105110275Ssam done: 105210275Ssam ack("DELE"); 105310275Ssam } 105410275Ssam 105510275Ssam cwd(path) 105610275Ssam char *path; 105710275Ssam { 105836620Srick if (chdir(path) < 0) 105936304Skarels perror_reply(550, path); 106036620Srick else 106136620Srick ack("CWD"); 106210275Ssam } 106310275Ssam 106410303Ssam makedir(name) 106510275Ssam char *name; 106610275Ssam { 106736276Sbostic if (mkdir(name, 0777) < 0) 106836304Skarels perror_reply(550, name); 106936276Sbostic else 107036276Sbostic reply(257, "MKD command successful."); 107110275Ssam } 107210275Ssam 107310303Ssam removedir(name) 107410275Ssam char *name; 107510275Ssam { 107636620Srick if (rmdir(name) < 0) 107736304Skarels perror_reply(550, name); 107836620Srick else 107936620Srick ack("RMD"); 108010275Ssam } 108110275Ssam 108210303Ssam pwd() 108310275Ssam { 108410303Ssam char path[MAXPATHLEN + 1]; 108536304Skarels extern char *getwd(); 108610275Ssam 108736620Srick if (getwd(path) == (char *)NULL) 108827106Smckusick reply(550, "%s.", path); 108936620Srick else 109036620Srick reply(257, "\"%s\" is current directory.", path); 109110275Ssam } 109210275Ssam 109310275Ssam char * 109410275Ssam renamefrom(name) 109510275Ssam char *name; 109610275Ssam { 109710275Ssam struct stat st; 109810275Ssam 109910275Ssam if (stat(name, &st) < 0) { 110036304Skarels perror_reply(550, name); 110110275Ssam return ((char *)0); 110210275Ssam } 110310303Ssam reply(350, "File exists, ready for destination name"); 110410275Ssam return (name); 110510275Ssam } 110610275Ssam 110710275Ssam renamecmd(from, to) 110810275Ssam char *from, *to; 110910275Ssam { 111036620Srick if (rename(from, to) < 0) 111136304Skarels perror_reply(550, "rename"); 111236620Srick else 111336620Srick ack("RNTO"); 111410275Ssam } 111510275Ssam 111610275Ssam dolog(sin) 111710275Ssam struct sockaddr_in *sin; 111810275Ssam { 111936304Skarels struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 112010275Ssam sizeof (struct in_addr), AF_INET); 112136304Skarels time_t t, time(); 112226493Sminshall extern char *ctime(); 112310275Ssam 112436304Skarels if (hp) 112526493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 112636304Skarels else 112726493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 112813247Ssam sizeof (remotehost)); 112936620Srick #ifdef SETPROCTITLE 113036933Skarels sprintf(proctitle, "%s: connected", remotehost); 113136933Skarels setproctitle(proctitle); 113236620Srick #endif /* SETPROCTITLE */ 113336933Skarels 113436933Skarels if (logging) { 113536933Skarels t = time((time_t *) 0); 113636933Skarels syslog(LOG_INFO, "connection from %s at %s", 113736933Skarels remotehost, ctime(&t)); 113836933Skarels } 113910275Ssam } 114010695Ssam 114110695Ssam /* 114213247Ssam * Record logout in wtmp file 114313247Ssam * and exit with supplied status. 114413247Ssam */ 114513247Ssam dologout(status) 114613247Ssam int status; 114713247Ssam { 114817580Ssam if (logged_in) { 114936304Skarels (void) seteuid((uid_t)0); 115035672Sbostic logwtmp(ttyline, "", ""); 115113247Ssam } 115214436Ssam /* beware of flushing buffers after a SIGPIPE */ 115314436Ssam _exit(status); 115413247Ssam } 115513247Ssam 115626044Sminshall myoob() 115726044Sminshall { 115827750Sminshall char *cp; 115926044Sminshall 116027750Sminshall /* only process if transfer occurring */ 116136304Skarels if (!transflag) 116226044Sminshall return; 116327750Sminshall cp = tmpline; 116427750Sminshall if (getline(cp, 7, stdin) == NULL) { 116536304Skarels reply(221, "You could at least say goodbye."); 116627750Sminshall dologout(0); 116726044Sminshall } 116826044Sminshall upper(cp); 116936933Skarels if (strcmp(cp, "ABOR\r\n") == 0) { 117036933Skarels tmpline[0] = '\0'; 117136933Skarels reply(426, "Transfer aborted. Data connection closed."); 117236933Skarels reply(226, "Abort successful"); 117336933Skarels longjmp(urgcatch, 1); 117436933Skarels } 117536933Skarels if (strcmp(cp, "STAT\r\n") == 0) { 117636933Skarels if (file_size != (off_t) -1) 117736933Skarels reply(213, "Status: %lu of %lu bytes transferred", 117836933Skarels byte_count, file_size); 117936933Skarels else 118036933Skarels reply(213, "Status: %lu bytes transferred", byte_count); 118136933Skarels } 118226044Sminshall } 118326044Sminshall 118427106Smckusick /* 118536620Srick * Note: a response of 425 is not mentioned as a possible response to 118636620Srick * the PASV command in RFC959. However, it has been blessed as 118736620Srick * a legitimate response by Jon Postel in a telephone conversation 118836620Srick * with Rick Adams on 25 Jan 89. 118927106Smckusick */ 119026044Sminshall passive() 119126044Sminshall { 119226044Sminshall int len; 119326044Sminshall register char *p, *a; 119426044Sminshall 119526044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 119626044Sminshall if (pdata < 0) { 119736620Srick perror_reply(425, "Can't open passive connection"); 119826044Sminshall return; 119926044Sminshall } 120036933Skarels pasv_addr = ctrl_addr; 120136933Skarels pasv_addr.sin_port = 0; 120236304Skarels (void) seteuid((uid_t)0); 120336933Skarels if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { 120436304Skarels (void) seteuid((uid_t)pw->pw_uid); 120536620Srick goto pasv_error; 120626044Sminshall } 120736304Skarels (void) seteuid((uid_t)pw->pw_uid); 120836933Skarels len = sizeof(pasv_addr); 120936933Skarels if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 121036620Srick goto pasv_error; 121136620Srick if (listen(pdata, 1) < 0) 121236620Srick goto pasv_error; 121336933Skarels a = (char *) &pasv_addr.sin_addr; 121436933Skarels p = (char *) &pasv_addr.sin_port; 121526044Sminshall 121626044Sminshall #define UC(b) (((int) b) & 0xff) 121726044Sminshall 121826044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 121926044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 122036620Srick return; 122136620Srick 122236620Srick pasv_error: 122336620Srick (void) close(pdata); 122436620Srick pdata = -1; 122536620Srick perror_reply(425, "Can't open passive connection"); 122636620Srick return; 122726044Sminshall } 122826044Sminshall 122936304Skarels /* 123036304Skarels * Generate unique name for file with basename "local". 123136304Skarels * The file named "local" is already known to exist. 123236304Skarels * Generates failure reply on error. 123336304Skarels */ 123426044Sminshall char * 123526044Sminshall gunique(local) 123626044Sminshall char *local; 123726044Sminshall { 123826044Sminshall static char new[MAXPATHLEN]; 123936304Skarels struct stat st; 124026044Sminshall char *cp = rindex(local, '/'); 124136933Skarels int count = 0; 124226044Sminshall 124336304Skarels if (cp) 124426044Sminshall *cp = '\0'; 124536620Srick if (stat(cp ? local : ".", &st) < 0) { 124636933Skarels perror_reply(553, cp ? local : "."); 124726044Sminshall return((char *) 0); 124826044Sminshall } 124936620Srick if (cp) 125036620Srick *cp = '/'; 125126044Sminshall (void) strcpy(new, local); 125226044Sminshall cp = new + strlen(new); 125326044Sminshall *cp++ = '.'; 125436304Skarels for (count = 1; count < 100; count++) { 125536304Skarels (void) sprintf(cp, "%d", count); 125636304Skarels if (stat(new, &st) < 0) 125736304Skarels return(new); 125826044Sminshall } 125936304Skarels reply(452, "Unique file name cannot be created."); 126036304Skarels return((char *) 0); 126126044Sminshall } 126236304Skarels 126336304Skarels /* 126436304Skarels * Format and send reply containing system error number. 126536304Skarels */ 126636304Skarels perror_reply(code, string) 126736304Skarels int code; 126836304Skarels char *string; 126936304Skarels { 1270*42412Sbostic reply(code, "%s: %s.", string, strerror(errno)); 127136304Skarels } 127236620Srick 127336620Srick static char *onefile[] = { 127436620Srick "", 127536620Srick 0 127636620Srick }; 127736620Srick 127836620Srick send_file_list(whichfiles) 127936620Srick char *whichfiles; 128036620Srick { 128136620Srick struct stat st; 128236620Srick DIR *dirp = NULL; 128336620Srick struct direct *dir; 128436620Srick FILE *dout = NULL; 128536620Srick register char **dirlist, *dirname; 128637459Skarels int simple = 0; 128736620Srick char *strpbrk(); 128836620Srick 128936620Srick if (strpbrk(whichfiles, "~{[*?") != NULL) { 129036620Srick extern char **glob(), *globerr; 129136933Skarels 129236620Srick globerr = NULL; 129336620Srick dirlist = glob(whichfiles); 129436620Srick if (globerr != NULL) { 129536620Srick reply(550, globerr); 129636620Srick return; 129736620Srick } else if (dirlist == NULL) { 129836620Srick errno = ENOENT; 129936620Srick perror_reply(550, whichfiles); 130036620Srick return; 130136620Srick } 130236620Srick } else { 130336620Srick onefile[0] = whichfiles; 130436620Srick dirlist = onefile; 130537459Skarels simple = 1; 130636620Srick } 130736933Skarels 130836933Skarels if (setjmp(urgcatch)) { 130936933Skarels transflag = 0; 131036933Skarels return; 131136933Skarels } 131236620Srick while (dirname = *dirlist++) { 131336620Srick if (stat(dirname, &st) < 0) { 131436933Skarels /* 131536933Skarels * If user typed "ls -l", etc, and the client 131636933Skarels * used NLST, do what the user meant. 131736933Skarels */ 131836933Skarels if (dirname[0] == '-' && *dirlist == NULL && 131936933Skarels transflag == 0) { 132036933Skarels retrieve("/bin/ls %s", dirname); 132136933Skarels return; 132236933Skarels } 132336620Srick perror_reply(550, whichfiles); 132436620Srick if (dout != NULL) { 132536620Srick (void) fclose(dout); 132636933Skarels transflag = 0; 132736620Srick data = -1; 132836620Srick pdata = -1; 132936620Srick } 133036620Srick return; 133136620Srick } 133236933Skarels 133336620Srick if ((st.st_mode&S_IFMT) == S_IFREG) { 133436620Srick if (dout == NULL) { 133537459Skarels dout = dataconn("file list", (off_t)-1, "w"); 133636620Srick if (dout == NULL) 133736620Srick return; 133836933Skarels transflag++; 133936620Srick } 134038158Srick fprintf(dout, "%s%s\n", dirname, 134138158Srick type == TYPE_A ? "\r" : ""); 134236933Skarels byte_count += strlen(dirname) + 1; 134336620Srick continue; 134436933Skarels } else if ((st.st_mode&S_IFMT) != S_IFDIR) 134536620Srick continue; 134636620Srick 134736620Srick if ((dirp = opendir(dirname)) == NULL) 134836620Srick continue; 134936620Srick 135036620Srick while ((dir = readdir(dirp)) != NULL) { 135136933Skarels char nbuf[MAXPATHLEN]; 135236620Srick 135336620Srick if (dir->d_name[0] == '.' && dir->d_namlen == 1) 135436620Srick continue; 135536933Skarels if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 135636933Skarels dir->d_namlen == 2) 135736620Srick continue; 135836620Srick 135936933Skarels sprintf(nbuf, "%s/%s", dirname, dir->d_name); 136036933Skarels 136136620Srick /* 136236933Skarels * We have to do a stat to insure it's 136336933Skarels * not a directory or special file. 136436620Srick */ 136537459Skarels if (simple || (stat(nbuf, &st) == 0 && 136637459Skarels (st.st_mode&S_IFMT) == S_IFREG)) { 136736620Srick if (dout == NULL) { 136837459Skarels dout = dataconn("file list", (off_t)-1, 136936620Srick "w"); 137036620Srick if (dout == NULL) 137136620Srick return; 137236933Skarels transflag++; 137336620Srick } 137436620Srick if (nbuf[0] == '.' && nbuf[1] == '/') 137538158Srick fprintf(dout, "%s%s\n", &nbuf[2], 137638158Srick type == TYPE_A ? "\r" : ""); 137736620Srick else 137838158Srick fprintf(dout, "%s%s\n", nbuf, 137938158Srick type == TYPE_A ? "\r" : ""); 138036933Skarels byte_count += strlen(nbuf) + 1; 138136620Srick } 138236620Srick } 138336620Srick (void) closedir(dirp); 138436620Srick } 138536620Srick 138636933Skarels if (dout == NULL) 138736933Skarels reply(550, "No files found."); 138836933Skarels else if (ferror(dout) != 0) 138936933Skarels perror_reply(550, "Data connection"); 139036933Skarels else 139136620Srick reply(226, "Transfer complete."); 139236620Srick 139336933Skarels transflag = 0; 139436933Skarels if (dout != NULL) 139536620Srick (void) fclose(dout); 139636620Srick data = -1; 139736620Srick pdata = -1; 139836620Srick } 139936620Srick 140036620Srick #ifdef SETPROCTITLE 140136620Srick /* 140236620Srick * clobber argv so ps will show what we're doing. 140336620Srick * (stolen from sendmail) 140436620Srick * warning, since this is usually started from inetd.conf, it 140536620Srick * often doesn't have much of an environment or arglist to overwrite. 140636620Srick */ 140736620Srick 140836620Srick /*VARARGS1*/ 140936620Srick setproctitle(fmt, a, b, c) 141036620Srick char *fmt; 141136620Srick { 141236620Srick register char *p, *bp, ch; 141336620Srick register int i; 141436620Srick char buf[BUFSIZ]; 141536620Srick 141636620Srick (void) sprintf(buf, fmt, a, b, c); 141736620Srick 141836620Srick /* make ps print our process name */ 141936620Srick p = Argv[0]; 142036620Srick *p++ = '-'; 142136620Srick 142236620Srick i = strlen(buf); 142336620Srick if (i > LastArgv - p - 2) { 142436620Srick i = LastArgv - p - 2; 142536620Srick buf[i] = '\0'; 142636620Srick } 142736620Srick bp = buf; 142836620Srick while (ch = *bp++) 142936620Srick if (ch != '\n' && ch != '\r') 143036620Srick *p++ = ch; 143136620Srick while (p < LastArgv) 143236620Srick *p++ = ' '; 143336620Srick } 143436620Srick #endif /* SETPROCTITLE */ 1435