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*37459Skarels static char sccsid[] = "@(#)ftpd.c 5.28 (Berkeley) 04/20/89"; 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> 5326044Sminshall #include <strings.h> 5426493Sminshall #include <syslog.h> 5536435Sbostic #include <varargs.h> 56*37459Skarels #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 *sys_errlist[]; 6536304Skarels extern int sys_nerr; 6610275Ssam extern char *crypt(); 6710275Ssam extern char version[]; 6810275Ssam extern char *home; /* pointer to home directory for glob */ 6936276Sbostic extern FILE *ftpd_popen(), *fopen(), *freopen(); 7036304Skarels extern int ftpd_pclose(), fclose(); 7126044Sminshall extern char *getline(); 7226044Sminshall extern char cbuf[]; 73*37459Skarels extern off_t restart_point; 7410275Ssam 7510275Ssam struct sockaddr_in ctrl_addr; 7610275Ssam struct sockaddr_in data_source; 7710275Ssam struct sockaddr_in data_dest; 7810275Ssam struct sockaddr_in his_addr; 7936933Skarels struct sockaddr_in pasv_addr; 8010275Ssam 8110275Ssam int data; 8226044Sminshall jmp_buf errcatch, urgcatch; 8310275Ssam int logged_in; 8410275Ssam struct passwd *pw; 8510275Ssam int debug; 8626493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */ 8736933Skarels int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 8811757Ssam int logging; 8910275Ssam int guest; 9010275Ssam int type; 9110275Ssam int form; 9210275Ssam int stru; /* avoid C keyword */ 9310275Ssam int mode; 9410321Ssam int usedefault = 1; /* for data transfers */ 9536304Skarels int pdata = -1; /* for passive mode */ 9626044Sminshall int transflag; 9736933Skarels off_t file_size; 9836933Skarels off_t byte_count; 9936933Skarels #if !defined(CMASK) || CMASK == 0 10036933Skarels #undef CMASK 10136933Skarels #define CMASK 027 10236933Skarels #endif 10336933Skarels int defumask = CMASK; /* default umask value */ 10426044Sminshall char tmpline[7]; 10536276Sbostic char hostname[MAXHOSTNAMELEN]; 10636276Sbostic char remotehost[MAXHOSTNAMELEN]; 10710275Ssam 10811653Ssam /* 10911653Ssam * Timeout intervals for retrying connections 11011653Ssam * to hosts that don't accept PORT cmds. This 11111653Ssam * is a kludge, but given the problems with TCP... 11211653Ssam */ 11311653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 11411653Ssam #define SWAITINT 5 /* interval between retries */ 11511653Ssam 11611653Ssam int swaitmax = SWAITMAX; 11711653Ssam int swaitint = SWAITINT; 11811653Ssam 11910275Ssam int lostconn(); 12026044Sminshall int myoob(); 12110275Ssam FILE *getdatasock(), *dataconn(); 12210275Ssam 12336620Srick #ifdef SETPROCTITLE 12436620Srick char **Argv = NULL; /* pointer to argument vector */ 12536620Srick char *LastArgv = NULL; /* end of argv */ 12636933Skarels char proctitle[BUFSIZ]; /* initial part of title */ 12736620Srick #endif /* SETPROCTITLE */ 12836620Srick 12936620Srick main(argc, argv, envp) 13010275Ssam int argc; 13110275Ssam char *argv[]; 13236620Srick char **envp; 13310275Ssam { 13427750Sminshall int addrlen, on = 1; 13510275Ssam char *cp; 13610275Ssam 13716339Skarels addrlen = sizeof (his_addr); 13836304Skarels if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 13926493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 14010275Ssam exit(1); 14110275Ssam } 14216339Skarels addrlen = sizeof (ctrl_addr); 14336304Skarels if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 14426493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 14516339Skarels exit(1); 14616339Skarels } 14716339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 14810275Ssam debug = 0; 14926493Sminshall openlog("ftpd", LOG_PID, LOG_DAEMON); 15036620Srick #ifdef SETPROCTITLE 15136620Srick /* 15236620Srick * Save start and extent of argv for setproctitle. 15336620Srick */ 15436620Srick Argv = argv; 15536620Srick while (*envp) 15636620Srick envp++; 15736620Srick LastArgv = envp[-1] + strlen(envp[-1]); 15836620Srick #endif /* SETPROCTITLE */ 15936620Srick 16010275Ssam argc--, argv++; 16110275Ssam while (argc > 0 && *argv[0] == '-') { 16210275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 16310275Ssam 16411653Ssam case 'v': 16511653Ssam debug = 1; 16611653Ssam break; 16711653Ssam 16810275Ssam case 'd': 16910275Ssam debug = 1; 17010275Ssam break; 17110275Ssam 17211757Ssam case 'l': 17311757Ssam logging = 1; 17411757Ssam break; 17511757Ssam 17611653Ssam case 't': 17711653Ssam timeout = atoi(++cp); 17836933Skarels if (maxtimeout < timeout) 17936933Skarels maxtimeout = timeout; 18011653Ssam goto nextopt; 18111653Ssam 18236933Skarels case 'T': 18336933Skarels maxtimeout = atoi(++cp); 18436933Skarels if (timeout > maxtimeout) 18536933Skarels timeout = maxtimeout; 18636933Skarels goto nextopt; 18736933Skarels 18836933Skarels case 'u': 18936933Skarels { 19036933Skarels int val = 0; 19136933Skarels 19236933Skarels while (*++cp && *cp >= '0' && *cp <= '9') 19336933Skarels val = val*8 + *cp - '0'; 19436933Skarels if (*cp) 19536933Skarels fprintf(stderr, "ftpd: Bad value for -u\n"); 19636933Skarels else 19736933Skarels defumask = val; 19836933Skarels goto nextopt; 19936933Skarels } 20036933Skarels 20110275Ssam default: 20216339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 20316339Skarels *cp); 20410275Ssam break; 20510275Ssam } 20611653Ssam nextopt: 20710275Ssam argc--, argv++; 20810275Ssam } 209*37459Skarels (void) freopen(_PATH_DEVNULL, "w", stderr); 21026493Sminshall (void) signal(SIGPIPE, lostconn); 21126493Sminshall (void) signal(SIGCHLD, SIG_IGN); 21235691Sbostic if ((int)signal(SIGURG, myoob) < 0) 21326493Sminshall syslog(LOG_ERR, "signal: %m"); 21435691Sbostic 21527750Sminshall /* handle urgent data inline */ 21636276Sbostic /* Sequent defines this, but it doesn't work */ 21727750Sminshall #ifdef SO_OOBINLINE 21836276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 21927750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 22036276Sbostic #endif 22136933Skarels #ifdef F_SETOWN 22236304Skarels if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 22336304Skarels syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 22436933Skarels #endif 22516760Slepreau dolog(&his_addr); 22616339Skarels /* 22716339Skarels * Set up default state 22816339Skarels */ 22916339Skarels data = -1; 23016339Skarels type = TYPE_A; 23116339Skarels form = FORM_N; 23216339Skarels stru = STRU_F; 23316339Skarels mode = MODE_S; 23426044Sminshall tmpline[0] = '\0'; 23526493Sminshall (void) gethostname(hostname, sizeof (hostname)); 23636276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version); 23736304Skarels (void) setjmp(errcatch); 23836304Skarels for (;;) 23926493Sminshall (void) yyparse(); 24036620Srick /* NOTREACHED */ 24110275Ssam } 24210419Ssam 24310275Ssam lostconn() 24410275Ssam { 24510275Ssam 24614089Ssam if (debug) 24726493Sminshall syslog(LOG_DEBUG, "lost connection"); 24814089Ssam dologout(-1); 24910275Ssam } 25010275Ssam 25135672Sbostic static char ttyline[20]; 25235672Sbostic 25336185Sbostic /* 25436185Sbostic * Helper function for sgetpwnam(). 25536185Sbostic */ 25636185Sbostic char * 25736185Sbostic sgetsave(s) 25836185Sbostic char *s; 25936185Sbostic { 26036185Sbostic char *malloc(); 26136185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 26236933Skarels 26336185Sbostic if (new == NULL) { 26436620Srick perror_reply(421, "Local resource failure: malloc"); 26536185Sbostic dologout(1); 26636620Srick /* NOTREACHED */ 26736185Sbostic } 26836185Sbostic (void) strcpy(new, s); 26936185Sbostic return (new); 27036185Sbostic } 27136185Sbostic 27236185Sbostic /* 27336185Sbostic * Save the result of a getpwnam. Used for USER command, since 27436185Sbostic * the data returned must not be clobbered by any other command 27536185Sbostic * (e.g., globbing). 27636185Sbostic */ 27736185Sbostic struct passwd * 27836185Sbostic sgetpwnam(name) 27936185Sbostic char *name; 28036185Sbostic { 28136185Sbostic static struct passwd save; 28236185Sbostic register struct passwd *p; 28336185Sbostic char *sgetsave(); 28436185Sbostic 28536185Sbostic if ((p = getpwnam(name)) == NULL) 28636185Sbostic return (p); 28736185Sbostic if (save.pw_name) { 28836185Sbostic free(save.pw_name); 28936185Sbostic free(save.pw_passwd); 29036185Sbostic free(save.pw_gecos); 29136185Sbostic free(save.pw_dir); 29236185Sbostic free(save.pw_shell); 29336185Sbostic } 29436185Sbostic save = *p; 29536185Sbostic save.pw_name = sgetsave(p->pw_name); 29636185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 29736185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 29836185Sbostic save.pw_dir = sgetsave(p->pw_dir); 29936185Sbostic save.pw_shell = sgetsave(p->pw_shell); 30036185Sbostic return (&save); 30136185Sbostic } 30236185Sbostic 30336304Skarels int login_attempts; /* number of failed login attempts */ 30436304Skarels int askpasswd; /* had user command, ask for passwd */ 30536304Skarels 30636304Skarels /* 30736304Skarels * USER command. 30836304Skarels * Sets global passwd pointer pw if named account exists 30936304Skarels * and is acceptable; sets askpasswd if a PASS command is 31036304Skarels * expected. If logged in previously, need to reset state. 31136304Skarels * If name is "ftp" or "anonymous" and ftp account exists, 31236304Skarels * set guest and pw, then just return. 31336304Skarels * If account doesn't exist, ask for passwd anyway. 31436304Skarels * Otherwise, check user requesting login privileges. 31536304Skarels * Disallow anyone who does not have a standard 316*37459Skarels * shell as returned by getusershell(). 317*37459Skarels * Disallow anyone mentioned in the file _PATH_FTPUSERS 31836304Skarels * to allow people such as root and uucp to be avoided. 31936304Skarels */ 32036304Skarels user(name) 32136304Skarels char *name; 32236304Skarels { 32336304Skarels register char *cp; 32436304Skarels FILE *fd; 32536304Skarels char *shell; 32636551Sbostic char line[BUFSIZ], *getusershell(); 32736304Skarels 32836304Skarels if (logged_in) { 32936304Skarels if (guest) { 33036304Skarels reply(530, "Can't change user from guest login."); 33136304Skarels return; 33236304Skarels } 33336304Skarels end_login(); 33436304Skarels } 33536304Skarels 33636304Skarels guest = 0; 33736304Skarels if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 33836304Skarels if ((pw = sgetpwnam("ftp")) != NULL) { 33936304Skarels guest = 1; 34036304Skarels askpasswd = 1; 34136304Skarels reply(331, "Guest login ok, send ident as password."); 34236933Skarels } else 34336304Skarels reply(530, "User %s unknown.", name); 34436304Skarels return; 34536304Skarels } 34636304Skarels if (pw = sgetpwnam(name)) { 34736304Skarels if ((shell = pw->pw_shell) == NULL || *shell == 0) 348*37459Skarels shell = _PATH_BSHELL; 34936304Skarels while ((cp = getusershell()) != NULL) 35036304Skarels if (strcmp(cp, shell) == 0) 35136304Skarels break; 35236304Skarels endusershell(); 35336304Skarels if (cp == NULL) { 35436304Skarels reply(530, "User %s access denied.", name); 35536933Skarels if (logging) 35636933Skarels syslog(LOG_NOTICE, 35736933Skarels "FTP LOGIN REFUSED FROM %s, %s", 35836933Skarels remotehost, name); 35936304Skarels pw = (struct passwd *) NULL; 36036304Skarels return; 36136304Skarels } 362*37459Skarels if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { 36336304Skarels while (fgets(line, sizeof (line), fd) != NULL) { 36436304Skarels if ((cp = index(line, '\n')) != NULL) 36536304Skarels *cp = '\0'; 36636304Skarels if (strcmp(line, name) == 0) { 36736304Skarels reply(530, "User %s access denied.", name); 36836933Skarels if (logging) 36936933Skarels syslog(LOG_NOTICE, 37036933Skarels "FTP LOGIN REFUSED FROM %s, %s", 37136933Skarels remotehost, name); 37236304Skarels pw = (struct passwd *) NULL; 37336304Skarels return; 37436304Skarels } 37536304Skarels } 37636304Skarels } 37736304Skarels (void) fclose(fd); 37836304Skarels } 37936304Skarels reply(331, "Password required for %s.", name); 38036304Skarels askpasswd = 1; 38136304Skarels /* 38236304Skarels * Delay before reading passwd after first failed 38336304Skarels * attempt to slow down passwd-guessing programs. 38436304Skarels */ 38536304Skarels if (login_attempts) 38636304Skarels sleep((unsigned) login_attempts); 38736304Skarels } 38836304Skarels 38936304Skarels /* 39036304Skarels * Terminate login as previous user, if any, resetting state; 39136304Skarels * used when USER command is given or login fails. 39236304Skarels */ 39336304Skarels end_login() 39436304Skarels { 39536304Skarels 39636304Skarels (void) seteuid((uid_t)0); 39736304Skarels if (logged_in) 39836304Skarels logwtmp(ttyline, "", ""); 39936304Skarels pw = NULL; 40036304Skarels logged_in = 0; 40136304Skarels guest = 0; 40236304Skarels } 40336304Skarels 40410275Ssam pass(passwd) 40510275Ssam char *passwd; 40610275Ssam { 40736304Skarels char *xpasswd, *salt; 40810275Ssam 40936304Skarels if (logged_in || askpasswd == 0) { 41010275Ssam reply(503, "Login with USER first."); 41110275Ssam return; 41210275Ssam } 41336304Skarels askpasswd = 0; 41410275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 41536304Skarels if (pw == NULL) 41636304Skarels salt = "xx"; 41736304Skarels else 41836304Skarels salt = pw->pw_passwd; 41936304Skarels xpasswd = crypt(passwd, salt); 42016760Slepreau /* The strcmp does not catch null passwords! */ 42136304Skarels if (pw == NULL || *pw->pw_passwd == '\0' || 42236304Skarels strcmp(xpasswd, pw->pw_passwd)) { 42310275Ssam reply(530, "Login incorrect."); 42410275Ssam pw = NULL; 42536304Skarels if (login_attempts++ >= 5) { 42636933Skarels syslog(LOG_NOTICE, 42736304Skarels "repeated login failures from %s", 42836304Skarels remotehost); 42936304Skarels exit(0); 43036304Skarels } 43110275Ssam return; 43210275Ssam } 43310275Ssam } 43436304Skarels login_attempts = 0; /* this time successful */ 43536304Skarels (void) setegid((gid_t)pw->pw_gid); 43636304Skarels (void) initgroups(pw->pw_name, pw->pw_gid); 43716033Sralph 43836192Sbostic /* open wtmp before chroot */ 43936192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 44036192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 44136192Sbostic logged_in = 1; 44236192Sbostic 44336446Sbostic if (guest) { 44436620Srick /* 44536933Skarels * We MUST do a chdir() after the chroot. Otherwise 44636933Skarels * the old current directory will be accessible as "." 44736933Skarels * outside the new root! 44836620Srick */ 44936620Srick if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 45036446Sbostic reply(550, "Can't set guest privileges."); 45136446Sbostic goto bad; 45236446Sbostic } 45336933Skarels } else if (chdir(pw->pw_dir) < 0) { 45436620Srick if (chdir("/") < 0) { 45536446Sbostic reply(530, "User %s: can't change directory to %s.", 45636446Sbostic pw->pw_name, pw->pw_dir); 45736446Sbostic goto bad; 45836933Skarels } else 45936446Sbostic lreply(230, "No directory! Logging in with home=/"); 46036620Srick } 46136304Skarels if (seteuid((uid_t)pw->pw_uid) < 0) { 46236304Skarels reply(550, "Can't set uid."); 46336304Skarels goto bad; 46436304Skarels } 46536550Sbostic if (guest) { 46636192Sbostic reply(230, "Guest login ok, access restrictions apply."); 46736933Skarels #ifdef SETPROCTITLE 46836933Skarels sprintf(proctitle, "%s: anonymous/%.*s", remotehost, 46936933Skarels sizeof(proctitle) - sizeof(remotehost) - 47036933Skarels sizeof(": anonymous/"), passwd); 47136933Skarels setproctitle(proctitle); 47236933Skarels #endif /* SETPROCTITLE */ 47336933Skarels if (logging) 47436933Skarels syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 47536933Skarels remotehost, passwd); 47636933Skarels } else { 47710275Ssam reply(230, "User %s logged in.", pw->pw_name); 47836933Skarels #ifdef SETPROCTITLE 47936933Skarels sprintf(proctitle, "%s: %s", remotehost, pw->pw_name); 48036933Skarels setproctitle(proctitle); 48136933Skarels #endif /* SETPROCTITLE */ 48236933Skarels if (logging) 48336933Skarels syslog(LOG_INFO, "FTP LOGIN FROM %s, %s", 48436933Skarels remotehost, pw->pw_name); 48536550Sbostic } 48610303Ssam home = pw->pw_dir; /* home dir for globbing */ 48736933Skarels (void) umask(defumask); 48810303Ssam return; 48910303Ssam bad: 49036304Skarels /* Forget all about it... */ 49136304Skarels end_login(); 49210275Ssam } 49310275Ssam 49410275Ssam retrieve(cmd, name) 49510275Ssam char *cmd, *name; 49610275Ssam { 49710275Ssam FILE *fin, *dout; 49810275Ssam struct stat st; 49936620Srick int (*closefunc)(); 50010275Ssam 50136557Sbostic if (cmd == 0) { 50236446Sbostic fin = fopen(name, "r"), closefunc = fclose; 50336557Sbostic st.st_size = 0; 50436557Sbostic } else { 50510275Ssam char line[BUFSIZ]; 50610275Ssam 50726493Sminshall (void) sprintf(line, cmd, name), name = line; 50836304Skarels fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 50936557Sbostic st.st_size = -1; 51036933Skarels st.st_blksize = BUFSIZ; 51110275Ssam } 51210275Ssam if (fin == NULL) { 51313152Ssam if (errno != 0) 51436304Skarels perror_reply(550, name); 51510275Ssam return; 51610275Ssam } 51710275Ssam if (cmd == 0 && 51836933Skarels (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 51910275Ssam reply(550, "%s: not a plain file.", name); 52010275Ssam goto done; 52110275Ssam } 522*37459Skarels if (restart_point) { 523*37459Skarels if (type == TYPE_A) { 524*37459Skarels register int i, n, c; 525*37459Skarels 526*37459Skarels n = restart_point; 527*37459Skarels i = 0; 528*37459Skarels while (i++ < n) { 529*37459Skarels if ((c=getc(fin)) == EOF) { 530*37459Skarels perror_reply(550, name); 531*37459Skarels goto done; 532*37459Skarels } 533*37459Skarels if (c == '\n') 534*37459Skarels i++; 535*37459Skarels } 536*37459Skarels } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 537*37459Skarels perror_reply(550, name); 538*37459Skarels goto done; 539*37459Skarels } 540*37459Skarels } 54110275Ssam dout = dataconn(name, st.st_size, "w"); 54210275Ssam if (dout == NULL) 54310275Ssam goto done; 54436620Srick send_data(fin, dout, st.st_blksize); 54526493Sminshall (void) fclose(dout); 54626044Sminshall data = -1; 54726044Sminshall pdata = -1; 54810275Ssam done: 54910275Ssam (*closefunc)(fin); 55010275Ssam } 55110275Ssam 55236304Skarels store(name, mode, unique) 55310275Ssam char *name, *mode; 55436304Skarels int unique; 55510275Ssam { 55610275Ssam FILE *fout, *din; 55736446Sbostic struct stat st; 55836620Srick int (*closefunc)(); 55936304Skarels char *gunique(); 56010275Ssam 56136446Sbostic if (unique && stat(name, &st) == 0 && 56236446Sbostic (name = gunique(name)) == NULL) 56336446Sbostic return; 56410303Ssam 565*37459Skarels if (restart_point) 566*37459Skarels mode = "r+w"; 56736620Srick fout = fopen(name, mode); 56836620Srick closefunc = fclose; 56910275Ssam if (fout == NULL) { 57036304Skarels perror_reply(553, name); 57110275Ssam return; 57210275Ssam } 573*37459Skarels if (restart_point) { 574*37459Skarels if (type == TYPE_A) { 575*37459Skarels register int i, n, c; 576*37459Skarels 577*37459Skarels n = restart_point; 578*37459Skarels i = 0; 579*37459Skarels while (i++ < n) { 580*37459Skarels if ((c=getc(fout)) == EOF) { 581*37459Skarels perror_reply(550, name); 582*37459Skarels goto done; 583*37459Skarels } 584*37459Skarels if (c == '\n') 585*37459Skarels i++; 586*37459Skarels } 587*37459Skarels /* 588*37459Skarels * We must do this seek to "current" position 589*37459Skarels * because we are changing from reading to 590*37459Skarels * writing. 591*37459Skarels */ 592*37459Skarels if (fseek(fout, 0L, L_INCR) < 0) { 593*37459Skarels perror_reply(550, name); 594*37459Skarels goto done; 595*37459Skarels } 596*37459Skarels } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 597*37459Skarels perror_reply(550, name); 598*37459Skarels goto done; 599*37459Skarels } 600*37459Skarels } 60136304Skarels din = dataconn(name, (off_t)-1, "r"); 60210275Ssam if (din == NULL) 60310275Ssam goto done; 60436620Srick if (receive_data(din, fout) == 0) { 60536620Srick if (unique) 60636304Skarels reply(226, "Transfer complete (unique file name:%s).", 60736933Skarels name); 60836304Skarels else 60936304Skarels reply(226, "Transfer complete."); 61026044Sminshall } 61126493Sminshall (void) fclose(din); 61226044Sminshall data = -1; 61326044Sminshall pdata = -1; 61410275Ssam done: 61510275Ssam (*closefunc)(fout); 61610275Ssam } 61710275Ssam 61810275Ssam FILE * 61910275Ssam getdatasock(mode) 62010275Ssam char *mode; 62110275Ssam { 622*37459Skarels int s, on = 1, tries; 62310275Ssam 62410275Ssam if (data >= 0) 62510275Ssam return (fdopen(data, mode)); 62613247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 62710602Ssam if (s < 0) 62810275Ssam return (NULL); 62936304Skarels (void) seteuid((uid_t)0); 630*37459Skarels if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 631*37459Skarels (char *) &on, sizeof (on)) < 0) 63210602Ssam goto bad; 63313152Ssam /* anchor socket to avoid multi-homing problems */ 63413152Ssam data_source.sin_family = AF_INET; 63513152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 636*37459Skarels for (tries = 1; ; tries++) { 637*37459Skarels if (bind(s, (struct sockaddr *)&data_source, 638*37459Skarels sizeof (data_source)) >= 0) 639*37459Skarels break; 640*37459Skarels if (errno != EADDRINUSE || tries > 10) 641*37459Skarels goto bad; 642*37459Skarels sleep(tries); 643*37459Skarels } 64436304Skarels (void) seteuid((uid_t)pw->pw_uid); 64510275Ssam return (fdopen(s, mode)); 64610602Ssam bad: 64736304Skarels (void) seteuid((uid_t)pw->pw_uid); 64826493Sminshall (void) close(s); 64910602Ssam return (NULL); 65010275Ssam } 65110275Ssam 65210275Ssam FILE * 65310275Ssam dataconn(name, size, mode) 65410275Ssam char *name; 65511653Ssam off_t size; 65610275Ssam char *mode; 65710275Ssam { 65810275Ssam char sizebuf[32]; 65910275Ssam FILE *file; 66011653Ssam int retry = 0; 66110275Ssam 66236933Skarels file_size = size; 66336933Skarels byte_count = 0; 66436304Skarels if (size != (off_t) -1) 66526493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 66610275Ssam else 66710275Ssam (void) strcpy(sizebuf, ""); 66836304Skarels if (pdata >= 0) { 66926044Sminshall struct sockaddr_in from; 67026044Sminshall int s, fromlen = sizeof(from); 67126044Sminshall 67236304Skarels s = accept(pdata, (struct sockaddr *)&from, &fromlen); 67326044Sminshall if (s < 0) { 67426044Sminshall reply(425, "Can't open data connection."); 67526044Sminshall (void) close(pdata); 67626044Sminshall pdata = -1; 67726044Sminshall return(NULL); 67826044Sminshall } 67926044Sminshall (void) close(pdata); 68026044Sminshall pdata = s; 68136235Skarels reply(150, "Opening %s mode data connection for %s%s.", 68236235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 68326044Sminshall return(fdopen(pdata, mode)); 68426044Sminshall } 68510275Ssam if (data >= 0) { 68610275Ssam reply(125, "Using existing data connection for %s%s.", 68710275Ssam name, sizebuf); 68810321Ssam usedefault = 1; 68910275Ssam return (fdopen(data, mode)); 69010275Ssam } 69110566Ssam if (usedefault) 69210422Ssam data_dest = his_addr; 69310422Ssam usedefault = 1; 69410275Ssam file = getdatasock(mode); 69510275Ssam if (file == NULL) { 69610275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 69713247Ssam inet_ntoa(data_source.sin_addr), 69810275Ssam ntohs(data_source.sin_port), 69936304Skarels errno < sys_nerr ? sys_errlist[errno] : "unknown error"); 70010275Ssam return (NULL); 70110275Ssam } 70210275Ssam data = fileno(file); 70336304Skarels while (connect(data, (struct sockaddr *)&data_dest, 70436304Skarels sizeof (data_dest)) < 0) { 70511653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 70626493Sminshall sleep((unsigned) swaitint); 70711653Ssam retry += swaitint; 70811653Ssam continue; 70911653Ssam } 71036304Skarels perror_reply(425, "Can't build data connection"); 71110275Ssam (void) fclose(file); 71210275Ssam data = -1; 71310275Ssam return (NULL); 71410275Ssam } 71536235Skarels reply(150, "Opening %s mode data connection for %s%s.", 71636235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 71710275Ssam return (file); 71810275Ssam } 71910275Ssam 72010275Ssam /* 72110275Ssam * Tranfer the contents of "instr" to 72210275Ssam * "outstr" peer using the appropriate 72336933Skarels * encapsulation of the data subject 72410275Ssam * to Mode, Structure, and Type. 72510275Ssam * 72610275Ssam * NB: Form isn't handled. 72710275Ssam */ 72836446Sbostic send_data(instr, outstr, blksize) 72910275Ssam FILE *instr, *outstr; 73036446Sbostic off_t blksize; 73110275Ssam { 73236446Sbostic register int c, cnt; 73336446Sbostic register char *buf; 73436446Sbostic int netfd, filefd; 73510275Ssam 73626044Sminshall transflag++; 73726044Sminshall if (setjmp(urgcatch)) { 73826044Sminshall transflag = 0; 73936620Srick return; 74026044Sminshall } 74110275Ssam switch (type) { 74210275Ssam 74310275Ssam case TYPE_A: 74410275Ssam while ((c = getc(instr)) != EOF) { 74536933Skarels byte_count++; 74611220Ssam if (c == '\n') { 74736933Skarels if (ferror(outstr)) 74836620Srick goto data_err; 74927750Sminshall (void) putc('\r', outstr); 75011220Ssam } 75127750Sminshall (void) putc(c, outstr); 75210275Ssam } 75336933Skarels fflush(outstr); 75426044Sminshall transflag = 0; 75536933Skarels if (ferror(instr)) 75636933Skarels goto file_err; 75736933Skarels if (ferror(outstr)) 75836620Srick goto data_err; 75936620Srick reply(226, "Transfer complete."); 76036620Srick return; 76136446Sbostic 76210275Ssam case TYPE_I: 76310275Ssam case TYPE_L: 76436446Sbostic if ((buf = malloc((u_int)blksize)) == NULL) { 76536446Sbostic transflag = 0; 76636933Skarels perror_reply(451, "Local resource failure: malloc"); 76736933Skarels return; 76836446Sbostic } 76910275Ssam netfd = fileno(outstr); 77010275Ssam filefd = fileno(instr); 77136933Skarels while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 77236620Srick write(netfd, buf, cnt) == cnt) 77336933Skarels byte_count += cnt; 77426044Sminshall transflag = 0; 77536446Sbostic (void)free(buf); 77636933Skarels if (cnt != 0) { 77736933Skarels if (cnt < 0) 77836933Skarels goto file_err; 77936620Srick goto data_err; 78036933Skarels } 78136620Srick reply(226, "Transfer complete."); 78236620Srick return; 78336620Srick default: 78436620Srick transflag = 0; 78536620Srick reply(550, "Unimplemented TYPE %d in send_data", type); 78636620Srick return; 78710275Ssam } 78836620Srick 78936620Srick data_err: 79026044Sminshall transflag = 0; 79136933Skarels perror_reply(426, "Data connection"); 79236933Skarels return; 79336933Skarels 79436933Skarels file_err: 79536933Skarels transflag = 0; 79636933Skarels perror_reply(551, "Error on input file"); 79710275Ssam } 79810275Ssam 79910275Ssam /* 80010275Ssam * Transfer data from peer to 80110275Ssam * "outstr" using the appropriate 80210275Ssam * encapulation of the data subject 80310275Ssam * to Mode, Structure, and Type. 80410275Ssam * 80510275Ssam * N.B.: Form isn't handled. 80610275Ssam */ 80710275Ssam receive_data(instr, outstr) 80810275Ssam FILE *instr, *outstr; 80910275Ssam { 81010275Ssam register int c; 81111220Ssam int cnt; 81210275Ssam char buf[BUFSIZ]; 81310275Ssam 81426044Sminshall transflag++; 81526044Sminshall if (setjmp(urgcatch)) { 81626044Sminshall transflag = 0; 81736933Skarels return (-1); 81826044Sminshall } 81910275Ssam switch (type) { 82010275Ssam 82110275Ssam case TYPE_I: 82210275Ssam case TYPE_L: 82326044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 82436620Srick if (write(fileno(outstr), buf, cnt) != cnt) 82536933Skarels goto file_err; 82636933Skarels byte_count += cnt; 82726044Sminshall } 82836933Skarels if (cnt < 0) 82936620Srick goto data_err; 83026044Sminshall transflag = 0; 83136933Skarels return (0); 83210275Ssam 83310275Ssam case TYPE_E: 83427106Smckusick reply(553, "TYPE E not implemented."); 83526044Sminshall transflag = 0; 83627106Smckusick return (-1); 83710275Ssam 83810275Ssam case TYPE_A: 83910275Ssam while ((c = getc(instr)) != EOF) { 84036933Skarels byte_count++; 84127750Sminshall while (c == '\r') { 84236933Skarels if (ferror(outstr)) 84336620Srick goto data_err; 84436933Skarels if ((c = getc(instr)) != '\n') { 84527750Sminshall (void) putc ('\r', outstr); 84636933Skarels if (c == '\0' || c == EOF) 84736933Skarels goto contin2; 84836933Skarels } 84910275Ssam } 85036933Skarels (void) putc(c, outstr); 85136933Skarels contin2: ; 85210275Ssam } 85336620Srick fflush(outstr); 85436933Skarels if (ferror(instr)) 85536620Srick goto data_err; 85636933Skarels if (ferror(outstr)) 85736933Skarels goto file_err; 85826044Sminshall transflag = 0; 85910275Ssam return (0); 86036620Srick default: 86136620Srick reply(550, "Unimplemented TYPE %d in receive_data", type); 86236620Srick transflag = 0; 86336933Skarels return (-1); 86410275Ssam } 86536620Srick 86636620Srick data_err: 86726044Sminshall transflag = 0; 86836933Skarels perror_reply(426, "Data Connection"); 86936933Skarels return (-1); 87036933Skarels 87136933Skarels file_err: 87236933Skarels transflag = 0; 87336933Skarels perror_reply(452, "Error writing file"); 87436933Skarels return (-1); 87510275Ssam } 87610275Ssam 87736933Skarels statfilecmd(filename) 87836933Skarels char *filename; 87936933Skarels { 88036933Skarels char line[BUFSIZ]; 88136933Skarels FILE *fin; 88236933Skarels int c; 88336933Skarels 88436933Skarels (void) sprintf(line, "/bin/ls -lgA %s", filename); 88536933Skarels fin = ftpd_popen(line, "r"); 88636933Skarels lreply(211, "status of %s:", filename); 88736933Skarels while ((c = getc(fin)) != EOF) { 88836933Skarels if (c == '\n') { 88936933Skarels if (ferror(stdout)){ 89036933Skarels perror_reply(421, "control connection"); 89136933Skarels (void) ftpd_pclose(fin); 89236933Skarels dologout(1); 89336933Skarels /* NOTREACHED */ 89436933Skarels } 89536933Skarels if (ferror(fin)) { 89636933Skarels perror_reply(551, filename); 89736933Skarels (void) ftpd_pclose(fin); 89836933Skarels return; 89936933Skarels } 90036933Skarels (void) putc('\r', stdout); 90136933Skarels } 90236933Skarels (void) putc(c, stdout); 90336933Skarels } 90436933Skarels (void) ftpd_pclose(fin); 90536933Skarels reply(211, "End of Status"); 90636933Skarels } 90736933Skarels 90836933Skarels statcmd() 90936933Skarels { 91036933Skarels struct sockaddr_in *sin; 91136933Skarels u_char *a, *p; 91236933Skarels 91336933Skarels lreply(211, "%s FTP server status:", hostname, version); 91436933Skarels printf(" %s\r\n", version); 91536933Skarels printf(" Connected to %s", remotehost); 91636933Skarels if (isdigit(remotehost[0])) 91736933Skarels printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 91836933Skarels printf("\r\n"); 91936933Skarels if (logged_in) { 92036933Skarels if (guest) 92136933Skarels printf(" Logged in anonymously\r\n"); 92236933Skarels else 92336933Skarels printf(" Logged in as %s\r\n", pw->pw_name); 92436933Skarels } else if (askpasswd) 92536933Skarels printf(" Waiting for password\r\n"); 92636933Skarels else 92736933Skarels printf(" Waiting for user name\r\n"); 92836933Skarels printf(" TYPE: %s", typenames[type]); 92936933Skarels if (type == TYPE_A || type == TYPE_E) 93036933Skarels printf(", FORM: %s", formnames[form]); 93136933Skarels if (type == TYPE_L) 93236933Skarels #if NBBY == 8 93336933Skarels printf(" %d", NBBY); 93436933Skarels #else 93536933Skarels printf(" %d", bytesize); /* need definition! */ 93636933Skarels #endif 93736933Skarels printf("; STRUcture: %s; transfer MODE: %s\r\n", 93836933Skarels strunames[stru], modenames[mode]); 93936933Skarels if (data != -1) 94036933Skarels printf(" Data connection open\r\n"); 94136933Skarels else if (pdata != -1) { 94236933Skarels printf(" in Passive mode"); 94336933Skarels sin = &pasv_addr; 94436933Skarels goto printaddr; 94536933Skarels } else if (usedefault == 0) { 94636933Skarels printf(" PORT"); 94736933Skarels sin = &data_dest; 94836933Skarels printaddr: 94936933Skarels a = (u_char *) &sin->sin_addr; 95036933Skarels p = (u_char *) &sin->sin_port; 95136933Skarels #define UC(b) (((int) b) & 0xff) 95236933Skarels printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 95336933Skarels UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 95436933Skarels #undef UC 95536933Skarels } else 95636933Skarels printf(" No data connection\r\n"); 95736933Skarels reply(211, "End of status"); 95836933Skarels } 95936933Skarels 96010275Ssam fatal(s) 96110275Ssam char *s; 96210275Ssam { 96310275Ssam reply(451, "Error in server: %s\n", s); 96410275Ssam reply(221, "Closing connection due to server error."); 96513247Ssam dologout(0); 96636620Srick /* NOTREACHED */ 96710275Ssam } 96810275Ssam 96936446Sbostic /* VARARGS2 */ 97036446Sbostic reply(n, fmt, p0, p1, p2, p3, p4, p5) 97110275Ssam int n; 97236446Sbostic char *fmt; 97310275Ssam { 97410275Ssam printf("%d ", n); 97536446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 97610275Ssam printf("\r\n"); 97736435Sbostic (void)fflush(stdout); 97810275Ssam if (debug) { 97926493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 98036446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 98110275Ssam } 98236620Srick } 98310275Ssam 98436446Sbostic /* VARARGS2 */ 98536446Sbostic lreply(n, fmt, p0, p1, p2, p3, p4, p5) 98610275Ssam int n; 98736446Sbostic char *fmt; 98810275Ssam { 98936446Sbostic printf("%d- ", n); 99036446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 99136446Sbostic printf("\r\n"); 99236435Sbostic (void)fflush(stdout); 99336446Sbostic if (debug) { 99436446Sbostic syslog(LOG_DEBUG, "<--- %d- ", n); 99536446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 99636446Sbostic } 99710275Ssam } 99810275Ssam 99910275Ssam ack(s) 100010275Ssam char *s; 100110275Ssam { 100227106Smckusick reply(250, "%s command successful.", s); 100310275Ssam } 100410275Ssam 100510275Ssam nack(s) 100610275Ssam char *s; 100710275Ssam { 100810275Ssam reply(502, "%s command not implemented.", s); 100910275Ssam } 101010275Ssam 101136304Skarels /* ARGSUSED */ 101226493Sminshall yyerror(s) 101326493Sminshall char *s; 101410275Ssam { 101526044Sminshall char *cp; 101626044Sminshall 101736551Sbostic if (cp = index(cbuf,'\n')) 101836551Sbostic *cp = '\0'; 101936933Skarels reply(500, "'%s': command not understood.", cbuf); 102010275Ssam } 102110275Ssam 102210275Ssam delete(name) 102310275Ssam char *name; 102410275Ssam { 102510275Ssam struct stat st; 102610275Ssam 102710275Ssam if (stat(name, &st) < 0) { 102836304Skarels perror_reply(550, name); 102910275Ssam return; 103010275Ssam } 103110275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 103210275Ssam if (rmdir(name) < 0) { 103336304Skarels perror_reply(550, name); 103410275Ssam return; 103510275Ssam } 103610275Ssam goto done; 103710275Ssam } 103810275Ssam if (unlink(name) < 0) { 103936304Skarels perror_reply(550, name); 104010275Ssam return; 104110275Ssam } 104210275Ssam done: 104310275Ssam ack("DELE"); 104410275Ssam } 104510275Ssam 104610275Ssam cwd(path) 104710275Ssam char *path; 104810275Ssam { 104936620Srick if (chdir(path) < 0) 105036304Skarels perror_reply(550, path); 105136620Srick else 105236620Srick ack("CWD"); 105310275Ssam } 105410275Ssam 105510303Ssam makedir(name) 105610275Ssam char *name; 105710275Ssam { 105836276Sbostic if (mkdir(name, 0777) < 0) 105936304Skarels perror_reply(550, name); 106036276Sbostic else 106136276Sbostic reply(257, "MKD command successful."); 106210275Ssam } 106310275Ssam 106410303Ssam removedir(name) 106510275Ssam char *name; 106610275Ssam { 106736620Srick if (rmdir(name) < 0) 106836304Skarels perror_reply(550, name); 106936620Srick else 107036620Srick ack("RMD"); 107110275Ssam } 107210275Ssam 107310303Ssam pwd() 107410275Ssam { 107510303Ssam char path[MAXPATHLEN + 1]; 107636304Skarels extern char *getwd(); 107710275Ssam 107836620Srick if (getwd(path) == (char *)NULL) 107927106Smckusick reply(550, "%s.", path); 108036620Srick else 108136620Srick reply(257, "\"%s\" is current directory.", path); 108210275Ssam } 108310275Ssam 108410275Ssam char * 108510275Ssam renamefrom(name) 108610275Ssam char *name; 108710275Ssam { 108810275Ssam struct stat st; 108910275Ssam 109010275Ssam if (stat(name, &st) < 0) { 109136304Skarels perror_reply(550, name); 109210275Ssam return ((char *)0); 109310275Ssam } 109410303Ssam reply(350, "File exists, ready for destination name"); 109510275Ssam return (name); 109610275Ssam } 109710275Ssam 109810275Ssam renamecmd(from, to) 109910275Ssam char *from, *to; 110010275Ssam { 110136620Srick if (rename(from, to) < 0) 110236304Skarels perror_reply(550, "rename"); 110336620Srick else 110436620Srick ack("RNTO"); 110510275Ssam } 110610275Ssam 110710275Ssam dolog(sin) 110810275Ssam struct sockaddr_in *sin; 110910275Ssam { 111036304Skarels struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 111110275Ssam sizeof (struct in_addr), AF_INET); 111236304Skarels time_t t, time(); 111326493Sminshall extern char *ctime(); 111410275Ssam 111536304Skarels if (hp) 111626493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 111736304Skarels else 111826493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 111913247Ssam sizeof (remotehost)); 112036620Srick #ifdef SETPROCTITLE 112136933Skarels sprintf(proctitle, "%s: connected", remotehost); 112236933Skarels setproctitle(proctitle); 112336620Srick #endif /* SETPROCTITLE */ 112436933Skarels 112536933Skarels if (logging) { 112636933Skarels t = time((time_t *) 0); 112736933Skarels syslog(LOG_INFO, "connection from %s at %s", 112836933Skarels remotehost, ctime(&t)); 112936933Skarels } 113010275Ssam } 113110695Ssam 113210695Ssam /* 113313247Ssam * Record logout in wtmp file 113413247Ssam * and exit with supplied status. 113513247Ssam */ 113613247Ssam dologout(status) 113713247Ssam int status; 113813247Ssam { 113917580Ssam if (logged_in) { 114036304Skarels (void) seteuid((uid_t)0); 114135672Sbostic logwtmp(ttyline, "", ""); 114213247Ssam } 114314436Ssam /* beware of flushing buffers after a SIGPIPE */ 114414436Ssam _exit(status); 114513247Ssam } 114613247Ssam 114726044Sminshall myoob() 114826044Sminshall { 114927750Sminshall char *cp; 115026044Sminshall 115127750Sminshall /* only process if transfer occurring */ 115236304Skarels if (!transflag) 115326044Sminshall return; 115427750Sminshall cp = tmpline; 115527750Sminshall if (getline(cp, 7, stdin) == NULL) { 115636304Skarels reply(221, "You could at least say goodbye."); 115727750Sminshall dologout(0); 115826044Sminshall } 115926044Sminshall upper(cp); 116036933Skarels if (strcmp(cp, "ABOR\r\n") == 0) { 116136933Skarels tmpline[0] = '\0'; 116236933Skarels reply(426, "Transfer aborted. Data connection closed."); 116336933Skarels reply(226, "Abort successful"); 116436933Skarels longjmp(urgcatch, 1); 116536933Skarels } 116636933Skarels if (strcmp(cp, "STAT\r\n") == 0) { 116736933Skarels if (file_size != (off_t) -1) 116836933Skarels reply(213, "Status: %lu of %lu bytes transferred", 116936933Skarels byte_count, file_size); 117036933Skarels else 117136933Skarels reply(213, "Status: %lu bytes transferred", byte_count); 117236933Skarels } 117326044Sminshall } 117426044Sminshall 117527106Smckusick /* 117636620Srick * Note: a response of 425 is not mentioned as a possible response to 117736620Srick * the PASV command in RFC959. However, it has been blessed as 117836620Srick * a legitimate response by Jon Postel in a telephone conversation 117936620Srick * with Rick Adams on 25 Jan 89. 118027106Smckusick */ 118126044Sminshall passive() 118226044Sminshall { 118326044Sminshall int len; 118426044Sminshall register char *p, *a; 118526044Sminshall 118626044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 118726044Sminshall if (pdata < 0) { 118836620Srick perror_reply(425, "Can't open passive connection"); 118926044Sminshall return; 119026044Sminshall } 119136933Skarels pasv_addr = ctrl_addr; 119236933Skarels pasv_addr.sin_port = 0; 119336304Skarels (void) seteuid((uid_t)0); 119436933Skarels if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { 119536304Skarels (void) seteuid((uid_t)pw->pw_uid); 119636620Srick goto pasv_error; 119726044Sminshall } 119836304Skarels (void) seteuid((uid_t)pw->pw_uid); 119936933Skarels len = sizeof(pasv_addr); 120036933Skarels if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 120136620Srick goto pasv_error; 120236620Srick if (listen(pdata, 1) < 0) 120336620Srick goto pasv_error; 120436933Skarels a = (char *) &pasv_addr.sin_addr; 120536933Skarels p = (char *) &pasv_addr.sin_port; 120626044Sminshall 120726044Sminshall #define UC(b) (((int) b) & 0xff) 120826044Sminshall 120926044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 121026044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 121136620Srick return; 121236620Srick 121336620Srick pasv_error: 121436620Srick (void) close(pdata); 121536620Srick pdata = -1; 121636620Srick perror_reply(425, "Can't open passive connection"); 121736620Srick return; 121826044Sminshall } 121926044Sminshall 122036304Skarels /* 122136304Skarels * Generate unique name for file with basename "local". 122236304Skarels * The file named "local" is already known to exist. 122336304Skarels * Generates failure reply on error. 122436304Skarels */ 122526044Sminshall char * 122626044Sminshall gunique(local) 122726044Sminshall char *local; 122826044Sminshall { 122926044Sminshall static char new[MAXPATHLEN]; 123036304Skarels struct stat st; 123126044Sminshall char *cp = rindex(local, '/'); 123236933Skarels int count = 0; 123326044Sminshall 123436304Skarels if (cp) 123526044Sminshall *cp = '\0'; 123636620Srick if (stat(cp ? local : ".", &st) < 0) { 123736933Skarels perror_reply(553, cp ? local : "."); 123826044Sminshall return((char *) 0); 123926044Sminshall } 124036620Srick if (cp) 124136620Srick *cp = '/'; 124226044Sminshall (void) strcpy(new, local); 124326044Sminshall cp = new + strlen(new); 124426044Sminshall *cp++ = '.'; 124536304Skarels for (count = 1; count < 100; count++) { 124636304Skarels (void) sprintf(cp, "%d", count); 124736304Skarels if (stat(new, &st) < 0) 124836304Skarels return(new); 124926044Sminshall } 125036304Skarels reply(452, "Unique file name cannot be created."); 125136304Skarels return((char *) 0); 125226044Sminshall } 125336304Skarels 125436304Skarels /* 125536304Skarels * Format and send reply containing system error number. 125636304Skarels */ 125736304Skarels perror_reply(code, string) 125836304Skarels int code; 125936304Skarels char *string; 126036304Skarels { 126136304Skarels if (errno < sys_nerr) 126236304Skarels reply(code, "%s: %s.", string, sys_errlist[errno]); 126336304Skarels else 126436304Skarels reply(code, "%s: unknown error %d.", string, errno); 126536304Skarels } 126636620Srick 126736620Srick static char *onefile[] = { 126836620Srick "", 126936620Srick 0 127036620Srick }; 127136620Srick 127236620Srick send_file_list(whichfiles) 127336620Srick char *whichfiles; 127436620Srick { 127536620Srick struct stat st; 127636620Srick DIR *dirp = NULL; 127736620Srick struct direct *dir; 127836620Srick FILE *dout = NULL; 127936620Srick register char **dirlist, *dirname; 1280*37459Skarels int simple = 0; 128136620Srick char *strpbrk(); 128236620Srick 128336620Srick if (strpbrk(whichfiles, "~{[*?") != NULL) { 128436620Srick extern char **glob(), *globerr; 128536933Skarels 128636620Srick globerr = NULL; 128736620Srick dirlist = glob(whichfiles); 128836620Srick if (globerr != NULL) { 128936620Srick reply(550, globerr); 129036620Srick return; 129136620Srick } else if (dirlist == NULL) { 129236620Srick errno = ENOENT; 129336620Srick perror_reply(550, whichfiles); 129436620Srick return; 129536620Srick } 129636620Srick } else { 129736620Srick onefile[0] = whichfiles; 129836620Srick dirlist = onefile; 1299*37459Skarels simple = 1; 130036620Srick } 130136933Skarels 130236933Skarels if (setjmp(urgcatch)) { 130336933Skarels transflag = 0; 130436933Skarels return; 130536933Skarels } 130636620Srick while (dirname = *dirlist++) { 130736620Srick if (stat(dirname, &st) < 0) { 130836933Skarels /* 130936933Skarels * If user typed "ls -l", etc, and the client 131036933Skarels * used NLST, do what the user meant. 131136933Skarels */ 131236933Skarels if (dirname[0] == '-' && *dirlist == NULL && 131336933Skarels transflag == 0) { 131436933Skarels retrieve("/bin/ls %s", dirname); 131536933Skarels return; 131636933Skarels } 131736620Srick perror_reply(550, whichfiles); 131836620Srick if (dout != NULL) { 131936620Srick (void) fclose(dout); 132036933Skarels transflag = 0; 132136620Srick data = -1; 132236620Srick pdata = -1; 132336620Srick } 132436620Srick return; 132536620Srick } 132636933Skarels 132736620Srick if ((st.st_mode&S_IFMT) == S_IFREG) { 132836620Srick if (dout == NULL) { 1329*37459Skarels dout = dataconn("file list", (off_t)-1, "w"); 133036620Srick if (dout == NULL) 133136620Srick return; 133236933Skarels transflag++; 133336620Srick } 133436620Srick fprintf(dout, "%s\n", dirname); 133536933Skarels byte_count += strlen(dirname) + 1; 133636620Srick continue; 133736933Skarels } else if ((st.st_mode&S_IFMT) != S_IFDIR) 133836620Srick continue; 133936620Srick 134036620Srick if ((dirp = opendir(dirname)) == NULL) 134136620Srick continue; 134236620Srick 134336620Srick while ((dir = readdir(dirp)) != NULL) { 134436933Skarels char nbuf[MAXPATHLEN]; 134536620Srick 134636620Srick if (dir->d_name[0] == '.' && dir->d_namlen == 1) 134736620Srick continue; 134836933Skarels if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 134936933Skarels dir->d_namlen == 2) 135036620Srick continue; 135136620Srick 135236933Skarels sprintf(nbuf, "%s/%s", dirname, dir->d_name); 135336933Skarels 135436620Srick /* 135536933Skarels * We have to do a stat to insure it's 135636933Skarels * not a directory or special file. 135736620Srick */ 1358*37459Skarels if (simple || (stat(nbuf, &st) == 0 && 1359*37459Skarels (st.st_mode&S_IFMT) == S_IFREG)) { 136036620Srick if (dout == NULL) { 1361*37459Skarels dout = dataconn("file list", (off_t)-1, 136236620Srick "w"); 136336620Srick if (dout == NULL) 136436620Srick return; 136536933Skarels transflag++; 136636620Srick } 136736620Srick if (nbuf[0] == '.' && nbuf[1] == '/') 136836620Srick fprintf(dout, "%s\n", &nbuf[2]); 136936620Srick else 137036620Srick fprintf(dout, "%s\n", nbuf); 137136933Skarels byte_count += strlen(nbuf) + 1; 137236620Srick } 137336620Srick } 137436620Srick (void) closedir(dirp); 137536620Srick } 137636620Srick 137736933Skarels if (dout == NULL) 137836933Skarels reply(550, "No files found."); 137936933Skarels else if (ferror(dout) != 0) 138036933Skarels perror_reply(550, "Data connection"); 138136933Skarels else 138236620Srick reply(226, "Transfer complete."); 138336620Srick 138436933Skarels transflag = 0; 138536933Skarels if (dout != NULL) 138636620Srick (void) fclose(dout); 138736620Srick data = -1; 138836620Srick pdata = -1; 138936620Srick } 139036620Srick 139136620Srick #ifdef SETPROCTITLE 139236620Srick /* 139336620Srick * clobber argv so ps will show what we're doing. 139436620Srick * (stolen from sendmail) 139536620Srick * warning, since this is usually started from inetd.conf, it 139636620Srick * often doesn't have much of an environment or arglist to overwrite. 139736620Srick */ 139836620Srick 139936620Srick /*VARARGS1*/ 140036620Srick setproctitle(fmt, a, b, c) 140136620Srick char *fmt; 140236620Srick { 140336620Srick register char *p, *bp, ch; 140436620Srick register int i; 140536620Srick char buf[BUFSIZ]; 140636620Srick 140736620Srick (void) sprintf(buf, fmt, a, b, c); 140836620Srick 140936620Srick /* make ps print our process name */ 141036620Srick p = Argv[0]; 141136620Srick *p++ = '-'; 141236620Srick 141336620Srick i = strlen(buf); 141436620Srick if (i > LastArgv - p - 2) { 141536620Srick i = LastArgv - p - 2; 141636620Srick buf[i] = '\0'; 141736620Srick } 141836620Srick bp = buf; 141936620Srick while (ch = *bp++) 142036620Srick if (ch != '\n' && ch != '\r') 142136620Srick *p++ = ch; 142236620Srick while (p < LastArgv) 142336620Srick *p++ = ' '; 142436620Srick } 142536620Srick #endif /* SETPROCTITLE */ 1426