122499Sdist /* 244339Skarels * Copyright (c) 1985, 1988, 1990 Regents of the University of California. 333738Sbostic * All rights reserved. 433738Sbostic * 542666Sbostic * %sccs.include.redist.c% 622499Sdist */ 722499Sdist 810275Ssam #ifndef lint 922499Sdist char copyright[] = 1044339Skarels "@(#) Copyright (c) 1985, 1988, 1990 Regents of the University of California.\n\ 1122499Sdist All rights reserved.\n"; 1233738Sbostic #endif /* not lint */ 1310275Ssam 1422499Sdist #ifndef lint 15*45028Skarels static char sccsid[] = "@(#)ftpd.c 5.38 (Berkeley) 08/07/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> 3044339Skarels #include <netinet/in_systm.h> 3144339Skarels #include <netinet/ip.h> 3210275Ssam 3336933Skarels #define FTP_NAMES 3413034Ssam #include <arpa/ftp.h> 3513211Sroot #include <arpa/inet.h> 3626044Sminshall #include <arpa/telnet.h> 3713034Ssam 3836933Skarels #include <ctype.h> 3910275Ssam #include <stdio.h> 4010275Ssam #include <signal.h> 4110275Ssam #include <pwd.h> 4210275Ssam #include <setjmp.h> 4310275Ssam #include <netdb.h> 4410423Ssam #include <errno.h> 4542031Sbostic #include <string.h> 4626493Sminshall #include <syslog.h> 4736435Sbostic #include <varargs.h> 4837459Skarels #include "pathnames.h" 4910275Ssam 5010695Ssam /* 5110695Ssam * File containing login names 5210695Ssam * NOT to be used on this machine. 5310695Ssam * Commonly used to disallow uucp. 5410695Ssam */ 5510275Ssam extern int errno; 5610275Ssam extern char *crypt(); 5710275Ssam extern char version[]; 5810275Ssam extern char *home; /* pointer to home directory for glob */ 5936276Sbostic extern FILE *ftpd_popen(), *fopen(), *freopen(); 6036304Skarels extern int ftpd_pclose(), fclose(); 6126044Sminshall extern char *getline(); 6226044Sminshall extern char cbuf[]; 6337459Skarels extern off_t restart_point; 6410275Ssam 6510275Ssam struct sockaddr_in ctrl_addr; 6610275Ssam struct sockaddr_in data_source; 6710275Ssam struct sockaddr_in data_dest; 6810275Ssam struct sockaddr_in his_addr; 6936933Skarels struct sockaddr_in pasv_addr; 7010275Ssam 7110275Ssam int data; 7226044Sminshall jmp_buf errcatch, urgcatch; 7310275Ssam int logged_in; 7410275Ssam struct passwd *pw; 7510275Ssam int debug; 7626493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */ 7736933Skarels int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 7811757Ssam int logging; 7910275Ssam int guest; 8010275Ssam int type; 8110275Ssam int form; 8210275Ssam int stru; /* avoid C keyword */ 8310275Ssam int mode; 8410321Ssam int usedefault = 1; /* for data transfers */ 8536304Skarels int pdata = -1; /* for passive mode */ 8626044Sminshall int transflag; 8736933Skarels off_t file_size; 8836933Skarels off_t byte_count; 8936933Skarels #if !defined(CMASK) || CMASK == 0 9036933Skarels #undef CMASK 9136933Skarels #define CMASK 027 9236933Skarels #endif 9336933Skarels int defumask = CMASK; /* default umask value */ 9426044Sminshall char tmpline[7]; 9536276Sbostic char hostname[MAXHOSTNAMELEN]; 9636276Sbostic char remotehost[MAXHOSTNAMELEN]; 9710275Ssam 9811653Ssam /* 9911653Ssam * Timeout intervals for retrying connections 10011653Ssam * to hosts that don't accept PORT cmds. This 10111653Ssam * is a kludge, but given the problems with TCP... 10211653Ssam */ 10311653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 10411653Ssam #define SWAITINT 5 /* interval between retries */ 10511653Ssam 10611653Ssam int swaitmax = SWAITMAX; 10711653Ssam int swaitint = SWAITINT; 10811653Ssam 10910275Ssam int lostconn(); 11026044Sminshall int myoob(); 11110275Ssam FILE *getdatasock(), *dataconn(); 11210275Ssam 11336620Srick #ifdef SETPROCTITLE 11436620Srick char **Argv = NULL; /* pointer to argument vector */ 11536620Srick char *LastArgv = NULL; /* end of argv */ 11636933Skarels char proctitle[BUFSIZ]; /* initial part of title */ 11736620Srick #endif /* SETPROCTITLE */ 11836620Srick 11936620Srick main(argc, argv, envp) 12010275Ssam int argc; 12110275Ssam char *argv[]; 12236620Srick char **envp; 12310275Ssam { 12444339Skarels int addrlen, on = 1, tos; 12510275Ssam char *cp; 12610275Ssam 127*45028Skarels /* 128*45028Skarels * LOG_NDELAY sets up the logging connection immediately, 129*45028Skarels * necessary for anonymous ftp's that chroot and can't do it later. 130*45028Skarels */ 131*45028Skarels openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON); 13216339Skarels addrlen = sizeof (his_addr); 13336304Skarels if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 13426493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 13510275Ssam exit(1); 13610275Ssam } 13716339Skarels addrlen = sizeof (ctrl_addr); 13836304Skarels if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 13926493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 14016339Skarels exit(1); 14116339Skarels } 14244339Skarels #ifdef IP_TOS 14344339Skarels tos = IPTOS_LOWDELAY; 14444339Skarels if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) 14544339Skarels syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 14644339Skarels #endif 14716339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 14810275Ssam debug = 0; 14936620Srick #ifdef SETPROCTITLE 15036620Srick /* 15136620Srick * Save start and extent of argv for setproctitle. 15236620Srick */ 15336620Srick Argv = argv; 15436620Srick while (*envp) 15536620Srick envp++; 15636620Srick LastArgv = envp[-1] + strlen(envp[-1]); 15736620Srick #endif /* SETPROCTITLE */ 15836620Srick 15910275Ssam argc--, argv++; 16010275Ssam while (argc > 0 && *argv[0] == '-') { 16110275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 16210275Ssam 16311653Ssam case 'v': 16411653Ssam debug = 1; 16511653Ssam break; 16611653Ssam 16710275Ssam case 'd': 16810275Ssam debug = 1; 16910275Ssam break; 17010275Ssam 17111757Ssam case 'l': 17211757Ssam logging = 1; 17311757Ssam break; 17411757Ssam 17511653Ssam case 't': 17611653Ssam timeout = atoi(++cp); 17736933Skarels if (maxtimeout < timeout) 17836933Skarels maxtimeout = timeout; 17911653Ssam goto nextopt; 18011653Ssam 18136933Skarels case 'T': 18236933Skarels maxtimeout = atoi(++cp); 18336933Skarels if (timeout > maxtimeout) 18436933Skarels timeout = maxtimeout; 18536933Skarels goto nextopt; 18636933Skarels 18736933Skarels case 'u': 18836933Skarels { 18936933Skarels int val = 0; 19036933Skarels 19136933Skarels while (*++cp && *cp >= '0' && *cp <= '9') 19236933Skarels val = val*8 + *cp - '0'; 19336933Skarels if (*cp) 19436933Skarels fprintf(stderr, "ftpd: Bad value for -u\n"); 19536933Skarels else 19636933Skarels defumask = val; 19736933Skarels goto nextopt; 19836933Skarels } 19936933Skarels 20010275Ssam default: 20116339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 20216339Skarels *cp); 20310275Ssam break; 20410275Ssam } 20511653Ssam nextopt: 20610275Ssam argc--, argv++; 20710275Ssam } 20837459Skarels (void) freopen(_PATH_DEVNULL, "w", stderr); 20926493Sminshall (void) signal(SIGPIPE, lostconn); 21026493Sminshall (void) signal(SIGCHLD, SIG_IGN); 21135691Sbostic if ((int)signal(SIGURG, myoob) < 0) 21226493Sminshall syslog(LOG_ERR, "signal: %m"); 21335691Sbostic 21438134Srick /* Try to handle urgent data inline */ 21527750Sminshall #ifdef SO_OOBINLINE 21636276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 21727750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 21836276Sbostic #endif 21938134Srick 22036933Skarels #ifdef F_SETOWN 22136304Skarels if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 22236304Skarels syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 22336933Skarels #endif 22416760Slepreau dolog(&his_addr); 22516339Skarels /* 22616339Skarels * Set up default state 22716339Skarels */ 22816339Skarels data = -1; 22916339Skarels type = TYPE_A; 23016339Skarels form = FORM_N; 23116339Skarels stru = STRU_F; 23216339Skarels mode = MODE_S; 23326044Sminshall tmpline[0] = '\0'; 23426493Sminshall (void) gethostname(hostname, sizeof (hostname)); 23536276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version); 23636304Skarels (void) setjmp(errcatch); 23736304Skarels for (;;) 23826493Sminshall (void) yyparse(); 23936620Srick /* NOTREACHED */ 24010275Ssam } 24110419Ssam 24210275Ssam lostconn() 24310275Ssam { 24410275Ssam 24514089Ssam if (debug) 24626493Sminshall syslog(LOG_DEBUG, "lost connection"); 24714089Ssam dologout(-1); 24810275Ssam } 24910275Ssam 25035672Sbostic static char ttyline[20]; 25135672Sbostic 25236185Sbostic /* 25336185Sbostic * Helper function for sgetpwnam(). 25436185Sbostic */ 25536185Sbostic char * 25636185Sbostic sgetsave(s) 25736185Sbostic char *s; 25836185Sbostic { 25936185Sbostic char *malloc(); 26036185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 26136933Skarels 26236185Sbostic if (new == NULL) { 26336620Srick perror_reply(421, "Local resource failure: malloc"); 26436185Sbostic dologout(1); 26536620Srick /* NOTREACHED */ 26636185Sbostic } 26736185Sbostic (void) strcpy(new, s); 26836185Sbostic return (new); 26936185Sbostic } 27036185Sbostic 27136185Sbostic /* 27236185Sbostic * Save the result of a getpwnam. Used for USER command, since 27336185Sbostic * the data returned must not be clobbered by any other command 27436185Sbostic * (e.g., globbing). 27536185Sbostic */ 27636185Sbostic struct passwd * 27736185Sbostic sgetpwnam(name) 27836185Sbostic char *name; 27936185Sbostic { 28036185Sbostic static struct passwd save; 28136185Sbostic register struct passwd *p; 28236185Sbostic char *sgetsave(); 28336185Sbostic 28436185Sbostic if ((p = getpwnam(name)) == NULL) 28536185Sbostic return (p); 28636185Sbostic if (save.pw_name) { 28736185Sbostic free(save.pw_name); 28836185Sbostic free(save.pw_passwd); 28936185Sbostic free(save.pw_gecos); 29036185Sbostic free(save.pw_dir); 29136185Sbostic free(save.pw_shell); 29236185Sbostic } 29336185Sbostic save = *p; 29436185Sbostic save.pw_name = sgetsave(p->pw_name); 29536185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 29636185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 29736185Sbostic save.pw_dir = sgetsave(p->pw_dir); 29836185Sbostic save.pw_shell = sgetsave(p->pw_shell); 29936185Sbostic return (&save); 30036185Sbostic } 30136185Sbostic 30236304Skarels int login_attempts; /* number of failed login attempts */ 30336304Skarels int askpasswd; /* had user command, ask for passwd */ 30436304Skarels 30536304Skarels /* 30636304Skarels * USER command. 30742327Sbostic * Sets global passwd pointer pw if named account exists and is acceptable; 30842327Sbostic * sets askpasswd if a PASS command is expected. If logged in previously, 30942327Sbostic * need to reset state. If name is "ftp" or "anonymous", the name is not in 31042327Sbostic * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 31142327Sbostic * If account doesn't exist, ask for passwd anyway. Otherwise, check user 31242327Sbostic * requesting login privileges. Disallow anyone who does not have a standard 31342327Sbostic * shell as returned by getusershell(). Disallow anyone mentioned in the file 31442327Sbostic * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 31536304Skarels */ 31636304Skarels user(name) 31736304Skarels char *name; 31836304Skarels { 31936304Skarels register char *cp; 32036304Skarels char *shell; 32140183Smckusick char *getusershell(); 32236304Skarels 32336304Skarels if (logged_in) { 32436304Skarels if (guest) { 32536304Skarels reply(530, "Can't change user from guest login."); 32636304Skarels return; 32736304Skarels } 32836304Skarels end_login(); 32936304Skarels } 33036304Skarels 33136304Skarels guest = 0; 33236304Skarels if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 33340183Smckusick if (checkuser("ftp") || checkuser("anonymous")) 33440155Smckusick reply(530, "User %s access denied.", name); 33540155Smckusick else if ((pw = sgetpwnam("ftp")) != NULL) { 33636304Skarels guest = 1; 33736304Skarels askpasswd = 1; 33836304Skarels reply(331, "Guest login ok, send ident as password."); 33936933Skarels } else 34036304Skarels reply(530, "User %s unknown.", name); 34136304Skarels return; 34236304Skarels } 34336304Skarels if (pw = sgetpwnam(name)) { 34436304Skarels if ((shell = pw->pw_shell) == NULL || *shell == 0) 34537459Skarels shell = _PATH_BSHELL; 34636304Skarels while ((cp = getusershell()) != NULL) 34736304Skarels if (strcmp(cp, shell) == 0) 34836304Skarels break; 34936304Skarels endusershell(); 35040183Smckusick if (cp == NULL || checkuser(name)) { 35136304Skarels reply(530, "User %s access denied.", name); 35236933Skarels if (logging) 35336933Skarels syslog(LOG_NOTICE, 35436933Skarels "FTP LOGIN REFUSED FROM %s, %s", 35536933Skarels remotehost, name); 35636304Skarels pw = (struct passwd *) NULL; 35736304Skarels return; 35836304Skarels } 35936304Skarels } 36036304Skarels reply(331, "Password required for %s.", name); 36136304Skarels askpasswd = 1; 36236304Skarels /* 36336304Skarels * Delay before reading passwd after first failed 36436304Skarels * attempt to slow down passwd-guessing programs. 36536304Skarels */ 36636304Skarels if (login_attempts) 36736304Skarels sleep((unsigned) login_attempts); 36836304Skarels } 36936304Skarels 37036304Skarels /* 37140183Smckusick * Check if a user is in the file _PATH_FTPUSERS 37240183Smckusick */ 37340183Smckusick checkuser(name) 37440183Smckusick char *name; 37540183Smckusick { 37642327Sbostic register FILE *fd; 37742327Sbostic register char *p; 37842327Sbostic char line[BUFSIZ]; 37940183Smckusick 38040183Smckusick if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { 38142327Sbostic while (fgets(line, sizeof(line), fd) != NULL) 38242327Sbostic if ((p = index(line, '\n')) != NULL) { 38342327Sbostic *p = '\0'; 38442327Sbostic if (line[0] == '#') 38542327Sbostic continue; 38642327Sbostic if (strcmp(line, name) == 0) 38742327Sbostic return (1); 38842327Sbostic } 38940183Smckusick (void) fclose(fd); 39040183Smckusick } 39140183Smckusick return (0); 39240183Smckusick } 39340183Smckusick 39440183Smckusick /* 39536304Skarels * Terminate login as previous user, if any, resetting state; 39636304Skarels * used when USER command is given or login fails. 39736304Skarels */ 39836304Skarels end_login() 39936304Skarels { 40036304Skarels 40136304Skarels (void) seteuid((uid_t)0); 40236304Skarels if (logged_in) 40336304Skarels logwtmp(ttyline, "", ""); 40436304Skarels pw = NULL; 40536304Skarels logged_in = 0; 40636304Skarels guest = 0; 40736304Skarels } 40836304Skarels 40910275Ssam pass(passwd) 41010275Ssam char *passwd; 41110275Ssam { 41236304Skarels char *xpasswd, *salt; 41310275Ssam 41436304Skarels if (logged_in || askpasswd == 0) { 41510275Ssam reply(503, "Login with USER first."); 41610275Ssam return; 41710275Ssam } 41836304Skarels askpasswd = 0; 41910275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 42036304Skarels if (pw == NULL) 42136304Skarels salt = "xx"; 42236304Skarels else 42336304Skarels salt = pw->pw_passwd; 42436304Skarels xpasswd = crypt(passwd, salt); 42516760Slepreau /* The strcmp does not catch null passwords! */ 42636304Skarels if (pw == NULL || *pw->pw_passwd == '\0' || 42736304Skarels strcmp(xpasswd, pw->pw_passwd)) { 42810275Ssam reply(530, "Login incorrect."); 42910275Ssam pw = NULL; 43036304Skarels if (login_attempts++ >= 5) { 43136933Skarels syslog(LOG_NOTICE, 43236304Skarels "repeated login failures from %s", 43336304Skarels remotehost); 43436304Skarels exit(0); 43536304Skarels } 43610275Ssam return; 43710275Ssam } 43810275Ssam } 43936304Skarels login_attempts = 0; /* this time successful */ 44036304Skarels (void) setegid((gid_t)pw->pw_gid); 44136304Skarels (void) initgroups(pw->pw_name, pw->pw_gid); 44216033Sralph 44336192Sbostic /* open wtmp before chroot */ 44436192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 44536192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 44636192Sbostic logged_in = 1; 44736192Sbostic 44836446Sbostic if (guest) { 44936620Srick /* 45036933Skarels * We MUST do a chdir() after the chroot. Otherwise 45136933Skarels * the old current directory will be accessible as "." 45236933Skarels * outside the new root! 45336620Srick */ 45436620Srick if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 45536446Sbostic reply(550, "Can't set guest privileges."); 45636446Sbostic goto bad; 45736446Sbostic } 45836933Skarels } else if (chdir(pw->pw_dir) < 0) { 45936620Srick if (chdir("/") < 0) { 46036446Sbostic reply(530, "User %s: can't change directory to %s.", 46136446Sbostic pw->pw_name, pw->pw_dir); 46236446Sbostic goto bad; 46336933Skarels } else 46436446Sbostic lreply(230, "No directory! Logging in with home=/"); 46536620Srick } 46636304Skarels if (seteuid((uid_t)pw->pw_uid) < 0) { 46736304Skarels reply(550, "Can't set uid."); 46836304Skarels goto bad; 46936304Skarels } 47036550Sbostic if (guest) { 47136192Sbostic reply(230, "Guest login ok, access restrictions apply."); 47236933Skarels #ifdef SETPROCTITLE 47336933Skarels sprintf(proctitle, "%s: anonymous/%.*s", remotehost, 47436933Skarels sizeof(proctitle) - sizeof(remotehost) - 47536933Skarels sizeof(": anonymous/"), passwd); 47636933Skarels setproctitle(proctitle); 47736933Skarels #endif /* SETPROCTITLE */ 47836933Skarels if (logging) 47936933Skarels syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 48036933Skarels remotehost, passwd); 48136933Skarels } else { 48210275Ssam reply(230, "User %s logged in.", pw->pw_name); 48336933Skarels #ifdef SETPROCTITLE 48436933Skarels sprintf(proctitle, "%s: %s", remotehost, pw->pw_name); 48536933Skarels setproctitle(proctitle); 48636933Skarels #endif /* SETPROCTITLE */ 48736933Skarels if (logging) 48836933Skarels syslog(LOG_INFO, "FTP LOGIN FROM %s, %s", 48936933Skarels remotehost, pw->pw_name); 49036550Sbostic } 49110303Ssam home = pw->pw_dir; /* home dir for globbing */ 49236933Skarels (void) umask(defumask); 49310303Ssam return; 49410303Ssam bad: 49536304Skarels /* Forget all about it... */ 49636304Skarels end_login(); 49710275Ssam } 49810275Ssam 49910275Ssam retrieve(cmd, name) 50010275Ssam char *cmd, *name; 50110275Ssam { 50210275Ssam FILE *fin, *dout; 50310275Ssam struct stat st; 50436620Srick int (*closefunc)(); 50510275Ssam 50636557Sbostic if (cmd == 0) { 50736446Sbostic fin = fopen(name, "r"), closefunc = fclose; 50836557Sbostic st.st_size = 0; 50936557Sbostic } else { 51010275Ssam char line[BUFSIZ]; 51110275Ssam 51226493Sminshall (void) sprintf(line, cmd, name), name = line; 51336304Skarels fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 51436557Sbostic st.st_size = -1; 51536933Skarels st.st_blksize = BUFSIZ; 51610275Ssam } 51710275Ssam if (fin == NULL) { 51813152Ssam if (errno != 0) 51936304Skarels perror_reply(550, name); 52010275Ssam return; 52110275Ssam } 52210275Ssam if (cmd == 0 && 52336933Skarels (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 52410275Ssam reply(550, "%s: not a plain file.", name); 52510275Ssam goto done; 52610275Ssam } 52737459Skarels if (restart_point) { 52837459Skarels if (type == TYPE_A) { 52937459Skarels register int i, n, c; 53037459Skarels 53137459Skarels n = restart_point; 53237459Skarels i = 0; 53337459Skarels while (i++ < n) { 53437459Skarels if ((c=getc(fin)) == EOF) { 53537459Skarels perror_reply(550, name); 53637459Skarels goto done; 53737459Skarels } 53837459Skarels if (c == '\n') 53937459Skarels i++; 54037459Skarels } 54137459Skarels } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 54237459Skarels perror_reply(550, name); 54337459Skarels goto done; 54437459Skarels } 54537459Skarels } 54610275Ssam dout = dataconn(name, st.st_size, "w"); 54710275Ssam if (dout == NULL) 54810275Ssam goto done; 54936620Srick send_data(fin, dout, st.st_blksize); 55026493Sminshall (void) fclose(dout); 55126044Sminshall data = -1; 55226044Sminshall pdata = -1; 55310275Ssam done: 55410275Ssam (*closefunc)(fin); 55510275Ssam } 55610275Ssam 55736304Skarels store(name, mode, unique) 55810275Ssam char *name, *mode; 55936304Skarels int unique; 56010275Ssam { 56110275Ssam FILE *fout, *din; 56236446Sbostic struct stat st; 56336620Srick int (*closefunc)(); 56436304Skarels char *gunique(); 56510275Ssam 56636446Sbostic if (unique && stat(name, &st) == 0 && 56736446Sbostic (name = gunique(name)) == NULL) 56836446Sbostic return; 56910303Ssam 57037459Skarels if (restart_point) 57137459Skarels mode = "r+w"; 57236620Srick fout = fopen(name, mode); 57336620Srick closefunc = fclose; 57410275Ssam if (fout == NULL) { 57536304Skarels perror_reply(553, name); 57610275Ssam return; 57710275Ssam } 57837459Skarels if (restart_point) { 57937459Skarels if (type == TYPE_A) { 58037459Skarels register int i, n, c; 58137459Skarels 58237459Skarels n = restart_point; 58337459Skarels i = 0; 58437459Skarels while (i++ < n) { 58537459Skarels if ((c=getc(fout)) == EOF) { 58637459Skarels perror_reply(550, name); 58737459Skarels goto done; 58837459Skarels } 58937459Skarels if (c == '\n') 59037459Skarels i++; 59137459Skarels } 59237459Skarels /* 59337459Skarels * We must do this seek to "current" position 59437459Skarels * because we are changing from reading to 59537459Skarels * writing. 59637459Skarels */ 59737459Skarels if (fseek(fout, 0L, L_INCR) < 0) { 59837459Skarels perror_reply(550, name); 59937459Skarels goto done; 60037459Skarels } 60137459Skarels } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 60237459Skarels perror_reply(550, name); 60337459Skarels goto done; 60437459Skarels } 60537459Skarels } 60636304Skarels din = dataconn(name, (off_t)-1, "r"); 60710275Ssam if (din == NULL) 60810275Ssam goto done; 60936620Srick if (receive_data(din, fout) == 0) { 61036620Srick if (unique) 61136304Skarels reply(226, "Transfer complete (unique file name:%s).", 61236933Skarels name); 61336304Skarels else 61436304Skarels reply(226, "Transfer complete."); 61526044Sminshall } 61626493Sminshall (void) fclose(din); 61726044Sminshall data = -1; 61826044Sminshall pdata = -1; 61910275Ssam done: 62010275Ssam (*closefunc)(fout); 62110275Ssam } 62210275Ssam 62310275Ssam FILE * 62410275Ssam getdatasock(mode) 62510275Ssam char *mode; 62610275Ssam { 62737459Skarels int s, on = 1, tries; 62810275Ssam 62910275Ssam if (data >= 0) 63010275Ssam return (fdopen(data, mode)); 63113247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 63210602Ssam if (s < 0) 63310275Ssam return (NULL); 63436304Skarels (void) seteuid((uid_t)0); 63537459Skarels if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 63637459Skarels (char *) &on, sizeof (on)) < 0) 63710602Ssam goto bad; 63813152Ssam /* anchor socket to avoid multi-homing problems */ 63913152Ssam data_source.sin_family = AF_INET; 64013152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 64137459Skarels for (tries = 1; ; tries++) { 64237459Skarels if (bind(s, (struct sockaddr *)&data_source, 64337459Skarels sizeof (data_source)) >= 0) 64437459Skarels break; 64537459Skarels if (errno != EADDRINUSE || tries > 10) 64637459Skarels goto bad; 64737459Skarels sleep(tries); 64837459Skarels } 64936304Skarels (void) seteuid((uid_t)pw->pw_uid); 65044339Skarels #ifdef IP_TOS 65144339Skarels on = IPTOS_THROUGHPUT; 65244339Skarels if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) 65344339Skarels syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 65444339Skarels #endif 65510275Ssam return (fdopen(s, mode)); 65610602Ssam bad: 65736304Skarels (void) seteuid((uid_t)pw->pw_uid); 65826493Sminshall (void) close(s); 65910602Ssam return (NULL); 66010275Ssam } 66110275Ssam 66210275Ssam FILE * 66310275Ssam dataconn(name, size, mode) 66410275Ssam char *name; 66511653Ssam off_t size; 66610275Ssam char *mode; 66710275Ssam { 66810275Ssam char sizebuf[32]; 66910275Ssam FILE *file; 67044339Skarels int retry = 0, tos; 67110275Ssam 67236933Skarels file_size = size; 67336933Skarels byte_count = 0; 67436304Skarels if (size != (off_t) -1) 67526493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 67610275Ssam else 67710275Ssam (void) strcpy(sizebuf, ""); 67836304Skarels if (pdata >= 0) { 67926044Sminshall struct sockaddr_in from; 68026044Sminshall int s, fromlen = sizeof(from); 68126044Sminshall 68236304Skarels s = accept(pdata, (struct sockaddr *)&from, &fromlen); 68326044Sminshall if (s < 0) { 68426044Sminshall reply(425, "Can't open data connection."); 68526044Sminshall (void) close(pdata); 68626044Sminshall pdata = -1; 68726044Sminshall return(NULL); 68826044Sminshall } 68926044Sminshall (void) close(pdata); 69026044Sminshall pdata = s; 69144339Skarels #ifdef IP_TOS 69244339Skarels tos = IPTOS_LOWDELAY; 69344339Skarels (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, 69444339Skarels sizeof(int)); 69544339Skarels #endif 69636235Skarels reply(150, "Opening %s mode data connection for %s%s.", 69736235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 69826044Sminshall return(fdopen(pdata, mode)); 69926044Sminshall } 70010275Ssam if (data >= 0) { 70110275Ssam reply(125, "Using existing data connection for %s%s.", 70210275Ssam name, sizebuf); 70310321Ssam usedefault = 1; 70410275Ssam return (fdopen(data, mode)); 70510275Ssam } 70610566Ssam if (usedefault) 70710422Ssam data_dest = his_addr; 70810422Ssam usedefault = 1; 70910275Ssam file = getdatasock(mode); 71010275Ssam if (file == NULL) { 71110275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 71213247Ssam inet_ntoa(data_source.sin_addr), 71342412Sbostic ntohs(data_source.sin_port), strerror(errno)); 71410275Ssam return (NULL); 71510275Ssam } 71610275Ssam data = fileno(file); 71736304Skarels while (connect(data, (struct sockaddr *)&data_dest, 71836304Skarels sizeof (data_dest)) < 0) { 71911653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 72026493Sminshall sleep((unsigned) swaitint); 72111653Ssam retry += swaitint; 72211653Ssam continue; 72311653Ssam } 72436304Skarels perror_reply(425, "Can't build data connection"); 72510275Ssam (void) fclose(file); 72610275Ssam data = -1; 72710275Ssam return (NULL); 72810275Ssam } 72936235Skarels reply(150, "Opening %s mode data connection for %s%s.", 73036235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 73110275Ssam return (file); 73210275Ssam } 73310275Ssam 73410275Ssam /* 73510275Ssam * Tranfer the contents of "instr" to 73610275Ssam * "outstr" peer using the appropriate 73736933Skarels * encapsulation of the data subject 73810275Ssam * to Mode, Structure, and Type. 73910275Ssam * 74010275Ssam * NB: Form isn't handled. 74110275Ssam */ 74236446Sbostic send_data(instr, outstr, blksize) 74310275Ssam FILE *instr, *outstr; 74436446Sbostic off_t blksize; 74510275Ssam { 74636446Sbostic register int c, cnt; 74736446Sbostic register char *buf; 74836446Sbostic int netfd, filefd; 74910275Ssam 75026044Sminshall transflag++; 75126044Sminshall if (setjmp(urgcatch)) { 75226044Sminshall transflag = 0; 75336620Srick return; 75426044Sminshall } 75510275Ssam switch (type) { 75610275Ssam 75710275Ssam case TYPE_A: 75810275Ssam while ((c = getc(instr)) != EOF) { 75936933Skarels byte_count++; 76011220Ssam if (c == '\n') { 76136933Skarels if (ferror(outstr)) 76236620Srick goto data_err; 76327750Sminshall (void) putc('\r', outstr); 76411220Ssam } 76527750Sminshall (void) putc(c, outstr); 76610275Ssam } 76736933Skarels fflush(outstr); 76826044Sminshall transflag = 0; 76936933Skarels if (ferror(instr)) 77036933Skarels goto file_err; 77136933Skarels if (ferror(outstr)) 77236620Srick goto data_err; 77336620Srick reply(226, "Transfer complete."); 77436620Srick return; 77536446Sbostic 77610275Ssam case TYPE_I: 77710275Ssam case TYPE_L: 77836446Sbostic if ((buf = malloc((u_int)blksize)) == NULL) { 77936446Sbostic transflag = 0; 78036933Skarels perror_reply(451, "Local resource failure: malloc"); 78136933Skarels return; 78236446Sbostic } 78310275Ssam netfd = fileno(outstr); 78410275Ssam filefd = fileno(instr); 78536933Skarels while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 78636620Srick write(netfd, buf, cnt) == cnt) 78736933Skarels byte_count += cnt; 78826044Sminshall transflag = 0; 78936446Sbostic (void)free(buf); 79036933Skarels if (cnt != 0) { 79136933Skarels if (cnt < 0) 79236933Skarels goto file_err; 79336620Srick goto data_err; 79436933Skarels } 79536620Srick reply(226, "Transfer complete."); 79636620Srick return; 79736620Srick default: 79836620Srick transflag = 0; 79936620Srick reply(550, "Unimplemented TYPE %d in send_data", type); 80036620Srick return; 80110275Ssam } 80236620Srick 80336620Srick data_err: 80426044Sminshall transflag = 0; 80536933Skarels perror_reply(426, "Data connection"); 80636933Skarels return; 80736933Skarels 80836933Skarels file_err: 80936933Skarels transflag = 0; 81036933Skarels perror_reply(551, "Error on input file"); 81110275Ssam } 81210275Ssam 81310275Ssam /* 81410275Ssam * Transfer data from peer to 81510275Ssam * "outstr" using the appropriate 81610275Ssam * encapulation of the data subject 81710275Ssam * to Mode, Structure, and Type. 81810275Ssam * 81910275Ssam * N.B.: Form isn't handled. 82010275Ssam */ 82110275Ssam receive_data(instr, outstr) 82210275Ssam FILE *instr, *outstr; 82310275Ssam { 82410275Ssam register int c; 82538134Srick int cnt, bare_lfs = 0; 82610275Ssam char buf[BUFSIZ]; 82710275Ssam 82826044Sminshall transflag++; 82926044Sminshall if (setjmp(urgcatch)) { 83026044Sminshall transflag = 0; 83136933Skarels return (-1); 83226044Sminshall } 83310275Ssam switch (type) { 83410275Ssam 83510275Ssam case TYPE_I: 83610275Ssam case TYPE_L: 83726044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 83836620Srick if (write(fileno(outstr), buf, cnt) != cnt) 83936933Skarels goto file_err; 84036933Skarels byte_count += cnt; 84126044Sminshall } 84236933Skarels if (cnt < 0) 84336620Srick goto data_err; 84426044Sminshall transflag = 0; 84536933Skarels return (0); 84610275Ssam 84710275Ssam case TYPE_E: 84827106Smckusick reply(553, "TYPE E not implemented."); 84926044Sminshall transflag = 0; 85027106Smckusick return (-1); 85110275Ssam 85210275Ssam case TYPE_A: 85310275Ssam while ((c = getc(instr)) != EOF) { 85436933Skarels byte_count++; 85538134Srick if (c == '\n') 85638134Srick bare_lfs++; 85727750Sminshall while (c == '\r') { 85836933Skarels if (ferror(outstr)) 85936620Srick goto data_err; 86036933Skarels if ((c = getc(instr)) != '\n') { 86127750Sminshall (void) putc ('\r', outstr); 86236933Skarels if (c == '\0' || c == EOF) 86336933Skarels goto contin2; 86436933Skarels } 86510275Ssam } 86636933Skarels (void) putc(c, outstr); 86736933Skarels contin2: ; 86810275Ssam } 86936620Srick fflush(outstr); 87036933Skarels if (ferror(instr)) 87136620Srick goto data_err; 87236933Skarels if (ferror(outstr)) 87336933Skarels goto file_err; 87426044Sminshall transflag = 0; 87538134Srick if (bare_lfs) { 87638134Srick lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs); 87738134Srick printf(" File may not have transferred correctly.\r\n"); 87838134Srick } 87910275Ssam return (0); 88036620Srick default: 88136620Srick reply(550, "Unimplemented TYPE %d in receive_data", type); 88236620Srick transflag = 0; 88336933Skarels return (-1); 88410275Ssam } 88536620Srick 88636620Srick data_err: 88726044Sminshall transflag = 0; 88836933Skarels perror_reply(426, "Data Connection"); 88936933Skarels return (-1); 89036933Skarels 89136933Skarels file_err: 89236933Skarels transflag = 0; 89336933Skarels perror_reply(452, "Error writing file"); 89436933Skarels return (-1); 89510275Ssam } 89610275Ssam 89736933Skarels statfilecmd(filename) 89836933Skarels char *filename; 89936933Skarels { 90036933Skarels char line[BUFSIZ]; 90136933Skarels FILE *fin; 90236933Skarels int c; 90336933Skarels 90436933Skarels (void) sprintf(line, "/bin/ls -lgA %s", filename); 90536933Skarels fin = ftpd_popen(line, "r"); 90636933Skarels lreply(211, "status of %s:", filename); 90736933Skarels while ((c = getc(fin)) != EOF) { 90836933Skarels if (c == '\n') { 90936933Skarels if (ferror(stdout)){ 91036933Skarels perror_reply(421, "control connection"); 91136933Skarels (void) ftpd_pclose(fin); 91236933Skarels dologout(1); 91336933Skarels /* NOTREACHED */ 91436933Skarels } 91536933Skarels if (ferror(fin)) { 91636933Skarels perror_reply(551, filename); 91736933Skarels (void) ftpd_pclose(fin); 91836933Skarels return; 91936933Skarels } 92036933Skarels (void) putc('\r', stdout); 92136933Skarels } 92236933Skarels (void) putc(c, stdout); 92336933Skarels } 92436933Skarels (void) ftpd_pclose(fin); 92536933Skarels reply(211, "End of Status"); 92636933Skarels } 92736933Skarels 92836933Skarels statcmd() 92936933Skarels { 93036933Skarels struct sockaddr_in *sin; 93136933Skarels u_char *a, *p; 93236933Skarels 93336933Skarels lreply(211, "%s FTP server status:", hostname, version); 93436933Skarels printf(" %s\r\n", version); 93536933Skarels printf(" Connected to %s", remotehost); 93638134Srick if (!isdigit(remotehost[0])) 93736933Skarels printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 93836933Skarels printf("\r\n"); 93936933Skarels if (logged_in) { 94036933Skarels if (guest) 94136933Skarels printf(" Logged in anonymously\r\n"); 94236933Skarels else 94336933Skarels printf(" Logged in as %s\r\n", pw->pw_name); 94436933Skarels } else if (askpasswd) 94536933Skarels printf(" Waiting for password\r\n"); 94636933Skarels else 94736933Skarels printf(" Waiting for user name\r\n"); 94836933Skarels printf(" TYPE: %s", typenames[type]); 94936933Skarels if (type == TYPE_A || type == TYPE_E) 95036933Skarels printf(", FORM: %s", formnames[form]); 95136933Skarels if (type == TYPE_L) 95236933Skarels #if NBBY == 8 95336933Skarels printf(" %d", NBBY); 95436933Skarels #else 95536933Skarels printf(" %d", bytesize); /* need definition! */ 95636933Skarels #endif 95736933Skarels printf("; STRUcture: %s; transfer MODE: %s\r\n", 95836933Skarels strunames[stru], modenames[mode]); 95936933Skarels if (data != -1) 96036933Skarels printf(" Data connection open\r\n"); 96136933Skarels else if (pdata != -1) { 96236933Skarels printf(" in Passive mode"); 96336933Skarels sin = &pasv_addr; 96436933Skarels goto printaddr; 96536933Skarels } else if (usedefault == 0) { 96636933Skarels printf(" PORT"); 96736933Skarels sin = &data_dest; 96836933Skarels printaddr: 96936933Skarels a = (u_char *) &sin->sin_addr; 97036933Skarels p = (u_char *) &sin->sin_port; 97136933Skarels #define UC(b) (((int) b) & 0xff) 97236933Skarels printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 97336933Skarels UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 97436933Skarels #undef UC 97536933Skarels } else 97636933Skarels printf(" No data connection\r\n"); 97736933Skarels reply(211, "End of status"); 97836933Skarels } 97936933Skarels 98010275Ssam fatal(s) 98110275Ssam char *s; 98210275Ssam { 98310275Ssam reply(451, "Error in server: %s\n", s); 98410275Ssam reply(221, "Closing connection due to server error."); 98513247Ssam dologout(0); 98636620Srick /* NOTREACHED */ 98710275Ssam } 98810275Ssam 98936446Sbostic /* VARARGS2 */ 99036446Sbostic reply(n, fmt, p0, p1, p2, p3, p4, p5) 99110275Ssam int n; 99236446Sbostic char *fmt; 99310275Ssam { 99410275Ssam printf("%d ", n); 99536446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 99610275Ssam printf("\r\n"); 99736435Sbostic (void)fflush(stdout); 99810275Ssam if (debug) { 99926493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 100036446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 100110275Ssam } 100236620Srick } 100310275Ssam 100436446Sbostic /* VARARGS2 */ 100536446Sbostic lreply(n, fmt, p0, p1, p2, p3, p4, p5) 100610275Ssam int n; 100736446Sbostic char *fmt; 100810275Ssam { 100936446Sbostic printf("%d- ", n); 101036446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 101136446Sbostic printf("\r\n"); 101236435Sbostic (void)fflush(stdout); 101336446Sbostic if (debug) { 101436446Sbostic syslog(LOG_DEBUG, "<--- %d- ", n); 101536446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 101636446Sbostic } 101710275Ssam } 101810275Ssam 101910275Ssam ack(s) 102010275Ssam char *s; 102110275Ssam { 102227106Smckusick reply(250, "%s command successful.", s); 102310275Ssam } 102410275Ssam 102510275Ssam nack(s) 102610275Ssam char *s; 102710275Ssam { 102810275Ssam reply(502, "%s command not implemented.", s); 102910275Ssam } 103010275Ssam 103136304Skarels /* ARGSUSED */ 103226493Sminshall yyerror(s) 103326493Sminshall char *s; 103410275Ssam { 103526044Sminshall char *cp; 103626044Sminshall 103736551Sbostic if (cp = index(cbuf,'\n')) 103836551Sbostic *cp = '\0'; 103936933Skarels reply(500, "'%s': command not understood.", cbuf); 104010275Ssam } 104110275Ssam 104210275Ssam delete(name) 104310275Ssam char *name; 104410275Ssam { 104510275Ssam struct stat st; 104610275Ssam 104710275Ssam if (stat(name, &st) < 0) { 104836304Skarels perror_reply(550, name); 104910275Ssam return; 105010275Ssam } 105110275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 105210275Ssam if (rmdir(name) < 0) { 105336304Skarels perror_reply(550, name); 105410275Ssam return; 105510275Ssam } 105610275Ssam goto done; 105710275Ssam } 105810275Ssam if (unlink(name) < 0) { 105936304Skarels perror_reply(550, name); 106010275Ssam return; 106110275Ssam } 106210275Ssam done: 106310275Ssam ack("DELE"); 106410275Ssam } 106510275Ssam 106610275Ssam cwd(path) 106710275Ssam char *path; 106810275Ssam { 106936620Srick if (chdir(path) < 0) 107036304Skarels perror_reply(550, path); 107136620Srick else 107236620Srick ack("CWD"); 107310275Ssam } 107410275Ssam 107510303Ssam makedir(name) 107610275Ssam char *name; 107710275Ssam { 107836276Sbostic if (mkdir(name, 0777) < 0) 107936304Skarels perror_reply(550, name); 108036276Sbostic else 108136276Sbostic reply(257, "MKD command successful."); 108210275Ssam } 108310275Ssam 108410303Ssam removedir(name) 108510275Ssam char *name; 108610275Ssam { 108736620Srick if (rmdir(name) < 0) 108836304Skarels perror_reply(550, name); 108936620Srick else 109036620Srick ack("RMD"); 109110275Ssam } 109210275Ssam 109310303Ssam pwd() 109410275Ssam { 109510303Ssam char path[MAXPATHLEN + 1]; 109636304Skarels extern char *getwd(); 109710275Ssam 109836620Srick if (getwd(path) == (char *)NULL) 109927106Smckusick reply(550, "%s.", path); 110036620Srick else 110136620Srick reply(257, "\"%s\" is current directory.", path); 110210275Ssam } 110310275Ssam 110410275Ssam char * 110510275Ssam renamefrom(name) 110610275Ssam char *name; 110710275Ssam { 110810275Ssam struct stat st; 110910275Ssam 111010275Ssam if (stat(name, &st) < 0) { 111136304Skarels perror_reply(550, name); 111210275Ssam return ((char *)0); 111310275Ssam } 111410303Ssam reply(350, "File exists, ready for destination name"); 111510275Ssam return (name); 111610275Ssam } 111710275Ssam 111810275Ssam renamecmd(from, to) 111910275Ssam char *from, *to; 112010275Ssam { 112136620Srick if (rename(from, to) < 0) 112236304Skarels perror_reply(550, "rename"); 112336620Srick else 112436620Srick ack("RNTO"); 112510275Ssam } 112610275Ssam 112710275Ssam dolog(sin) 112810275Ssam struct sockaddr_in *sin; 112910275Ssam { 113036304Skarels struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 113110275Ssam sizeof (struct in_addr), AF_INET); 113236304Skarels time_t t, time(); 113326493Sminshall extern char *ctime(); 113410275Ssam 113536304Skarels if (hp) 113626493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 113736304Skarels else 113826493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 113913247Ssam sizeof (remotehost)); 114036620Srick #ifdef SETPROCTITLE 114136933Skarels sprintf(proctitle, "%s: connected", remotehost); 114236933Skarels setproctitle(proctitle); 114336620Srick #endif /* SETPROCTITLE */ 114436933Skarels 114536933Skarels if (logging) { 114636933Skarels t = time((time_t *) 0); 114736933Skarels syslog(LOG_INFO, "connection from %s at %s", 114836933Skarels remotehost, ctime(&t)); 114936933Skarels } 115010275Ssam } 115110695Ssam 115210695Ssam /* 115313247Ssam * Record logout in wtmp file 115413247Ssam * and exit with supplied status. 115513247Ssam */ 115613247Ssam dologout(status) 115713247Ssam int status; 115813247Ssam { 115917580Ssam if (logged_in) { 116036304Skarels (void) seteuid((uid_t)0); 116135672Sbostic logwtmp(ttyline, "", ""); 116213247Ssam } 116314436Ssam /* beware of flushing buffers after a SIGPIPE */ 116414436Ssam _exit(status); 116513247Ssam } 116613247Ssam 116726044Sminshall myoob() 116826044Sminshall { 116927750Sminshall char *cp; 117026044Sminshall 117127750Sminshall /* only process if transfer occurring */ 117236304Skarels if (!transflag) 117326044Sminshall return; 117427750Sminshall cp = tmpline; 117527750Sminshall if (getline(cp, 7, stdin) == NULL) { 117636304Skarels reply(221, "You could at least say goodbye."); 117727750Sminshall dologout(0); 117826044Sminshall } 117926044Sminshall upper(cp); 118036933Skarels if (strcmp(cp, "ABOR\r\n") == 0) { 118136933Skarels tmpline[0] = '\0'; 118236933Skarels reply(426, "Transfer aborted. Data connection closed."); 118336933Skarels reply(226, "Abort successful"); 118436933Skarels longjmp(urgcatch, 1); 118536933Skarels } 118636933Skarels if (strcmp(cp, "STAT\r\n") == 0) { 118736933Skarels if (file_size != (off_t) -1) 118836933Skarels reply(213, "Status: %lu of %lu bytes transferred", 118936933Skarels byte_count, file_size); 119036933Skarels else 119136933Skarels reply(213, "Status: %lu bytes transferred", byte_count); 119236933Skarels } 119326044Sminshall } 119426044Sminshall 119527106Smckusick /* 119636620Srick * Note: a response of 425 is not mentioned as a possible response to 119736620Srick * the PASV command in RFC959. However, it has been blessed as 119836620Srick * a legitimate response by Jon Postel in a telephone conversation 119936620Srick * with Rick Adams on 25 Jan 89. 120027106Smckusick */ 120126044Sminshall passive() 120226044Sminshall { 120326044Sminshall int len; 120426044Sminshall register char *p, *a; 120526044Sminshall 120626044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 120726044Sminshall if (pdata < 0) { 120836620Srick perror_reply(425, "Can't open passive connection"); 120926044Sminshall return; 121026044Sminshall } 121136933Skarels pasv_addr = ctrl_addr; 121236933Skarels pasv_addr.sin_port = 0; 121336304Skarels (void) seteuid((uid_t)0); 121436933Skarels if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { 121536304Skarels (void) seteuid((uid_t)pw->pw_uid); 121636620Srick goto pasv_error; 121726044Sminshall } 121836304Skarels (void) seteuid((uid_t)pw->pw_uid); 121936933Skarels len = sizeof(pasv_addr); 122036933Skarels if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 122136620Srick goto pasv_error; 122236620Srick if (listen(pdata, 1) < 0) 122336620Srick goto pasv_error; 122436933Skarels a = (char *) &pasv_addr.sin_addr; 122536933Skarels p = (char *) &pasv_addr.sin_port; 122626044Sminshall 122726044Sminshall #define UC(b) (((int) b) & 0xff) 122826044Sminshall 122926044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 123026044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 123136620Srick return; 123236620Srick 123336620Srick pasv_error: 123436620Srick (void) close(pdata); 123536620Srick pdata = -1; 123636620Srick perror_reply(425, "Can't open passive connection"); 123736620Srick return; 123826044Sminshall } 123926044Sminshall 124036304Skarels /* 124136304Skarels * Generate unique name for file with basename "local". 124236304Skarels * The file named "local" is already known to exist. 124336304Skarels * Generates failure reply on error. 124436304Skarels */ 124526044Sminshall char * 124626044Sminshall gunique(local) 124726044Sminshall char *local; 124826044Sminshall { 124926044Sminshall static char new[MAXPATHLEN]; 125036304Skarels struct stat st; 125126044Sminshall char *cp = rindex(local, '/'); 125236933Skarels int count = 0; 125326044Sminshall 125436304Skarels if (cp) 125526044Sminshall *cp = '\0'; 125636620Srick if (stat(cp ? local : ".", &st) < 0) { 125736933Skarels perror_reply(553, cp ? local : "."); 125826044Sminshall return((char *) 0); 125926044Sminshall } 126036620Srick if (cp) 126136620Srick *cp = '/'; 126226044Sminshall (void) strcpy(new, local); 126326044Sminshall cp = new + strlen(new); 126426044Sminshall *cp++ = '.'; 126536304Skarels for (count = 1; count < 100; count++) { 126636304Skarels (void) sprintf(cp, "%d", count); 126736304Skarels if (stat(new, &st) < 0) 126836304Skarels return(new); 126926044Sminshall } 127036304Skarels reply(452, "Unique file name cannot be created."); 127136304Skarels return((char *) 0); 127226044Sminshall } 127336304Skarels 127436304Skarels /* 127536304Skarels * Format and send reply containing system error number. 127636304Skarels */ 127736304Skarels perror_reply(code, string) 127836304Skarels int code; 127936304Skarels char *string; 128036304Skarels { 128142412Sbostic reply(code, "%s: %s.", string, strerror(errno)); 128236304Skarels } 128336620Srick 128436620Srick static char *onefile[] = { 128536620Srick "", 128636620Srick 0 128736620Srick }; 128836620Srick 128936620Srick send_file_list(whichfiles) 129036620Srick char *whichfiles; 129136620Srick { 129236620Srick struct stat st; 129336620Srick DIR *dirp = NULL; 129436620Srick struct direct *dir; 129536620Srick FILE *dout = NULL; 129636620Srick register char **dirlist, *dirname; 129737459Skarels int simple = 0; 129836620Srick char *strpbrk(); 129936620Srick 130036620Srick if (strpbrk(whichfiles, "~{[*?") != NULL) { 130136620Srick extern char **glob(), *globerr; 130236933Skarels 130336620Srick globerr = NULL; 130436620Srick dirlist = glob(whichfiles); 130536620Srick if (globerr != NULL) { 130636620Srick reply(550, globerr); 130736620Srick return; 130836620Srick } else if (dirlist == NULL) { 130936620Srick errno = ENOENT; 131036620Srick perror_reply(550, whichfiles); 131136620Srick return; 131236620Srick } 131336620Srick } else { 131436620Srick onefile[0] = whichfiles; 131536620Srick dirlist = onefile; 131637459Skarels simple = 1; 131736620Srick } 131836933Skarels 131936933Skarels if (setjmp(urgcatch)) { 132036933Skarels transflag = 0; 132136933Skarels return; 132236933Skarels } 132336620Srick while (dirname = *dirlist++) { 132436620Srick if (stat(dirname, &st) < 0) { 132536933Skarels /* 132636933Skarels * If user typed "ls -l", etc, and the client 132736933Skarels * used NLST, do what the user meant. 132836933Skarels */ 132936933Skarels if (dirname[0] == '-' && *dirlist == NULL && 133036933Skarels transflag == 0) { 133136933Skarels retrieve("/bin/ls %s", dirname); 133236933Skarels return; 133336933Skarels } 133436620Srick perror_reply(550, whichfiles); 133536620Srick if (dout != NULL) { 133636620Srick (void) fclose(dout); 133736933Skarels transflag = 0; 133836620Srick data = -1; 133936620Srick pdata = -1; 134036620Srick } 134136620Srick return; 134236620Srick } 134336933Skarels 134436620Srick if ((st.st_mode&S_IFMT) == S_IFREG) { 134536620Srick if (dout == NULL) { 134637459Skarels dout = dataconn("file list", (off_t)-1, "w"); 134736620Srick if (dout == NULL) 134836620Srick return; 134936933Skarels transflag++; 135036620Srick } 135138158Srick fprintf(dout, "%s%s\n", dirname, 135238158Srick type == TYPE_A ? "\r" : ""); 135336933Skarels byte_count += strlen(dirname) + 1; 135436620Srick continue; 135536933Skarels } else if ((st.st_mode&S_IFMT) != S_IFDIR) 135636620Srick continue; 135736620Srick 135836620Srick if ((dirp = opendir(dirname)) == NULL) 135936620Srick continue; 136036620Srick 136136620Srick while ((dir = readdir(dirp)) != NULL) { 136236933Skarels char nbuf[MAXPATHLEN]; 136336620Srick 136436620Srick if (dir->d_name[0] == '.' && dir->d_namlen == 1) 136536620Srick continue; 136636933Skarels if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 136736933Skarels dir->d_namlen == 2) 136836620Srick continue; 136936620Srick 137036933Skarels sprintf(nbuf, "%s/%s", dirname, dir->d_name); 137136933Skarels 137236620Srick /* 137336933Skarels * We have to do a stat to insure it's 137436933Skarels * not a directory or special file. 137536620Srick */ 137637459Skarels if (simple || (stat(nbuf, &st) == 0 && 137737459Skarels (st.st_mode&S_IFMT) == S_IFREG)) { 137836620Srick if (dout == NULL) { 137937459Skarels dout = dataconn("file list", (off_t)-1, 138036620Srick "w"); 138136620Srick if (dout == NULL) 138236620Srick return; 138336933Skarels transflag++; 138436620Srick } 138536620Srick if (nbuf[0] == '.' && nbuf[1] == '/') 138638158Srick fprintf(dout, "%s%s\n", &nbuf[2], 138738158Srick type == TYPE_A ? "\r" : ""); 138836620Srick else 138938158Srick fprintf(dout, "%s%s\n", nbuf, 139038158Srick type == TYPE_A ? "\r" : ""); 139136933Skarels byte_count += strlen(nbuf) + 1; 139236620Srick } 139336620Srick } 139436620Srick (void) closedir(dirp); 139536620Srick } 139636620Srick 139736933Skarels if (dout == NULL) 139836933Skarels reply(550, "No files found."); 139936933Skarels else if (ferror(dout) != 0) 140036933Skarels perror_reply(550, "Data connection"); 140136933Skarels else 140236620Srick reply(226, "Transfer complete."); 140336620Srick 140436933Skarels transflag = 0; 140536933Skarels if (dout != NULL) 140636620Srick (void) fclose(dout); 140736620Srick data = -1; 140836620Srick pdata = -1; 140936620Srick } 141036620Srick 141136620Srick #ifdef SETPROCTITLE 141236620Srick /* 141336620Srick * clobber argv so ps will show what we're doing. 141436620Srick * (stolen from sendmail) 141536620Srick * warning, since this is usually started from inetd.conf, it 141636620Srick * often doesn't have much of an environment or arglist to overwrite. 141736620Srick */ 141836620Srick 141936620Srick /*VARARGS1*/ 142036620Srick setproctitle(fmt, a, b, c) 142136620Srick char *fmt; 142236620Srick { 142336620Srick register char *p, *bp, ch; 142436620Srick register int i; 142536620Srick char buf[BUFSIZ]; 142636620Srick 142736620Srick (void) sprintf(buf, fmt, a, b, c); 142836620Srick 142936620Srick /* make ps print our process name */ 143036620Srick p = Argv[0]; 143136620Srick *p++ = '-'; 143236620Srick 143336620Srick i = strlen(buf); 143436620Srick if (i > LastArgv - p - 2) { 143536620Srick i = LastArgv - p - 2; 143636620Srick buf[i] = '\0'; 143736620Srick } 143836620Srick bp = buf; 143936620Srick while (ch = *bp++) 144036620Srick if (ch != '\n' && ch != '\r') 144136620Srick *p++ = ch; 144236620Srick while (p < LastArgv) 144336620Srick *p++ = ' '; 144436620Srick } 144536620Srick #endif /* SETPROCTITLE */ 1446