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 1534769Sbostic * WARRANTIES OF MERCHANTIBILITY 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*36620Srick static char sccsid[] = "@(#)ftpd.c 5.26 (Berkeley) 01/25/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> 37*36620Srick #include <sys/dir.h> 3810275Ssam 3910275Ssam #include <netinet/in.h> 4010275Ssam 4113034Ssam #include <arpa/ftp.h> 4213211Sroot #include <arpa/inet.h> 4326044Sminshall #include <arpa/telnet.h> 4413034Ssam 4510275Ssam #include <stdio.h> 4610275Ssam #include <signal.h> 4710275Ssam #include <pwd.h> 4810275Ssam #include <setjmp.h> 4910275Ssam #include <netdb.h> 5010423Ssam #include <errno.h> 5126044Sminshall #include <strings.h> 5226493Sminshall #include <syslog.h> 5336435Sbostic #include <varargs.h> 5410275Ssam 5510695Ssam /* 5610695Ssam * File containing login names 5710695Ssam * NOT to be used on this machine. 5810695Ssam * Commonly used to disallow uucp. 5910695Ssam */ 6010695Ssam #define FTPUSERS "/etc/ftpusers" 6110695Ssam 6210275Ssam extern int errno; 6310275Ssam extern char *sys_errlist[]; 6436304Skarels extern int sys_nerr; 6510275Ssam extern char *crypt(); 6610275Ssam extern char version[]; 6710275Ssam extern char *home; /* pointer to home directory for glob */ 6836276Sbostic extern FILE *ftpd_popen(), *fopen(), *freopen(); 6936304Skarels extern int ftpd_pclose(), fclose(); 7026044Sminshall extern char *getline(); 7126044Sminshall extern char cbuf[]; 7236551Sbostic extern off_t restart_point; 7310275Ssam 7410275Ssam struct sockaddr_in ctrl_addr; 7510275Ssam struct sockaddr_in data_source; 7610275Ssam struct sockaddr_in data_dest; 7710275Ssam struct sockaddr_in his_addr; 7810275Ssam 7910275Ssam int data; 8026044Sminshall jmp_buf errcatch, urgcatch; 8110275Ssam int logged_in; 8210275Ssam struct passwd *pw; 8310275Ssam int debug; 8426493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */ 8511757Ssam int logging; 8610275Ssam int guest; 8710275Ssam int type; 8810275Ssam int form; 8910275Ssam int stru; /* avoid C keyword */ 9010275Ssam int mode; 9110321Ssam int usedefault = 1; /* for data transfers */ 9236304Skarels int pdata = -1; /* for passive mode */ 9326044Sminshall int transflag; 9426044Sminshall char tmpline[7]; 9536276Sbostic char hostname[MAXHOSTNAMELEN]; 9636276Sbostic char remotehost[MAXHOSTNAMELEN]; 9710275Ssam 9811653Ssam /* 9911653Ssam * Timeout intervals for retrying connections 10011653Ssam * to hosts that don't accept PORT cmds. This 10111653Ssam * is a kludge, but given the problems with TCP... 10211653Ssam */ 10311653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 10411653Ssam #define SWAITINT 5 /* interval between retries */ 10511653Ssam 10611653Ssam int swaitmax = SWAITMAX; 10711653Ssam int swaitint = SWAITINT; 10811653Ssam 10910275Ssam int lostconn(); 11026044Sminshall int myoob(); 11110275Ssam FILE *getdatasock(), *dataconn(); 11210275Ssam 113*36620Srick #ifdef SETPROCTITLE 114*36620Srick char **Argv = NULL; /* pointer to argument vector */ 115*36620Srick char *LastArgv = NULL; /* end of argv */ 116*36620Srick #endif /* SETPROCTITLE */ 117*36620Srick 118*36620Srick main(argc, argv, envp) 11910275Ssam int argc; 12010275Ssam char *argv[]; 121*36620Srick char **envp; 12210275Ssam { 12327750Sminshall int addrlen, on = 1; 12410275Ssam char *cp; 12510275Ssam 12616339Skarels addrlen = sizeof (his_addr); 12736304Skarels if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 12826493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 12910275Ssam exit(1); 13010275Ssam } 13116339Skarels addrlen = sizeof (ctrl_addr); 13236304Skarels if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 13326493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 13416339Skarels exit(1); 13516339Skarels } 13616339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 13710275Ssam debug = 0; 13826493Sminshall openlog("ftpd", LOG_PID, LOG_DAEMON); 139*36620Srick #ifdef SETPROCTITLE 140*36620Srick /* 141*36620Srick * Save start and extent of argv for setproctitle. 142*36620Srick */ 143*36620Srick Argv = argv; 144*36620Srick while (*envp) 145*36620Srick envp++; 146*36620Srick LastArgv = envp[-1] + strlen(envp[-1]); 147*36620Srick #endif /* SETPROCTITLE */ 148*36620Srick 14910275Ssam argc--, argv++; 15010275Ssam while (argc > 0 && *argv[0] == '-') { 15110275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 15210275Ssam 15311653Ssam case 'v': 15411653Ssam debug = 1; 15511653Ssam break; 15611653Ssam 15710275Ssam case 'd': 15810275Ssam debug = 1; 15910275Ssam break; 16010275Ssam 16111757Ssam case 'l': 16211757Ssam logging = 1; 16311757Ssam break; 16411757Ssam 16511653Ssam case 't': 16611653Ssam timeout = atoi(++cp); 16711653Ssam goto nextopt; 16811653Ssam 16910275Ssam default: 17016339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 17116339Skarels *cp); 17210275Ssam break; 17310275Ssam } 17411653Ssam nextopt: 17510275Ssam argc--, argv++; 17610275Ssam } 17730944Scsvsj (void) freopen("/dev/null", "w", stderr); 17826493Sminshall (void) signal(SIGPIPE, lostconn); 17926493Sminshall (void) signal(SIGCHLD, SIG_IGN); 18035691Sbostic if ((int)signal(SIGURG, myoob) < 0) 18126493Sminshall syslog(LOG_ERR, "signal: %m"); 18235691Sbostic 18327750Sminshall /* handle urgent data inline */ 18436276Sbostic /* Sequent defines this, but it doesn't work */ 18527750Sminshall #ifdef SO_OOBINLINE 18636276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 18727750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 18836276Sbostic #endif 18936304Skarels if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 19036304Skarels syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 19116760Slepreau dolog(&his_addr); 19216339Skarels /* do telnet option negotiation here */ 19316339Skarels /* 19416339Skarels * Set up default state 19516339Skarels */ 19616339Skarels data = -1; 19716339Skarels type = TYPE_A; 19816339Skarels form = FORM_N; 19916339Skarels stru = STRU_F; 20016339Skarels mode = MODE_S; 20126044Sminshall tmpline[0] = '\0'; 20226493Sminshall (void) gethostname(hostname, sizeof (hostname)); 20336276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version); 20436304Skarels (void) setjmp(errcatch); 20536304Skarels for (;;) 20626493Sminshall (void) yyparse(); 207*36620Srick /* NOTREACHED */ 20810275Ssam } 20910419Ssam 21010275Ssam lostconn() 21110275Ssam { 21210275Ssam 21314089Ssam if (debug) 21426493Sminshall syslog(LOG_DEBUG, "lost connection"); 21514089Ssam dologout(-1); 21610275Ssam } 21710275Ssam 21835672Sbostic static char ttyline[20]; 21935672Sbostic 22036185Sbostic /* 22136185Sbostic * Helper function for sgetpwnam(). 22236185Sbostic */ 22336185Sbostic char * 22436185Sbostic sgetsave(s) 22536185Sbostic char *s; 22636185Sbostic { 22736185Sbostic char *malloc(); 22836185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 22936185Sbostic 23036185Sbostic if (new == NULL) { 231*36620Srick perror_reply(421, "Local resource failure: malloc"); 23236185Sbostic dologout(1); 233*36620Srick /* NOTREACHED */ 23436185Sbostic } 23536185Sbostic (void) strcpy(new, s); 23636185Sbostic return (new); 23736185Sbostic } 23836185Sbostic 23936185Sbostic /* 24036185Sbostic * Save the result of a getpwnam. Used for USER command, since 24136185Sbostic * the data returned must not be clobbered by any other command 24236185Sbostic * (e.g., globbing). 24336185Sbostic */ 24436185Sbostic struct passwd * 24536185Sbostic sgetpwnam(name) 24636185Sbostic char *name; 24736185Sbostic { 24836185Sbostic static struct passwd save; 24936185Sbostic register struct passwd *p; 25036185Sbostic char *sgetsave(); 25136185Sbostic 25236185Sbostic if ((p = getpwnam(name)) == NULL) 25336185Sbostic return (p); 25436185Sbostic if (save.pw_name) { 25536185Sbostic free(save.pw_name); 25636185Sbostic free(save.pw_passwd); 25736185Sbostic free(save.pw_comment); 25836185Sbostic free(save.pw_gecos); 25936185Sbostic free(save.pw_dir); 26036185Sbostic free(save.pw_shell); 26136185Sbostic } 26236185Sbostic save = *p; 26336185Sbostic save.pw_name = sgetsave(p->pw_name); 26436185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 26536185Sbostic save.pw_comment = sgetsave(p->pw_comment); 26636185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 26736185Sbostic save.pw_dir = sgetsave(p->pw_dir); 26836185Sbostic save.pw_shell = sgetsave(p->pw_shell); 26936185Sbostic return (&save); 27036185Sbostic } 27136185Sbostic 27236304Skarels int login_attempts; /* number of failed login attempts */ 27336304Skarels int askpasswd; /* had user command, ask for passwd */ 27436304Skarels 27536304Skarels /* 27636304Skarels * USER command. 27736304Skarels * Sets global passwd pointer pw if named account exists 27836304Skarels * and is acceptable; sets askpasswd if a PASS command is 27936304Skarels * expected. If logged in previously, need to reset state. 28036304Skarels * If name is "ftp" or "anonymous" and ftp account exists, 28136304Skarels * set guest and pw, then just return. 28236304Skarels * If account doesn't exist, ask for passwd anyway. 28336304Skarels * Otherwise, check user requesting login privileges. 28436304Skarels * Disallow anyone who does not have a standard 28536304Skarels * shell returned by getusershell() (/etc/shells). 28636304Skarels * Disallow anyone mentioned in the file FTPUSERS 28736304Skarels * to allow people such as root and uucp to be avoided. 28836304Skarels */ 28936304Skarels user(name) 29036304Skarels char *name; 29136304Skarels { 29236304Skarels register char *cp; 29336304Skarels FILE *fd; 29436304Skarels char *shell; 29536551Sbostic char line[BUFSIZ], *getusershell(); 29636304Skarels 29736304Skarels if (logged_in) { 29836304Skarels if (guest) { 29936304Skarels reply(530, "Can't change user from guest login."); 30036304Skarels return; 30136304Skarels } 30236304Skarels end_login(); 30336304Skarels } 30436304Skarels 30536304Skarels guest = 0; 30636304Skarels if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 30736304Skarels if ((pw = sgetpwnam("ftp")) != NULL) { 30836304Skarels guest = 1; 30936304Skarels askpasswd = 1; 31036304Skarels reply(331, "Guest login ok, send ident as password."); 31136551Sbostic } 31236551Sbostic else 31336304Skarels reply(530, "User %s unknown.", name); 31436304Skarels return; 31536304Skarels } 31636304Skarels if (pw = sgetpwnam(name)) { 31736304Skarels if ((shell = pw->pw_shell) == NULL || *shell == 0) 31836304Skarels shell = "/bin/sh"; 31936304Skarels while ((cp = getusershell()) != NULL) 32036304Skarels if (strcmp(cp, shell) == 0) 32136304Skarels break; 32236304Skarels endusershell(); 32336304Skarels if (cp == NULL) { 32436304Skarels reply(530, "User %s access denied.", name); 32536550Sbostic syslog(LOG_ERR, "FTP LOGIN REFUSED FROM %s, %s", 32636550Sbostic remotehost, name); 32736304Skarels pw = (struct passwd *) NULL; 32836304Skarels return; 32936304Skarels } 33036304Skarels if ((fd = fopen(FTPUSERS, "r")) != NULL) { 33136304Skarels while (fgets(line, sizeof (line), fd) != NULL) { 33236304Skarels if ((cp = index(line, '\n')) != NULL) 33336304Skarels *cp = '\0'; 33436304Skarels if (strcmp(line, name) == 0) { 33536304Skarels reply(530, "User %s access denied.", name); 33636550Sbostic syslog(LOG_ERR, "FTP LOGIN REFUSED FROM %s, %s", 33736550Sbostic remotehost, name); 33836304Skarels pw = (struct passwd *) NULL; 33936304Skarels return; 34036304Skarels } 34136304Skarels } 34236304Skarels } 34336304Skarels (void) fclose(fd); 34436304Skarels } 34536304Skarels reply(331, "Password required for %s.", name); 34636304Skarels askpasswd = 1; 34736304Skarels /* 34836304Skarels * Delay before reading passwd after first failed 34936304Skarels * attempt to slow down passwd-guessing programs. 35036304Skarels */ 35136304Skarels if (login_attempts) 35236304Skarels sleep((unsigned) login_attempts); 35336304Skarels } 35436304Skarels 35536304Skarels /* 35636304Skarels * Terminate login as previous user, if any, resetting state; 35736304Skarels * used when USER command is given or login fails. 35836304Skarels */ 35936304Skarels end_login() 36036304Skarels { 36136304Skarels 36236304Skarels (void) seteuid((uid_t)0); 36336304Skarels if (logged_in) 36436304Skarels logwtmp(ttyline, "", ""); 36536304Skarels pw = NULL; 36636304Skarels logged_in = 0; 36736304Skarels guest = 0; 36836304Skarels } 36936304Skarels 37010275Ssam pass(passwd) 37110275Ssam char *passwd; 37210275Ssam { 37336304Skarels char *xpasswd, *salt; 37410275Ssam 37536304Skarels if (logged_in || askpasswd == 0) { 37610275Ssam reply(503, "Login with USER first."); 37710275Ssam return; 37810275Ssam } 37936304Skarels askpasswd = 0; 38010275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 38136304Skarels if (pw == NULL) 38236304Skarels salt = "xx"; 38336304Skarels else 38436304Skarels salt = pw->pw_passwd; 38536304Skarels xpasswd = crypt(passwd, salt); 38616760Slepreau /* The strcmp does not catch null passwords! */ 38736304Skarels if (pw == NULL || *pw->pw_passwd == '\0' || 38836304Skarels strcmp(xpasswd, pw->pw_passwd)) { 38910275Ssam reply(530, "Login incorrect."); 39010275Ssam pw = NULL; 39136304Skarels if (login_attempts++ >= 5) { 39236304Skarels syslog(LOG_ERR, 39336304Skarels "repeated login failures from %s", 39436304Skarels remotehost); 39536304Skarels exit(0); 39636304Skarels } 39710275Ssam return; 39810275Ssam } 39910275Ssam } 40036304Skarels login_attempts = 0; /* this time successful */ 40136304Skarels (void) setegid((gid_t)pw->pw_gid); 40236304Skarels (void) initgroups(pw->pw_name, pw->pw_gid); 40316033Sralph 40436192Sbostic /* open wtmp before chroot */ 40536192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 40636192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 40736192Sbostic logged_in = 1; 40836192Sbostic 40936446Sbostic if (guest) { 410*36620Srick /* 411*36620Srick * We MUST do a chdir() after the chroot. Otherwise "." 412*36620Srick * will be accessible outside the root! 413*36620Srick */ 414*36620Srick if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 41536446Sbostic reply(550, "Can't set guest privileges."); 41636446Sbostic goto bad; 41736446Sbostic } 41836304Skarels } 419*36620Srick else if (chdir(pw->pw_dir) < 0) { 420*36620Srick if (chdir("/") < 0) { 42136446Sbostic reply(530, "User %s: can't change directory to %s.", 42236446Sbostic pw->pw_name, pw->pw_dir); 42336446Sbostic goto bad; 42436446Sbostic } 42536446Sbostic else 42636446Sbostic lreply(230, "No directory! Logging in with home=/"); 427*36620Srick } 42836304Skarels if (seteuid((uid_t)pw->pw_uid) < 0) { 42936304Skarels reply(550, "Can't set uid."); 43036304Skarels goto bad; 43136304Skarels } 43236550Sbostic if (guest) { 43336192Sbostic reply(230, "Guest login ok, access restrictions apply."); 43436550Sbostic syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 43536550Sbostic remotehost, passwd); 43636551Sbostic } 43736551Sbostic else { 43810275Ssam reply(230, "User %s logged in.", pw->pw_name); 43936550Sbostic syslog(LOG_INFO, "FTP LOGIN FROM %s, %s", 44036550Sbostic remotehost, pw->pw_name); 44136550Sbostic } 44210303Ssam home = pw->pw_dir; /* home dir for globbing */ 44310303Ssam return; 44410303Ssam bad: 44536304Skarels /* Forget all about it... */ 44636304Skarels end_login(); 44710275Ssam } 44810275Ssam 44910275Ssam retrieve(cmd, name) 45010275Ssam char *cmd, *name; 45110275Ssam { 45210275Ssam FILE *fin, *dout; 45310275Ssam struct stat st; 454*36620Srick int (*closefunc)(); 45510275Ssam 45636557Sbostic if (cmd == 0) { 45736446Sbostic fin = fopen(name, "r"), closefunc = fclose; 45836557Sbostic st.st_size = 0; 45936557Sbostic } else { 46010275Ssam char line[BUFSIZ]; 46110275Ssam 46226493Sminshall (void) sprintf(line, cmd, name), name = line; 46336304Skarels fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 46436557Sbostic st.st_size = -1; 46510275Ssam } 46610275Ssam if (fin == NULL) { 46713152Ssam if (errno != 0) 46836304Skarels perror_reply(550, name); 46910275Ssam return; 47010275Ssam } 47110275Ssam if (cmd == 0 && 47210275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 47310275Ssam reply(550, "%s: not a plain file.", name); 47410275Ssam goto done; 47510275Ssam } 476*36620Srick if (restart_point) { 47736551Sbostic if (type == TYPE_A) { 478*36620Srick if (fseek(fin, restart_point, L_SET) < 0) { 47936551Sbostic perror_reply(550, name); 480*36620Srick goto done; 481*36620Srick } 48236551Sbostic } 483*36620Srick else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 48436551Sbostic perror_reply(550, name); 485*36620Srick goto done; 486*36620Srick } 487*36620Srick } 48810275Ssam dout = dataconn(name, st.st_size, "w"); 48910275Ssam if (dout == NULL) 49010275Ssam goto done; 491*36620Srick send_data(fin, dout, st.st_blksize); 49226493Sminshall (void) fclose(dout); 49326044Sminshall data = -1; 49426044Sminshall pdata = -1; 49510275Ssam done: 49610275Ssam (*closefunc)(fin); 49710275Ssam } 49810275Ssam 49936304Skarels store(name, mode, unique) 50010275Ssam char *name, *mode; 50136304Skarels int unique; 50210275Ssam { 50310275Ssam FILE *fout, *din; 50436446Sbostic struct stat st; 505*36620Srick int (*closefunc)(); 50636304Skarels char *gunique(); 50710275Ssam 50836446Sbostic if (unique && stat(name, &st) == 0 && 50936446Sbostic (name = gunique(name)) == NULL) 51036446Sbostic return; 51110303Ssam 512*36620Srick if (restart_point) 513*36620Srick mode = "r+w"; 514*36620Srick fout = fopen(name, mode); 515*36620Srick closefunc = fclose; 51610275Ssam if (fout == NULL) { 51736304Skarels perror_reply(553, name); 51810275Ssam return; 51910275Ssam } 520*36620Srick if (restart_point) { 52136551Sbostic if (type == TYPE_A) { 522*36620Srick if (fseek(fout, restart_point, L_SET) < 0) { 52336551Sbostic perror_reply(550, name); 524*36620Srick goto done; 525*36620Srick } 52636551Sbostic } 527*36620Srick else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 52836551Sbostic perror_reply(550, name); 529*36620Srick goto done; 530*36620Srick } 531*36620Srick } 53236304Skarels din = dataconn(name, (off_t)-1, "r"); 53310275Ssam if (din == NULL) 53410275Ssam goto done; 535*36620Srick if (receive_data(din, fout) == 0) { 536*36620Srick if (unique) 53736304Skarels reply(226, "Transfer complete (unique file name:%s).", 538*36620Srick name); 53936304Skarels else 54036304Skarels reply(226, "Transfer complete."); 54126044Sminshall } 54226493Sminshall (void) fclose(din); 54326044Sminshall data = -1; 54426044Sminshall pdata = -1; 54510275Ssam done: 54610275Ssam (*closefunc)(fout); 54710275Ssam } 54810275Ssam 54910275Ssam FILE * 55010275Ssam getdatasock(mode) 55110275Ssam char *mode; 55210275Ssam { 55317157Ssam int s, on = 1; 55410275Ssam 55510275Ssam if (data >= 0) 55610275Ssam return (fdopen(data, mode)); 55713247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 55810602Ssam if (s < 0) 55910275Ssam return (NULL); 56036304Skarels (void) seteuid((uid_t)0); 56126493Sminshall if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) 56210602Ssam goto bad; 56313152Ssam /* anchor socket to avoid multi-homing problems */ 56413152Ssam data_source.sin_family = AF_INET; 56513152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 56636304Skarels if (bind(s, (struct sockaddr *)&data_source, sizeof (data_source)) < 0) 56710602Ssam goto bad; 56836304Skarels (void) seteuid((uid_t)pw->pw_uid); 56910275Ssam return (fdopen(s, mode)); 57010602Ssam bad: 57136304Skarels (void) seteuid((uid_t)pw->pw_uid); 57226493Sminshall (void) close(s); 57310602Ssam return (NULL); 57410275Ssam } 57510275Ssam 57610275Ssam FILE * 57710275Ssam dataconn(name, size, mode) 57810275Ssam char *name; 57911653Ssam off_t size; 58010275Ssam char *mode; 58110275Ssam { 58210275Ssam char sizebuf[32]; 58310275Ssam FILE *file; 58411653Ssam int retry = 0; 58510275Ssam 58636304Skarels if (size != (off_t) -1) 58726493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 58810275Ssam else 58910275Ssam (void) strcpy(sizebuf, ""); 59036304Skarels if (pdata >= 0) { 59126044Sminshall struct sockaddr_in from; 59226044Sminshall int s, fromlen = sizeof(from); 59326044Sminshall 59436304Skarels s = accept(pdata, (struct sockaddr *)&from, &fromlen); 59526044Sminshall if (s < 0) { 59626044Sminshall reply(425, "Can't open data connection."); 59726044Sminshall (void) close(pdata); 59826044Sminshall pdata = -1; 59926044Sminshall return(NULL); 60026044Sminshall } 60126044Sminshall (void) close(pdata); 60226044Sminshall pdata = s; 60336235Skarels reply(150, "Opening %s mode data connection for %s%s.", 60436235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 60526044Sminshall return(fdopen(pdata, mode)); 60626044Sminshall } 60710275Ssam if (data >= 0) { 60810275Ssam reply(125, "Using existing data connection for %s%s.", 60910275Ssam name, sizebuf); 61010321Ssam usedefault = 1; 61110275Ssam return (fdopen(data, mode)); 61210275Ssam } 61310566Ssam if (usedefault) 61410422Ssam data_dest = his_addr; 61510422Ssam usedefault = 1; 61610275Ssam file = getdatasock(mode); 61710275Ssam if (file == NULL) { 61810275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 61913247Ssam inet_ntoa(data_source.sin_addr), 62010275Ssam ntohs(data_source.sin_port), 62136304Skarels errno < sys_nerr ? sys_errlist[errno] : "unknown error"); 62210275Ssam return (NULL); 62310275Ssam } 62410275Ssam data = fileno(file); 62536304Skarels while (connect(data, (struct sockaddr *)&data_dest, 62636304Skarels sizeof (data_dest)) < 0) { 62711653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 62826493Sminshall sleep((unsigned) swaitint); 62911653Ssam retry += swaitint; 63011653Ssam continue; 63111653Ssam } 63236304Skarels perror_reply(425, "Can't build data connection"); 63310275Ssam (void) fclose(file); 63410275Ssam data = -1; 63510275Ssam return (NULL); 63610275Ssam } 63736235Skarels reply(150, "Opening %s mode data connection for %s%s.", 63836235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 63910275Ssam return (file); 64010275Ssam } 64110275Ssam 64210275Ssam /* 64310275Ssam * Tranfer the contents of "instr" to 64410275Ssam * "outstr" peer using the appropriate 64510275Ssam * encapulation of the date subject 64610275Ssam * to Mode, Structure, and Type. 64710275Ssam * 64810275Ssam * NB: Form isn't handled. 64910275Ssam */ 65036446Sbostic send_data(instr, outstr, blksize) 65110275Ssam FILE *instr, *outstr; 65236446Sbostic off_t blksize; 65310275Ssam { 65436446Sbostic register int c, cnt; 65536446Sbostic register char *buf; 65636446Sbostic int netfd, filefd; 65710275Ssam 65826044Sminshall transflag++; 65926044Sminshall if (setjmp(urgcatch)) { 66026044Sminshall transflag = 0; 661*36620Srick return; 66226044Sminshall } 66310275Ssam switch (type) { 66410275Ssam 66510275Ssam case TYPE_A: 66610275Ssam while ((c = getc(instr)) != EOF) { 66711220Ssam if (c == '\n') { 668*36620Srick if (ferror (outstr)) 669*36620Srick goto data_err; 67027750Sminshall (void) putc('\r', outstr); 67111220Ssam } 67227750Sminshall (void) putc(c, outstr); 67310275Ssam } 67426044Sminshall transflag = 0; 675*36620Srick fflush(outstr); 676*36620Srick if (ferror (instr) || ferror (outstr)) 677*36620Srick goto data_err; 678*36620Srick reply(226, "Transfer complete."); 679*36620Srick return; 68036446Sbostic 68110275Ssam case TYPE_I: 68210275Ssam case TYPE_L: 68336446Sbostic if ((buf = malloc((u_int)blksize)) == NULL) { 68436446Sbostic transflag = 0; 685*36620Srick perror_reply(421, "Local resource failure: malloc"); 686*36620Srick dologout(1); 687*36620Srick /* NOTREACHED */ 68836446Sbostic } 68910275Ssam netfd = fileno(outstr); 69010275Ssam filefd = fileno(instr); 69136446Sbostic while ((cnt = read(filefd, buf, sizeof(buf))) > 0 && 692*36620Srick write(netfd, buf, cnt) == cnt) 693*36620Srick /* LOOP */; 69426044Sminshall transflag = 0; 69536446Sbostic (void)free(buf); 696*36620Srick if (cnt != 0) 697*36620Srick goto data_err; 698*36620Srick reply(226, "Transfer complete."); 699*36620Srick return; 700*36620Srick default: 701*36620Srick transflag = 0; 702*36620Srick reply(550, "Unimplemented TYPE %d in send_data", type); 703*36620Srick return; 70410275Ssam } 705*36620Srick 706*36620Srick data_err: 70726044Sminshall transflag = 0; 708*36620Srick perror_reply(421, "Data connection"); 709*36620Srick dologout(1); 710*36620Srick /* NOTREACHED */ 71110275Ssam } 71210275Ssam 71310275Ssam /* 71410275Ssam * Transfer data from peer to 71510275Ssam * "outstr" using the appropriate 71610275Ssam * encapulation of the data subject 71710275Ssam * to Mode, Structure, and Type. 71810275Ssam * 71910275Ssam * N.B.: Form isn't handled. 72010275Ssam */ 72110275Ssam receive_data(instr, outstr) 72210275Ssam FILE *instr, *outstr; 72310275Ssam { 72410275Ssam register int c; 72511220Ssam int cnt; 72610275Ssam char buf[BUFSIZ]; 72710275Ssam 72810275Ssam 72926044Sminshall transflag++; 73026044Sminshall if (setjmp(urgcatch)) { 73126044Sminshall transflag = 0; 73226044Sminshall return(-1); 73326044Sminshall } 73410275Ssam switch (type) { 73510275Ssam 73610275Ssam case TYPE_I: 73710275Ssam case TYPE_L: 73826044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 739*36620Srick if (write(fileno(outstr), buf, cnt) != cnt) 740*36620Srick goto data_err; 74126044Sminshall } 742*36620Srick if (cnt < 0) 743*36620Srick goto data_err; 74426044Sminshall transflag = 0; 745*36620Srick return 0; 74610275Ssam 74710275Ssam case TYPE_E: 74827106Smckusick reply(553, "TYPE E not implemented."); 74926044Sminshall transflag = 0; 75027106Smckusick return (-1); 75110275Ssam 75210275Ssam case TYPE_A: 75310275Ssam while ((c = getc(instr)) != EOF) { 75427750Sminshall while (c == '\r') { 755*36620Srick if (ferror (outstr)) 756*36620Srick goto data_err; 75711220Ssam if ((c = getc(instr)) != '\n') 75827750Sminshall (void) putc ('\r', outstr); 75910275Ssam } 76027750Sminshall (void) putc (c, outstr); 76110275Ssam } 762*36620Srick fflush(outstr); 763*36620Srick if (ferror (instr) || ferror (outstr)) 764*36620Srick goto data_err; 76526044Sminshall transflag = 0; 76610275Ssam return (0); 767*36620Srick default: 768*36620Srick reply(550, "Unimplemented TYPE %d in receive_data", type); 769*36620Srick transflag = 0; 770*36620Srick return 1; 77110275Ssam } 772*36620Srick 773*36620Srick data_err: 77426044Sminshall transflag = 0; 775*36620Srick perror_reply(421, "Data Connection"); 776*36620Srick dologout(1); 777*36620Srick /* NOTREACHED */ 77810275Ssam } 77910275Ssam 78010275Ssam fatal(s) 78110275Ssam char *s; 78210275Ssam { 78310275Ssam reply(451, "Error in server: %s\n", s); 78410275Ssam reply(221, "Closing connection due to server error."); 78513247Ssam dologout(0); 786*36620Srick /* NOTREACHED */ 78710275Ssam } 78810275Ssam 78936446Sbostic /* VARARGS2 */ 79036446Sbostic reply(n, fmt, p0, p1, p2, p3, p4, p5) 79110275Ssam int n; 79236446Sbostic char *fmt; 79310275Ssam { 79410275Ssam printf("%d ", n); 79536446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 79610275Ssam printf("\r\n"); 79736435Sbostic (void)fflush(stdout); 79810275Ssam if (debug) { 79926493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 80036446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 80110275Ssam } 802*36620Srick } 80310275Ssam 80436446Sbostic /* VARARGS2 */ 80536446Sbostic lreply(n, fmt, p0, p1, p2, p3, p4, p5) 80610275Ssam int n; 80736446Sbostic char *fmt; 80810275Ssam { 80936446Sbostic printf("%d- ", n); 81036446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 81136446Sbostic printf("\r\n"); 81236435Sbostic (void)fflush(stdout); 81336446Sbostic if (debug) { 81436446Sbostic syslog(LOG_DEBUG, "<--- %d- ", n); 81536446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 81636446Sbostic } 81710275Ssam } 81810275Ssam 81910275Ssam ack(s) 82010275Ssam char *s; 82110275Ssam { 82227106Smckusick reply(250, "%s command successful.", s); 82310275Ssam } 82410275Ssam 82510275Ssam nack(s) 82610275Ssam char *s; 82710275Ssam { 82810275Ssam reply(502, "%s command not implemented.", s); 82910275Ssam } 83010275Ssam 83136304Skarels /* ARGSUSED */ 83226493Sminshall yyerror(s) 83326493Sminshall char *s; 83410275Ssam { 83526044Sminshall char *cp; 83626044Sminshall 83736551Sbostic if (cp = index(cbuf,'\n')) 83836551Sbostic *cp = '\0'; 83926044Sminshall reply(500, "'%s': command not understood.",cbuf); 84010275Ssam } 84110275Ssam 84210275Ssam delete(name) 84310275Ssam char *name; 84410275Ssam { 84510275Ssam struct stat st; 84610275Ssam 84710275Ssam if (stat(name, &st) < 0) { 84836304Skarels perror_reply(550, name); 84910275Ssam return; 85010275Ssam } 85110275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 85210275Ssam if (rmdir(name) < 0) { 85336304Skarels perror_reply(550, name); 85410275Ssam return; 85510275Ssam } 85610275Ssam goto done; 85710275Ssam } 85810275Ssam if (unlink(name) < 0) { 85936304Skarels perror_reply(550, name); 86010275Ssam return; 86110275Ssam } 86210275Ssam done: 86310275Ssam ack("DELE"); 86410275Ssam } 86510275Ssam 86610275Ssam cwd(path) 86710275Ssam char *path; 86810275Ssam { 869*36620Srick if (chdir(path) < 0) 87036304Skarels perror_reply(550, path); 871*36620Srick else 872*36620Srick ack("CWD"); 87310275Ssam } 87410275Ssam 87510303Ssam makedir(name) 87610275Ssam char *name; 87710275Ssam { 87836276Sbostic if (mkdir(name, 0777) < 0) 87936304Skarels perror_reply(550, name); 88036276Sbostic else 88136276Sbostic reply(257, "MKD command successful."); 88210275Ssam } 88310275Ssam 88410303Ssam removedir(name) 88510275Ssam char *name; 88610275Ssam { 887*36620Srick if (rmdir(name) < 0) 88836304Skarels perror_reply(550, name); 889*36620Srick else 890*36620Srick ack("RMD"); 89110275Ssam } 89210275Ssam 89310303Ssam pwd() 89410275Ssam { 89510303Ssam char path[MAXPATHLEN + 1]; 89636304Skarels extern char *getwd(); 89710275Ssam 898*36620Srick if (getwd(path) == (char *)NULL) 89927106Smckusick reply(550, "%s.", path); 900*36620Srick else 901*36620Srick reply(257, "\"%s\" is current directory.", path); 90210275Ssam } 90310275Ssam 90410275Ssam char * 90510275Ssam renamefrom(name) 90610275Ssam char *name; 90710275Ssam { 90810275Ssam struct stat st; 90910275Ssam 91010275Ssam if (stat(name, &st) < 0) { 91136304Skarels perror_reply(550, name); 91210275Ssam return ((char *)0); 91310275Ssam } 91410303Ssam reply(350, "File exists, ready for destination name"); 91510275Ssam return (name); 91610275Ssam } 91710275Ssam 91810275Ssam renamecmd(from, to) 91910275Ssam char *from, *to; 92010275Ssam { 921*36620Srick if (rename(from, to) < 0) 92236304Skarels perror_reply(550, "rename"); 923*36620Srick else 924*36620Srick ack("RNTO"); 92510275Ssam } 92610275Ssam 92710275Ssam dolog(sin) 92810275Ssam struct sockaddr_in *sin; 92910275Ssam { 93036304Skarels struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 93110275Ssam sizeof (struct in_addr), AF_INET); 93236304Skarels time_t t, time(); 93326493Sminshall extern char *ctime(); 93410275Ssam 93536304Skarels if (hp) 93626493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 93736304Skarels else 93826493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 93913247Ssam sizeof (remotehost)); 94013247Ssam if (!logging) 94113247Ssam return; 94226493Sminshall t = time((time_t *) 0); 94336304Skarels syslog(LOG_INFO, "connection from %s at %s", 94436304Skarels remotehost, ctime(&t)); 945*36620Srick #ifdef SETPROCTITLE 946*36620Srick setproctitle("%s: connected", remotehost); 947*36620Srick #endif /* SETPROCTITLE */ 94810275Ssam } 94910695Ssam 95010695Ssam /* 95113247Ssam * Record logout in wtmp file 95213247Ssam * and exit with supplied status. 95313247Ssam */ 95413247Ssam dologout(status) 95513247Ssam int status; 95613247Ssam { 95717580Ssam if (logged_in) { 95836304Skarels (void) seteuid((uid_t)0); 95935672Sbostic logwtmp(ttyline, "", ""); 96013247Ssam } 96114436Ssam /* beware of flushing buffers after a SIGPIPE */ 96214436Ssam _exit(status); 96313247Ssam } 96413247Ssam 96526044Sminshall myoob() 96626044Sminshall { 96727750Sminshall char *cp; 96826044Sminshall 96927750Sminshall /* only process if transfer occurring */ 97036304Skarels if (!transflag) 97126044Sminshall return; 97227750Sminshall cp = tmpline; 97327750Sminshall if (getline(cp, 7, stdin) == NULL) { 97436304Skarels reply(221, "You could at least say goodbye."); 97527750Sminshall dologout(0); 97626044Sminshall } 97726044Sminshall upper(cp); 97826227Ssam if (strcmp(cp, "ABOR\r\n")) 97926044Sminshall return; 98026044Sminshall tmpline[0] = '\0'; 98126044Sminshall reply(426,"Transfer aborted. Data connection closed."); 98226044Sminshall reply(226,"Abort successful"); 98326044Sminshall longjmp(urgcatch, 1); 98426044Sminshall } 98526044Sminshall 98627106Smckusick /* 987*36620Srick * Note: a response of 425 is not mentioned as a possible response to 988*36620Srick * the PASV command in RFC959. However, it has been blessed as 989*36620Srick * a legitimate response by Jon Postel in a telephone conversation 990*36620Srick * with Rick Adams on 25 Jan 89. 99127106Smckusick */ 99226044Sminshall passive() 99326044Sminshall { 99426044Sminshall int len; 99526044Sminshall struct sockaddr_in tmp; 99626044Sminshall register char *p, *a; 99726044Sminshall 99826044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 99926044Sminshall if (pdata < 0) { 1000*36620Srick perror_reply(425, "Can't open passive connection"); 100126044Sminshall return; 100226044Sminshall } 100326044Sminshall tmp = ctrl_addr; 100426044Sminshall tmp.sin_port = 0; 100536304Skarels (void) seteuid((uid_t)0); 100626493Sminshall if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) { 100736304Skarels (void) seteuid((uid_t)pw->pw_uid); 1008*36620Srick goto pasv_error; 100926044Sminshall } 101036304Skarels (void) seteuid((uid_t)pw->pw_uid); 101126044Sminshall len = sizeof(tmp); 1012*36620Srick if (getsockname(pdata, (struct sockaddr *) &tmp, &len) < 0) 1013*36620Srick goto pasv_error; 1014*36620Srick if (listen(pdata, 1) < 0) 1015*36620Srick goto pasv_error; 101626044Sminshall a = (char *) &tmp.sin_addr; 101726044Sminshall p = (char *) &tmp.sin_port; 101826044Sminshall 101926044Sminshall #define UC(b) (((int) b) & 0xff) 102026044Sminshall 102126044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 102226044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 1023*36620Srick return; 1024*36620Srick 1025*36620Srick pasv_error: 1026*36620Srick (void) close(pdata); 1027*36620Srick pdata = -1; 1028*36620Srick perror_reply(425, "Can't open passive connection"); 1029*36620Srick return; 103026044Sminshall } 103126044Sminshall 103236304Skarels /* 103336304Skarels * Generate unique name for file with basename "local". 103436304Skarels * The file named "local" is already known to exist. 103536304Skarels * Generates failure reply on error. 103636304Skarels */ 103726044Sminshall char * 103826044Sminshall gunique(local) 103926044Sminshall char *local; 104026044Sminshall { 104126044Sminshall static char new[MAXPATHLEN]; 104236304Skarels struct stat st; 104326044Sminshall char *cp = rindex(local, '/'); 1044*36620Srick int count=0; 104526044Sminshall 104636304Skarels if (cp) 104726044Sminshall *cp = '\0'; 1048*36620Srick if (stat(cp ? local : ".", &st) < 0) { 104936304Skarels perror_reply(553, local); 105026044Sminshall return((char *) 0); 105126044Sminshall } 1052*36620Srick if (cp) 1053*36620Srick *cp = '/'; 105426044Sminshall (void) strcpy(new, local); 105526044Sminshall cp = new + strlen(new); 105626044Sminshall *cp++ = '.'; 105736304Skarels for (count = 1; count < 100; count++) { 105836304Skarels (void) sprintf(cp, "%d", count); 105936304Skarels if (stat(new, &st) < 0) 106036304Skarels return(new); 106126044Sminshall } 106236304Skarels reply(452, "Unique file name cannot be created."); 106336304Skarels return((char *) 0); 106426044Sminshall } 106536304Skarels 106636304Skarels /* 106736304Skarels * Format and send reply containing system error number. 106836304Skarels */ 106936304Skarels perror_reply(code, string) 107036304Skarels int code; 107136304Skarels char *string; 107236304Skarels { 107336304Skarels if (errno < sys_nerr) 107436304Skarels reply(code, "%s: %s.", string, sys_errlist[errno]); 107536304Skarels else 107636304Skarels reply(code, "%s: unknown error %d.", string, errno); 107736304Skarels } 1078*36620Srick 1079*36620Srick static char *onefile[] = { 1080*36620Srick "", 1081*36620Srick 0 1082*36620Srick }; 1083*36620Srick 1084*36620Srick send_file_list(whichfiles) 1085*36620Srick char *whichfiles; 1086*36620Srick { 1087*36620Srick struct stat st; 1088*36620Srick DIR *dirp = NULL; 1089*36620Srick struct direct *dir; 1090*36620Srick FILE *dout = NULL; 1091*36620Srick register char **dirlist, *dirname; 1092*36620Srick char *strpbrk(); 1093*36620Srick 1094*36620Srick if (strpbrk(whichfiles, "~{[*?") != NULL) { 1095*36620Srick extern char **glob(), *globerr; 1096*36620Srick globerr = NULL; 1097*36620Srick dirlist = glob(whichfiles); 1098*36620Srick if (globerr != NULL) { 1099*36620Srick reply(550, globerr); 1100*36620Srick return; 1101*36620Srick } else if (dirlist == NULL) { 1102*36620Srick errno = ENOENT; 1103*36620Srick perror_reply(550, whichfiles); 1104*36620Srick return; 1105*36620Srick } 1106*36620Srick } else { 1107*36620Srick onefile[0] = whichfiles; 1108*36620Srick dirlist = onefile; 1109*36620Srick } 1110*36620Srick 1111*36620Srick while (dirname = *dirlist++) { 1112*36620Srick if (stat(dirname, &st) < 0) { 1113*36620Srick perror_reply(550, whichfiles); 1114*36620Srick if (dout != NULL) { 1115*36620Srick (void) fclose(dout); 1116*36620Srick data = -1; 1117*36620Srick pdata = -1; 1118*36620Srick } 1119*36620Srick return; 1120*36620Srick } 1121*36620Srick 1122*36620Srick if ((st.st_mode&S_IFMT) == S_IFREG) { 1123*36620Srick if (dout == NULL) { 1124*36620Srick dout = dataconn(whichfiles, (off_t)-1, "w"); 1125*36620Srick if (dout == NULL) 1126*36620Srick return; 1127*36620Srick } 1128*36620Srick fprintf(dout, "%s\n", dirname); 1129*36620Srick continue; 1130*36620Srick } else if ((st.st_mode&S_IFMT) != S_IFDIR) 1131*36620Srick continue; 1132*36620Srick 1133*36620Srick if ((dirp = opendir(dirname)) == NULL) 1134*36620Srick continue; 1135*36620Srick 1136*36620Srick while ((dir = readdir(dirp)) != NULL) { 1137*36620Srick char nbuf[BUFSIZ]; 1138*36620Srick 1139*36620Srick if (dir->d_name[0] == '.' && dir->d_namlen == 1) 1140*36620Srick continue; 1141*36620Srick if (dir->d_name[0] == '.' && dir->d_name[1] == '.' 1142*36620Srick && dir->d_namlen == 2) 1143*36620Srick continue; 1144*36620Srick 1145*36620Srick sprintf(nbuf, "%s/%s", dirname, dir->d_name); 1146*36620Srick 1147*36620Srick /* 1148*36620Srick * we have to do a stat to insure it's 1149*36620Srick * not a directory 1150*36620Srick */ 1151*36620Srick if (stat(nbuf, &st) == 0 && 1152*36620Srick (st.st_mode&S_IFMT) == S_IFREG) { 1153*36620Srick if (dout == NULL) { 1154*36620Srick dout = dataconn(whichfiles, (off_t)-1, 1155*36620Srick "w"); 1156*36620Srick if (dout == NULL) 1157*36620Srick return; 1158*36620Srick } 1159*36620Srick if (nbuf[0] == '.' && nbuf[1] == '/') 1160*36620Srick fprintf(dout, "%s\n", &nbuf[2]); 1161*36620Srick else 1162*36620Srick fprintf(dout, "%s\n", nbuf); 1163*36620Srick } 1164*36620Srick } 1165*36620Srick (void) closedir(dirp); 1166*36620Srick } 1167*36620Srick 1168*36620Srick if (dout != NULL && ferror(dout) != 0) 1169*36620Srick perror_reply(550, whichfiles); 1170*36620Srick else 1171*36620Srick reply(226, "Transfer complete."); 1172*36620Srick 1173*36620Srick if (dout != NULL) 1174*36620Srick (void) fclose(dout); 1175*36620Srick data = -1; 1176*36620Srick pdata = -1; 1177*36620Srick } 1178*36620Srick 1179*36620Srick #ifdef SETPROCTITLE 1180*36620Srick /* 1181*36620Srick * clobber argv so ps will show what we're doing. 1182*36620Srick * (stolen from sendmail) 1183*36620Srick * warning, since this is usually started from inetd.conf, it 1184*36620Srick * often doesn't have much of an environment or arglist to overwrite. 1185*36620Srick */ 1186*36620Srick 1187*36620Srick /*VARARGS1*/ 1188*36620Srick setproctitle(fmt, a, b, c) 1189*36620Srick char *fmt; 1190*36620Srick { 1191*36620Srick register char *p, *bp, ch; 1192*36620Srick register int i; 1193*36620Srick char buf[BUFSIZ]; 1194*36620Srick 1195*36620Srick (void) sprintf(buf, fmt, a, b, c); 1196*36620Srick 1197*36620Srick /* make ps print our process name */ 1198*36620Srick p = Argv[0]; 1199*36620Srick *p++ = '-'; 1200*36620Srick 1201*36620Srick i = strlen(buf); 1202*36620Srick if (i > LastArgv - p - 2) { 1203*36620Srick i = LastArgv - p - 2; 1204*36620Srick buf[i] = '\0'; 1205*36620Srick } 1206*36620Srick bp = buf; 1207*36620Srick while (ch = *bp++) 1208*36620Srick if (ch != '\n' && ch != '\r') 1209*36620Srick *p++ = ch; 1210*36620Srick while (p < LastArgv) 1211*36620Srick *p++ = ' '; 1212*36620Srick } 1213*36620Srick #endif /* SETPROCTITLE */ 1214