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