/* * Copyright (c) 1985, 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint char copyright[] = "@(#) Copyright (c) 1985, 1988 Regents of the University of California.\n\ All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)ftpd.c 5.26 (Berkeley) 01/25/89"; #endif /* not lint */ /* * FTP server. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * File containing login names * NOT to be used on this machine. * Commonly used to disallow uucp. */ #define FTPUSERS "/etc/ftpusers" extern int errno; extern char *sys_errlist[]; extern int sys_nerr; extern char *crypt(); extern char version[]; extern char *home; /* pointer to home directory for glob */ extern FILE *ftpd_popen(), *fopen(), *freopen(); extern int ftpd_pclose(), fclose(); extern char *getline(); extern char cbuf[]; extern off_t restart_point; struct sockaddr_in ctrl_addr; struct sockaddr_in data_source; struct sockaddr_in data_dest; struct sockaddr_in his_addr; int data; jmp_buf errcatch, urgcatch; int logged_in; struct passwd *pw; int debug; int timeout = 900; /* timeout after 15 minutes of inactivity */ int logging; int guest; int type; int form; int stru; /* avoid C keyword */ int mode; int usedefault = 1; /* for data transfers */ int pdata = -1; /* for passive mode */ int transflag; char tmpline[7]; char hostname[MAXHOSTNAMELEN]; char remotehost[MAXHOSTNAMELEN]; /* * Timeout intervals for retrying connections * to hosts that don't accept PORT cmds. This * is a kludge, but given the problems with TCP... */ #define SWAITMAX 90 /* wait at most 90 seconds */ #define SWAITINT 5 /* interval between retries */ int swaitmax = SWAITMAX; int swaitint = SWAITINT; int lostconn(); int myoob(); FILE *getdatasock(), *dataconn(); #ifdef SETPROCTITLE char **Argv = NULL; /* pointer to argument vector */ char *LastArgv = NULL; /* end of argv */ #endif /* SETPROCTITLE */ main(argc, argv, envp) int argc; char *argv[]; char **envp; { int addrlen, on = 1; char *cp; addrlen = sizeof (his_addr); if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); exit(1); } addrlen = sizeof (ctrl_addr); if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); exit(1); } data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); debug = 0; openlog("ftpd", LOG_PID, LOG_DAEMON); #ifdef SETPROCTITLE /* * Save start and extent of argv for setproctitle. */ Argv = argv; while (*envp) envp++; LastArgv = envp[-1] + strlen(envp[-1]); #endif /* SETPROCTITLE */ argc--, argv++; while (argc > 0 && *argv[0] == '-') { for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { case 'v': debug = 1; break; case 'd': debug = 1; break; case 'l': logging = 1; break; case 't': timeout = atoi(++cp); goto nextopt; default: fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", *cp); break; } nextopt: argc--, argv++; } (void) freopen("/dev/null", "w", stderr); (void) signal(SIGPIPE, lostconn); (void) signal(SIGCHLD, SIG_IGN); if ((int)signal(SIGURG, myoob) < 0) syslog(LOG_ERR, "signal: %m"); /* handle urgent data inline */ /* Sequent defines this, but it doesn't work */ #ifdef SO_OOBINLINE if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) syslog(LOG_ERR, "setsockopt: %m"); #endif if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) syslog(LOG_ERR, "fcntl F_SETOWN: %m"); dolog(&his_addr); /* do telnet option negotiation here */ /* * Set up default state */ data = -1; type = TYPE_A; form = FORM_N; stru = STRU_F; mode = MODE_S; tmpline[0] = '\0'; (void) gethostname(hostname, sizeof (hostname)); reply(220, "%s FTP server (%s) ready.", hostname, version); (void) setjmp(errcatch); for (;;) (void) yyparse(); /* NOTREACHED */ } lostconn() { if (debug) syslog(LOG_DEBUG, "lost connection"); dologout(-1); } static char ttyline[20]; /* * Helper function for sgetpwnam(). */ char * sgetsave(s) char *s; { char *malloc(); char *new = malloc((unsigned) strlen(s) + 1); if (new == NULL) { perror_reply(421, "Local resource failure: malloc"); dologout(1); /* NOTREACHED */ } (void) strcpy(new, s); return (new); } /* * Save the result of a getpwnam. Used for USER command, since * the data returned must not be clobbered by any other command * (e.g., globbing). */ struct passwd * sgetpwnam(name) char *name; { static struct passwd save; register struct passwd *p; char *sgetsave(); if ((p = getpwnam(name)) == NULL) return (p); if (save.pw_name) { free(save.pw_name); free(save.pw_passwd); free(save.pw_comment); free(save.pw_gecos); free(save.pw_dir); free(save.pw_shell); } save = *p; save.pw_name = sgetsave(p->pw_name); save.pw_passwd = sgetsave(p->pw_passwd); save.pw_comment = sgetsave(p->pw_comment); save.pw_gecos = sgetsave(p->pw_gecos); save.pw_dir = sgetsave(p->pw_dir); save.pw_shell = sgetsave(p->pw_shell); return (&save); } int login_attempts; /* number of failed login attempts */ int askpasswd; /* had user command, ask for passwd */ /* * USER command. * Sets global passwd pointer pw if named account exists * and is acceptable; sets askpasswd if a PASS command is * expected. If logged in previously, need to reset state. * If name is "ftp" or "anonymous" and ftp account exists, * set guest and pw, then just return. * If account doesn't exist, ask for passwd anyway. * Otherwise, check user requesting login privileges. * Disallow anyone who does not have a standard * shell returned by getusershell() (/etc/shells). * Disallow anyone mentioned in the file FTPUSERS * to allow people such as root and uucp to be avoided. */ user(name) char *name; { register char *cp; FILE *fd; char *shell; char line[BUFSIZ], *getusershell(); if (logged_in) { if (guest) { reply(530, "Can't change user from guest login."); return; } end_login(); } guest = 0; if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { if ((pw = sgetpwnam("ftp")) != NULL) { guest = 1; askpasswd = 1; reply(331, "Guest login ok, send ident as password."); } else reply(530, "User %s unknown.", name); return; } if (pw = sgetpwnam(name)) { if ((shell = pw->pw_shell) == NULL || *shell == 0) shell = "/bin/sh"; while ((cp = getusershell()) != NULL) if (strcmp(cp, shell) == 0) break; endusershell(); if (cp == NULL) { reply(530, "User %s access denied.", name); syslog(LOG_ERR, "FTP LOGIN REFUSED FROM %s, %s", remotehost, name); pw = (struct passwd *) NULL; return; } if ((fd = fopen(FTPUSERS, "r")) != NULL) { while (fgets(line, sizeof (line), fd) != NULL) { if ((cp = index(line, '\n')) != NULL) *cp = '\0'; if (strcmp(line, name) == 0) { reply(530, "User %s access denied.", name); syslog(LOG_ERR, "FTP LOGIN REFUSED FROM %s, %s", remotehost, name); pw = (struct passwd *) NULL; return; } } } (void) fclose(fd); } reply(331, "Password required for %s.", name); askpasswd = 1; /* * Delay before reading passwd after first failed * attempt to slow down passwd-guessing programs. */ if (login_attempts) sleep((unsigned) login_attempts); } /* * Terminate login as previous user, if any, resetting state; * used when USER command is given or login fails. */ end_login() { (void) seteuid((uid_t)0); if (logged_in) logwtmp(ttyline, "", ""); pw = NULL; logged_in = 0; guest = 0; } pass(passwd) char *passwd; { char *xpasswd, *salt; if (logged_in || askpasswd == 0) { reply(503, "Login with USER first."); return; } askpasswd = 0; if (!guest) { /* "ftp" is only account allowed no password */ if (pw == NULL) salt = "xx"; else salt = pw->pw_passwd; xpasswd = crypt(passwd, salt); /* The strcmp does not catch null passwords! */ if (pw == NULL || *pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { reply(530, "Login incorrect."); pw = NULL; if (login_attempts++ >= 5) { syslog(LOG_ERR, "repeated login failures from %s", remotehost); exit(0); } return; } } login_attempts = 0; /* this time successful */ (void) setegid((gid_t)pw->pw_gid); (void) initgroups(pw->pw_name, pw->pw_gid); /* open wtmp before chroot */ (void)sprintf(ttyline, "ftp%d", getpid()); logwtmp(ttyline, pw->pw_name, remotehost); logged_in = 1; if (guest) { /* * We MUST do a chdir() after the chroot. Otherwise "." * will be accessible outside the root! */ if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { reply(550, "Can't set guest privileges."); goto bad; } } else if (chdir(pw->pw_dir) < 0) { if (chdir("/") < 0) { reply(530, "User %s: can't change directory to %s.", pw->pw_name, pw->pw_dir); goto bad; } else lreply(230, "No directory! Logging in with home=/"); } if (seteuid((uid_t)pw->pw_uid) < 0) { reply(550, "Can't set uid."); goto bad; } if (guest) { reply(230, "Guest login ok, access restrictions apply."); syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", remotehost, passwd); } else { reply(230, "User %s logged in.", pw->pw_name); syslog(LOG_INFO, "FTP LOGIN FROM %s, %s", remotehost, pw->pw_name); } home = pw->pw_dir; /* home dir for globbing */ return; bad: /* Forget all about it... */ end_login(); } retrieve(cmd, name) char *cmd, *name; { FILE *fin, *dout; struct stat st; int (*closefunc)(); if (cmd == 0) { fin = fopen(name, "r"), closefunc = fclose; st.st_size = 0; } else { char line[BUFSIZ]; (void) sprintf(line, cmd, name), name = line; fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; st.st_size = -1; } if (fin == NULL) { if (errno != 0) perror_reply(550, name); return; } if (cmd == 0 && (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { reply(550, "%s: not a plain file.", name); goto done; } if (restart_point) { if (type == TYPE_A) { if (fseek(fin, restart_point, L_SET) < 0) { perror_reply(550, name); goto done; } } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { perror_reply(550, name); goto done; } } dout = dataconn(name, st.st_size, "w"); if (dout == NULL) goto done; send_data(fin, dout, st.st_blksize); (void) fclose(dout); data = -1; pdata = -1; done: (*closefunc)(fin); } store(name, mode, unique) char *name, *mode; int unique; { FILE *fout, *din; struct stat st; int (*closefunc)(); char *gunique(); if (unique && stat(name, &st) == 0 && (name = gunique(name)) == NULL) return; if (restart_point) mode = "r+w"; fout = fopen(name, mode); closefunc = fclose; if (fout == NULL) { perror_reply(553, name); return; } if (restart_point) { if (type == TYPE_A) { if (fseek(fout, restart_point, L_SET) < 0) { perror_reply(550, name); goto done; } } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { perror_reply(550, name); goto done; } } din = dataconn(name, (off_t)-1, "r"); if (din == NULL) goto done; if (receive_data(din, fout) == 0) { if (unique) reply(226, "Transfer complete (unique file name:%s).", name); else reply(226, "Transfer complete."); } (void) fclose(din); data = -1; pdata = -1; done: (*closefunc)(fout); } FILE * getdatasock(mode) char *mode; { int s, on = 1; if (data >= 0) return (fdopen(data, mode)); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) return (NULL); (void) seteuid((uid_t)0); if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) goto bad; /* anchor socket to avoid multi-homing problems */ data_source.sin_family = AF_INET; data_source.sin_addr = ctrl_addr.sin_addr; if (bind(s, (struct sockaddr *)&data_source, sizeof (data_source)) < 0) goto bad; (void) seteuid((uid_t)pw->pw_uid); return (fdopen(s, mode)); bad: (void) seteuid((uid_t)pw->pw_uid); (void) close(s); return (NULL); } FILE * dataconn(name, size, mode) char *name; off_t size; char *mode; { char sizebuf[32]; FILE *file; int retry = 0; if (size != (off_t) -1) (void) sprintf (sizebuf, " (%ld bytes)", size); else (void) strcpy(sizebuf, ""); if (pdata >= 0) { struct sockaddr_in from; int s, fromlen = sizeof(from); s = accept(pdata, (struct sockaddr *)&from, &fromlen); if (s < 0) { reply(425, "Can't open data connection."); (void) close(pdata); pdata = -1; return(NULL); } (void) close(pdata); pdata = s; reply(150, "Opening %s mode data connection for %s%s.", type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); return(fdopen(pdata, mode)); } if (data >= 0) { reply(125, "Using existing data connection for %s%s.", name, sizebuf); usedefault = 1; return (fdopen(data, mode)); } if (usedefault) data_dest = his_addr; usedefault = 1; file = getdatasock(mode); if (file == NULL) { reply(425, "Can't create data socket (%s,%d): %s.", inet_ntoa(data_source.sin_addr), ntohs(data_source.sin_port), errno < sys_nerr ? sys_errlist[errno] : "unknown error"); return (NULL); } data = fileno(file); while (connect(data, (struct sockaddr *)&data_dest, sizeof (data_dest)) < 0) { if (errno == EADDRINUSE && retry < swaitmax) { sleep((unsigned) swaitint); retry += swaitint; continue; } perror_reply(425, "Can't build data connection"); (void) fclose(file); data = -1; return (NULL); } reply(150, "Opening %s mode data connection for %s%s.", type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); return (file); } /* * Tranfer the contents of "instr" to * "outstr" peer using the appropriate * encapulation of the date subject * to Mode, Structure, and Type. * * NB: Form isn't handled. */ send_data(instr, outstr, blksize) FILE *instr, *outstr; off_t blksize; { register int c, cnt; register char *buf; int netfd, filefd; transflag++; if (setjmp(urgcatch)) { transflag = 0; return; } switch (type) { case TYPE_A: while ((c = getc(instr)) != EOF) { if (c == '\n') { if (ferror (outstr)) goto data_err; (void) putc('\r', outstr); } (void) putc(c, outstr); } transflag = 0; fflush(outstr); if (ferror (instr) || ferror (outstr)) goto data_err; reply(226, "Transfer complete."); return; case TYPE_I: case TYPE_L: if ((buf = malloc((u_int)blksize)) == NULL) { transflag = 0; perror_reply(421, "Local resource failure: malloc"); dologout(1); /* NOTREACHED */ } netfd = fileno(outstr); filefd = fileno(instr); while ((cnt = read(filefd, buf, sizeof(buf))) > 0 && write(netfd, buf, cnt) == cnt) /* LOOP */; transflag = 0; (void)free(buf); if (cnt != 0) goto data_err; reply(226, "Transfer complete."); return; default: transflag = 0; reply(550, "Unimplemented TYPE %d in send_data", type); return; } data_err: transflag = 0; perror_reply(421, "Data connection"); dologout(1); /* NOTREACHED */ } /* * Transfer data from peer to * "outstr" using the appropriate * encapulation of the data subject * to Mode, Structure, and Type. * * N.B.: Form isn't handled. */ receive_data(instr, outstr) FILE *instr, *outstr; { register int c; int cnt; char buf[BUFSIZ]; transflag++; if (setjmp(urgcatch)) { transflag = 0; return(-1); } switch (type) { case TYPE_I: case TYPE_L: while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { if (write(fileno(outstr), buf, cnt) != cnt) goto data_err; } if (cnt < 0) goto data_err; transflag = 0; return 0; case TYPE_E: reply(553, "TYPE E not implemented."); transflag = 0; return (-1); case TYPE_A: while ((c = getc(instr)) != EOF) { while (c == '\r') { if (ferror (outstr)) goto data_err; if ((c = getc(instr)) != '\n') (void) putc ('\r', outstr); } (void) putc (c, outstr); } fflush(outstr); if (ferror (instr) || ferror (outstr)) goto data_err; transflag = 0; return (0); default: reply(550, "Unimplemented TYPE %d in receive_data", type); transflag = 0; return 1; } data_err: transflag = 0; perror_reply(421, "Data Connection"); dologout(1); /* NOTREACHED */ } fatal(s) char *s; { reply(451, "Error in server: %s\n", s); reply(221, "Closing connection due to server error."); dologout(0); /* NOTREACHED */ } /* VARARGS2 */ reply(n, fmt, p0, p1, p2, p3, p4, p5) int n; char *fmt; { printf("%d ", n); printf(fmt, p0, p1, p2, p3, p4, p5); printf("\r\n"); (void)fflush(stdout); if (debug) { syslog(LOG_DEBUG, "<--- %d ", n); syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); } } /* VARARGS2 */ lreply(n, fmt, p0, p1, p2, p3, p4, p5) int n; char *fmt; { printf("%d- ", n); printf(fmt, p0, p1, p2, p3, p4, p5); printf("\r\n"); (void)fflush(stdout); if (debug) { syslog(LOG_DEBUG, "<--- %d- ", n); syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); } } ack(s) char *s; { reply(250, "%s command successful.", s); } nack(s) char *s; { reply(502, "%s command not implemented.", s); } /* ARGSUSED */ yyerror(s) char *s; { char *cp; if (cp = index(cbuf,'\n')) *cp = '\0'; reply(500, "'%s': command not understood.",cbuf); } delete(name) char *name; { struct stat st; if (stat(name, &st) < 0) { perror_reply(550, name); return; } if ((st.st_mode&S_IFMT) == S_IFDIR) { if (rmdir(name) < 0) { perror_reply(550, name); return; } goto done; } if (unlink(name) < 0) { perror_reply(550, name); return; } done: ack("DELE"); } cwd(path) char *path; { if (chdir(path) < 0) perror_reply(550, path); else ack("CWD"); } makedir(name) char *name; { if (mkdir(name, 0777) < 0) perror_reply(550, name); else reply(257, "MKD command successful."); } removedir(name) char *name; { if (rmdir(name) < 0) perror_reply(550, name); else ack("RMD"); } pwd() { char path[MAXPATHLEN + 1]; extern char *getwd(); if (getwd(path) == (char *)NULL) reply(550, "%s.", path); else reply(257, "\"%s\" is current directory.", path); } char * renamefrom(name) char *name; { struct stat st; if (stat(name, &st) < 0) { perror_reply(550, name); return ((char *)0); } reply(350, "File exists, ready for destination name"); return (name); } renamecmd(from, to) char *from, *to; { if (rename(from, to) < 0) perror_reply(550, "rename"); else ack("RNTO"); } dolog(sin) struct sockaddr_in *sin; { struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, sizeof (struct in_addr), AF_INET); time_t t, time(); extern char *ctime(); if (hp) (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); else (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof (remotehost)); if (!logging) return; t = time((time_t *) 0); syslog(LOG_INFO, "connection from %s at %s", remotehost, ctime(&t)); #ifdef SETPROCTITLE setproctitle("%s: connected", remotehost); #endif /* SETPROCTITLE */ } /* * Record logout in wtmp file * and exit with supplied status. */ dologout(status) int status; { if (logged_in) { (void) seteuid((uid_t)0); logwtmp(ttyline, "", ""); } /* beware of flushing buffers after a SIGPIPE */ _exit(status); } myoob() { char *cp; /* only process if transfer occurring */ if (!transflag) return; cp = tmpline; if (getline(cp, 7, stdin) == NULL) { reply(221, "You could at least say goodbye."); dologout(0); } upper(cp); if (strcmp(cp, "ABOR\r\n")) return; tmpline[0] = '\0'; reply(426,"Transfer aborted. Data connection closed."); reply(226,"Abort successful"); longjmp(urgcatch, 1); } /* * Note: a response of 425 is not mentioned as a possible response to * the PASV command in RFC959. However, it has been blessed as * a legitimate response by Jon Postel in a telephone conversation * with Rick Adams on 25 Jan 89. */ passive() { int len; struct sockaddr_in tmp; register char *p, *a; pdata = socket(AF_INET, SOCK_STREAM, 0); if (pdata < 0) { perror_reply(425, "Can't open passive connection"); return; } tmp = ctrl_addr; tmp.sin_port = 0; (void) seteuid((uid_t)0); if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) { (void) seteuid((uid_t)pw->pw_uid); goto pasv_error; } (void) seteuid((uid_t)pw->pw_uid); len = sizeof(tmp); if (getsockname(pdata, (struct sockaddr *) &tmp, &len) < 0) goto pasv_error; if (listen(pdata, 1) < 0) goto pasv_error; a = (char *) &tmp.sin_addr; p = (char *) &tmp.sin_port; #define UC(b) (((int) b) & 0xff) reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); return; pasv_error: (void) close(pdata); pdata = -1; perror_reply(425, "Can't open passive connection"); return; } /* * Generate unique name for file with basename "local". * The file named "local" is already known to exist. * Generates failure reply on error. */ char * gunique(local) char *local; { static char new[MAXPATHLEN]; struct stat st; char *cp = rindex(local, '/'); int count=0; if (cp) *cp = '\0'; if (stat(cp ? local : ".", &st) < 0) { perror_reply(553, local); return((char *) 0); } if (cp) *cp = '/'; (void) strcpy(new, local); cp = new + strlen(new); *cp++ = '.'; for (count = 1; count < 100; count++) { (void) sprintf(cp, "%d", count); if (stat(new, &st) < 0) return(new); } reply(452, "Unique file name cannot be created."); return((char *) 0); } /* * Format and send reply containing system error number. */ perror_reply(code, string) int code; char *string; { if (errno < sys_nerr) reply(code, "%s: %s.", string, sys_errlist[errno]); else reply(code, "%s: unknown error %d.", string, errno); } static char *onefile[] = { "", 0 }; send_file_list(whichfiles) char *whichfiles; { struct stat st; DIR *dirp = NULL; struct direct *dir; FILE *dout = NULL; register char **dirlist, *dirname; char *strpbrk(); if (strpbrk(whichfiles, "~{[*?") != NULL) { extern char **glob(), *globerr; globerr = NULL; dirlist = glob(whichfiles); if (globerr != NULL) { reply(550, globerr); return; } else if (dirlist == NULL) { errno = ENOENT; perror_reply(550, whichfiles); return; } } else { onefile[0] = whichfiles; dirlist = onefile; } while (dirname = *dirlist++) { if (stat(dirname, &st) < 0) { perror_reply(550, whichfiles); if (dout != NULL) { (void) fclose(dout); data = -1; pdata = -1; } return; } if ((st.st_mode&S_IFMT) == S_IFREG) { if (dout == NULL) { dout = dataconn(whichfiles, (off_t)-1, "w"); if (dout == NULL) return; } fprintf(dout, "%s\n", dirname); continue; } else if ((st.st_mode&S_IFMT) != S_IFDIR) continue; if ((dirp = opendir(dirname)) == NULL) continue; while ((dir = readdir(dirp)) != NULL) { char nbuf[BUFSIZ]; if (dir->d_name[0] == '.' && dir->d_namlen == 1) continue; if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && dir->d_namlen == 2) continue; sprintf(nbuf, "%s/%s", dirname, dir->d_name); /* * we have to do a stat to insure it's * not a directory */ if (stat(nbuf, &st) == 0 && (st.st_mode&S_IFMT) == S_IFREG) { if (dout == NULL) { dout = dataconn(whichfiles, (off_t)-1, "w"); if (dout == NULL) return; } if (nbuf[0] == '.' && nbuf[1] == '/') fprintf(dout, "%s\n", &nbuf[2]); else fprintf(dout, "%s\n", nbuf); } } (void) closedir(dirp); } if (dout != NULL && ferror(dout) != 0) perror_reply(550, whichfiles); else reply(226, "Transfer complete."); if (dout != NULL) (void) fclose(dout); data = -1; pdata = -1; } #ifdef SETPROCTITLE /* * clobber argv so ps will show what we're doing. * (stolen from sendmail) * warning, since this is usually started from inetd.conf, it * often doesn't have much of an environment or arglist to overwrite. */ /*VARARGS1*/ setproctitle(fmt, a, b, c) char *fmt; { register char *p, *bp, ch; register int i; char buf[BUFSIZ]; (void) sprintf(buf, fmt, a, b, c); /* make ps print our process name */ p = Argv[0]; *p++ = '-'; i = strlen(buf); if (i > LastArgv - p - 2) { i = LastArgv - p - 2; buf[i] = '\0'; } bp = buf; while (ch = *bp++) if (ch != '\n' && ch != '\r') *p++ = ch; while (p < LastArgv) *p++ = ' '; } #endif /* SETPROCTITLE */