110275Ssam #ifndef lint 2*10419Ssam static char sccsid[] = "@(#)ftpd.c 4.8 (Berkeley) 83/01/18"; 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(); 56*10419Ssam 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 } 109*10419Ssam if (options & SO_DEBUG) 110*10419Ssam if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 111*10419Ssam perror("ftpd: setsockopt (SO_DEBUG)"); 112*10419Ssam #ifdef notdef 113*10419Ssam if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0) 114*10419Ssam perror("ftpd: setsockopt (SO_KEEPALIVE)"); 115*10419Ssam #endif 11610275Ssam while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) { 11710275Ssam perror("ftpd: bind"); 11810275Ssam sleep(5); 11910275Ssam } 120*10419Ssam 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) { 127*10419Ssam if (errno == EINTR) 128*10419Ssam 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 159*10419Ssam reapchild() 160*10419Ssam { 161*10419Ssam union wait status; 162*10419Ssam 163*10419Ssam while (wait3(&status, WNOHANG, 0) > 0) 164*10419Ssam ; 165*10419Ssam } 166*10419Ssam 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 25710275Ssam sprintf(line, cmd, name); 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 } 37110275Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 37210275Ssam name, ntoa(data_dest.sin_addr.s_addr), 37310275Ssam ntohs(data_dest.sin_port), sizebuf); 37410275Ssam file = getdatasock(mode); 37510275Ssam if (file == NULL) { 37610275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 37710275Ssam ntoa(data_source.sin_addr), 37810275Ssam ntohs(data_source.sin_port), 37910275Ssam sys_errlist[errno]); 38010321Ssam usedefault = 1; 38110275Ssam return (NULL); 38210275Ssam } 38310275Ssam data = fileno(file); 38410321Ssam /* 38510321Ssam * If no PORT command was specified, 38610321Ssam * use the default address. 38710321Ssam */ 38810321Ssam if (usedefault) { 38910321Ssam data_dest = his_addr; 39010321Ssam data_dest.sin_port = htons(ntohs(sp->s_port) - 1); 39110321Ssam } 39210329Ssam usedefault = 1; 39310275Ssam if (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 39410275Ssam reply(425, "Can't build data connection: %s.", 39510275Ssam sys_errlist[errno]); 39610275Ssam (void) fclose(file); 39710275Ssam data = -1; 39810275Ssam return (NULL); 39910275Ssam } 40010275Ssam return (file); 40110275Ssam } 40210275Ssam 40310275Ssam /* 40410275Ssam * Tranfer the contents of "instr" to 40510275Ssam * "outstr" peer using the appropriate 40610275Ssam * encapulation of the date subject 40710275Ssam * to Mode, Structure, and Type. 40810275Ssam * 40910275Ssam * NB: Form isn't handled. 41010275Ssam */ 41110275Ssam send_data(instr, outstr) 41210275Ssam FILE *instr, *outstr; 41310275Ssam { 41410275Ssam register int c; 41510275Ssam int netfd, filefd, cnt; 41610275Ssam char buf[BUFSIZ]; 41710275Ssam 41810275Ssam switch (type) { 41910275Ssam 42010275Ssam case TYPE_A: 42110275Ssam while ((c = getc(instr)) != EOF) { 42210275Ssam if (c == '\n') 42310275Ssam putc('\r', outstr); 42410275Ssam if (putc(c, outstr) == EOF) 42510275Ssam return (1); 42610275Ssam } 42710275Ssam return (0); 42810275Ssam 42910275Ssam case TYPE_I: 43010275Ssam case TYPE_L: 43110275Ssam netfd = fileno(outstr); 43210275Ssam filefd = fileno(instr); 43310275Ssam 43410303Ssam while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 43510275Ssam if (write(netfd, buf, cnt) < 0) 43610275Ssam return (1); 43710275Ssam return (cnt < 0); 43810275Ssam } 43910275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 44010275Ssam return (1); 44110275Ssam } 44210275Ssam 44310275Ssam /* 44410275Ssam * Transfer data from peer to 44510275Ssam * "outstr" using the appropriate 44610275Ssam * encapulation of the data subject 44710275Ssam * to Mode, Structure, and Type. 44810275Ssam * 44910275Ssam * N.B.: Form isn't handled. 45010275Ssam */ 45110275Ssam receive_data(instr, outstr) 45210275Ssam FILE *instr, *outstr; 45310275Ssam { 45410275Ssam register int c; 45510275Ssam int cr, escape, eof; 45610275Ssam int netfd, filefd, cnt; 45710275Ssam char buf[BUFSIZ]; 45810275Ssam 45910275Ssam 46010275Ssam switch (type) { 46110275Ssam 46210275Ssam case TYPE_I: 46310275Ssam case TYPE_L: 46410275Ssam netfd = fileno(instr); 46510275Ssam netfd = fileno(outstr); 46610275Ssam while ((cnt = read(netfd, buf, sizeof buf)) > 0) 46710275Ssam if (write(filefd, buf, cnt) < 0) 46810275Ssam return (1); 46910275Ssam return (cnt < 0); 47010275Ssam 47110275Ssam case TYPE_E: 47210275Ssam reply(504, "TYPE E not implemented."); 47310275Ssam return (1); 47410275Ssam 47510275Ssam case TYPE_A: 47610275Ssam cr = 0; 47710275Ssam while ((c = getc(instr)) != EOF) { 47810275Ssam if (cr) { 47910275Ssam if (c != '\r' && c != '\n') 48010275Ssam putc('\r', outstr); 48110275Ssam putc(c, outstr); 48210275Ssam cr = c == '\r'; 48310275Ssam continue; 48410275Ssam } 48510275Ssam if (c == '\r') { 48610275Ssam cr = 1; 48710275Ssam continue; 48810275Ssam } 48910275Ssam putc(c, outstr); 49010275Ssam } 49110275Ssam if (cr) 49210275Ssam putc('\r', outstr); 49310275Ssam return (0); 49410275Ssam } 49510275Ssam fatal("Unknown type in receive_data."); 49610275Ssam /*NOTREACHED*/ 49710275Ssam } 49810275Ssam 49910275Ssam fatal(s) 50010275Ssam char *s; 50110275Ssam { 50210275Ssam reply(451, "Error in server: %s\n", s); 50310275Ssam reply(221, "Closing connection due to server error."); 50410275Ssam exit(0); 50510275Ssam } 50610275Ssam 50710275Ssam reply(n, s, args) 50810275Ssam int n; 50910275Ssam char *s; 51010275Ssam { 51110275Ssam 51210275Ssam printf("%d ", n); 51310275Ssam _doprnt(s, &args, stdout); 51410275Ssam printf("\r\n"); 51510275Ssam fflush(stdout); 51610275Ssam if (debug) { 51710275Ssam fprintf(stderr, "<--- %d ", n); 51810275Ssam _doprnt(s, &args, stderr); 51910275Ssam fprintf(stderr, "\n"); 52010275Ssam fflush(stderr); 52110275Ssam } 52210275Ssam } 52310275Ssam 52410275Ssam lreply(n, s, args) 52510275Ssam int n; 52610275Ssam char *s; 52710275Ssam { 52810275Ssam printf("%d-", n); 52910275Ssam _doprnt(s, &args, stdout); 53010275Ssam printf("\r\n"); 53110275Ssam fflush(stdout); 53210275Ssam if (debug) { 53310275Ssam fprintf(stderr, "<--- %d-", n); 53410275Ssam _doprnt(s, &args, stderr); 53510275Ssam fprintf(stderr, "\n"); 53610275Ssam } 53710275Ssam } 53810275Ssam 53910275Ssam replystr(s) 54010275Ssam char *s; 54110275Ssam { 54210275Ssam printf("%s\r\n", s); 54310275Ssam fflush(stdout); 54410275Ssam if (debug) 54510275Ssam fprintf(stderr, "<--- %s\n", s); 54610275Ssam } 54710275Ssam 54810275Ssam ack(s) 54910275Ssam char *s; 55010275Ssam { 55110275Ssam reply(200, "%s command okay.", s); 55210275Ssam } 55310275Ssam 55410275Ssam nack(s) 55510275Ssam char *s; 55610275Ssam { 55710275Ssam reply(502, "%s command not implemented.", s); 55810275Ssam } 55910275Ssam 56010275Ssam yyerror() 56110275Ssam { 56210275Ssam reply(500, "Command not understood."); 56310275Ssam } 56410275Ssam 56510275Ssam delete(name) 56610275Ssam char *name; 56710275Ssam { 56810275Ssam struct stat st; 56910275Ssam 57010275Ssam if (stat(name, &st) < 0) { 57110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 57210275Ssam return; 57310275Ssam } 57410275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 57510275Ssam if (rmdir(name) < 0) { 57610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 57710275Ssam return; 57810275Ssam } 57910275Ssam goto done; 58010275Ssam } 58110275Ssam if (unlink(name) < 0) { 58210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 58310275Ssam return; 58410275Ssam } 58510275Ssam done: 58610275Ssam ack("DELE"); 58710275Ssam } 58810275Ssam 58910275Ssam cwd(path) 59010275Ssam char *path; 59110275Ssam { 59210275Ssam 59310275Ssam if (chdir(path) < 0) { 59410275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 59510275Ssam return; 59610275Ssam } 59710275Ssam ack("CWD"); 59810275Ssam } 59910275Ssam 60010303Ssam makedir(name) 60110275Ssam char *name; 60210275Ssam { 60310303Ssam struct stat st; 60410303Ssam int dochown = stat(name, &st) < 0; 60510275Ssam 60610275Ssam if (mkdir(name, 0777) < 0) { 60710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 60810275Ssam return; 60910275Ssam } 61010303Ssam if (dochown) 61110303Ssam (void) chown(name, pw->pw_uid, -1); 61210275Ssam ack("MKDIR"); 61310275Ssam } 61410275Ssam 61510303Ssam removedir(name) 61610275Ssam char *name; 61710275Ssam { 61810275Ssam 61910275Ssam if (rmdir(name) < 0) { 62010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 62110275Ssam return; 62210275Ssam } 62310275Ssam ack("RMDIR"); 62410275Ssam } 62510275Ssam 62610303Ssam pwd() 62710275Ssam { 62810303Ssam char path[MAXPATHLEN + 1]; 62910275Ssam char *p; 63010275Ssam 63110275Ssam if (getwd(path) == NULL) { 63210275Ssam reply(451, "%s.", path); 63310275Ssam return; 63410275Ssam } 63510275Ssam reply(251, "\"%s\" is current directory.", path); 63610275Ssam } 63710275Ssam 63810275Ssam char * 63910275Ssam renamefrom(name) 64010275Ssam char *name; 64110275Ssam { 64210275Ssam struct stat st; 64310275Ssam 64410275Ssam if (stat(name, &st) < 0) { 64510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 64610275Ssam return ((char *)0); 64710275Ssam } 64810303Ssam reply(350, "File exists, ready for destination name"); 64910275Ssam return (name); 65010275Ssam } 65110275Ssam 65210275Ssam renamecmd(from, to) 65310275Ssam char *from, *to; 65410275Ssam { 65510275Ssam 65610275Ssam if (rename(from, to) < 0) { 65710275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 65810275Ssam return; 65910275Ssam } 66010275Ssam ack("RNTO"); 66110275Ssam } 66210275Ssam 66310275Ssam int guest; 66410275Ssam /* 66510275Ssam * Test pathname for guest-user safety. 66610275Ssam */ 66710275Ssam inappropriate_request(name) 66810275Ssam char *name; 66910275Ssam { 67010275Ssam int bogus = 0, depth = 0, length = strlen(name); 67110275Ssam char *p, *s; 67210275Ssam 67310275Ssam if (!guest) 67410275Ssam return (0); 67510275Ssam if (name[0] == '/' || name[0] == '|') 67610275Ssam bogus = 1; 67710275Ssam for (p = name; p < name+length;) { 67810275Ssam s = p; /* start of token */ 67910275Ssam while ( *p && *p!= '/') 68010275Ssam p++; 68110275Ssam *p = 0; 68210275Ssam if (strcmp(s, "..") == 0) 68310275Ssam depth -= 1; /* backing up */ 68410275Ssam else if (strcmp(s, ".") == 0) 68510275Ssam depth += 0; /* no change */ 68610275Ssam else 68710275Ssam depth += 1; /* descending */ 68810275Ssam if (depth < 0) { 68910275Ssam bogus = 1; 69010275Ssam break; 69110275Ssam } 69210275Ssam } 69310275Ssam if (bogus) 69410275Ssam reply(553, "%s: pathname disallowed guest users", name); 69510275Ssam return (bogus); 69610275Ssam } 69710275Ssam 69810275Ssam /* 69910275Ssam * Convert network-format internet address 70010275Ssam * to base 256 d.d.d.d representation. 70110275Ssam */ 70210275Ssam char * 70310275Ssam ntoa(in) 70410275Ssam struct in_addr in; 70510275Ssam { 70610275Ssam static char b[18]; 70710275Ssam register char *p; 70810275Ssam 70910275Ssam in.s_addr = ntohl(in.s_addr); 71010275Ssam p = (char *)∈ 71110275Ssam #define UC(b) (((int)b)&0xff) 71210275Ssam sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 71310275Ssam return (b); 71410275Ssam } 71510275Ssam 71610275Ssam dolog(sin) 71710275Ssam struct sockaddr_in *sin; 71810275Ssam { 71910275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 72010275Ssam sizeof (struct in_addr), AF_INET); 72110275Ssam char *remotehost; 72210275Ssam time_t t; 72310275Ssam 72410275Ssam if (hp) 72510275Ssam remotehost = hp->h_name; 72610275Ssam else 72710275Ssam remotehost = "UNKNOWNHOST"; 72810275Ssam t = time(0); 72910303Ssam fprintf(stderr,"FTP: connection from %s at %s", remotehost, ctime(&t)); 73010275Ssam fflush(stderr); 73110275Ssam } 732