110275Ssam #ifndef lint 2*10422Ssam static char sccsid[] = "@(#)ftpd.c 4.9 (Berkeley) 01/19/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> 2110275Ssam 2210275Ssam #include "ftp.h" 2310275Ssam 2410275Ssam extern int errno; 2510275Ssam extern char *sys_errlist[]; 2610275Ssam extern char *crypt(); 2710275Ssam extern char version[]; 2810275Ssam extern char *home; /* pointer to home directory for glob */ 2910275Ssam extern FILE *popen(), *fopen(); 3010275Ssam extern int pclose(), fclose(); 3110275Ssam 3210275Ssam struct sockaddr_in ctrl_addr; 3310275Ssam struct sockaddr_in data_source; 3410275Ssam struct sockaddr_in data_dest; 3510275Ssam struct sockaddr_in his_addr; 3610275Ssam 3710275Ssam struct hostent *hp; 3810275Ssam 3910275Ssam int data; 4010275Ssam jmp_buf errcatch; 4110275Ssam int logged_in; 4210275Ssam struct passwd *pw; 4310275Ssam int debug; 4410275Ssam int logging = 1; 4510275Ssam int guest; 4610275Ssam int type; 4710275Ssam int form; 4810275Ssam int stru; /* avoid C keyword */ 4910275Ssam int mode; 5010321Ssam int usedefault = 1; /* for data transfers */ 5110275Ssam char hostname[32]; 5210275Ssam char *remotehost; 5310321Ssam struct servent *sp; 5410275Ssam 5510275Ssam int lostconn(); 5610419Ssam int reapchild(); 5710275Ssam FILE *getdatasock(), *dataconn(); 5810275Ssam char *ntoa(); 5910275Ssam 6010275Ssam main(argc, argv) 6110275Ssam int argc; 6210275Ssam char *argv[]; 6310275Ssam { 6410275Ssam int ctrl, s, options = 0; 6510275Ssam char *cp; 6610275Ssam 6710275Ssam sp = getservbyname("ftp", "tcp"); 6810275Ssam if (sp == 0) { 6910275Ssam fprintf(stderr, "ftpd: fpt/tcp: unknown service\n"); 7010275Ssam exit(1); 7110275Ssam } 7210275Ssam ctrl_addr.sin_port = sp->s_port; 7310275Ssam data_source.sin_port = htons(ntohs(sp->s_port) - 1); 7410275Ssam signal(SIGPIPE, lostconn); 7510275Ssam debug = 0; 7610275Ssam argc--, argv++; 7710275Ssam while (argc > 0 && *argv[0] == '-') { 7810275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 7910275Ssam 8010275Ssam case 'd': 8110275Ssam debug = 1; 8210275Ssam options |= SO_DEBUG; 8310275Ssam break; 8410275Ssam 8510275Ssam default: 8610275Ssam fprintf(stderr, "Unknown flag -%c ignored.\n", cp); 8710275Ssam break; 8810275Ssam } 8910275Ssam argc--, argv++; 9010275Ssam } 9110275Ssam #ifndef DEBUG 9210275Ssam if (fork()) 9310275Ssam exit(0); 9410275Ssam for (s = 0; s < 10; s++) 9510317Ssam (void) close(s); 9610275Ssam (void) open("/dev/null", 0); 9710275Ssam (void) dup2(0, 1); 9810275Ssam { int tt = open("/dev/tty", 2); 9910275Ssam if (tt > 0) { 10010275Ssam ioctl(tt, TIOCNOTTY, 0); 10110275Ssam close(tt); 10210275Ssam } 10310275Ssam } 10410275Ssam #endif 10510303Ssam while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) { 10610275Ssam perror("ftpd: socket"); 10710275Ssam sleep(5); 10810275Ssam } 10910419Ssam if (options & SO_DEBUG) 11010419Ssam if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 11110419Ssam perror("ftpd: setsockopt (SO_DEBUG)"); 11210419Ssam #ifdef notdef 11310419Ssam if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0) 11410419Ssam perror("ftpd: setsockopt (SO_KEEPALIVE)"); 11510419Ssam #endif 11610275Ssam while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) { 11710275Ssam perror("ftpd: bind"); 11810275Ssam sleep(5); 11910275Ssam } 12010419Ssam signal(SIGCHLD, reapchild); 12110303Ssam listen(s, 10); 12210275Ssam for (;;) { 12310275Ssam int hisaddrlen = sizeof (his_addr); 12410275Ssam 12510275Ssam ctrl = accept(s, &his_addr, &hisaddrlen, 0); 12610275Ssam if (ctrl < 0) { 12710419Ssam if (errno == EINTR) 12810419Ssam continue; 12910275Ssam perror("ftpd: accept"); 13010275Ssam continue; 13110275Ssam } 13210275Ssam if (fork() == 0) { 13310275Ssam if (logging) 13410275Ssam dolog(&his_addr); 13510275Ssam close(s); 13610275Ssam dup2(ctrl, 0), close(ctrl), dup2(0, 1); 13710275Ssam /* do telnet option negotiation here */ 13810303Ssam /* 13910303Ssam * Set up default state 14010303Ssam */ 14110275Ssam logged_in = 0; 14210275Ssam data = -1; 14310303Ssam type = TYPE_A; 14410303Ssam form = FORM_N; 14510303Ssam stru = STRU_F; 14610303Ssam mode = MODE_S; 14710275Ssam gethostname(hostname, sizeof (hostname)); 14810275Ssam reply(220, "%s FTP server (%s) ready.", 14910275Ssam hostname, version); 15010275Ssam for (;;) { 15110275Ssam setjmp(errcatch); 15210275Ssam yyparse(); 15310275Ssam } 15410275Ssam } 15510275Ssam close(ctrl); 15610275Ssam } 15710275Ssam } 15810275Ssam 15910419Ssam reapchild() 16010419Ssam { 16110419Ssam union wait status; 16210419Ssam 16310419Ssam while (wait3(&status, WNOHANG, 0) > 0) 16410419Ssam ; 16510419Ssam } 16610419Ssam 16710275Ssam lostconn() 16810275Ssam { 16910275Ssam 17010275Ssam fatal("Connection closed."); 17110275Ssam } 17210275Ssam 17310275Ssam pass(passwd) 17410275Ssam char *passwd; 17510275Ssam { 17610303Ssam char *xpasswd, *savestr(); 17710303Ssam static struct passwd save; 17810275Ssam 17910275Ssam if (logged_in || pw == NULL) { 18010275Ssam reply(503, "Login with USER first."); 18110275Ssam return; 18210275Ssam } 18310275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 18410275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 18510275Ssam if (strcmp(xpasswd, pw->pw_passwd) != 0) { 18610275Ssam reply(530, "Login incorrect."); 18710275Ssam pw = NULL; 18810275Ssam return; 18910275Ssam } 19010275Ssam } 19110303Ssam setegid(pw->pw_gid); 19210275Ssam initgroups(pw->pw_name, pw->pw_gid); 19310275Ssam if (chdir(pw->pw_dir)) { 19410275Ssam reply(550, "User %s: can't change directory to $s.", 19510275Ssam pw->pw_name, pw->pw_dir); 19610303Ssam goto bad; 19710275Ssam } 19810303Ssam if (guest && chroot(pw->pw_dir) < 0) { 19910275Ssam reply(550, "Can't set guest privileges."); 20010303Ssam goto bad; 20110275Ssam } 20210275Ssam if (!guest) 20310275Ssam reply(230, "User %s logged in.", pw->pw_name); 20410275Ssam else 20510275Ssam reply(230, "Guest login ok, access restrictions apply."); 20610275Ssam logged_in = 1; 20710303Ssam seteuid(pw->pw_uid); 20810303Ssam /* 20910303Ssam * Save everything so globbing doesn't 21010303Ssam * clobber the fields. 21110303Ssam */ 21210303Ssam save = *pw; 21310303Ssam save.pw_name = savestr(pw->pw_name); 21410303Ssam save.pw_passwd = savestr(pw->pw_passwd); 21510303Ssam save.pw_comment = savestr(pw->pw_comment); 21610303Ssam save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 21710303Ssam save.pw_dir = savestr(pw->pw_dir); 21810303Ssam save.pw_shell = savestr(pw->pw_shell); 21910303Ssam pw = &save; 22010303Ssam home = pw->pw_dir; /* home dir for globbing */ 22110303Ssam return; 22210303Ssam bad: 22310303Ssam seteuid(0); 22410303Ssam pw = NULL; 22510275Ssam } 22610275Ssam 22710303Ssam char * 22810303Ssam savestr(s) 22910303Ssam char *s; 23010303Ssam { 23110303Ssam char *malloc(); 23210303Ssam char *new = malloc(strlen(s) + 1); 23310303Ssam 23410303Ssam if (new != NULL) 23510303Ssam strcpy(new, s); 23610303Ssam return(new); 23710303Ssam } 23810303Ssam 23910275Ssam retrieve(cmd, name) 24010275Ssam char *cmd, *name; 24110275Ssam { 24210275Ssam FILE *fin, *dout; 24310275Ssam struct stat st; 24410275Ssam int (*closefunc)(); 24510275Ssam 24610275Ssam if (cmd == 0) { 24710317Ssam #ifdef notdef 24810317Ssam /* no remote command execution -- it's a security hole */ 24910275Ssam if (*name == '!') 25010275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 25110275Ssam else 25210317Ssam #endif 25310275Ssam fin = fopen(name, "r"), closefunc = fclose; 25410275Ssam } else { 25510275Ssam char line[BUFSIZ]; 25610275Ssam 257*10422Ssam sprintf(line, cmd, name), name = line; 25810275Ssam fin = popen(line, "r"), closefunc = pclose; 25910275Ssam } 26010275Ssam if (fin == NULL) { 26110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 26210275Ssam return; 26310275Ssam } 26410275Ssam st.st_size = 0; 26510275Ssam if (cmd == 0 && 26610275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 26710275Ssam reply(550, "%s: not a plain file.", name); 26810275Ssam goto done; 26910275Ssam } 27010275Ssam dout = dataconn(name, st.st_size, "w"); 27110275Ssam if (dout == NULL) 27210275Ssam goto done; 27310303Ssam if (send_data(fin, dout) || ferror(dout)) 27410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 27510275Ssam else 27610275Ssam reply(226, "Transfer complete."); 27710303Ssam fclose(dout), data = -1; 27810275Ssam done: 27910275Ssam (*closefunc)(fin); 28010275Ssam } 28110275Ssam 28210275Ssam store(name, mode) 28310275Ssam char *name, *mode; 28410275Ssam { 28510275Ssam FILE *fout, *din; 28610303Ssam int (*closefunc)(), dochown = 0; 28710275Ssam 28810317Ssam #ifdef notdef 28910317Ssam /* no remote command execution -- it's a security hole */ 29010275Ssam if (name[0] == '!') 29110275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 29210317Ssam else 29310317Ssam #endif 29410317Ssam { 29510303Ssam struct stat st; 29610303Ssam 29710303Ssam if (stat(name, &st) < 0) 29810303Ssam dochown++; 29910275Ssam fout = fopen(name, mode), closefunc = fclose; 30010303Ssam } 30110275Ssam if (fout == NULL) { 30210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 30310275Ssam return; 30410275Ssam } 30510275Ssam din = dataconn(name, -1, "r"); 30610275Ssam if (din == NULL) 30710275Ssam goto done; 30810303Ssam if (receive_data(din, fout) || ferror(fout)) 30910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 31010275Ssam else 31110275Ssam reply(226, "Transfer complete."); 31210275Ssam fclose(din), data = -1; 31310275Ssam done: 31410303Ssam if (dochown) 31510303Ssam (void) chown(name, pw->pw_uid, -1); 31610275Ssam (*closefunc)(fout); 31710275Ssam } 31810275Ssam 31910275Ssam FILE * 32010275Ssam getdatasock(mode) 32110275Ssam char *mode; 32210275Ssam { 32310275Ssam int retrytime, s; 32410275Ssam 32510275Ssam if (data >= 0) 32610275Ssam return (fdopen(data, mode)); 32710275Ssam retrytime = 1; 32810275Ssam while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) { 32910275Ssam if (retrytime < 5) { 33010275Ssam sleep(retrytime); 33110275Ssam retrytime <<= 1; 33210275Ssam continue; 33310275Ssam } 33410275Ssam return (NULL); 33510275Ssam } 33610275Ssam retrytime = 1; 33710275Ssam seteuid(0); 33810275Ssam while (bind(s, &data_source, sizeof (data_source), 0) < 0) { 33910275Ssam if (retrytime < 5) { 34010275Ssam sleep(retrytime); 34110275Ssam retrytime <<= 1; 34210275Ssam continue; 34310275Ssam } 34410311Ssam seteuid(pw->pw_uid); 34510275Ssam close(s); 34610275Ssam return (NULL); 34710275Ssam } 34810311Ssam seteuid(pw->pw_uid); 34910275Ssam return (fdopen(s, mode)); 35010275Ssam } 35110275Ssam 35210275Ssam FILE * 35310275Ssam dataconn(name, size, mode) 35410275Ssam char *name; 35510275Ssam int size; 35610275Ssam char *mode; 35710275Ssam { 35810275Ssam char sizebuf[32]; 35910275Ssam FILE *file; 36010275Ssam 36110275Ssam if (size >= 0) 36210275Ssam sprintf(sizebuf, " (%d bytes)", size); 36310275Ssam else 36410275Ssam (void) strcpy(sizebuf, ""); 36510275Ssam if (data >= 0) { 36610275Ssam reply(125, "Using existing data connection for %s%s.", 36710275Ssam name, sizebuf); 36810321Ssam usedefault = 1; 36910275Ssam return (fdopen(data, mode)); 37010275Ssam } 371*10422Ssam /* 372*10422Ssam * If no PORT command was specified, 373*10422Ssam * use the default address. 374*10422Ssam */ 375*10422Ssam if (usedefault) { 376*10422Ssam data_dest = his_addr; 377*10422Ssam data_dest.sin_port = htons(ntohs(sp->s_port) - 1); 378*10422Ssam } 379*10422Ssam usedefault = 1; 38010275Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 38110275Ssam name, ntoa(data_dest.sin_addr.s_addr), 38210275Ssam ntohs(data_dest.sin_port), sizebuf); 38310275Ssam file = getdatasock(mode); 38410275Ssam if (file == NULL) { 38510275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 38610275Ssam ntoa(data_source.sin_addr), 38710275Ssam ntohs(data_source.sin_port), 38810275Ssam sys_errlist[errno]); 38910275Ssam return (NULL); 39010275Ssam } 39110275Ssam data = fileno(file); 39210275Ssam if (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 39310275Ssam reply(425, "Can't build data connection: %s.", 39410275Ssam sys_errlist[errno]); 39510275Ssam (void) fclose(file); 39610275Ssam data = -1; 39710275Ssam return (NULL); 39810275Ssam } 39910275Ssam return (file); 40010275Ssam } 40110275Ssam 40210275Ssam /* 40310275Ssam * Tranfer the contents of "instr" to 40410275Ssam * "outstr" peer using the appropriate 40510275Ssam * encapulation of the date subject 40610275Ssam * to Mode, Structure, and Type. 40710275Ssam * 40810275Ssam * NB: Form isn't handled. 40910275Ssam */ 41010275Ssam send_data(instr, outstr) 41110275Ssam FILE *instr, *outstr; 41210275Ssam { 41310275Ssam register int c; 41410275Ssam int netfd, filefd, cnt; 41510275Ssam char buf[BUFSIZ]; 41610275Ssam 41710275Ssam switch (type) { 41810275Ssam 41910275Ssam case TYPE_A: 42010275Ssam while ((c = getc(instr)) != EOF) { 42110275Ssam if (c == '\n') 42210275Ssam putc('\r', outstr); 42310275Ssam if (putc(c, outstr) == EOF) 42410275Ssam return (1); 42510275Ssam } 42610275Ssam return (0); 42710275Ssam 42810275Ssam case TYPE_I: 42910275Ssam case TYPE_L: 43010275Ssam netfd = fileno(outstr); 43110275Ssam filefd = fileno(instr); 43210275Ssam 43310303Ssam while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 43410275Ssam if (write(netfd, buf, cnt) < 0) 43510275Ssam return (1); 43610275Ssam return (cnt < 0); 43710275Ssam } 43810275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 43910275Ssam return (1); 44010275Ssam } 44110275Ssam 44210275Ssam /* 44310275Ssam * Transfer data from peer to 44410275Ssam * "outstr" using the appropriate 44510275Ssam * encapulation of the data subject 44610275Ssam * to Mode, Structure, and Type. 44710275Ssam * 44810275Ssam * N.B.: Form isn't handled. 44910275Ssam */ 45010275Ssam receive_data(instr, outstr) 45110275Ssam FILE *instr, *outstr; 45210275Ssam { 45310275Ssam register int c; 45410275Ssam int cr, escape, eof; 45510275Ssam int netfd, filefd, cnt; 45610275Ssam char buf[BUFSIZ]; 45710275Ssam 45810275Ssam 45910275Ssam switch (type) { 46010275Ssam 46110275Ssam case TYPE_I: 46210275Ssam case TYPE_L: 46310275Ssam netfd = fileno(instr); 46410275Ssam netfd = fileno(outstr); 46510275Ssam while ((cnt = read(netfd, buf, sizeof buf)) > 0) 46610275Ssam if (write(filefd, buf, cnt) < 0) 46710275Ssam return (1); 46810275Ssam return (cnt < 0); 46910275Ssam 47010275Ssam case TYPE_E: 47110275Ssam reply(504, "TYPE E not implemented."); 47210275Ssam return (1); 47310275Ssam 47410275Ssam case TYPE_A: 47510275Ssam cr = 0; 47610275Ssam while ((c = getc(instr)) != EOF) { 47710275Ssam if (cr) { 47810275Ssam if (c != '\r' && c != '\n') 47910275Ssam putc('\r', outstr); 48010275Ssam putc(c, outstr); 48110275Ssam cr = c == '\r'; 48210275Ssam continue; 48310275Ssam } 48410275Ssam if (c == '\r') { 48510275Ssam cr = 1; 48610275Ssam continue; 48710275Ssam } 48810275Ssam putc(c, outstr); 48910275Ssam } 49010275Ssam if (cr) 49110275Ssam putc('\r', outstr); 49210275Ssam return (0); 49310275Ssam } 49410275Ssam fatal("Unknown type in receive_data."); 49510275Ssam /*NOTREACHED*/ 49610275Ssam } 49710275Ssam 49810275Ssam fatal(s) 49910275Ssam char *s; 50010275Ssam { 50110275Ssam reply(451, "Error in server: %s\n", s); 50210275Ssam reply(221, "Closing connection due to server error."); 50310275Ssam exit(0); 50410275Ssam } 50510275Ssam 50610275Ssam reply(n, s, args) 50710275Ssam int n; 50810275Ssam char *s; 50910275Ssam { 51010275Ssam 51110275Ssam printf("%d ", n); 51210275Ssam _doprnt(s, &args, stdout); 51310275Ssam printf("\r\n"); 51410275Ssam fflush(stdout); 51510275Ssam if (debug) { 51610275Ssam fprintf(stderr, "<--- %d ", n); 51710275Ssam _doprnt(s, &args, stderr); 51810275Ssam fprintf(stderr, "\n"); 51910275Ssam fflush(stderr); 52010275Ssam } 52110275Ssam } 52210275Ssam 52310275Ssam lreply(n, s, args) 52410275Ssam int n; 52510275Ssam char *s; 52610275Ssam { 52710275Ssam printf("%d-", n); 52810275Ssam _doprnt(s, &args, stdout); 52910275Ssam printf("\r\n"); 53010275Ssam fflush(stdout); 53110275Ssam if (debug) { 53210275Ssam fprintf(stderr, "<--- %d-", n); 53310275Ssam _doprnt(s, &args, stderr); 53410275Ssam fprintf(stderr, "\n"); 53510275Ssam } 53610275Ssam } 53710275Ssam 53810275Ssam replystr(s) 53910275Ssam char *s; 54010275Ssam { 54110275Ssam printf("%s\r\n", s); 54210275Ssam fflush(stdout); 54310275Ssam if (debug) 54410275Ssam fprintf(stderr, "<--- %s\n", s); 54510275Ssam } 54610275Ssam 54710275Ssam ack(s) 54810275Ssam char *s; 54910275Ssam { 55010275Ssam reply(200, "%s command okay.", s); 55110275Ssam } 55210275Ssam 55310275Ssam nack(s) 55410275Ssam char *s; 55510275Ssam { 55610275Ssam reply(502, "%s command not implemented.", s); 55710275Ssam } 55810275Ssam 55910275Ssam yyerror() 56010275Ssam { 56110275Ssam reply(500, "Command not understood."); 56210275Ssam } 56310275Ssam 56410275Ssam delete(name) 56510275Ssam char *name; 56610275Ssam { 56710275Ssam struct stat st; 56810275Ssam 56910275Ssam if (stat(name, &st) < 0) { 57010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 57110275Ssam return; 57210275Ssam } 57310275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 57410275Ssam if (rmdir(name) < 0) { 57510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 57610275Ssam return; 57710275Ssam } 57810275Ssam goto done; 57910275Ssam } 58010275Ssam if (unlink(name) < 0) { 58110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 58210275Ssam return; 58310275Ssam } 58410275Ssam done: 58510275Ssam ack("DELE"); 58610275Ssam } 58710275Ssam 58810275Ssam cwd(path) 58910275Ssam char *path; 59010275Ssam { 59110275Ssam 59210275Ssam if (chdir(path) < 0) { 59310275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 59410275Ssam return; 59510275Ssam } 59610275Ssam ack("CWD"); 59710275Ssam } 59810275Ssam 59910303Ssam makedir(name) 60010275Ssam char *name; 60110275Ssam { 60210303Ssam struct stat st; 60310303Ssam int dochown = stat(name, &st) < 0; 60410275Ssam 60510275Ssam if (mkdir(name, 0777) < 0) { 60610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 60710275Ssam return; 60810275Ssam } 60910303Ssam if (dochown) 61010303Ssam (void) chown(name, pw->pw_uid, -1); 61110275Ssam ack("MKDIR"); 61210275Ssam } 61310275Ssam 61410303Ssam removedir(name) 61510275Ssam char *name; 61610275Ssam { 61710275Ssam 61810275Ssam if (rmdir(name) < 0) { 61910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 62010275Ssam return; 62110275Ssam } 62210275Ssam ack("RMDIR"); 62310275Ssam } 62410275Ssam 62510303Ssam pwd() 62610275Ssam { 62710303Ssam char path[MAXPATHLEN + 1]; 62810275Ssam char *p; 62910275Ssam 63010275Ssam if (getwd(path) == NULL) { 63110275Ssam reply(451, "%s.", path); 63210275Ssam return; 63310275Ssam } 63410275Ssam reply(251, "\"%s\" is current directory.", path); 63510275Ssam } 63610275Ssam 63710275Ssam char * 63810275Ssam renamefrom(name) 63910275Ssam char *name; 64010275Ssam { 64110275Ssam struct stat st; 64210275Ssam 64310275Ssam if (stat(name, &st) < 0) { 64410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 64510275Ssam return ((char *)0); 64610275Ssam } 64710303Ssam reply(350, "File exists, ready for destination name"); 64810275Ssam return (name); 64910275Ssam } 65010275Ssam 65110275Ssam renamecmd(from, to) 65210275Ssam char *from, *to; 65310275Ssam { 65410275Ssam 65510275Ssam if (rename(from, to) < 0) { 65610275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 65710275Ssam return; 65810275Ssam } 65910275Ssam ack("RNTO"); 66010275Ssam } 66110275Ssam 66210275Ssam int guest; 66310275Ssam /* 66410275Ssam * Test pathname for guest-user safety. 66510275Ssam */ 66610275Ssam inappropriate_request(name) 66710275Ssam char *name; 66810275Ssam { 66910275Ssam int bogus = 0, depth = 0, length = strlen(name); 67010275Ssam char *p, *s; 67110275Ssam 67210275Ssam if (!guest) 67310275Ssam return (0); 67410275Ssam if (name[0] == '/' || name[0] == '|') 67510275Ssam bogus = 1; 67610275Ssam for (p = name; p < name+length;) { 67710275Ssam s = p; /* start of token */ 67810275Ssam while ( *p && *p!= '/') 67910275Ssam p++; 68010275Ssam *p = 0; 68110275Ssam if (strcmp(s, "..") == 0) 68210275Ssam depth -= 1; /* backing up */ 68310275Ssam else if (strcmp(s, ".") == 0) 68410275Ssam depth += 0; /* no change */ 68510275Ssam else 68610275Ssam depth += 1; /* descending */ 68710275Ssam if (depth < 0) { 68810275Ssam bogus = 1; 68910275Ssam break; 69010275Ssam } 69110275Ssam } 69210275Ssam if (bogus) 69310275Ssam reply(553, "%s: pathname disallowed guest users", name); 69410275Ssam return (bogus); 69510275Ssam } 69610275Ssam 69710275Ssam /* 69810275Ssam * Convert network-format internet address 69910275Ssam * to base 256 d.d.d.d representation. 70010275Ssam */ 70110275Ssam char * 70210275Ssam ntoa(in) 70310275Ssam struct in_addr in; 70410275Ssam { 70510275Ssam static char b[18]; 70610275Ssam register char *p; 70710275Ssam 70810275Ssam in.s_addr = ntohl(in.s_addr); 70910275Ssam p = (char *)∈ 71010275Ssam #define UC(b) (((int)b)&0xff) 71110275Ssam sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 71210275Ssam return (b); 71310275Ssam } 71410275Ssam 71510275Ssam dolog(sin) 71610275Ssam struct sockaddr_in *sin; 71710275Ssam { 71810275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 71910275Ssam sizeof (struct in_addr), AF_INET); 72010275Ssam char *remotehost; 72110275Ssam time_t t; 72210275Ssam 72310275Ssam if (hp) 72410275Ssam remotehost = hp->h_name; 72510275Ssam else 72610275Ssam remotehost = "UNKNOWNHOST"; 72710275Ssam t = time(0); 72810303Ssam fprintf(stderr,"FTP: connection from %s at %s", remotehost, ctime(&t)); 72910275Ssam fflush(stderr); 73010275Ssam } 731