xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 10303)
110275Ssam #ifndef lint
2*10303Ssam static char sccsid[] = "@(#)ftpd.c	4.2 (Berkeley) 83/01/15";
310275Ssam #endif
410275Ssam 
510275Ssam /*
610275Ssam  * FTP server.
710275Ssam  */
8*10303Ssam #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++)
9410275Ssam 		if (s != 2)		/* don't screw stderr */
9510275Ssam 			(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
105*10303Ssam 	while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) {
10610275Ssam 		perror("ftpd: socket");
10710275Ssam 		sleep(5);
10810275Ssam 	}
10910275Ssam 	while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) {
11010275Ssam 		perror("ftpd: bind");
11110275Ssam 		sleep(5);
11210275Ssam 	}
113*10303Ssam 	listen(s, 10);
11410275Ssam 	for (;;) {
11510275Ssam 		int hisaddrlen = sizeof (his_addr);
11610275Ssam 
11710275Ssam 		ctrl = accept(s, &his_addr, &hisaddrlen, 0);
11810275Ssam 		if (ctrl < 0) {
11910275Ssam 			perror("ftpd: accept");
12010275Ssam 			sleep(5);
12110275Ssam 			continue;
12210275Ssam 		}
12310275Ssam 		data_dest = his_addr;
12410275Ssam 		if (fork() == 0) {
12510275Ssam 			if (logging)
12610275Ssam 				dolog(&his_addr);
12710275Ssam 			close(s);
12810275Ssam 			dup2(ctrl, 0), close(ctrl), dup2(0, 1);
12910275Ssam 			/* do telnet option negotiation here */
130*10303Ssam 			/*
131*10303Ssam 			 * Set up default state
132*10303Ssam 			 */
13310275Ssam 			logged_in = 0;
13410275Ssam 			data = -1;
135*10303Ssam 			type = TYPE_A;
136*10303Ssam 			form = FORM_N;
137*10303Ssam 			stru = STRU_F;
138*10303Ssam 			mode = MODE_S;
13910275Ssam 			gethostname(hostname, sizeof (hostname));
14010275Ssam 			reply(220, "%s FTP server (%s) ready.",
14110275Ssam 				hostname, version);
14210275Ssam 			for (;;) {
14310275Ssam 				setjmp(errcatch);
14410275Ssam 				yyparse();
14510275Ssam 			}
14610275Ssam 		}
14710275Ssam 		close(ctrl);
14810275Ssam 		while (wait3(status, WNOHANG, 0) > 0)
14910275Ssam 			continue;
15010275Ssam 	}
15110275Ssam }
15210275Ssam 
15310275Ssam lostconn()
15410275Ssam {
15510275Ssam 
15610275Ssam 	fatal("Connection closed.");
15710275Ssam }
15810275Ssam 
15910275Ssam pass(passwd)
16010275Ssam 	char *passwd;
16110275Ssam {
162*10303Ssam 	char *xpasswd, *savestr();
163*10303Ssam 	static struct passwd save;
16410275Ssam 
16510275Ssam 	if (logged_in || pw == NULL) {
16610275Ssam 		reply(503, "Login with USER first.");
16710275Ssam 		return;
16810275Ssam 	}
16910275Ssam 	if (!guest) {		/* "ftp" is only account allowed no password */
17010275Ssam 		xpasswd = crypt(passwd, pw->pw_passwd);
17110275Ssam 		if (strcmp(xpasswd, pw->pw_passwd) != 0) {
17210275Ssam 			reply(530, "Login incorrect.");
17310275Ssam 			pw = NULL;
17410275Ssam 			return;
17510275Ssam 		}
17610275Ssam 	}
177*10303Ssam 	setegid(pw->pw_gid);
17810275Ssam 	initgroups(pw->pw_name, pw->pw_gid);
17910275Ssam 	if (chdir(pw->pw_dir)) {
18010275Ssam 		reply(550, "User %s: can't change directory to $s.",
18110275Ssam 			pw->pw_name, pw->pw_dir);
182*10303Ssam 		goto bad;
18310275Ssam 	}
184*10303Ssam 	if (guest && chroot(pw->pw_dir) < 0) {
18510275Ssam 		reply(550, "Can't set guest privileges.");
186*10303Ssam 		goto bad;
18710275Ssam 	}
18810275Ssam 	if (!guest)
18910275Ssam 		reply(230, "User %s logged in.", pw->pw_name);
19010275Ssam 	else
19110275Ssam 		reply(230, "Guest login ok, access restrictions apply.");
19210275Ssam 	logged_in = 1;
193*10303Ssam 	seteuid(pw->pw_uid);
194*10303Ssam 	/*
195*10303Ssam 	 * Save everything so globbing doesn't
196*10303Ssam 	 * clobber the fields.
197*10303Ssam 	 */
198*10303Ssam 	save = *pw;
199*10303Ssam 	save.pw_name = savestr(pw->pw_name);
200*10303Ssam 	save.pw_passwd = savestr(pw->pw_passwd);
201*10303Ssam 	save.pw_comment = savestr(pw->pw_comment);
202*10303Ssam 	save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos);
203*10303Ssam 	save.pw_dir = savestr(pw->pw_dir);
204*10303Ssam 	save.pw_shell = savestr(pw->pw_shell);
205*10303Ssam 	pw = &save;
206*10303Ssam 	home = pw->pw_dir;		/* home dir for globbing */
207*10303Ssam 	return;
208*10303Ssam bad:
209*10303Ssam 	seteuid(0);
210*10303Ssam 	pw = NULL;
21110275Ssam }
21210275Ssam 
213*10303Ssam char *
214*10303Ssam savestr(s)
215*10303Ssam 	char *s;
216*10303Ssam {
217*10303Ssam 	char *malloc();
218*10303Ssam 	char *new = malloc(strlen(s) + 1);
219*10303Ssam 
220*10303Ssam 	if (new != NULL)
221*10303Ssam 		strcpy(new, s);
222*10303Ssam 	return(new);
223*10303Ssam }
224*10303Ssam 
22510275Ssam retrieve(cmd, name)
22610275Ssam 	char *cmd, *name;
22710275Ssam {
22810275Ssam 	FILE *fin, *dout;
22910275Ssam 	struct stat st;
23010275Ssam 	int (*closefunc)();
23110275Ssam 
23210275Ssam 	if (cmd == 0) {
23310275Ssam 		if (*name == '!')
23410275Ssam 			fin = popen(name + 1, "r"), closefunc = pclose;
23510275Ssam 		else
23610275Ssam 			fin = fopen(name, "r"), closefunc = fclose;
23710275Ssam 	} else {
23810275Ssam 		char line[BUFSIZ];
23910275Ssam 
24010275Ssam 		sprintf(line, cmd, name);
24110275Ssam 		fin = popen(line, "r"), closefunc = pclose;
24210275Ssam 	}
24310275Ssam 	if (fin == NULL) {
24410275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
24510275Ssam 		return;
24610275Ssam 	}
24710275Ssam 	st.st_size = 0;
24810275Ssam 	if (cmd == 0 &&
24910275Ssam 	    (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
25010275Ssam 		reply(550, "%s: not a plain file.", name);
25110275Ssam 		goto done;
25210275Ssam 	}
25310275Ssam 	dout = dataconn(name, st.st_size, "w");
25410275Ssam 	if (dout == NULL)
25510275Ssam 		goto done;
256*10303Ssam 	if (send_data(fin, dout) || ferror(dout))
25710275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
25810275Ssam 	else
25910275Ssam 		reply(226, "Transfer complete.");
260*10303Ssam 	fclose(dout), data = -1;
26110275Ssam done:
26210275Ssam 	(*closefunc)(fin);
26310275Ssam }
26410275Ssam 
26510275Ssam store(name, mode)
26610275Ssam 	char *name, *mode;
26710275Ssam {
26810275Ssam 	FILE *fout, *din;
269*10303Ssam 	int (*closefunc)(), dochown = 0;
27010275Ssam 
27110275Ssam 	if (name[0] == '!')
27210275Ssam 		fout = popen(&name[1], "w"), closefunc = pclose;
273*10303Ssam 	else {
274*10303Ssam 		struct stat st;
275*10303Ssam 
276*10303Ssam 		if (stat(name, &st) < 0)
277*10303Ssam 			dochown++;
27810275Ssam 		fout = fopen(name, mode), closefunc = fclose;
279*10303Ssam 	}
28010275Ssam 	if (fout == NULL) {
28110275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
28210275Ssam 		return;
28310275Ssam 	}
28410275Ssam 	din = dataconn(name, -1, "r");
28510275Ssam 	if (din == NULL)
28610275Ssam 		goto done;
287*10303Ssam 	if (receive_data(din, fout) || ferror(fout))
28810275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
28910275Ssam 	else
29010275Ssam 		reply(226, "Transfer complete.");
29110275Ssam 	fclose(din), data = -1;
29210275Ssam done:
293*10303Ssam 	if (dochown)
294*10303Ssam 		(void) chown(name, pw->pw_uid, -1);
29510275Ssam 	(*closefunc)(fout);
29610275Ssam }
29710275Ssam 
29810275Ssam FILE *
29910275Ssam getdatasock(mode)
30010275Ssam 	char *mode;
30110275Ssam {
30210275Ssam 	int retrytime, s;
30310275Ssam 
30410275Ssam 	if (data >= 0)
30510275Ssam 		return (fdopen(data, mode));
30610275Ssam 	retrytime = 1;
30710275Ssam 	while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) {
30810275Ssam 		if (retrytime < 5) {
30910275Ssam 			sleep(retrytime);
31010275Ssam 			retrytime <<= 1;
31110275Ssam 			continue;
31210275Ssam 		}
31310275Ssam 		return (NULL);
31410275Ssam 	}
31510275Ssam 	retrytime = 1;
31610275Ssam 	seteuid(0);
31710275Ssam 	while (bind(s, &data_source, sizeof (data_source), 0) < 0) {
31810275Ssam 		if (retrytime < 5) {
31910275Ssam 			sleep(retrytime);
32010275Ssam 			retrytime <<= 1;
32110275Ssam 			continue;
32210275Ssam 		}
32310275Ssam 		seteuid(0);
32410275Ssam 		close(s);
32510275Ssam 		return (NULL);
32610275Ssam 	}
32710275Ssam 	seteuid(0);
32810275Ssam 	return (fdopen(s, mode));
32910275Ssam }
33010275Ssam 
33110275Ssam FILE *
33210275Ssam dataconn(name, size, mode)
33310275Ssam 	char *name;
33410275Ssam 	int size;
33510275Ssam 	char *mode;
33610275Ssam {
33710275Ssam 	char sizebuf[32];
33810275Ssam 	FILE *file;
33910275Ssam 
34010275Ssam 	if (size >= 0)
34110275Ssam 		sprintf(sizebuf, " (%d bytes)", size);
34210275Ssam 	else
34310275Ssam 		(void) strcpy(sizebuf, "");
34410275Ssam 	if (data >= 0) {
34510275Ssam 		reply(125, "Using existing data connection for %s%s.",
34610275Ssam 		    name, sizebuf);
34710275Ssam 		return (fdopen(data, mode));
34810275Ssam 	}
34910275Ssam 	reply(150, "Opening data connection for %s (%s,%d)%s.",
35010275Ssam 	    name, ntoa(data_dest.sin_addr.s_addr),
35110275Ssam 	    ntohs(data_dest.sin_port), sizebuf);
35210275Ssam 	file = getdatasock(mode);
35310275Ssam 	if (file == NULL) {
35410275Ssam 		reply(425, "Can't create data socket (%s,%d): %s.",
35510275Ssam 		    ntoa(data_source.sin_addr),
35610275Ssam 		    ntohs(data_source.sin_port),
35710275Ssam 		    sys_errlist[errno]);
35810275Ssam 		return (NULL);
35910275Ssam 	}
36010275Ssam 	data = fileno(file);
36110275Ssam 	if (connect(data, &data_dest, sizeof (data_dest), 0) < 0) {
36210275Ssam 		reply(425, "Can't build data connection: %s.",
36310275Ssam 		    sys_errlist[errno]);
36410275Ssam 		(void) fclose(file);
36510275Ssam 		data = -1;
36610275Ssam 		return (NULL);
36710275Ssam 	}
36810275Ssam 	return (file);
36910275Ssam }
37010275Ssam 
37110275Ssam /*
37210275Ssam  * Tranfer the contents of "instr" to
37310275Ssam  * "outstr" peer using the appropriate
37410275Ssam  * encapulation of the date subject
37510275Ssam  * to Mode, Structure, and Type.
37610275Ssam  *
37710275Ssam  * NB: Form isn't handled.
37810275Ssam  */
37910275Ssam send_data(instr, outstr)
38010275Ssam 	FILE *instr, *outstr;
38110275Ssam {
38210275Ssam 	register int c;
38310275Ssam 	int netfd, filefd, cnt;
38410275Ssam 	char buf[BUFSIZ];
38510275Ssam 
38610275Ssam 	switch (type) {
38710275Ssam 
38810275Ssam 	case TYPE_A:
38910275Ssam 		while ((c = getc(instr)) != EOF) {
39010275Ssam 			if (c == '\n')
39110275Ssam 				putc('\r', outstr);
39210275Ssam 			if (putc(c, outstr) == EOF)
39310275Ssam 				return (1);
39410275Ssam 		}
39510275Ssam 		return (0);
39610275Ssam 
39710275Ssam 	case TYPE_I:
39810275Ssam 	case TYPE_L:
39910275Ssam 		netfd = fileno(outstr);
40010275Ssam 		filefd = fileno(instr);
40110275Ssam 
402*10303Ssam 		while ((cnt = read(filefd, buf, sizeof (buf))) > 0)
40310275Ssam 			if (write(netfd, buf, cnt) < 0)
40410275Ssam 				return (1);
40510275Ssam 		return (cnt < 0);
40610275Ssam 	}
40710275Ssam 	reply(504,"Unimplemented TYPE %d in send_data", type);
40810275Ssam 	return (1);
40910275Ssam }
41010275Ssam 
41110275Ssam /*
41210275Ssam  * Transfer data from peer to
41310275Ssam  * "outstr" using the appropriate
41410275Ssam  * encapulation of the data subject
41510275Ssam  * to Mode, Structure, and Type.
41610275Ssam  *
41710275Ssam  * N.B.: Form isn't handled.
41810275Ssam  */
41910275Ssam receive_data(instr, outstr)
42010275Ssam 	FILE *instr, *outstr;
42110275Ssam {
42210275Ssam 	register int c;
42310275Ssam 	int cr, escape, eof;
42410275Ssam 	int netfd, filefd, cnt;
42510275Ssam 	char buf[BUFSIZ];
42610275Ssam 
42710275Ssam 
42810275Ssam 	switch (type) {
42910275Ssam 
43010275Ssam 	case TYPE_I:
43110275Ssam 	case TYPE_L:
43210275Ssam 		netfd = fileno(instr);
43310275Ssam 		netfd = fileno(outstr);
43410275Ssam 		while ((cnt = read(netfd, buf, sizeof buf)) > 0)
43510275Ssam 			if (write(filefd, buf, cnt) < 0)
43610275Ssam 				return (1);
43710275Ssam 		return (cnt < 0);
43810275Ssam 
43910275Ssam 	case TYPE_E:
44010275Ssam 		reply(504, "TYPE E not implemented.");
44110275Ssam 		return (1);
44210275Ssam 
44310275Ssam 	case TYPE_A:
44410275Ssam 		cr = 0;
44510275Ssam 		while ((c = getc(instr)) != EOF) {
44610275Ssam 			if (cr) {
44710275Ssam 				if (c != '\r' && c != '\n')
44810275Ssam 					putc('\r', outstr);
44910275Ssam 				putc(c, outstr);
45010275Ssam 				cr = c == '\r';
45110275Ssam 				continue;
45210275Ssam 			}
45310275Ssam 			if (c == '\r') {
45410275Ssam 				cr = 1;
45510275Ssam 				continue;
45610275Ssam 			}
45710275Ssam 			putc(c, outstr);
45810275Ssam 		}
45910275Ssam 		if (cr)
46010275Ssam 			putc('\r', outstr);
46110275Ssam 		return (0);
46210275Ssam 	}
46310275Ssam 	fatal("Unknown type in receive_data.");
46410275Ssam 	/*NOTREACHED*/
46510275Ssam }
46610275Ssam 
46710275Ssam fatal(s)
46810275Ssam 	char *s;
46910275Ssam {
47010275Ssam 	reply(451, "Error in server: %s\n", s);
47110275Ssam 	reply(221, "Closing connection due to server error.");
47210275Ssam 	exit(0);
47310275Ssam }
47410275Ssam 
47510275Ssam reply(n, s, args)
47610275Ssam 	int n;
47710275Ssam 	char *s;
47810275Ssam {
47910275Ssam 
48010275Ssam 	printf("%d ", n);
48110275Ssam 	_doprnt(s, &args, stdout);
48210275Ssam 	printf("\r\n");
48310275Ssam 	fflush(stdout);
48410275Ssam 	if (debug) {
48510275Ssam 		fprintf(stderr, "<--- %d ", n);
48610275Ssam 		_doprnt(s, &args, stderr);
48710275Ssam 		fprintf(stderr, "\n");
48810275Ssam 		fflush(stderr);
48910275Ssam 	}
49010275Ssam }
49110275Ssam 
49210275Ssam lreply(n, s, args)
49310275Ssam 	int n;
49410275Ssam 	char *s;
49510275Ssam {
49610275Ssam 	printf("%d-", n);
49710275Ssam 	_doprnt(s, &args, stdout);
49810275Ssam 	printf("\r\n");
49910275Ssam 	fflush(stdout);
50010275Ssam 	if (debug) {
50110275Ssam 		fprintf(stderr, "<--- %d-", n);
50210275Ssam 		_doprnt(s, &args, stderr);
50310275Ssam 		fprintf(stderr, "\n");
50410275Ssam 	}
50510275Ssam }
50610275Ssam 
50710275Ssam replystr(s)
50810275Ssam 	char *s;
50910275Ssam {
51010275Ssam 	printf("%s\r\n", s);
51110275Ssam 	fflush(stdout);
51210275Ssam 	if (debug)
51310275Ssam 		fprintf(stderr, "<--- %s\n", s);
51410275Ssam }
51510275Ssam 
51610275Ssam ack(s)
51710275Ssam 	char *s;
51810275Ssam {
51910275Ssam 	reply(200, "%s command okay.", s);
52010275Ssam }
52110275Ssam 
52210275Ssam nack(s)
52310275Ssam 	char *s;
52410275Ssam {
52510275Ssam 	reply(502, "%s command not implemented.", s);
52610275Ssam }
52710275Ssam 
52810275Ssam yyerror()
52910275Ssam {
53010275Ssam 	reply(500, "Command not understood.");
53110275Ssam }
53210275Ssam 
53310275Ssam delete(name)
53410275Ssam 	char *name;
53510275Ssam {
53610275Ssam 	struct stat st;
53710275Ssam 
53810275Ssam 	if (stat(name, &st) < 0) {
53910275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
54010275Ssam 		return;
54110275Ssam 	}
54210275Ssam 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
54310275Ssam 		if (rmdir(name) < 0) {
54410275Ssam 			reply(550, "%s: %s.", name, sys_errlist[errno]);
54510275Ssam 			return;
54610275Ssam 		}
54710275Ssam 		goto done;
54810275Ssam 	}
54910275Ssam 	if (unlink(name) < 0) {
55010275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
55110275Ssam 		return;
55210275Ssam 	}
55310275Ssam done:
55410275Ssam 	ack("DELE");
55510275Ssam }
55610275Ssam 
55710275Ssam cwd(path)
55810275Ssam 	char *path;
55910275Ssam {
56010275Ssam 
56110275Ssam 	if (chdir(path) < 0) {
56210275Ssam 		reply(550, "%s: %s.", path, sys_errlist[errno]);
56310275Ssam 		return;
56410275Ssam 	}
56510275Ssam 	ack("CWD");
56610275Ssam }
56710275Ssam 
568*10303Ssam makedir(name)
56910275Ssam 	char *name;
57010275Ssam {
571*10303Ssam 	struct stat st;
572*10303Ssam 	int dochown = stat(name, &st) < 0;
57310275Ssam 
57410275Ssam 	if (mkdir(name, 0777) < 0) {
57510275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
57610275Ssam 		return;
57710275Ssam 	}
578*10303Ssam 	if (dochown)
579*10303Ssam 		(void) chown(name, pw->pw_uid, -1);
58010275Ssam 	ack("MKDIR");
58110275Ssam }
58210275Ssam 
583*10303Ssam removedir(name)
58410275Ssam 	char *name;
58510275Ssam {
58610275Ssam 
58710275Ssam 	if (rmdir(name) < 0) {
58810275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
58910275Ssam 		return;
59010275Ssam 	}
59110275Ssam 	ack("RMDIR");
59210275Ssam }
59310275Ssam 
594*10303Ssam pwd()
59510275Ssam {
596*10303Ssam 	char path[MAXPATHLEN + 1];
59710275Ssam 	char *p;
59810275Ssam 
59910275Ssam 	if (getwd(path) == NULL) {
60010275Ssam 		reply(451, "%s.", path);
60110275Ssam 		return;
60210275Ssam 	}
60310275Ssam 	reply(251, "\"%s\" is current directory.", path);
60410275Ssam }
60510275Ssam 
60610275Ssam char *
60710275Ssam renamefrom(name)
60810275Ssam 	char *name;
60910275Ssam {
61010275Ssam 	struct stat st;
61110275Ssam 
61210275Ssam 	if (stat(name, &st) < 0) {
61310275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
61410275Ssam 		return ((char *)0);
61510275Ssam 	}
616*10303Ssam 	reply(350, "File exists, ready for destination name");
61710275Ssam 	return (name);
61810275Ssam }
61910275Ssam 
62010275Ssam renamecmd(from, to)
62110275Ssam 	char *from, *to;
62210275Ssam {
62310275Ssam 
62410275Ssam 	if (rename(from, to) < 0) {
62510275Ssam 		reply(550, "rename: %s.", sys_errlist[errno]);
62610275Ssam 		return;
62710275Ssam 	}
62810275Ssam 	ack("RNTO");
62910275Ssam }
63010275Ssam 
63110275Ssam int guest;
63210275Ssam /*
63310275Ssam  * Test pathname for guest-user safety.
63410275Ssam  */
63510275Ssam inappropriate_request(name)
63610275Ssam 	char *name;
63710275Ssam {
63810275Ssam 	int bogus = 0, depth = 0, length = strlen(name);
63910275Ssam 	char *p, *s;
64010275Ssam 
64110275Ssam 	if (!guest)
64210275Ssam 		return (0);
64310275Ssam 	if (name[0] == '/' || name[0] == '|')
64410275Ssam 		bogus = 1;
64510275Ssam 	for (p = name; p < name+length;) {
64610275Ssam 		s = p;				/* start of token */
64710275Ssam 		while ( *p && *p!= '/')
64810275Ssam 			p++;
64910275Ssam 		*p = 0;
65010275Ssam 		if (strcmp(s, "..") == 0)
65110275Ssam 			depth -= 1;		/* backing up */
65210275Ssam 		else if (strcmp(s, ".") == 0)
65310275Ssam 			depth += 0;		/* no change */
65410275Ssam 		else
65510275Ssam 			depth += 1;		/* descending */
65610275Ssam 		if (depth < 0) {
65710275Ssam 			bogus = 1;
65810275Ssam 			break;
65910275Ssam 		}
66010275Ssam 	}
66110275Ssam 	if (bogus)
66210275Ssam 		reply(553, "%s: pathname disallowed guest users", name);
66310275Ssam 	return (bogus);
66410275Ssam }
66510275Ssam 
66610275Ssam /*
66710275Ssam  * Convert network-format internet address
66810275Ssam  * to base 256 d.d.d.d representation.
66910275Ssam  */
67010275Ssam char *
67110275Ssam ntoa(in)
67210275Ssam 	struct in_addr in;
67310275Ssam {
67410275Ssam 	static char b[18];
67510275Ssam 	register char *p;
67610275Ssam 
67710275Ssam 	in.s_addr = ntohl(in.s_addr);
67810275Ssam 	p = (char *)&in;
67910275Ssam #define	UC(b)	(((int)b)&0xff)
68010275Ssam 	sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
68110275Ssam 	return (b);
68210275Ssam }
68310275Ssam 
68410275Ssam dolog(sin)
68510275Ssam 	struct sockaddr_in *sin;
68610275Ssam {
68710275Ssam 	struct hostent *hp = gethostbyaddr(&sin->sin_addr,
68810275Ssam 		sizeof (struct in_addr), AF_INET);
68910275Ssam 	char *remotehost;
69010275Ssam 	time_t t;
69110275Ssam 
69210275Ssam 	if (hp)
69310275Ssam 		remotehost = hp->h_name;
69410275Ssam 	else
69510275Ssam 		remotehost = "UNKNOWNHOST";
69610275Ssam 	t = time(0);
697*10303Ssam 	fprintf(stderr,"FTP: connection from %s at %s", remotehost, ctime(&t));
69810275Ssam 	fflush(stderr);
69910275Ssam }
700