110275Ssam #ifndef lint 2*11653Ssam static char sccsid[] = "@(#)ftpd.c 4.18 (Berkeley) 03/23/83"; 310275Ssam #endif 410275Ssam 510275Ssam /* 610275Ssam * FTP server. 710275Ssam */ 810303Ssam #include <sys/param.h> 910275Ssam #include <sys/stat.h> 1010275Ssam #include <sys/ioctl.h> 1110275Ssam #include <sys/socket.h> 1210275Ssam 1310275Ssam #include <netinet/in.h> 1410275Ssam 1510275Ssam #include <stdio.h> 1610275Ssam #include <signal.h> 1710275Ssam #include <wait.h> 1810275Ssam #include <pwd.h> 1910275Ssam #include <setjmp.h> 2010275Ssam #include <netdb.h> 2110423Ssam #include <errno.h> 2210275Ssam 2310275Ssam #include "ftp.h" 2410275Ssam 2510695Ssam /* 2610695Ssam * File containing login names 2710695Ssam * NOT to be used on this machine. 2810695Ssam * Commonly used to disallow uucp. 2910695Ssam */ 3010695Ssam #define FTPUSERS "/etc/ftpusers" 3110695Ssam 3210275Ssam extern int errno; 3310275Ssam extern char *sys_errlist[]; 3410275Ssam extern char *crypt(); 3510275Ssam extern char version[]; 3610275Ssam extern char *home; /* pointer to home directory for glob */ 3710275Ssam extern FILE *popen(), *fopen(); 3810275Ssam extern int pclose(), fclose(); 3910275Ssam 4010275Ssam struct sockaddr_in ctrl_addr; 4110275Ssam struct sockaddr_in data_source; 4210275Ssam struct sockaddr_in data_dest; 4310275Ssam struct sockaddr_in his_addr; 4410275Ssam 4510275Ssam struct hostent *hp; 4610275Ssam 4710275Ssam int data; 4810275Ssam jmp_buf errcatch; 4910275Ssam int logged_in; 5010275Ssam struct passwd *pw; 5110275Ssam int debug; 52*11653Ssam int timeout; 5310275Ssam int logging = 1; 5410275Ssam int guest; 5510275Ssam int type; 5610275Ssam int form; 5710275Ssam int stru; /* avoid C keyword */ 5810275Ssam int mode; 5910321Ssam int usedefault = 1; /* for data transfers */ 6010275Ssam char hostname[32]; 6110275Ssam char *remotehost; 6210321Ssam struct servent *sp; 6310275Ssam 64*11653Ssam /* 65*11653Ssam * Timeout intervals for retrying connections 66*11653Ssam * to hosts that don't accept PORT cmds. This 67*11653Ssam * is a kludge, but given the problems with TCP... 68*11653Ssam */ 69*11653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 70*11653Ssam #define SWAITINT 5 /* interval between retries */ 71*11653Ssam 72*11653Ssam int swaitmax = SWAITMAX; 73*11653Ssam int swaitint = SWAITINT; 74*11653Ssam 7510275Ssam int lostconn(); 7610419Ssam int reapchild(); 7710275Ssam FILE *getdatasock(), *dataconn(); 7810275Ssam char *ntoa(); 7910275Ssam 8010275Ssam main(argc, argv) 8110275Ssam int argc; 8210275Ssam char *argv[]; 8310275Ssam { 8410275Ssam int ctrl, s, options = 0; 8510275Ssam char *cp; 8610275Ssam 8710275Ssam sp = getservbyname("ftp", "tcp"); 8810275Ssam if (sp == 0) { 8911220Ssam fprintf(stderr, "ftpd: ftp/tcp: unknown service\n"); 9010275Ssam exit(1); 9110275Ssam } 9210275Ssam ctrl_addr.sin_port = sp->s_port; 9310275Ssam data_source.sin_port = htons(ntohs(sp->s_port) - 1); 9410275Ssam signal(SIGPIPE, lostconn); 9510275Ssam debug = 0; 9610275Ssam argc--, argv++; 9710275Ssam while (argc > 0 && *argv[0] == '-') { 9810275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 9910275Ssam 100*11653Ssam case 'v': 101*11653Ssam debug = 1; 102*11653Ssam break; 103*11653Ssam 10410275Ssam case 'd': 10510275Ssam debug = 1; 10610275Ssam options |= SO_DEBUG; 10710275Ssam break; 10810275Ssam 109*11653Ssam case 't': 110*11653Ssam timeout = atoi(++cp); 111*11653Ssam goto nextopt; 112*11653Ssam break; 113*11653Ssam 11410275Ssam default: 115*11653Ssam fprintf(stderr, "Unknown flag -%c ignored.\n", *cp); 11610275Ssam break; 11710275Ssam } 118*11653Ssam nextopt: 11910275Ssam argc--, argv++; 12010275Ssam } 12110275Ssam #ifndef DEBUG 12210275Ssam if (fork()) 12310275Ssam exit(0); 12410275Ssam for (s = 0; s < 10; s++) 125*11653Ssam if (!logging || (s != 2)) 126*11653Ssam (void) close(s); 12710317Ssam (void) close(s); 12811220Ssam (void) open("/", 0); 12910275Ssam (void) dup2(0, 1); 130*11653Ssam if (!logging) 131*11653Ssam (void) dup2(0, 2); 13210275Ssam { int tt = open("/dev/tty", 2); 13310275Ssam if (tt > 0) { 13410275Ssam ioctl(tt, TIOCNOTTY, 0); 13510275Ssam close(tt); 13610275Ssam } 13710275Ssam } 13810275Ssam #endif 13910303Ssam while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) { 14010275Ssam perror("ftpd: socket"); 14110275Ssam sleep(5); 14210275Ssam } 14310419Ssam if (options & SO_DEBUG) 14410419Ssam if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 14510419Ssam perror("ftpd: setsockopt (SO_DEBUG)"); 14610419Ssam #ifdef notdef 14710419Ssam if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0) 14810419Ssam perror("ftpd: setsockopt (SO_KEEPALIVE)"); 14910419Ssam #endif 15010275Ssam while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) { 15110275Ssam perror("ftpd: bind"); 15210275Ssam sleep(5); 15310275Ssam } 15410588Ssam sigset(SIGCHLD, reapchild); 15510303Ssam listen(s, 10); 15610275Ssam for (;;) { 15710275Ssam int hisaddrlen = sizeof (his_addr); 15810275Ssam 15910275Ssam ctrl = accept(s, &his_addr, &hisaddrlen, 0); 16010275Ssam if (ctrl < 0) { 16110419Ssam if (errno == EINTR) 16210419Ssam continue; 16310275Ssam perror("ftpd: accept"); 16410275Ssam continue; 16510275Ssam } 16610275Ssam if (fork() == 0) { 16711220Ssam signal (SIGCHLD, SIG_IGN); 16810275Ssam if (logging) 16910275Ssam dolog(&his_addr); 17010275Ssam close(s); 17110275Ssam dup2(ctrl, 0), close(ctrl), dup2(0, 1); 17210275Ssam /* do telnet option negotiation here */ 17310303Ssam /* 17410303Ssam * Set up default state 17510303Ssam */ 17610275Ssam logged_in = 0; 17710275Ssam data = -1; 17810303Ssam type = TYPE_A; 17910303Ssam form = FORM_N; 18010303Ssam stru = STRU_F; 18110303Ssam mode = MODE_S; 18210275Ssam gethostname(hostname, sizeof (hostname)); 18310275Ssam reply(220, "%s FTP server (%s) ready.", 18410275Ssam hostname, version); 18510275Ssam for (;;) { 18610275Ssam setjmp(errcatch); 18710275Ssam yyparse(); 18810275Ssam } 18910275Ssam } 19010275Ssam close(ctrl); 19110275Ssam } 19210275Ssam } 19310275Ssam 19410419Ssam reapchild() 19510419Ssam { 19610419Ssam union wait status; 19710419Ssam 19810419Ssam while (wait3(&status, WNOHANG, 0) > 0) 19910419Ssam ; 20010419Ssam } 20110419Ssam 20210275Ssam lostconn() 20310275Ssam { 20410275Ssam 20510275Ssam fatal("Connection closed."); 20610275Ssam } 20710275Ssam 20810275Ssam pass(passwd) 20910275Ssam char *passwd; 21010275Ssam { 21110303Ssam char *xpasswd, *savestr(); 21210303Ssam static struct passwd save; 21310275Ssam 21410275Ssam if (logged_in || pw == NULL) { 21510275Ssam reply(503, "Login with USER first."); 21610275Ssam return; 21710275Ssam } 21810275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 21910275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 22010275Ssam if (strcmp(xpasswd, pw->pw_passwd) != 0) { 22110275Ssam reply(530, "Login incorrect."); 22210275Ssam pw = NULL; 22310275Ssam return; 22410275Ssam } 22510275Ssam } 22610303Ssam setegid(pw->pw_gid); 22710275Ssam initgroups(pw->pw_name, pw->pw_gid); 22810275Ssam if (chdir(pw->pw_dir)) { 22910275Ssam reply(550, "User %s: can't change directory to $s.", 23010275Ssam pw->pw_name, pw->pw_dir); 23110303Ssam goto bad; 23210275Ssam } 23310303Ssam if (guest && chroot(pw->pw_dir) < 0) { 23410275Ssam reply(550, "Can't set guest privileges."); 23510303Ssam goto bad; 23610275Ssam } 23710275Ssam if (!guest) 23810275Ssam reply(230, "User %s logged in.", pw->pw_name); 23910275Ssam else 24010275Ssam reply(230, "Guest login ok, access restrictions apply."); 24110275Ssam logged_in = 1; 24210303Ssam seteuid(pw->pw_uid); 24310303Ssam /* 24410303Ssam * Save everything so globbing doesn't 24510303Ssam * clobber the fields. 24610303Ssam */ 24710303Ssam save = *pw; 24810303Ssam save.pw_name = savestr(pw->pw_name); 24910303Ssam save.pw_passwd = savestr(pw->pw_passwd); 25010303Ssam save.pw_comment = savestr(pw->pw_comment); 25110303Ssam save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 25210303Ssam save.pw_dir = savestr(pw->pw_dir); 25310303Ssam save.pw_shell = savestr(pw->pw_shell); 25410303Ssam pw = &save; 25510303Ssam home = pw->pw_dir; /* home dir for globbing */ 25610303Ssam return; 25710303Ssam bad: 25810303Ssam seteuid(0); 25910303Ssam pw = NULL; 26010275Ssam } 26110275Ssam 26210303Ssam char * 26310303Ssam savestr(s) 26410303Ssam char *s; 26510303Ssam { 26610303Ssam char *malloc(); 26710303Ssam char *new = malloc(strlen(s) + 1); 26810303Ssam 26910303Ssam if (new != NULL) 27010303Ssam strcpy(new, s); 27111347Ssam return (new); 27210303Ssam } 27310303Ssam 27410275Ssam retrieve(cmd, name) 27510275Ssam char *cmd, *name; 27610275Ssam { 27710275Ssam FILE *fin, *dout; 27810275Ssam struct stat st; 27910275Ssam int (*closefunc)(); 28010275Ssam 28110275Ssam if (cmd == 0) { 28210317Ssam #ifdef notdef 28310317Ssam /* no remote command execution -- it's a security hole */ 284*11653Ssam if (*name == '|') 28510275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 28610275Ssam else 28710317Ssam #endif 28810275Ssam fin = fopen(name, "r"), closefunc = fclose; 28910275Ssam } else { 29010275Ssam char line[BUFSIZ]; 29110275Ssam 29210422Ssam sprintf(line, cmd, name), name = line; 29310275Ssam fin = popen(line, "r"), closefunc = pclose; 29410275Ssam } 29510275Ssam if (fin == NULL) { 29610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 29710275Ssam return; 29810275Ssam } 29910275Ssam st.st_size = 0; 30010275Ssam if (cmd == 0 && 30110275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 30210275Ssam reply(550, "%s: not a plain file.", name); 30310275Ssam goto done; 30410275Ssam } 30510275Ssam dout = dataconn(name, st.st_size, "w"); 30610275Ssam if (dout == NULL) 30710275Ssam goto done; 30810303Ssam if (send_data(fin, dout) || ferror(dout)) 30910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 31010275Ssam else 31110275Ssam reply(226, "Transfer complete."); 31210303Ssam fclose(dout), data = -1; 31310275Ssam done: 31410275Ssam (*closefunc)(fin); 31510275Ssam } 31610275Ssam 31710275Ssam store(name, mode) 31810275Ssam char *name, *mode; 31910275Ssam { 32010275Ssam FILE *fout, *din; 32110303Ssam int (*closefunc)(), dochown = 0; 32210275Ssam 32310317Ssam #ifdef notdef 32410317Ssam /* no remote command execution -- it's a security hole */ 325*11653Ssam if (name[0] == '|') 32610275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 32710317Ssam else 32810317Ssam #endif 32910317Ssam { 33010303Ssam struct stat st; 33110303Ssam 33210303Ssam if (stat(name, &st) < 0) 33310303Ssam dochown++; 33410275Ssam fout = fopen(name, mode), closefunc = fclose; 33510303Ssam } 33610275Ssam if (fout == NULL) { 33710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 33810275Ssam return; 33910275Ssam } 340*11653Ssam din = dataconn(name, (off_t)-1, "r"); 34110275Ssam if (din == NULL) 34210275Ssam goto done; 34310303Ssam if (receive_data(din, fout) || ferror(fout)) 34410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 34510275Ssam else 34610275Ssam reply(226, "Transfer complete."); 34710275Ssam fclose(din), data = -1; 34810275Ssam done: 34910303Ssam if (dochown) 35010303Ssam (void) chown(name, pw->pw_uid, -1); 35110275Ssam (*closefunc)(fout); 35210275Ssam } 35310275Ssam 35410275Ssam FILE * 35510275Ssam getdatasock(mode) 35610275Ssam char *mode; 35710275Ssam { 35810602Ssam int s; 35910275Ssam 36010275Ssam if (data >= 0) 36110275Ssam return (fdopen(data, mode)); 36210602Ssam s = socket(AF_INET, SOCK_STREAM, 0, 0); 36310602Ssam if (s < 0) 36410275Ssam return (NULL); 36510275Ssam seteuid(0); 36610602Ssam if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) 36710602Ssam goto bad; 36810602Ssam if (bind(s, &data_source, sizeof (data_source), 0) < 0) 36910602Ssam goto bad; 37010311Ssam seteuid(pw->pw_uid); 37110275Ssam return (fdopen(s, mode)); 37210602Ssam bad: 37310602Ssam seteuid(pw->pw_uid); 37410602Ssam close(s); 37510602Ssam return (NULL); 37610275Ssam } 37710275Ssam 37810275Ssam FILE * 37910275Ssam dataconn(name, size, mode) 38010275Ssam char *name; 381*11653Ssam off_t size; 38210275Ssam char *mode; 38310275Ssam { 38410275Ssam char sizebuf[32]; 38510275Ssam FILE *file; 386*11653Ssam int retry = 0; 38710275Ssam 38810275Ssam if (size >= 0) 389*11653Ssam sprintf (sizebuf, " (%ld bytes)", size); 39010275Ssam else 39110275Ssam (void) strcpy(sizebuf, ""); 39210275Ssam if (data >= 0) { 39310275Ssam reply(125, "Using existing data connection for %s%s.", 39410275Ssam name, sizebuf); 39510321Ssam usedefault = 1; 39610275Ssam return (fdopen(data, mode)); 39710275Ssam } 39810566Ssam if (usedefault) 39910422Ssam data_dest = his_addr; 40010422Ssam usedefault = 1; 40110275Ssam file = getdatasock(mode); 40210275Ssam if (file == NULL) { 40310275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 40410275Ssam ntoa(data_source.sin_addr), 40510275Ssam ntohs(data_source.sin_port), 40610275Ssam sys_errlist[errno]); 40710275Ssam return (NULL); 40810275Ssam } 40910602Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 41010602Ssam name, ntoa(data_dest.sin_addr.s_addr), 41110602Ssam ntohs(data_dest.sin_port), sizebuf); 41210275Ssam data = fileno(file); 413*11653Ssam while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 414*11653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 415*11653Ssam sleep(swaitint); 416*11653Ssam retry += swaitint; 417*11653Ssam continue; 418*11653Ssam } 41910275Ssam reply(425, "Can't build data connection: %s.", 42010275Ssam sys_errlist[errno]); 42110275Ssam (void) fclose(file); 42210275Ssam data = -1; 42310275Ssam return (NULL); 42410275Ssam } 42510275Ssam return (file); 42610275Ssam } 42710275Ssam 42810275Ssam /* 42910275Ssam * Tranfer the contents of "instr" to 43010275Ssam * "outstr" peer using the appropriate 43110275Ssam * encapulation of the date subject 43210275Ssam * to Mode, Structure, and Type. 43310275Ssam * 43410275Ssam * NB: Form isn't handled. 43510275Ssam */ 43610275Ssam send_data(instr, outstr) 43710275Ssam FILE *instr, *outstr; 43810275Ssam { 43910275Ssam register int c; 44010275Ssam int netfd, filefd, cnt; 44110275Ssam char buf[BUFSIZ]; 44210275Ssam 44310275Ssam switch (type) { 44410275Ssam 44510275Ssam case TYPE_A: 44610275Ssam while ((c = getc(instr)) != EOF) { 44711220Ssam if (c == '\n') { 44811220Ssam if (ferror (outstr)) 44911220Ssam return (1); 45010275Ssam putc('\r', outstr); 45111220Ssam } 45211220Ssam putc(c, outstr); 45311220Ssam if (c == '\r') 45411220Ssam putc ('\0', outstr); 45510275Ssam } 45611220Ssam if (ferror (instr) || ferror (outstr)) 45711220Ssam return (1); 45810275Ssam return (0); 45910275Ssam 46010275Ssam case TYPE_I: 46110275Ssam case TYPE_L: 46210275Ssam netfd = fileno(outstr); 46310275Ssam filefd = fileno(instr); 46410275Ssam 46510303Ssam while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 46610275Ssam if (write(netfd, buf, cnt) < 0) 46710275Ssam return (1); 46810275Ssam return (cnt < 0); 46910275Ssam } 47010275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 47110275Ssam return (1); 47210275Ssam } 47310275Ssam 47410275Ssam /* 47510275Ssam * Transfer data from peer to 47610275Ssam * "outstr" using the appropriate 47710275Ssam * encapulation of the data subject 47810275Ssam * to Mode, Structure, and Type. 47910275Ssam * 48010275Ssam * N.B.: Form isn't handled. 48110275Ssam */ 48210275Ssam receive_data(instr, outstr) 48310275Ssam FILE *instr, *outstr; 48410275Ssam { 48510275Ssam register int c; 48611220Ssam int cnt; 48710275Ssam char buf[BUFSIZ]; 48810275Ssam 48910275Ssam 49010275Ssam switch (type) { 49110275Ssam 49210275Ssam case TYPE_I: 49310275Ssam case TYPE_L: 49410616Ssam while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) 49510616Ssam if (write(fileno(outstr), buf, cnt) < 0) 49610275Ssam return (1); 49710275Ssam return (cnt < 0); 49810275Ssam 49910275Ssam case TYPE_E: 50010275Ssam reply(504, "TYPE E not implemented."); 50110275Ssam return (1); 50210275Ssam 50310275Ssam case TYPE_A: 50410275Ssam while ((c = getc(instr)) != EOF) { 50510275Ssam if (c == '\r') { 50611220Ssam if (ferror (outstr)) 50711220Ssam return (1); 50811220Ssam if ((c = getc(instr)) != '\n') 50911220Ssam putc ('\r', outstr); 51011220Ssam if (c == '\0') 51111220Ssam continue; 51210275Ssam } 51311220Ssam putc (c, outstr); 51410275Ssam } 51511220Ssam if (ferror (instr) || ferror (outstr)) 51611220Ssam return (1); 51710275Ssam return (0); 51810275Ssam } 51910275Ssam fatal("Unknown type in receive_data."); 52010275Ssam /*NOTREACHED*/ 52110275Ssam } 52210275Ssam 52310275Ssam fatal(s) 52410275Ssam char *s; 52510275Ssam { 52610275Ssam reply(451, "Error in server: %s\n", s); 52710275Ssam reply(221, "Closing connection due to server error."); 52810275Ssam exit(0); 52910275Ssam } 53010275Ssam 53110275Ssam reply(n, s, args) 53210275Ssam int n; 53310275Ssam char *s; 53410275Ssam { 53510275Ssam 53610275Ssam printf("%d ", n); 53710275Ssam _doprnt(s, &args, stdout); 53810275Ssam printf("\r\n"); 53910275Ssam fflush(stdout); 54010275Ssam if (debug) { 54110275Ssam fprintf(stderr, "<--- %d ", n); 54210275Ssam _doprnt(s, &args, stderr); 54310275Ssam fprintf(stderr, "\n"); 54410275Ssam fflush(stderr); 54510275Ssam } 54610275Ssam } 54710275Ssam 54810275Ssam lreply(n, s, args) 54910275Ssam int n; 55010275Ssam char *s; 55110275Ssam { 55210275Ssam printf("%d-", n); 55310275Ssam _doprnt(s, &args, stdout); 55410275Ssam printf("\r\n"); 55510275Ssam fflush(stdout); 55610275Ssam if (debug) { 55710275Ssam fprintf(stderr, "<--- %d-", n); 55810275Ssam _doprnt(s, &args, stderr); 55910275Ssam fprintf(stderr, "\n"); 56010275Ssam } 56110275Ssam } 56210275Ssam 56310275Ssam replystr(s) 56410275Ssam char *s; 56510275Ssam { 56610275Ssam printf("%s\r\n", s); 56710275Ssam fflush(stdout); 56810275Ssam if (debug) 56910275Ssam fprintf(stderr, "<--- %s\n", s); 57010275Ssam } 57110275Ssam 57210275Ssam ack(s) 57310275Ssam char *s; 57410275Ssam { 57510275Ssam reply(200, "%s command okay.", s); 57610275Ssam } 57710275Ssam 57810275Ssam nack(s) 57910275Ssam char *s; 58010275Ssam { 58110275Ssam reply(502, "%s command not implemented.", s); 58210275Ssam } 58310275Ssam 58410275Ssam yyerror() 58510275Ssam { 58610275Ssam reply(500, "Command not understood."); 58710275Ssam } 58810275Ssam 58910275Ssam delete(name) 59010275Ssam char *name; 59110275Ssam { 59210275Ssam struct stat st; 59310275Ssam 59410275Ssam if (stat(name, &st) < 0) { 59510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 59610275Ssam return; 59710275Ssam } 59810275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 59910275Ssam if (rmdir(name) < 0) { 60010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 60110275Ssam return; 60210275Ssam } 60310275Ssam goto done; 60410275Ssam } 60510275Ssam if (unlink(name) < 0) { 60610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 60710275Ssam return; 60810275Ssam } 60910275Ssam done: 61010275Ssam ack("DELE"); 61110275Ssam } 61210275Ssam 61310275Ssam cwd(path) 61410275Ssam char *path; 61510275Ssam { 61610275Ssam 61710275Ssam if (chdir(path) < 0) { 61810275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 61910275Ssam return; 62010275Ssam } 62110275Ssam ack("CWD"); 62210275Ssam } 62310275Ssam 62410303Ssam makedir(name) 62510275Ssam char *name; 62610275Ssam { 62710303Ssam struct stat st; 62810303Ssam int dochown = stat(name, &st) < 0; 62910275Ssam 63010275Ssam if (mkdir(name, 0777) < 0) { 63110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 63210275Ssam return; 63310275Ssam } 63410303Ssam if (dochown) 63510303Ssam (void) chown(name, pw->pw_uid, -1); 63610275Ssam ack("MKDIR"); 63710275Ssam } 63810275Ssam 63910303Ssam removedir(name) 64010275Ssam char *name; 64110275Ssam { 64210275Ssam 64310275Ssam if (rmdir(name) < 0) { 64410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 64510275Ssam return; 64610275Ssam } 64710275Ssam ack("RMDIR"); 64810275Ssam } 64910275Ssam 65010303Ssam pwd() 65110275Ssam { 65210303Ssam char path[MAXPATHLEN + 1]; 65310275Ssam 65410275Ssam if (getwd(path) == NULL) { 65510275Ssam reply(451, "%s.", path); 65610275Ssam return; 65710275Ssam } 65810275Ssam reply(251, "\"%s\" is current directory.", path); 65910275Ssam } 66010275Ssam 66110275Ssam char * 66210275Ssam renamefrom(name) 66310275Ssam char *name; 66410275Ssam { 66510275Ssam struct stat st; 66610275Ssam 66710275Ssam if (stat(name, &st) < 0) { 66810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 66910275Ssam return ((char *)0); 67010275Ssam } 67110303Ssam reply(350, "File exists, ready for destination name"); 67210275Ssam return (name); 67310275Ssam } 67410275Ssam 67510275Ssam renamecmd(from, to) 67610275Ssam char *from, *to; 67710275Ssam { 67810275Ssam 67910275Ssam if (rename(from, to) < 0) { 68010275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 68110275Ssam return; 68210275Ssam } 68310275Ssam ack("RNTO"); 68410275Ssam } 68510275Ssam 68610275Ssam /* 68710275Ssam * Convert network-format internet address 68810275Ssam * to base 256 d.d.d.d representation. 68910275Ssam */ 69010275Ssam char * 69110275Ssam ntoa(in) 69210275Ssam struct in_addr in; 69310275Ssam { 69410275Ssam static char b[18]; 69510275Ssam register char *p; 69610275Ssam 69710275Ssam p = (char *)∈ 69810275Ssam #define UC(b) (((int)b)&0xff) 69910275Ssam sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 70010275Ssam return (b); 70110275Ssam } 70210275Ssam 70310275Ssam dolog(sin) 70410275Ssam struct sockaddr_in *sin; 70510275Ssam { 70610275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 70710275Ssam sizeof (struct in_addr), AF_INET); 70810275Ssam char *remotehost; 70910275Ssam time_t t; 71010275Ssam 71110275Ssam if (hp) 71210275Ssam remotehost = hp->h_name; 71310275Ssam else 71410275Ssam remotehost = "UNKNOWNHOST"; 71510275Ssam t = time(0); 71610303Ssam fprintf(stderr,"FTP: connection from %s at %s", remotehost, ctime(&t)); 71710275Ssam fflush(stderr); 71810275Ssam } 71910695Ssam 72010695Ssam /* 72110695Ssam * Special version of popen which avoids 72210695Ssam * call to shell. This insures noone may 72310695Ssam * create a pipe to a hidden program as a side 72410695Ssam * effect of a list or dir command. 72510695Ssam */ 72610695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 72710695Ssam #define RDR 0 72810695Ssam #define WTR 1 72910695Ssam static int popen_pid[5]; 73010695Ssam 73110695Ssam static char * 73210695Ssam nextarg(cpp) 73310695Ssam char *cpp; 73410695Ssam { 73510695Ssam register char *cp = cpp; 73610695Ssam 73710695Ssam if (cp == 0) 73810695Ssam return (cp); 73910695Ssam while (*cp && *cp != ' ' && *cp != '\t') 74010695Ssam cp++; 74110695Ssam if (*cp == ' ' || *cp == '\t') { 74210695Ssam *cp++ = '\0'; 74310695Ssam while (*cp == ' ' || *cp == '\t') 74410695Ssam cp++; 74510695Ssam } 74610695Ssam if (cp == cpp) 74710695Ssam return ((char *)0); 74810695Ssam return (cp); 74910695Ssam } 75010695Ssam 75110695Ssam FILE * 75210695Ssam popen(cmd, mode) 75310695Ssam char *cmd, *mode; 75410695Ssam { 75510695Ssam int p[2], ac; 75610695Ssam register myside, hisside, pid; 75710695Ssam char *av[10]; 75810695Ssam register char *cp; 75910695Ssam 76010695Ssam if (pipe(p) < 0) 76110695Ssam return (NULL); 76210695Ssam cp = cmd, ac = 0; 76310695Ssam do { 76410695Ssam av[ac++] = cp; 76510695Ssam cp = nextarg(cp); 76610695Ssam } while (cp && *cp); 76710695Ssam av[ac] = (char *)0; 76810695Ssam myside = tst(p[WTR], p[RDR]); 76910695Ssam hisside = tst(p[RDR], p[WTR]); 77010695Ssam if ((pid = fork()) == 0) { 77110695Ssam /* myside and hisside reverse roles in child */ 77210695Ssam close(myside); 77310695Ssam dup2(hisside, tst(0, 1)); 77410695Ssam close(hisside); 77510695Ssam execv(av[0], av); 77610695Ssam _exit(1); 77710695Ssam } 77810695Ssam if (pid == -1) 77910695Ssam return (NULL); 78010695Ssam popen_pid[myside] = pid; 78110695Ssam close(hisside); 78210695Ssam return (fdopen(myside, mode)); 78310695Ssam } 78410695Ssam 78510695Ssam pclose(ptr) 78610695Ssam FILE *ptr; 78710695Ssam { 78810695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 78910695Ssam int status; 79010695Ssam 79110695Ssam f = fileno(ptr); 79210695Ssam fclose(ptr); 79310695Ssam istat = signal(SIGINT, SIG_IGN); 79410695Ssam qstat = signal(SIGQUIT, SIG_IGN); 79510695Ssam hstat = signal(SIGHUP, SIG_IGN); 79610695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 79710695Ssam ; 79810695Ssam if (r == -1) 79910695Ssam status = -1; 80010695Ssam signal(SIGINT, istat); 80110695Ssam signal(SIGQUIT, qstat); 80210695Ssam signal(SIGHUP, hstat); 80310695Ssam return (status); 80410695Ssam } 80510695Ssam 80610695Ssam /* 80710695Ssam * Check user requesting login priviledges. 80810695Ssam * Disallow anyone mentioned in the file FTPUSERS 80910695Ssam * to allow people such as uucp to be avoided. 81010695Ssam */ 81110695Ssam checkuser(name) 81210695Ssam register char *name; 81310695Ssam { 81410695Ssam char line[BUFSIZ], *index(); 81510695Ssam FILE *fd; 81610695Ssam int found = 0; 81710695Ssam 81810695Ssam fd = fopen(FTPUSERS, "r"); 81910695Ssam if (fd == NULL) 82010695Ssam return (1); 82110695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 82210695Ssam register char *cp = index(line, '\n'); 82310695Ssam 82410695Ssam if (cp) 82510695Ssam *cp = '\0'; 82610695Ssam if (strcmp(line, name) == 0) { 82710695Ssam found++; 82810695Ssam break; 82910695Ssam } 83010695Ssam } 83110695Ssam fclose(fd); 83210695Ssam return (!found); 83310695Ssam } 834