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 15*36933Skarels * 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*36933Skarels static char sccsid[] = "@(#)ftpd.c 5.27 (Berkeley) 02/28/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 41*36933Skarels #define FTP_NAMES 4213034Ssam #include <arpa/ftp.h> 4313211Sroot #include <arpa/inet.h> 4426044Sminshall #include <arpa/telnet.h> 4513034Ssam 46*36933Skarels #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> 5610275Ssam 5710695Ssam /* 5810695Ssam * File containing login names 5910695Ssam * NOT to be used on this machine. 6010695Ssam * Commonly used to disallow uucp. 6110695Ssam */ 6210695Ssam #define FTPUSERS "/etc/ftpusers" 6310695Ssam 6410275Ssam extern int errno; 6510275Ssam extern char *sys_errlist[]; 6636304Skarels extern int sys_nerr; 6710275Ssam extern char *crypt(); 6810275Ssam extern char version[]; 6910275Ssam extern char *home; /* pointer to home directory for glob */ 7036276Sbostic extern FILE *ftpd_popen(), *fopen(), *freopen(); 7136304Skarels extern int ftpd_pclose(), fclose(); 7226044Sminshall extern char *getline(); 7326044Sminshall extern char cbuf[]; 7436551Sbostic extern off_t restart_point; 7510275Ssam 7610275Ssam struct sockaddr_in ctrl_addr; 7710275Ssam struct sockaddr_in data_source; 7810275Ssam struct sockaddr_in data_dest; 7910275Ssam struct sockaddr_in his_addr; 80*36933Skarels struct sockaddr_in pasv_addr; 8110275Ssam 8210275Ssam int data; 8326044Sminshall jmp_buf errcatch, urgcatch; 8410275Ssam int logged_in; 8510275Ssam struct passwd *pw; 8610275Ssam int debug; 8726493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */ 88*36933Skarels int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 8911757Ssam int logging; 9010275Ssam int guest; 9110275Ssam int type; 9210275Ssam int form; 9310275Ssam int stru; /* avoid C keyword */ 9410275Ssam int mode; 9510321Ssam int usedefault = 1; /* for data transfers */ 9636304Skarels int pdata = -1; /* for passive mode */ 9726044Sminshall int transflag; 98*36933Skarels off_t file_size; 99*36933Skarels off_t byte_count; 100*36933Skarels #if !defined(CMASK) || CMASK == 0 101*36933Skarels #undef CMASK 102*36933Skarels #define CMASK 027 103*36933Skarels #endif 104*36933Skarels int defumask = CMASK; /* default umask value */ 10526044Sminshall char tmpline[7]; 10636276Sbostic char hostname[MAXHOSTNAMELEN]; 10736276Sbostic char remotehost[MAXHOSTNAMELEN]; 10810275Ssam 10911653Ssam /* 11011653Ssam * Timeout intervals for retrying connections 11111653Ssam * to hosts that don't accept PORT cmds. This 11211653Ssam * is a kludge, but given the problems with TCP... 11311653Ssam */ 11411653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 11511653Ssam #define SWAITINT 5 /* interval between retries */ 11611653Ssam 11711653Ssam int swaitmax = SWAITMAX; 11811653Ssam int swaitint = SWAITINT; 11911653Ssam 12010275Ssam int lostconn(); 12126044Sminshall int myoob(); 12210275Ssam FILE *getdatasock(), *dataconn(); 12310275Ssam 12436620Srick #ifdef SETPROCTITLE 12536620Srick char **Argv = NULL; /* pointer to argument vector */ 12636620Srick char *LastArgv = NULL; /* end of argv */ 127*36933Skarels char proctitle[BUFSIZ]; /* initial part of title */ 12836620Srick #endif /* SETPROCTITLE */ 12936620Srick 13036620Srick main(argc, argv, envp) 13110275Ssam int argc; 13210275Ssam char *argv[]; 13336620Srick char **envp; 13410275Ssam { 13527750Sminshall int addrlen, on = 1; 13610275Ssam char *cp; 13710275Ssam 13816339Skarels addrlen = sizeof (his_addr); 13936304Skarels if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 14026493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 14110275Ssam exit(1); 14210275Ssam } 14316339Skarels addrlen = sizeof (ctrl_addr); 14436304Skarels if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 14526493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 14616339Skarels exit(1); 14716339Skarels } 14816339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 14910275Ssam debug = 0; 15026493Sminshall openlog("ftpd", LOG_PID, LOG_DAEMON); 15136620Srick #ifdef SETPROCTITLE 15236620Srick /* 15336620Srick * Save start and extent of argv for setproctitle. 15436620Srick */ 15536620Srick Argv = argv; 15636620Srick while (*envp) 15736620Srick envp++; 15836620Srick LastArgv = envp[-1] + strlen(envp[-1]); 15936620Srick #endif /* SETPROCTITLE */ 16036620Srick 16110275Ssam argc--, argv++; 16210275Ssam while (argc > 0 && *argv[0] == '-') { 16310275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 16410275Ssam 16511653Ssam case 'v': 16611653Ssam debug = 1; 16711653Ssam break; 16811653Ssam 16910275Ssam case 'd': 17010275Ssam debug = 1; 17110275Ssam break; 17210275Ssam 17311757Ssam case 'l': 17411757Ssam logging = 1; 17511757Ssam break; 17611757Ssam 17711653Ssam case 't': 17811653Ssam timeout = atoi(++cp); 179*36933Skarels if (maxtimeout < timeout) 180*36933Skarels maxtimeout = timeout; 18111653Ssam goto nextopt; 18211653Ssam 183*36933Skarels case 'T': 184*36933Skarels maxtimeout = atoi(++cp); 185*36933Skarels if (timeout > maxtimeout) 186*36933Skarels timeout = maxtimeout; 187*36933Skarels goto nextopt; 188*36933Skarels 189*36933Skarels case 'u': 190*36933Skarels { 191*36933Skarels int val = 0; 192*36933Skarels 193*36933Skarels while (*++cp && *cp >= '0' && *cp <= '9') 194*36933Skarels val = val*8 + *cp - '0'; 195*36933Skarels if (*cp) 196*36933Skarels fprintf(stderr, "ftpd: Bad value for -u\n"); 197*36933Skarels else 198*36933Skarels defumask = val; 199*36933Skarels goto nextopt; 200*36933Skarels } 201*36933Skarels 20210275Ssam default: 20316339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 20416339Skarels *cp); 20510275Ssam break; 20610275Ssam } 20711653Ssam nextopt: 20810275Ssam argc--, argv++; 20910275Ssam } 21030944Scsvsj (void) freopen("/dev/null", "w", stderr); 21126493Sminshall (void) signal(SIGPIPE, lostconn); 21226493Sminshall (void) signal(SIGCHLD, SIG_IGN); 21335691Sbostic if ((int)signal(SIGURG, myoob) < 0) 21426493Sminshall syslog(LOG_ERR, "signal: %m"); 21535691Sbostic 21627750Sminshall /* handle urgent data inline */ 21736276Sbostic /* Sequent defines this, but it doesn't work */ 21827750Sminshall #ifdef SO_OOBINLINE 21936276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 22027750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 22136276Sbostic #endif 222*36933Skarels #ifdef F_SETOWN 22336304Skarels if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 22436304Skarels syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 225*36933Skarels #endif 22616760Slepreau dolog(&his_addr); 22716339Skarels /* 22816339Skarels * Set up default state 22916339Skarels */ 23016339Skarels data = -1; 23116339Skarels type = TYPE_A; 23216339Skarels form = FORM_N; 23316339Skarels stru = STRU_F; 23416339Skarels mode = MODE_S; 23526044Sminshall tmpline[0] = '\0'; 23626493Sminshall (void) gethostname(hostname, sizeof (hostname)); 23736276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version); 23836304Skarels (void) setjmp(errcatch); 23936304Skarels for (;;) 24026493Sminshall (void) yyparse(); 24136620Srick /* NOTREACHED */ 24210275Ssam } 24310419Ssam 24410275Ssam lostconn() 24510275Ssam { 24610275Ssam 24714089Ssam if (debug) 24826493Sminshall syslog(LOG_DEBUG, "lost connection"); 24914089Ssam dologout(-1); 25010275Ssam } 25110275Ssam 25235672Sbostic static char ttyline[20]; 25335672Sbostic 25436185Sbostic /* 25536185Sbostic * Helper function for sgetpwnam(). 25636185Sbostic */ 25736185Sbostic char * 25836185Sbostic sgetsave(s) 25936185Sbostic char *s; 26036185Sbostic { 26136185Sbostic char *malloc(); 26236185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 263*36933Skarels 26436185Sbostic if (new == NULL) { 26536620Srick perror_reply(421, "Local resource failure: malloc"); 26636185Sbostic dologout(1); 26736620Srick /* NOTREACHED */ 26836185Sbostic } 26936185Sbostic (void) strcpy(new, s); 27036185Sbostic return (new); 27136185Sbostic } 27236185Sbostic 27336185Sbostic /* 27436185Sbostic * Save the result of a getpwnam. Used for USER command, since 27536185Sbostic * the data returned must not be clobbered by any other command 27636185Sbostic * (e.g., globbing). 27736185Sbostic */ 27836185Sbostic struct passwd * 27936185Sbostic sgetpwnam(name) 28036185Sbostic char *name; 28136185Sbostic { 28236185Sbostic static struct passwd save; 28336185Sbostic register struct passwd *p; 28436185Sbostic char *sgetsave(); 28536185Sbostic 28636185Sbostic if ((p = getpwnam(name)) == NULL) 28736185Sbostic return (p); 28836185Sbostic if (save.pw_name) { 28936185Sbostic free(save.pw_name); 29036185Sbostic free(save.pw_passwd); 29136185Sbostic free(save.pw_gecos); 29236185Sbostic free(save.pw_dir); 29336185Sbostic free(save.pw_shell); 29436185Sbostic } 29536185Sbostic save = *p; 29636185Sbostic save.pw_name = sgetsave(p->pw_name); 29736185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 29836185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 29936185Sbostic save.pw_dir = sgetsave(p->pw_dir); 30036185Sbostic save.pw_shell = sgetsave(p->pw_shell); 30136185Sbostic return (&save); 30236185Sbostic } 30336185Sbostic 30436304Skarels int login_attempts; /* number of failed login attempts */ 30536304Skarels int askpasswd; /* had user command, ask for passwd */ 30636304Skarels 30736304Skarels /* 30836304Skarels * USER command. 30936304Skarels * Sets global passwd pointer pw if named account exists 31036304Skarels * and is acceptable; sets askpasswd if a PASS command is 31136304Skarels * expected. If logged in previously, need to reset state. 31236304Skarels * If name is "ftp" or "anonymous" and ftp account exists, 31336304Skarels * set guest and pw, then just return. 31436304Skarels * If account doesn't exist, ask for passwd anyway. 31536304Skarels * Otherwise, check user requesting login privileges. 31636304Skarels * Disallow anyone who does not have a standard 31736304Skarels * shell returned by getusershell() (/etc/shells). 31836304Skarels * Disallow anyone mentioned in the file FTPUSERS 31936304Skarels * to allow people such as root and uucp to be avoided. 32036304Skarels */ 32136304Skarels user(name) 32236304Skarels char *name; 32336304Skarels { 32436304Skarels register char *cp; 32536304Skarels FILE *fd; 32636304Skarels char *shell; 32736551Sbostic char line[BUFSIZ], *getusershell(); 32836304Skarels 32936304Skarels if (logged_in) { 33036304Skarels if (guest) { 33136304Skarels reply(530, "Can't change user from guest login."); 33236304Skarels return; 33336304Skarels } 33436304Skarels end_login(); 33536304Skarels } 33636304Skarels 33736304Skarels guest = 0; 33836304Skarels if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 33936304Skarels if ((pw = sgetpwnam("ftp")) != NULL) { 34036304Skarels guest = 1; 34136304Skarels askpasswd = 1; 34236304Skarels reply(331, "Guest login ok, send ident as password."); 343*36933Skarels } else 34436304Skarels reply(530, "User %s unknown.", name); 34536304Skarels return; 34636304Skarels } 34736304Skarels if (pw = sgetpwnam(name)) { 34836304Skarels if ((shell = pw->pw_shell) == NULL || *shell == 0) 34936304Skarels shell = "/bin/sh"; 35036304Skarels while ((cp = getusershell()) != NULL) 35136304Skarels if (strcmp(cp, shell) == 0) 35236304Skarels break; 35336304Skarels endusershell(); 35436304Skarels if (cp == NULL) { 35536304Skarels reply(530, "User %s access denied.", name); 356*36933Skarels if (logging) 357*36933Skarels syslog(LOG_NOTICE, 358*36933Skarels "FTP LOGIN REFUSED FROM %s, %s", 359*36933Skarels remotehost, name); 36036304Skarels pw = (struct passwd *) NULL; 36136304Skarels return; 36236304Skarels } 36336304Skarels if ((fd = fopen(FTPUSERS, "r")) != NULL) { 36436304Skarels while (fgets(line, sizeof (line), fd) != NULL) { 36536304Skarels if ((cp = index(line, '\n')) != NULL) 36636304Skarels *cp = '\0'; 36736304Skarels if (strcmp(line, name) == 0) { 36836304Skarels reply(530, "User %s access denied.", name); 369*36933Skarels if (logging) 370*36933Skarels syslog(LOG_NOTICE, 371*36933Skarels "FTP LOGIN REFUSED FROM %s, %s", 372*36933Skarels remotehost, name); 37336304Skarels pw = (struct passwd *) NULL; 37436304Skarels return; 37536304Skarels } 37636304Skarels } 37736304Skarels } 37836304Skarels (void) fclose(fd); 37936304Skarels } 38036304Skarels reply(331, "Password required for %s.", name); 38136304Skarels askpasswd = 1; 38236304Skarels /* 38336304Skarels * Delay before reading passwd after first failed 38436304Skarels * attempt to slow down passwd-guessing programs. 38536304Skarels */ 38636304Skarels if (login_attempts) 38736304Skarels sleep((unsigned) login_attempts); 38836304Skarels } 38936304Skarels 39036304Skarels /* 39136304Skarels * Terminate login as previous user, if any, resetting state; 39236304Skarels * used when USER command is given or login fails. 39336304Skarels */ 39436304Skarels end_login() 39536304Skarels { 39636304Skarels 39736304Skarels (void) seteuid((uid_t)0); 39836304Skarels if (logged_in) 39936304Skarels logwtmp(ttyline, "", ""); 40036304Skarels pw = NULL; 40136304Skarels logged_in = 0; 40236304Skarels guest = 0; 40336304Skarels } 40436304Skarels 40510275Ssam pass(passwd) 40610275Ssam char *passwd; 40710275Ssam { 40836304Skarels char *xpasswd, *salt; 40910275Ssam 41036304Skarels if (logged_in || askpasswd == 0) { 41110275Ssam reply(503, "Login with USER first."); 41210275Ssam return; 41310275Ssam } 41436304Skarels askpasswd = 0; 41510275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 41636304Skarels if (pw == NULL) 41736304Skarels salt = "xx"; 41836304Skarels else 41936304Skarels salt = pw->pw_passwd; 42036304Skarels xpasswd = crypt(passwd, salt); 42116760Slepreau /* The strcmp does not catch null passwords! */ 42236304Skarels if (pw == NULL || *pw->pw_passwd == '\0' || 42336304Skarels strcmp(xpasswd, pw->pw_passwd)) { 42410275Ssam reply(530, "Login incorrect."); 42510275Ssam pw = NULL; 42636304Skarels if (login_attempts++ >= 5) { 427*36933Skarels syslog(LOG_NOTICE, 42836304Skarels "repeated login failures from %s", 42936304Skarels remotehost); 43036304Skarels exit(0); 43136304Skarels } 43210275Ssam return; 43310275Ssam } 43410275Ssam } 43536304Skarels login_attempts = 0; /* this time successful */ 43636304Skarels (void) setegid((gid_t)pw->pw_gid); 43736304Skarels (void) initgroups(pw->pw_name, pw->pw_gid); 43816033Sralph 43936192Sbostic /* open wtmp before chroot */ 44036192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 44136192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 44236192Sbostic logged_in = 1; 44336192Sbostic 44436446Sbostic if (guest) { 44536620Srick /* 446*36933Skarels * We MUST do a chdir() after the chroot. Otherwise 447*36933Skarels * the old current directory will be accessible as "." 448*36933Skarels * outside the new root! 44936620Srick */ 45036620Srick if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 45136446Sbostic reply(550, "Can't set guest privileges."); 45236446Sbostic goto bad; 45336446Sbostic } 454*36933Skarels } else if (chdir(pw->pw_dir) < 0) { 45536620Srick if (chdir("/") < 0) { 45636446Sbostic reply(530, "User %s: can't change directory to %s.", 45736446Sbostic pw->pw_name, pw->pw_dir); 45836446Sbostic goto bad; 459*36933Skarels } else 46036446Sbostic lreply(230, "No directory! Logging in with home=/"); 46136620Srick } 46236304Skarels if (seteuid((uid_t)pw->pw_uid) < 0) { 46336304Skarels reply(550, "Can't set uid."); 46436304Skarels goto bad; 46536304Skarels } 46636550Sbostic if (guest) { 46736192Sbostic reply(230, "Guest login ok, access restrictions apply."); 468*36933Skarels #ifdef SETPROCTITLE 469*36933Skarels sprintf(proctitle, "%s: anonymous/%.*s", remotehost, 470*36933Skarels sizeof(proctitle) - sizeof(remotehost) - 471*36933Skarels sizeof(": anonymous/"), passwd); 472*36933Skarels setproctitle(proctitle); 473*36933Skarels #endif /* SETPROCTITLE */ 474*36933Skarels if (logging) 475*36933Skarels syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 476*36933Skarels remotehost, passwd); 477*36933Skarels } else { 47810275Ssam reply(230, "User %s logged in.", pw->pw_name); 479*36933Skarels #ifdef SETPROCTITLE 480*36933Skarels sprintf(proctitle, "%s: %s", remotehost, pw->pw_name); 481*36933Skarels setproctitle(proctitle); 482*36933Skarels #endif /* SETPROCTITLE */ 483*36933Skarels if (logging) 484*36933Skarels syslog(LOG_INFO, "FTP LOGIN FROM %s, %s", 485*36933Skarels remotehost, pw->pw_name); 48636550Sbostic } 48710303Ssam home = pw->pw_dir; /* home dir for globbing */ 488*36933Skarels (void) umask(defumask); 48910303Ssam return; 49010303Ssam bad: 49136304Skarels /* Forget all about it... */ 49236304Skarels end_login(); 49310275Ssam } 49410275Ssam 49510275Ssam retrieve(cmd, name) 49610275Ssam char *cmd, *name; 49710275Ssam { 49810275Ssam FILE *fin, *dout; 49910275Ssam struct stat st; 50036620Srick int (*closefunc)(); 50110275Ssam 50236557Sbostic if (cmd == 0) { 50336446Sbostic fin = fopen(name, "r"), closefunc = fclose; 50436557Sbostic st.st_size = 0; 50536557Sbostic } else { 50610275Ssam char line[BUFSIZ]; 50710275Ssam 50826493Sminshall (void) sprintf(line, cmd, name), name = line; 50936304Skarels fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 51036557Sbostic st.st_size = -1; 511*36933Skarels st.st_blksize = BUFSIZ; 51210275Ssam } 51310275Ssam if (fin == NULL) { 51413152Ssam if (errno != 0) 51536304Skarels perror_reply(550, name); 51610275Ssam return; 51710275Ssam } 51810275Ssam if (cmd == 0 && 519*36933Skarels (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 52010275Ssam reply(550, "%s: not a plain file.", name); 52110275Ssam goto done; 52210275Ssam } 52336620Srick if (restart_point) { 52436551Sbostic if (type == TYPE_A) { 525*36933Skarels register int i, n, c; 526*36933Skarels 527*36933Skarels n = restart_point; 528*36933Skarels i = 0; 529*36933Skarels while (i++ < n) { 530*36933Skarels if ((c=getc(fin)) == EOF) { 531*36933Skarels perror_reply(550, name); 532*36933Skarels goto done; 533*36933Skarels } 534*36933Skarels if (c == '\n') 535*36933Skarels i++; 536*36933Skarels } 537*36933Skarels } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 53836551Sbostic perror_reply(550, name); 53936620Srick goto done; 54036620Srick } 54136620Srick } 54210275Ssam dout = dataconn(name, st.st_size, "w"); 54310275Ssam if (dout == NULL) 54410275Ssam goto done; 54536620Srick send_data(fin, dout, st.st_blksize); 54626493Sminshall (void) fclose(dout); 54726044Sminshall data = -1; 54826044Sminshall pdata = -1; 54910275Ssam done: 55010275Ssam (*closefunc)(fin); 55110275Ssam } 55210275Ssam 55336304Skarels store(name, mode, unique) 55410275Ssam char *name, *mode; 55536304Skarels int unique; 55610275Ssam { 55710275Ssam FILE *fout, *din; 55836446Sbostic struct stat st; 55936620Srick int (*closefunc)(); 56036304Skarels char *gunique(); 56110275Ssam 56236446Sbostic if (unique && stat(name, &st) == 0 && 56336446Sbostic (name = gunique(name)) == NULL) 56436446Sbostic return; 56510303Ssam 56636620Srick if (restart_point) 56736620Srick mode = "r+w"; 56836620Srick fout = fopen(name, mode); 56936620Srick closefunc = fclose; 57010275Ssam if (fout == NULL) { 57136304Skarels perror_reply(553, name); 57210275Ssam return; 57310275Ssam } 57436620Srick if (restart_point) { 57536551Sbostic if (type == TYPE_A) { 576*36933Skarels register int i, n, c; 577*36933Skarels 578*36933Skarels n = restart_point; 579*36933Skarels i = 0; 580*36933Skarels while (i++ < n) { 581*36933Skarels if ((c=getc(fout)) == EOF) { 582*36933Skarels perror_reply(550, name); 583*36933Skarels goto done; 584*36933Skarels } 585*36933Skarels if (c == '\n') 586*36933Skarels i++; 587*36933Skarels } 588*36933Skarels /* 589*36933Skarels * We must do this seek to "current" position 590*36933Skarels * because we are changing from reading to 591*36933Skarels * writing. 592*36933Skarels */ 593*36933Skarels if (fseek(fout, 0L, L_INCR) < 0) { 59436551Sbostic perror_reply(550, name); 59536620Srick goto done; 59636620Srick } 597*36933Skarels } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 59836551Sbostic perror_reply(550, name); 59936620Srick goto done; 60036620Srick } 60136620Srick } 60236304Skarels din = dataconn(name, (off_t)-1, "r"); 60310275Ssam if (din == NULL) 60410275Ssam goto done; 60536620Srick if (receive_data(din, fout) == 0) { 60636620Srick if (unique) 60736304Skarels reply(226, "Transfer complete (unique file name:%s).", 608*36933Skarels name); 60936304Skarels else 61036304Skarels reply(226, "Transfer complete."); 61126044Sminshall } 61226493Sminshall (void) fclose(din); 61326044Sminshall data = -1; 61426044Sminshall pdata = -1; 61510275Ssam done: 61610275Ssam (*closefunc)(fout); 61710275Ssam } 61810275Ssam 61910275Ssam FILE * 62010275Ssam getdatasock(mode) 62110275Ssam char *mode; 62210275Ssam { 62317157Ssam int s, on = 1; 62410275Ssam 62510275Ssam if (data >= 0) 62610275Ssam return (fdopen(data, mode)); 62713247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 62810602Ssam if (s < 0) 62910275Ssam return (NULL); 63036304Skarels (void) seteuid((uid_t)0); 63126493Sminshall if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (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; 63636304Skarels if (bind(s, (struct sockaddr *)&data_source, sizeof (data_source)) < 0) 63710602Ssam goto bad; 63836304Skarels (void) seteuid((uid_t)pw->pw_uid); 63910275Ssam return (fdopen(s, mode)); 64010602Ssam bad: 64136304Skarels (void) seteuid((uid_t)pw->pw_uid); 64226493Sminshall (void) close(s); 64310602Ssam return (NULL); 64410275Ssam } 64510275Ssam 64610275Ssam FILE * 64710275Ssam dataconn(name, size, mode) 64810275Ssam char *name; 64911653Ssam off_t size; 65010275Ssam char *mode; 65110275Ssam { 65210275Ssam char sizebuf[32]; 65310275Ssam FILE *file; 65411653Ssam int retry = 0; 65510275Ssam 656*36933Skarels file_size = size; 657*36933Skarels byte_count = 0; 65836304Skarels if (size != (off_t) -1) 65926493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 66010275Ssam else 66110275Ssam (void) strcpy(sizebuf, ""); 66236304Skarels if (pdata >= 0) { 66326044Sminshall struct sockaddr_in from; 66426044Sminshall int s, fromlen = sizeof(from); 66526044Sminshall 66636304Skarels s = accept(pdata, (struct sockaddr *)&from, &fromlen); 66726044Sminshall if (s < 0) { 66826044Sminshall reply(425, "Can't open data connection."); 66926044Sminshall (void) close(pdata); 67026044Sminshall pdata = -1; 67126044Sminshall return(NULL); 67226044Sminshall } 67326044Sminshall (void) close(pdata); 67426044Sminshall pdata = s; 67536235Skarels reply(150, "Opening %s mode data connection for %s%s.", 67636235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 67726044Sminshall return(fdopen(pdata, mode)); 67826044Sminshall } 67910275Ssam if (data >= 0) { 68010275Ssam reply(125, "Using existing data connection for %s%s.", 68110275Ssam name, sizebuf); 68210321Ssam usedefault = 1; 68310275Ssam return (fdopen(data, mode)); 68410275Ssam } 68510566Ssam if (usedefault) 68610422Ssam data_dest = his_addr; 68710422Ssam usedefault = 1; 68810275Ssam file = getdatasock(mode); 68910275Ssam if (file == NULL) { 69010275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 69113247Ssam inet_ntoa(data_source.sin_addr), 69210275Ssam ntohs(data_source.sin_port), 69336304Skarels errno < sys_nerr ? sys_errlist[errno] : "unknown error"); 69410275Ssam return (NULL); 69510275Ssam } 69610275Ssam data = fileno(file); 69736304Skarels while (connect(data, (struct sockaddr *)&data_dest, 69836304Skarels sizeof (data_dest)) < 0) { 69911653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 70026493Sminshall sleep((unsigned) swaitint); 70111653Ssam retry += swaitint; 70211653Ssam continue; 70311653Ssam } 70436304Skarels perror_reply(425, "Can't build data connection"); 70510275Ssam (void) fclose(file); 70610275Ssam data = -1; 70710275Ssam return (NULL); 70810275Ssam } 70936235Skarels reply(150, "Opening %s mode data connection for %s%s.", 71036235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 71110275Ssam return (file); 71210275Ssam } 71310275Ssam 71410275Ssam /* 71510275Ssam * Tranfer the contents of "instr" to 71610275Ssam * "outstr" peer using the appropriate 717*36933Skarels * encapsulation of the data subject 71810275Ssam * to Mode, Structure, and Type. 71910275Ssam * 72010275Ssam * NB: Form isn't handled. 72110275Ssam */ 72236446Sbostic send_data(instr, outstr, blksize) 72310275Ssam FILE *instr, *outstr; 72436446Sbostic off_t blksize; 72510275Ssam { 72636446Sbostic register int c, cnt; 72736446Sbostic register char *buf; 72836446Sbostic int netfd, filefd; 72910275Ssam 73026044Sminshall transflag++; 73126044Sminshall if (setjmp(urgcatch)) { 73226044Sminshall transflag = 0; 73336620Srick return; 73426044Sminshall } 73510275Ssam switch (type) { 73610275Ssam 73710275Ssam case TYPE_A: 73810275Ssam while ((c = getc(instr)) != EOF) { 739*36933Skarels byte_count++; 74011220Ssam if (c == '\n') { 741*36933Skarels if (ferror(outstr)) 74236620Srick goto data_err; 74327750Sminshall (void) putc('\r', outstr); 74411220Ssam } 74527750Sminshall (void) putc(c, outstr); 74610275Ssam } 747*36933Skarels fflush(outstr); 74826044Sminshall transflag = 0; 749*36933Skarels if (ferror(instr)) 750*36933Skarels goto file_err; 751*36933Skarels if (ferror(outstr)) 75236620Srick goto data_err; 75336620Srick reply(226, "Transfer complete."); 75436620Srick return; 75536446Sbostic 75610275Ssam case TYPE_I: 75710275Ssam case TYPE_L: 75836446Sbostic if ((buf = malloc((u_int)blksize)) == NULL) { 75936446Sbostic transflag = 0; 760*36933Skarels perror_reply(451, "Local resource failure: malloc"); 761*36933Skarels return; 76236446Sbostic } 76310275Ssam netfd = fileno(outstr); 76410275Ssam filefd = fileno(instr); 765*36933Skarels while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 76636620Srick write(netfd, buf, cnt) == cnt) 767*36933Skarels byte_count += cnt; 76826044Sminshall transflag = 0; 76936446Sbostic (void)free(buf); 770*36933Skarels if (cnt != 0) { 771*36933Skarels if (cnt < 0) 772*36933Skarels goto file_err; 77336620Srick goto data_err; 774*36933Skarels } 77536620Srick reply(226, "Transfer complete."); 77636620Srick return; 77736620Srick default: 77836620Srick transflag = 0; 77936620Srick reply(550, "Unimplemented TYPE %d in send_data", type); 78036620Srick return; 78110275Ssam } 78236620Srick 78336620Srick data_err: 78426044Sminshall transflag = 0; 785*36933Skarels perror_reply(426, "Data connection"); 786*36933Skarels return; 787*36933Skarels 788*36933Skarels file_err: 789*36933Skarels transflag = 0; 790*36933Skarels perror_reply(551, "Error on input file"); 79110275Ssam } 79210275Ssam 79310275Ssam /* 79410275Ssam * Transfer data from peer to 79510275Ssam * "outstr" using the appropriate 79610275Ssam * encapulation of the data subject 79710275Ssam * to Mode, Structure, and Type. 79810275Ssam * 79910275Ssam * N.B.: Form isn't handled. 80010275Ssam */ 80110275Ssam receive_data(instr, outstr) 80210275Ssam FILE *instr, *outstr; 80310275Ssam { 80410275Ssam register int c; 80511220Ssam int cnt; 80610275Ssam char buf[BUFSIZ]; 80710275Ssam 80826044Sminshall transflag++; 80926044Sminshall if (setjmp(urgcatch)) { 81026044Sminshall transflag = 0; 811*36933Skarels return (-1); 81226044Sminshall } 81310275Ssam switch (type) { 81410275Ssam 81510275Ssam case TYPE_I: 81610275Ssam case TYPE_L: 81726044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 81836620Srick if (write(fileno(outstr), buf, cnt) != cnt) 819*36933Skarels goto file_err; 820*36933Skarels byte_count += cnt; 82126044Sminshall } 822*36933Skarels if (cnt < 0) 82336620Srick goto data_err; 82426044Sminshall transflag = 0; 825*36933Skarels return (0); 82610275Ssam 82710275Ssam case TYPE_E: 82827106Smckusick reply(553, "TYPE E not implemented."); 82926044Sminshall transflag = 0; 83027106Smckusick return (-1); 83110275Ssam 83210275Ssam case TYPE_A: 83310275Ssam while ((c = getc(instr)) != EOF) { 834*36933Skarels byte_count++; 83527750Sminshall while (c == '\r') { 836*36933Skarels if (ferror(outstr)) 83736620Srick goto data_err; 838*36933Skarels if ((c = getc(instr)) != '\n') { 83927750Sminshall (void) putc ('\r', outstr); 840*36933Skarels if (c == '\0' || c == EOF) 841*36933Skarels goto contin2; 842*36933Skarels } 84310275Ssam } 844*36933Skarels (void) putc(c, outstr); 845*36933Skarels contin2: ; 84610275Ssam } 84736620Srick fflush(outstr); 848*36933Skarels if (ferror(instr)) 84936620Srick goto data_err; 850*36933Skarels if (ferror(outstr)) 851*36933Skarels goto file_err; 85226044Sminshall transflag = 0; 85310275Ssam return (0); 85436620Srick default: 85536620Srick reply(550, "Unimplemented TYPE %d in receive_data", type); 85636620Srick transflag = 0; 857*36933Skarels return (-1); 85810275Ssam } 85936620Srick 86036620Srick data_err: 86126044Sminshall transflag = 0; 862*36933Skarels perror_reply(426, "Data Connection"); 863*36933Skarels return (-1); 864*36933Skarels 865*36933Skarels file_err: 866*36933Skarels transflag = 0; 867*36933Skarels perror_reply(452, "Error writing file"); 868*36933Skarels return (-1); 86910275Ssam } 87010275Ssam 871*36933Skarels statfilecmd(filename) 872*36933Skarels char *filename; 873*36933Skarels { 874*36933Skarels char line[BUFSIZ]; 875*36933Skarels FILE *fin; 876*36933Skarels int c; 877*36933Skarels 878*36933Skarels (void) sprintf(line, "/bin/ls -lgA %s", filename); 879*36933Skarels fin = ftpd_popen(line, "r"); 880*36933Skarels lreply(211, "status of %s:", filename); 881*36933Skarels while ((c = getc(fin)) != EOF) { 882*36933Skarels if (c == '\n') { 883*36933Skarels if (ferror(stdout)){ 884*36933Skarels perror_reply(421, "control connection"); 885*36933Skarels (void) ftpd_pclose(fin); 886*36933Skarels dologout(1); 887*36933Skarels /* NOTREACHED */ 888*36933Skarels } 889*36933Skarels if (ferror(fin)) { 890*36933Skarels perror_reply(551, filename); 891*36933Skarels (void) ftpd_pclose(fin); 892*36933Skarels return; 893*36933Skarels } 894*36933Skarels (void) putc('\r', stdout); 895*36933Skarels } 896*36933Skarels (void) putc(c, stdout); 897*36933Skarels } 898*36933Skarels (void) ftpd_pclose(fin); 899*36933Skarels reply(211, "End of Status"); 900*36933Skarels } 901*36933Skarels 902*36933Skarels statcmd() 903*36933Skarels { 904*36933Skarels struct sockaddr_in *sin; 905*36933Skarels u_char *a, *p; 906*36933Skarels 907*36933Skarels lreply(211, "%s FTP server status:", hostname, version); 908*36933Skarels printf(" %s\r\n", version); 909*36933Skarels printf(" Connected to %s", remotehost); 910*36933Skarels if (isdigit(remotehost[0])) 911*36933Skarels printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 912*36933Skarels printf("\r\n"); 913*36933Skarels if (logged_in) { 914*36933Skarels if (guest) 915*36933Skarels printf(" Logged in anonymously\r\n"); 916*36933Skarels else 917*36933Skarels printf(" Logged in as %s\r\n", pw->pw_name); 918*36933Skarels } else if (askpasswd) 919*36933Skarels printf(" Waiting for password\r\n"); 920*36933Skarels else 921*36933Skarels printf(" Waiting for user name\r\n"); 922*36933Skarels printf(" TYPE: %s", typenames[type]); 923*36933Skarels if (type == TYPE_A || type == TYPE_E) 924*36933Skarels printf(", FORM: %s", formnames[form]); 925*36933Skarels if (type == TYPE_L) 926*36933Skarels #if NBBY == 8 927*36933Skarels printf(" %d", NBBY); 928*36933Skarels #else 929*36933Skarels printf(" %d", bytesize); /* need definition! */ 930*36933Skarels #endif 931*36933Skarels printf("; STRUcture: %s; transfer MODE: %s\r\n", 932*36933Skarels strunames[stru], modenames[mode]); 933*36933Skarels if (data != -1) 934*36933Skarels printf(" Data connection open\r\n"); 935*36933Skarels else if (pdata != -1) { 936*36933Skarels printf(" in Passive mode"); 937*36933Skarels sin = &pasv_addr; 938*36933Skarels goto printaddr; 939*36933Skarels } else if (usedefault == 0) { 940*36933Skarels printf(" PORT"); 941*36933Skarels sin = &data_dest; 942*36933Skarels printaddr: 943*36933Skarels a = (u_char *) &sin->sin_addr; 944*36933Skarels p = (u_char *) &sin->sin_port; 945*36933Skarels #define UC(b) (((int) b) & 0xff) 946*36933Skarels printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 947*36933Skarels UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 948*36933Skarels #undef UC 949*36933Skarels } else 950*36933Skarels printf(" No data connection\r\n"); 951*36933Skarels reply(211, "End of status"); 952*36933Skarels } 953*36933Skarels 95410275Ssam fatal(s) 95510275Ssam char *s; 95610275Ssam { 95710275Ssam reply(451, "Error in server: %s\n", s); 95810275Ssam reply(221, "Closing connection due to server error."); 95913247Ssam dologout(0); 96036620Srick /* NOTREACHED */ 96110275Ssam } 96210275Ssam 96336446Sbostic /* VARARGS2 */ 96436446Sbostic reply(n, fmt, p0, p1, p2, p3, p4, p5) 96510275Ssam int n; 96636446Sbostic char *fmt; 96710275Ssam { 96810275Ssam printf("%d ", n); 96936446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 97010275Ssam printf("\r\n"); 97136435Sbostic (void)fflush(stdout); 97210275Ssam if (debug) { 97326493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 97436446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 97510275Ssam } 97636620Srick } 97710275Ssam 97836446Sbostic /* VARARGS2 */ 97936446Sbostic lreply(n, fmt, p0, p1, p2, p3, p4, p5) 98010275Ssam int n; 98136446Sbostic char *fmt; 98210275Ssam { 98336446Sbostic printf("%d- ", n); 98436446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 98536446Sbostic printf("\r\n"); 98636435Sbostic (void)fflush(stdout); 98736446Sbostic if (debug) { 98836446Sbostic syslog(LOG_DEBUG, "<--- %d- ", n); 98936446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 99036446Sbostic } 99110275Ssam } 99210275Ssam 99310275Ssam ack(s) 99410275Ssam char *s; 99510275Ssam { 99627106Smckusick reply(250, "%s command successful.", s); 99710275Ssam } 99810275Ssam 99910275Ssam nack(s) 100010275Ssam char *s; 100110275Ssam { 100210275Ssam reply(502, "%s command not implemented.", s); 100310275Ssam } 100410275Ssam 100536304Skarels /* ARGSUSED */ 100626493Sminshall yyerror(s) 100726493Sminshall char *s; 100810275Ssam { 100926044Sminshall char *cp; 101026044Sminshall 101136551Sbostic if (cp = index(cbuf,'\n')) 101236551Sbostic *cp = '\0'; 1013*36933Skarels reply(500, "'%s': command not understood.", cbuf); 101410275Ssam } 101510275Ssam 101610275Ssam delete(name) 101710275Ssam char *name; 101810275Ssam { 101910275Ssam struct stat st; 102010275Ssam 102110275Ssam if (stat(name, &st) < 0) { 102236304Skarels perror_reply(550, name); 102310275Ssam return; 102410275Ssam } 102510275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 102610275Ssam if (rmdir(name) < 0) { 102736304Skarels perror_reply(550, name); 102810275Ssam return; 102910275Ssam } 103010275Ssam goto done; 103110275Ssam } 103210275Ssam if (unlink(name) < 0) { 103336304Skarels perror_reply(550, name); 103410275Ssam return; 103510275Ssam } 103610275Ssam done: 103710275Ssam ack("DELE"); 103810275Ssam } 103910275Ssam 104010275Ssam cwd(path) 104110275Ssam char *path; 104210275Ssam { 104336620Srick if (chdir(path) < 0) 104436304Skarels perror_reply(550, path); 104536620Srick else 104636620Srick ack("CWD"); 104710275Ssam } 104810275Ssam 104910303Ssam makedir(name) 105010275Ssam char *name; 105110275Ssam { 105236276Sbostic if (mkdir(name, 0777) < 0) 105336304Skarels perror_reply(550, name); 105436276Sbostic else 105536276Sbostic reply(257, "MKD command successful."); 105610275Ssam } 105710275Ssam 105810303Ssam removedir(name) 105910275Ssam char *name; 106010275Ssam { 106136620Srick if (rmdir(name) < 0) 106236304Skarels perror_reply(550, name); 106336620Srick else 106436620Srick ack("RMD"); 106510275Ssam } 106610275Ssam 106710303Ssam pwd() 106810275Ssam { 106910303Ssam char path[MAXPATHLEN + 1]; 107036304Skarels extern char *getwd(); 107110275Ssam 107236620Srick if (getwd(path) == (char *)NULL) 107327106Smckusick reply(550, "%s.", path); 107436620Srick else 107536620Srick reply(257, "\"%s\" is current directory.", path); 107610275Ssam } 107710275Ssam 107810275Ssam char * 107910275Ssam renamefrom(name) 108010275Ssam char *name; 108110275Ssam { 108210275Ssam struct stat st; 108310275Ssam 108410275Ssam if (stat(name, &st) < 0) { 108536304Skarels perror_reply(550, name); 108610275Ssam return ((char *)0); 108710275Ssam } 108810303Ssam reply(350, "File exists, ready for destination name"); 108910275Ssam return (name); 109010275Ssam } 109110275Ssam 109210275Ssam renamecmd(from, to) 109310275Ssam char *from, *to; 109410275Ssam { 109536620Srick if (rename(from, to) < 0) 109636304Skarels perror_reply(550, "rename"); 109736620Srick else 109836620Srick ack("RNTO"); 109910275Ssam } 110010275Ssam 110110275Ssam dolog(sin) 110210275Ssam struct sockaddr_in *sin; 110310275Ssam { 110436304Skarels struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 110510275Ssam sizeof (struct in_addr), AF_INET); 110636304Skarels time_t t, time(); 110726493Sminshall extern char *ctime(); 110810275Ssam 110936304Skarels if (hp) 111026493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 111136304Skarels else 111226493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 111313247Ssam sizeof (remotehost)); 111436620Srick #ifdef SETPROCTITLE 1115*36933Skarels sprintf(proctitle, "%s: connected", remotehost); 1116*36933Skarels setproctitle(proctitle); 111736620Srick #endif /* SETPROCTITLE */ 1118*36933Skarels 1119*36933Skarels if (logging) { 1120*36933Skarels t = time((time_t *) 0); 1121*36933Skarels syslog(LOG_INFO, "connection from %s at %s", 1122*36933Skarels remotehost, ctime(&t)); 1123*36933Skarels } 112410275Ssam } 112510695Ssam 112610695Ssam /* 112713247Ssam * Record logout in wtmp file 112813247Ssam * and exit with supplied status. 112913247Ssam */ 113013247Ssam dologout(status) 113113247Ssam int status; 113213247Ssam { 113317580Ssam if (logged_in) { 113436304Skarels (void) seteuid((uid_t)0); 113535672Sbostic logwtmp(ttyline, "", ""); 113613247Ssam } 113714436Ssam /* beware of flushing buffers after a SIGPIPE */ 113814436Ssam _exit(status); 113913247Ssam } 114013247Ssam 114126044Sminshall myoob() 114226044Sminshall { 114327750Sminshall char *cp; 114426044Sminshall 114527750Sminshall /* only process if transfer occurring */ 114636304Skarels if (!transflag) 114726044Sminshall return; 114827750Sminshall cp = tmpline; 114927750Sminshall if (getline(cp, 7, stdin) == NULL) { 115036304Skarels reply(221, "You could at least say goodbye."); 115127750Sminshall dologout(0); 115226044Sminshall } 115326044Sminshall upper(cp); 1154*36933Skarels if (strcmp(cp, "ABOR\r\n") == 0) { 1155*36933Skarels tmpline[0] = '\0'; 1156*36933Skarels reply(426, "Transfer aborted. Data connection closed."); 1157*36933Skarels reply(226, "Abort successful"); 1158*36933Skarels longjmp(urgcatch, 1); 1159*36933Skarels } 1160*36933Skarels if (strcmp(cp, "STAT\r\n") == 0) { 1161*36933Skarels if (file_size != (off_t) -1) 1162*36933Skarels reply(213, "Status: %lu of %lu bytes transferred", 1163*36933Skarels byte_count, file_size); 1164*36933Skarels else 1165*36933Skarels reply(213, "Status: %lu bytes transferred", byte_count); 1166*36933Skarels } 116726044Sminshall } 116826044Sminshall 116927106Smckusick /* 117036620Srick * Note: a response of 425 is not mentioned as a possible response to 117136620Srick * the PASV command in RFC959. However, it has been blessed as 117236620Srick * a legitimate response by Jon Postel in a telephone conversation 117336620Srick * with Rick Adams on 25 Jan 89. 117427106Smckusick */ 117526044Sminshall passive() 117626044Sminshall { 117726044Sminshall int len; 117826044Sminshall register char *p, *a; 117926044Sminshall 118026044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 118126044Sminshall if (pdata < 0) { 118236620Srick perror_reply(425, "Can't open passive connection"); 118326044Sminshall return; 118426044Sminshall } 1185*36933Skarels pasv_addr = ctrl_addr; 1186*36933Skarels pasv_addr.sin_port = 0; 118736304Skarels (void) seteuid((uid_t)0); 1188*36933Skarels if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { 118936304Skarels (void) seteuid((uid_t)pw->pw_uid); 119036620Srick goto pasv_error; 119126044Sminshall } 119236304Skarels (void) seteuid((uid_t)pw->pw_uid); 1193*36933Skarels len = sizeof(pasv_addr); 1194*36933Skarels if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 119536620Srick goto pasv_error; 119636620Srick if (listen(pdata, 1) < 0) 119736620Srick goto pasv_error; 1198*36933Skarels a = (char *) &pasv_addr.sin_addr; 1199*36933Skarels p = (char *) &pasv_addr.sin_port; 120026044Sminshall 120126044Sminshall #define UC(b) (((int) b) & 0xff) 120226044Sminshall 120326044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 120426044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 120536620Srick return; 120636620Srick 120736620Srick pasv_error: 120836620Srick (void) close(pdata); 120936620Srick pdata = -1; 121036620Srick perror_reply(425, "Can't open passive connection"); 121136620Srick return; 121226044Sminshall } 121326044Sminshall 121436304Skarels /* 121536304Skarels * Generate unique name for file with basename "local". 121636304Skarels * The file named "local" is already known to exist. 121736304Skarels * Generates failure reply on error. 121836304Skarels */ 121926044Sminshall char * 122026044Sminshall gunique(local) 122126044Sminshall char *local; 122226044Sminshall { 122326044Sminshall static char new[MAXPATHLEN]; 122436304Skarels struct stat st; 122526044Sminshall char *cp = rindex(local, '/'); 1226*36933Skarels int count = 0; 122726044Sminshall 122836304Skarels if (cp) 122926044Sminshall *cp = '\0'; 123036620Srick if (stat(cp ? local : ".", &st) < 0) { 1231*36933Skarels perror_reply(553, cp ? local : "."); 123226044Sminshall return((char *) 0); 123326044Sminshall } 123436620Srick if (cp) 123536620Srick *cp = '/'; 123626044Sminshall (void) strcpy(new, local); 123726044Sminshall cp = new + strlen(new); 123826044Sminshall *cp++ = '.'; 123936304Skarels for (count = 1; count < 100; count++) { 124036304Skarels (void) sprintf(cp, "%d", count); 124136304Skarels if (stat(new, &st) < 0) 124236304Skarels return(new); 124326044Sminshall } 124436304Skarels reply(452, "Unique file name cannot be created."); 124536304Skarels return((char *) 0); 124626044Sminshall } 124736304Skarels 124836304Skarels /* 124936304Skarels * Format and send reply containing system error number. 125036304Skarels */ 125136304Skarels perror_reply(code, string) 125236304Skarels int code; 125336304Skarels char *string; 125436304Skarels { 125536304Skarels if (errno < sys_nerr) 125636304Skarels reply(code, "%s: %s.", string, sys_errlist[errno]); 125736304Skarels else 125836304Skarels reply(code, "%s: unknown error %d.", string, errno); 125936304Skarels } 126036620Srick 126136620Srick static char *onefile[] = { 126236620Srick "", 126336620Srick 0 126436620Srick }; 126536620Srick 126636620Srick send_file_list(whichfiles) 126736620Srick char *whichfiles; 126836620Srick { 126936620Srick struct stat st; 127036620Srick DIR *dirp = NULL; 127136620Srick struct direct *dir; 127236620Srick FILE *dout = NULL; 127336620Srick register char **dirlist, *dirname; 127436620Srick char *strpbrk(); 127536620Srick 127636620Srick if (strpbrk(whichfiles, "~{[*?") != NULL) { 127736620Srick extern char **glob(), *globerr; 1278*36933Skarels 127936620Srick globerr = NULL; 128036620Srick dirlist = glob(whichfiles); 128136620Srick if (globerr != NULL) { 128236620Srick reply(550, globerr); 128336620Srick return; 128436620Srick } else if (dirlist == NULL) { 128536620Srick errno = ENOENT; 128636620Srick perror_reply(550, whichfiles); 128736620Srick return; 128836620Srick } 128936620Srick } else { 129036620Srick onefile[0] = whichfiles; 129136620Srick dirlist = onefile; 129236620Srick } 1293*36933Skarels 1294*36933Skarels if (setjmp(urgcatch)) { 1295*36933Skarels transflag = 0; 1296*36933Skarels return; 1297*36933Skarels } 129836620Srick while (dirname = *dirlist++) { 129936620Srick if (stat(dirname, &st) < 0) { 1300*36933Skarels /* 1301*36933Skarels * If user typed "ls -l", etc, and the client 1302*36933Skarels * used NLST, do what the user meant. 1303*36933Skarels */ 1304*36933Skarels if (dirname[0] == '-' && *dirlist == NULL && 1305*36933Skarels transflag == 0) { 1306*36933Skarels retrieve("/bin/ls %s", dirname); 1307*36933Skarels return; 1308*36933Skarels } 130936620Srick perror_reply(550, whichfiles); 131036620Srick if (dout != NULL) { 131136620Srick (void) fclose(dout); 1312*36933Skarels transflag = 0; 131336620Srick data = -1; 131436620Srick pdata = -1; 131536620Srick } 131636620Srick return; 131736620Srick } 1318*36933Skarels 131936620Srick if ((st.st_mode&S_IFMT) == S_IFREG) { 132036620Srick if (dout == NULL) { 132136620Srick dout = dataconn(whichfiles, (off_t)-1, "w"); 132236620Srick if (dout == NULL) 132336620Srick return; 1324*36933Skarels transflag++; 132536620Srick } 132636620Srick fprintf(dout, "%s\n", dirname); 1327*36933Skarels byte_count += strlen(dirname) + 1; 132836620Srick continue; 1329*36933Skarels } else if ((st.st_mode&S_IFMT) != S_IFDIR) 133036620Srick continue; 133136620Srick 133236620Srick if ((dirp = opendir(dirname)) == NULL) 133336620Srick continue; 133436620Srick 133536620Srick while ((dir = readdir(dirp)) != NULL) { 1336*36933Skarels char nbuf[MAXPATHLEN]; 133736620Srick 133836620Srick if (dir->d_name[0] == '.' && dir->d_namlen == 1) 133936620Srick continue; 1340*36933Skarels if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 1341*36933Skarels dir->d_namlen == 2) 134236620Srick continue; 134336620Srick 1344*36933Skarels sprintf(nbuf, "%s/%s", dirname, dir->d_name); 1345*36933Skarels 134636620Srick /* 1347*36933Skarels * We have to do a stat to insure it's 1348*36933Skarels * not a directory or special file. 134936620Srick */ 135036620Srick if (stat(nbuf, &st) == 0 && 135136620Srick (st.st_mode&S_IFMT) == S_IFREG) { 135236620Srick if (dout == NULL) { 135336620Srick dout = dataconn(whichfiles, (off_t)-1, 135436620Srick "w"); 135536620Srick if (dout == NULL) 135636620Srick return; 1357*36933Skarels transflag++; 135836620Srick } 135936620Srick if (nbuf[0] == '.' && nbuf[1] == '/') 136036620Srick fprintf(dout, "%s\n", &nbuf[2]); 136136620Srick else 136236620Srick fprintf(dout, "%s\n", nbuf); 1363*36933Skarels byte_count += strlen(nbuf) + 1; 136436620Srick } 136536620Srick } 136636620Srick (void) closedir(dirp); 136736620Srick } 136836620Srick 1369*36933Skarels if (dout == NULL) 1370*36933Skarels reply(550, "No files found."); 1371*36933Skarels else if (ferror(dout) != 0) 1372*36933Skarels perror_reply(550, "Data connection"); 1373*36933Skarels else 137436620Srick reply(226, "Transfer complete."); 137536620Srick 1376*36933Skarels transflag = 0; 1377*36933Skarels if (dout != NULL) 137836620Srick (void) fclose(dout); 137936620Srick data = -1; 138036620Srick pdata = -1; 138136620Srick } 138236620Srick 138336620Srick #ifdef SETPROCTITLE 138436620Srick /* 138536620Srick * clobber argv so ps will show what we're doing. 138636620Srick * (stolen from sendmail) 138736620Srick * warning, since this is usually started from inetd.conf, it 138836620Srick * often doesn't have much of an environment or arglist to overwrite. 138936620Srick */ 139036620Srick 139136620Srick /*VARARGS1*/ 139236620Srick setproctitle(fmt, a, b, c) 139336620Srick char *fmt; 139436620Srick { 139536620Srick register char *p, *bp, ch; 139636620Srick register int i; 139736620Srick char buf[BUFSIZ]; 139836620Srick 139936620Srick (void) sprintf(buf, fmt, a, b, c); 140036620Srick 140136620Srick /* make ps print our process name */ 140236620Srick p = Argv[0]; 140336620Srick *p++ = '-'; 140436620Srick 140536620Srick i = strlen(buf); 140636620Srick if (i > LastArgv - p - 2) { 140736620Srick i = LastArgv - p - 2; 140836620Srick buf[i] = '\0'; 140936620Srick } 141036620Srick bp = buf; 141136620Srick while (ch = *bp++) 141236620Srick if (ch != '\n' && ch != '\r') 141336620Srick *p++ = ch; 141436620Srick while (p < LastArgv) 141536620Srick *p++ = ' '; 141636620Srick } 141736620Srick #endif /* SETPROCTITLE */ 1418