110275Ssam #ifndef lint 2*10317Ssam static char sccsid[] = "@(#)ftpd.c 4.5 (Berkeley) 83/01/16"; 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; 5010275Ssam char hostname[32]; 5110275Ssam char *remotehost; 5210275Ssam 5310275Ssam int lostconn(); 5410275Ssam FILE *getdatasock(), *dataconn(); 5510275Ssam char *ntoa(); 5610275Ssam 5710275Ssam main(argc, argv) 5810275Ssam int argc; 5910275Ssam char *argv[]; 6010275Ssam { 6110275Ssam int ctrl, s, options = 0; 6210275Ssam struct servent *sp; 6310275Ssam union wait status; 6410275Ssam char *cp; 6510275Ssam 6610275Ssam sp = getservbyname("ftp", "tcp"); 6710275Ssam if (sp == 0) { 6810275Ssam fprintf(stderr, "ftpd: fpt/tcp: unknown service\n"); 6910275Ssam exit(1); 7010275Ssam } 7110275Ssam ctrl_addr.sin_port = sp->s_port; 7210275Ssam data_source.sin_port = htons(ntohs(sp->s_port) - 1); 7310275Ssam signal(SIGPIPE, lostconn); 7410275Ssam debug = 0; 7510275Ssam argc--, argv++; 7610275Ssam while (argc > 0 && *argv[0] == '-') { 7710275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 7810275Ssam 7910275Ssam case 'd': 8010275Ssam debug = 1; 8110275Ssam options |= SO_DEBUG; 8210275Ssam break; 8310275Ssam 8410275Ssam default: 8510275Ssam fprintf(stderr, "Unknown flag -%c ignored.\n", cp); 8610275Ssam break; 8710275Ssam } 8810275Ssam argc--, argv++; 8910275Ssam } 9010275Ssam #ifndef DEBUG 9110275Ssam if (fork()) 9210275Ssam exit(0); 9310275Ssam for (s = 0; s < 10; s++) 94*10317Ssam (void) close(s); 9510275Ssam (void) open("/dev/null", 0); 9610275Ssam (void) dup2(0, 1); 9710275Ssam { int tt = open("/dev/tty", 2); 9810275Ssam if (tt > 0) { 9910275Ssam ioctl(tt, TIOCNOTTY, 0); 10010275Ssam close(tt); 10110275Ssam } 10210275Ssam } 10310275Ssam #endif 10410303Ssam while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) { 10510275Ssam perror("ftpd: socket"); 10610275Ssam sleep(5); 10710275Ssam } 10810275Ssam while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) { 10910275Ssam perror("ftpd: bind"); 11010275Ssam sleep(5); 11110275Ssam } 11210303Ssam listen(s, 10); 11310275Ssam for (;;) { 11410275Ssam int hisaddrlen = sizeof (his_addr); 11510275Ssam 11610275Ssam ctrl = accept(s, &his_addr, &hisaddrlen, 0); 11710275Ssam if (ctrl < 0) { 11810275Ssam perror("ftpd: accept"); 11910275Ssam sleep(5); 12010275Ssam continue; 12110275Ssam } 12210275Ssam data_dest = his_addr; 12310275Ssam if (fork() == 0) { 12410275Ssam if (logging) 12510275Ssam dolog(&his_addr); 12610275Ssam close(s); 12710275Ssam dup2(ctrl, 0), close(ctrl), dup2(0, 1); 12810275Ssam /* do telnet option negotiation here */ 12910303Ssam /* 13010303Ssam * Set up default state 13110303Ssam */ 13210275Ssam logged_in = 0; 13310275Ssam data = -1; 13410303Ssam type = TYPE_A; 13510303Ssam form = FORM_N; 13610303Ssam stru = STRU_F; 13710303Ssam mode = MODE_S; 13810275Ssam gethostname(hostname, sizeof (hostname)); 13910275Ssam reply(220, "%s FTP server (%s) ready.", 14010275Ssam hostname, version); 14110275Ssam for (;;) { 14210275Ssam setjmp(errcatch); 14310275Ssam yyparse(); 14410275Ssam } 14510275Ssam } 14610275Ssam close(ctrl); 14710275Ssam while (wait3(status, WNOHANG, 0) > 0) 14810275Ssam continue; 14910275Ssam } 15010275Ssam } 15110275Ssam 15210275Ssam lostconn() 15310275Ssam { 15410275Ssam 15510275Ssam fatal("Connection closed."); 15610275Ssam } 15710275Ssam 15810275Ssam pass(passwd) 15910275Ssam char *passwd; 16010275Ssam { 16110303Ssam char *xpasswd, *savestr(); 16210303Ssam static struct passwd save; 16310275Ssam 16410275Ssam if (logged_in || pw == NULL) { 16510275Ssam reply(503, "Login with USER first."); 16610275Ssam return; 16710275Ssam } 16810275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 16910275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 17010275Ssam if (strcmp(xpasswd, pw->pw_passwd) != 0) { 17110275Ssam reply(530, "Login incorrect."); 17210275Ssam pw = NULL; 17310275Ssam return; 17410275Ssam } 17510275Ssam } 17610303Ssam setegid(pw->pw_gid); 17710275Ssam initgroups(pw->pw_name, pw->pw_gid); 17810275Ssam if (chdir(pw->pw_dir)) { 17910275Ssam reply(550, "User %s: can't change directory to $s.", 18010275Ssam pw->pw_name, pw->pw_dir); 18110303Ssam goto bad; 18210275Ssam } 18310303Ssam if (guest && chroot(pw->pw_dir) < 0) { 18410275Ssam reply(550, "Can't set guest privileges."); 18510303Ssam goto bad; 18610275Ssam } 18710275Ssam if (!guest) 18810275Ssam reply(230, "User %s logged in.", pw->pw_name); 18910275Ssam else 19010275Ssam reply(230, "Guest login ok, access restrictions apply."); 19110275Ssam logged_in = 1; 19210303Ssam seteuid(pw->pw_uid); 19310303Ssam /* 19410303Ssam * Save everything so globbing doesn't 19510303Ssam * clobber the fields. 19610303Ssam */ 19710303Ssam save = *pw; 19810303Ssam save.pw_name = savestr(pw->pw_name); 19910303Ssam save.pw_passwd = savestr(pw->pw_passwd); 20010303Ssam save.pw_comment = savestr(pw->pw_comment); 20110303Ssam save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 20210303Ssam save.pw_dir = savestr(pw->pw_dir); 20310303Ssam save.pw_shell = savestr(pw->pw_shell); 20410303Ssam pw = &save; 20510303Ssam home = pw->pw_dir; /* home dir for globbing */ 20610303Ssam return; 20710303Ssam bad: 20810303Ssam seteuid(0); 20910303Ssam pw = NULL; 21010275Ssam } 21110275Ssam 21210303Ssam char * 21310303Ssam savestr(s) 21410303Ssam char *s; 21510303Ssam { 21610303Ssam char *malloc(); 21710303Ssam char *new = malloc(strlen(s) + 1); 21810303Ssam 21910303Ssam if (new != NULL) 22010303Ssam strcpy(new, s); 22110303Ssam return(new); 22210303Ssam } 22310303Ssam 22410275Ssam retrieve(cmd, name) 22510275Ssam char *cmd, *name; 22610275Ssam { 22710275Ssam FILE *fin, *dout; 22810275Ssam struct stat st; 22910275Ssam int (*closefunc)(); 23010275Ssam 23110275Ssam if (cmd == 0) { 232*10317Ssam #ifdef notdef 233*10317Ssam /* no remote command execution -- it's a security hole */ 23410275Ssam if (*name == '!') 23510275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 23610275Ssam else 237*10317Ssam #endif 23810275Ssam fin = fopen(name, "r"), closefunc = fclose; 23910275Ssam } else { 24010275Ssam char line[BUFSIZ]; 24110275Ssam 24210275Ssam sprintf(line, cmd, name); 24310275Ssam fin = popen(line, "r"), closefunc = pclose; 24410275Ssam } 24510275Ssam if (fin == NULL) { 24610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 24710275Ssam return; 24810275Ssam } 24910275Ssam st.st_size = 0; 25010275Ssam if (cmd == 0 && 25110275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 25210275Ssam reply(550, "%s: not a plain file.", name); 25310275Ssam goto done; 25410275Ssam } 25510275Ssam dout = dataconn(name, st.st_size, "w"); 25610275Ssam if (dout == NULL) 25710275Ssam goto done; 25810303Ssam if (send_data(fin, dout) || ferror(dout)) 25910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 26010275Ssam else 26110275Ssam reply(226, "Transfer complete."); 26210303Ssam fclose(dout), data = -1; 26310275Ssam done: 26410275Ssam (*closefunc)(fin); 26510275Ssam } 26610275Ssam 26710275Ssam store(name, mode) 26810275Ssam char *name, *mode; 26910275Ssam { 27010275Ssam FILE *fout, *din; 27110303Ssam int (*closefunc)(), dochown = 0; 27210275Ssam 273*10317Ssam #ifdef notdef 274*10317Ssam /* no remote command execution -- it's a security hole */ 27510275Ssam if (name[0] == '!') 27610275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 277*10317Ssam else 278*10317Ssam #endif 279*10317Ssam { 28010303Ssam struct stat st; 28110303Ssam 28210303Ssam if (stat(name, &st) < 0) 28310303Ssam dochown++; 28410275Ssam fout = fopen(name, mode), closefunc = fclose; 28510303Ssam } 28610275Ssam if (fout == NULL) { 28710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 28810275Ssam return; 28910275Ssam } 29010275Ssam din = dataconn(name, -1, "r"); 29110275Ssam if (din == NULL) 29210275Ssam goto done; 29310303Ssam if (receive_data(din, fout) || ferror(fout)) 29410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 29510275Ssam else 29610275Ssam reply(226, "Transfer complete."); 29710275Ssam fclose(din), data = -1; 29810275Ssam done: 29910303Ssam if (dochown) 30010303Ssam (void) chown(name, pw->pw_uid, -1); 30110275Ssam (*closefunc)(fout); 30210275Ssam } 30310275Ssam 30410275Ssam FILE * 30510275Ssam getdatasock(mode) 30610275Ssam char *mode; 30710275Ssam { 30810275Ssam int retrytime, s; 30910275Ssam 31010275Ssam if (data >= 0) 31110275Ssam return (fdopen(data, mode)); 31210275Ssam retrytime = 1; 31310275Ssam while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) { 31410275Ssam if (retrytime < 5) { 31510275Ssam sleep(retrytime); 31610275Ssam retrytime <<= 1; 31710275Ssam continue; 31810275Ssam } 31910275Ssam return (NULL); 32010275Ssam } 32110275Ssam retrytime = 1; 32210275Ssam seteuid(0); 32310275Ssam while (bind(s, &data_source, sizeof (data_source), 0) < 0) { 32410275Ssam if (retrytime < 5) { 32510275Ssam sleep(retrytime); 32610275Ssam retrytime <<= 1; 32710275Ssam continue; 32810275Ssam } 32910311Ssam seteuid(pw->pw_uid); 33010275Ssam close(s); 33110275Ssam return (NULL); 33210275Ssam } 33310311Ssam seteuid(pw->pw_uid); 33410275Ssam return (fdopen(s, mode)); 33510275Ssam } 33610275Ssam 33710275Ssam FILE * 33810275Ssam dataconn(name, size, mode) 33910275Ssam char *name; 34010275Ssam int size; 34110275Ssam char *mode; 34210275Ssam { 34310275Ssam char sizebuf[32]; 34410275Ssam FILE *file; 34510275Ssam 34610275Ssam if (size >= 0) 34710275Ssam sprintf(sizebuf, " (%d bytes)", size); 34810275Ssam else 34910275Ssam (void) strcpy(sizebuf, ""); 35010275Ssam if (data >= 0) { 35110275Ssam reply(125, "Using existing data connection for %s%s.", 35210275Ssam name, sizebuf); 35310275Ssam return (fdopen(data, mode)); 35410275Ssam } 35510275Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 35610275Ssam name, ntoa(data_dest.sin_addr.s_addr), 35710275Ssam ntohs(data_dest.sin_port), sizebuf); 35810275Ssam file = getdatasock(mode); 35910275Ssam if (file == NULL) { 36010275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 36110275Ssam ntoa(data_source.sin_addr), 36210275Ssam ntohs(data_source.sin_port), 36310275Ssam sys_errlist[errno]); 36410275Ssam return (NULL); 36510275Ssam } 36610275Ssam data = fileno(file); 36710275Ssam if (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 36810275Ssam reply(425, "Can't build data connection: %s.", 36910275Ssam sys_errlist[errno]); 37010275Ssam (void) fclose(file); 37110275Ssam data = -1; 37210275Ssam return (NULL); 37310275Ssam } 37410275Ssam return (file); 37510275Ssam } 37610275Ssam 37710275Ssam /* 37810275Ssam * Tranfer the contents of "instr" to 37910275Ssam * "outstr" peer using the appropriate 38010275Ssam * encapulation of the date subject 38110275Ssam * to Mode, Structure, and Type. 38210275Ssam * 38310275Ssam * NB: Form isn't handled. 38410275Ssam */ 38510275Ssam send_data(instr, outstr) 38610275Ssam FILE *instr, *outstr; 38710275Ssam { 38810275Ssam register int c; 38910275Ssam int netfd, filefd, cnt; 39010275Ssam char buf[BUFSIZ]; 39110275Ssam 39210275Ssam switch (type) { 39310275Ssam 39410275Ssam case TYPE_A: 39510275Ssam while ((c = getc(instr)) != EOF) { 39610275Ssam if (c == '\n') 39710275Ssam putc('\r', outstr); 39810275Ssam if (putc(c, outstr) == EOF) 39910275Ssam return (1); 40010275Ssam } 40110275Ssam return (0); 40210275Ssam 40310275Ssam case TYPE_I: 40410275Ssam case TYPE_L: 40510275Ssam netfd = fileno(outstr); 40610275Ssam filefd = fileno(instr); 40710275Ssam 40810303Ssam while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 40910275Ssam if (write(netfd, buf, cnt) < 0) 41010275Ssam return (1); 41110275Ssam return (cnt < 0); 41210275Ssam } 41310275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 41410275Ssam return (1); 41510275Ssam } 41610275Ssam 41710275Ssam /* 41810275Ssam * Transfer data from peer to 41910275Ssam * "outstr" using the appropriate 42010275Ssam * encapulation of the data subject 42110275Ssam * to Mode, Structure, and Type. 42210275Ssam * 42310275Ssam * N.B.: Form isn't handled. 42410275Ssam */ 42510275Ssam receive_data(instr, outstr) 42610275Ssam FILE *instr, *outstr; 42710275Ssam { 42810275Ssam register int c; 42910275Ssam int cr, escape, eof; 43010275Ssam int netfd, filefd, cnt; 43110275Ssam char buf[BUFSIZ]; 43210275Ssam 43310275Ssam 43410275Ssam switch (type) { 43510275Ssam 43610275Ssam case TYPE_I: 43710275Ssam case TYPE_L: 43810275Ssam netfd = fileno(instr); 43910275Ssam netfd = fileno(outstr); 44010275Ssam while ((cnt = read(netfd, buf, sizeof buf)) > 0) 44110275Ssam if (write(filefd, buf, cnt) < 0) 44210275Ssam return (1); 44310275Ssam return (cnt < 0); 44410275Ssam 44510275Ssam case TYPE_E: 44610275Ssam reply(504, "TYPE E not implemented."); 44710275Ssam return (1); 44810275Ssam 44910275Ssam case TYPE_A: 45010275Ssam cr = 0; 45110275Ssam while ((c = getc(instr)) != EOF) { 45210275Ssam if (cr) { 45310275Ssam if (c != '\r' && c != '\n') 45410275Ssam putc('\r', outstr); 45510275Ssam putc(c, outstr); 45610275Ssam cr = c == '\r'; 45710275Ssam continue; 45810275Ssam } 45910275Ssam if (c == '\r') { 46010275Ssam cr = 1; 46110275Ssam continue; 46210275Ssam } 46310275Ssam putc(c, outstr); 46410275Ssam } 46510275Ssam if (cr) 46610275Ssam putc('\r', outstr); 46710275Ssam return (0); 46810275Ssam } 46910275Ssam fatal("Unknown type in receive_data."); 47010275Ssam /*NOTREACHED*/ 47110275Ssam } 47210275Ssam 47310275Ssam fatal(s) 47410275Ssam char *s; 47510275Ssam { 47610275Ssam reply(451, "Error in server: %s\n", s); 47710275Ssam reply(221, "Closing connection due to server error."); 47810275Ssam exit(0); 47910275Ssam } 48010275Ssam 48110275Ssam reply(n, s, args) 48210275Ssam int n; 48310275Ssam char *s; 48410275Ssam { 48510275Ssam 48610275Ssam printf("%d ", n); 48710275Ssam _doprnt(s, &args, stdout); 48810275Ssam printf("\r\n"); 48910275Ssam fflush(stdout); 49010275Ssam if (debug) { 49110275Ssam fprintf(stderr, "<--- %d ", n); 49210275Ssam _doprnt(s, &args, stderr); 49310275Ssam fprintf(stderr, "\n"); 49410275Ssam fflush(stderr); 49510275Ssam } 49610275Ssam } 49710275Ssam 49810275Ssam lreply(n, s, args) 49910275Ssam int n; 50010275Ssam char *s; 50110275Ssam { 50210275Ssam printf("%d-", n); 50310275Ssam _doprnt(s, &args, stdout); 50410275Ssam printf("\r\n"); 50510275Ssam fflush(stdout); 50610275Ssam if (debug) { 50710275Ssam fprintf(stderr, "<--- %d-", n); 50810275Ssam _doprnt(s, &args, stderr); 50910275Ssam fprintf(stderr, "\n"); 51010275Ssam } 51110275Ssam } 51210275Ssam 51310275Ssam replystr(s) 51410275Ssam char *s; 51510275Ssam { 51610275Ssam printf("%s\r\n", s); 51710275Ssam fflush(stdout); 51810275Ssam if (debug) 51910275Ssam fprintf(stderr, "<--- %s\n", s); 52010275Ssam } 52110275Ssam 52210275Ssam ack(s) 52310275Ssam char *s; 52410275Ssam { 52510275Ssam reply(200, "%s command okay.", s); 52610275Ssam } 52710275Ssam 52810275Ssam nack(s) 52910275Ssam char *s; 53010275Ssam { 53110275Ssam reply(502, "%s command not implemented.", s); 53210275Ssam } 53310275Ssam 53410275Ssam yyerror() 53510275Ssam { 53610275Ssam reply(500, "Command not understood."); 53710275Ssam } 53810275Ssam 53910275Ssam delete(name) 54010275Ssam char *name; 54110275Ssam { 54210275Ssam struct stat st; 54310275Ssam 54410275Ssam if (stat(name, &st) < 0) { 54510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 54610275Ssam return; 54710275Ssam } 54810275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 54910275Ssam if (rmdir(name) < 0) { 55010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 55110275Ssam return; 55210275Ssam } 55310275Ssam goto done; 55410275Ssam } 55510275Ssam if (unlink(name) < 0) { 55610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 55710275Ssam return; 55810275Ssam } 55910275Ssam done: 56010275Ssam ack("DELE"); 56110275Ssam } 56210275Ssam 56310275Ssam cwd(path) 56410275Ssam char *path; 56510275Ssam { 56610275Ssam 56710275Ssam if (chdir(path) < 0) { 56810275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 56910275Ssam return; 57010275Ssam } 57110275Ssam ack("CWD"); 57210275Ssam } 57310275Ssam 57410303Ssam makedir(name) 57510275Ssam char *name; 57610275Ssam { 57710303Ssam struct stat st; 57810303Ssam int dochown = stat(name, &st) < 0; 57910275Ssam 58010275Ssam if (mkdir(name, 0777) < 0) { 58110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 58210275Ssam return; 58310275Ssam } 58410303Ssam if (dochown) 58510303Ssam (void) chown(name, pw->pw_uid, -1); 58610275Ssam ack("MKDIR"); 58710275Ssam } 58810275Ssam 58910303Ssam removedir(name) 59010275Ssam char *name; 59110275Ssam { 59210275Ssam 59310275Ssam if (rmdir(name) < 0) { 59410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 59510275Ssam return; 59610275Ssam } 59710275Ssam ack("RMDIR"); 59810275Ssam } 59910275Ssam 60010303Ssam pwd() 60110275Ssam { 60210303Ssam char path[MAXPATHLEN + 1]; 60310275Ssam char *p; 60410275Ssam 60510275Ssam if (getwd(path) == NULL) { 60610275Ssam reply(451, "%s.", path); 60710275Ssam return; 60810275Ssam } 60910275Ssam reply(251, "\"%s\" is current directory.", path); 61010275Ssam } 61110275Ssam 61210275Ssam char * 61310275Ssam renamefrom(name) 61410275Ssam char *name; 61510275Ssam { 61610275Ssam struct stat st; 61710275Ssam 61810275Ssam if (stat(name, &st) < 0) { 61910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 62010275Ssam return ((char *)0); 62110275Ssam } 62210303Ssam reply(350, "File exists, ready for destination name"); 62310275Ssam return (name); 62410275Ssam } 62510275Ssam 62610275Ssam renamecmd(from, to) 62710275Ssam char *from, *to; 62810275Ssam { 62910275Ssam 63010275Ssam if (rename(from, to) < 0) { 63110275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 63210275Ssam return; 63310275Ssam } 63410275Ssam ack("RNTO"); 63510275Ssam } 63610275Ssam 63710275Ssam int guest; 63810275Ssam /* 63910275Ssam * Test pathname for guest-user safety. 64010275Ssam */ 64110275Ssam inappropriate_request(name) 64210275Ssam char *name; 64310275Ssam { 64410275Ssam int bogus = 0, depth = 0, length = strlen(name); 64510275Ssam char *p, *s; 64610275Ssam 64710275Ssam if (!guest) 64810275Ssam return (0); 64910275Ssam if (name[0] == '/' || name[0] == '|') 65010275Ssam bogus = 1; 65110275Ssam for (p = name; p < name+length;) { 65210275Ssam s = p; /* start of token */ 65310275Ssam while ( *p && *p!= '/') 65410275Ssam p++; 65510275Ssam *p = 0; 65610275Ssam if (strcmp(s, "..") == 0) 65710275Ssam depth -= 1; /* backing up */ 65810275Ssam else if (strcmp(s, ".") == 0) 65910275Ssam depth += 0; /* no change */ 66010275Ssam else 66110275Ssam depth += 1; /* descending */ 66210275Ssam if (depth < 0) { 66310275Ssam bogus = 1; 66410275Ssam break; 66510275Ssam } 66610275Ssam } 66710275Ssam if (bogus) 66810275Ssam reply(553, "%s: pathname disallowed guest users", name); 66910275Ssam return (bogus); 67010275Ssam } 67110275Ssam 67210275Ssam /* 67310275Ssam * Convert network-format internet address 67410275Ssam * to base 256 d.d.d.d representation. 67510275Ssam */ 67610275Ssam char * 67710275Ssam ntoa(in) 67810275Ssam struct in_addr in; 67910275Ssam { 68010275Ssam static char b[18]; 68110275Ssam register char *p; 68210275Ssam 68310275Ssam in.s_addr = ntohl(in.s_addr); 68410275Ssam p = (char *)∈ 68510275Ssam #define UC(b) (((int)b)&0xff) 68610275Ssam sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 68710275Ssam return (b); 68810275Ssam } 68910275Ssam 69010275Ssam dolog(sin) 69110275Ssam struct sockaddr_in *sin; 69210275Ssam { 69310275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 69410275Ssam sizeof (struct in_addr), AF_INET); 69510275Ssam char *remotehost; 69610275Ssam time_t t; 69710275Ssam 69810275Ssam if (hp) 69910275Ssam remotehost = hp->h_name; 70010275Ssam else 70110275Ssam remotehost = "UNKNOWNHOST"; 70210275Ssam t = time(0); 70310303Ssam fprintf(stderr,"FTP: connection from %s at %s", remotehost, ctime(&t)); 70410275Ssam fflush(stderr); 70510275Ssam } 706