xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 10275)
1*10275Ssam #ifndef lint
2*10275Ssam static char sccsid[] = "@(#)ftpd.c	4.1 (Berkeley) 83/01/13";
3*10275Ssam #endif
4*10275Ssam 
5*10275Ssam /*
6*10275Ssam  * FTP server.
7*10275Ssam  */
8*10275Ssam #include <sys/types.h>
9*10275Ssam #include <sys/stat.h>
10*10275Ssam #include <sys/ioctl.h>
11*10275Ssam #include <sys/socket.h>
12*10275Ssam 
13*10275Ssam #include <netinet/in.h>
14*10275Ssam 
15*10275Ssam #include <stdio.h>
16*10275Ssam #include <signal.h>
17*10275Ssam #include <wait.h>
18*10275Ssam #include <pwd.h>
19*10275Ssam #include <setjmp.h>
20*10275Ssam #include <netdb.h>
21*10275Ssam 
22*10275Ssam #include "ftp.h"
23*10275Ssam 
24*10275Ssam extern	int errno;
25*10275Ssam extern	char *sys_errlist[];
26*10275Ssam extern	char *crypt();
27*10275Ssam extern	char version[];
28*10275Ssam extern	char *home;		/* pointer to home directory for glob */
29*10275Ssam extern	FILE *popen(), *fopen();
30*10275Ssam extern	int pclose(), fclose();
31*10275Ssam 
32*10275Ssam struct	sockaddr_in ctrl_addr;
33*10275Ssam struct	sockaddr_in data_source;
34*10275Ssam struct	sockaddr_in data_dest;
35*10275Ssam struct	sockaddr_in his_addr;
36*10275Ssam 
37*10275Ssam struct	hostent *hp;
38*10275Ssam 
39*10275Ssam int	data;
40*10275Ssam jmp_buf	errcatch;
41*10275Ssam int	logged_in;
42*10275Ssam struct	passwd *pw;
43*10275Ssam int	debug;
44*10275Ssam int	logging = 1;
45*10275Ssam int	guest;
46*10275Ssam int	type;
47*10275Ssam int	form;
48*10275Ssam int	stru;			/* avoid C keyword */
49*10275Ssam int	mode;
50*10275Ssam char	hostname[32];
51*10275Ssam char	*remotehost;
52*10275Ssam 
53*10275Ssam int	lostconn();
54*10275Ssam FILE	*getdatasock(), *dataconn();
55*10275Ssam char	*ntoa();
56*10275Ssam 
57*10275Ssam main(argc, argv)
58*10275Ssam 	int argc;
59*10275Ssam 	char *argv[];
60*10275Ssam {
61*10275Ssam 	int ctrl, s, options = 0;
62*10275Ssam 	struct servent *sp;
63*10275Ssam 	union wait status;
64*10275Ssam 	char *cp;
65*10275Ssam 
66*10275Ssam 	sp = getservbyname("ftp", "tcp");
67*10275Ssam 	if (sp == 0) {
68*10275Ssam 		fprintf(stderr, "ftpd: fpt/tcp: unknown service\n");
69*10275Ssam 		exit(1);
70*10275Ssam 	}
71*10275Ssam 	ctrl_addr.sin_port = sp->s_port;
72*10275Ssam 	data_source.sin_port = htons(ntohs(sp->s_port) - 1);
73*10275Ssam 	signal(SIGPIPE, lostconn);
74*10275Ssam 	debug = 0;
75*10275Ssam 	argc--, argv++;
76*10275Ssam 	while (argc > 0 && *argv[0] == '-') {
77*10275Ssam 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
78*10275Ssam 
79*10275Ssam 		case 'd':
80*10275Ssam 			debug = 1;
81*10275Ssam 			options |= SO_DEBUG;
82*10275Ssam 			break;
83*10275Ssam 
84*10275Ssam 		default:
85*10275Ssam 			fprintf(stderr, "Unknown flag -%c ignored.\n", cp);
86*10275Ssam 			break;
87*10275Ssam 		}
88*10275Ssam 		argc--, argv++;
89*10275Ssam 	}
90*10275Ssam #ifndef DEBUG
91*10275Ssam 	if (fork())
92*10275Ssam 		exit(0);
93*10275Ssam 	for (s = 0; s < 10; s++)
94*10275Ssam 		if (s != 2)		/* don't screw stderr */
95*10275Ssam 			(void) close(s);
96*10275Ssam 	(void) open("/dev/null", 0);
97*10275Ssam 	(void) dup2(0, 1);
98*10275Ssam 	{ int tt = open("/dev/tty", 2);
99*10275Ssam 	  if (tt > 0) {
100*10275Ssam 		ioctl(tt, TIOCNOTTY, 0);
101*10275Ssam 		close(tt);
102*10275Ssam 	  }
103*10275Ssam 	}
104*10275Ssam #endif
105*10275Ssam 	while ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
106*10275Ssam 		perror("ftpd: socket");
107*10275Ssam 		sleep(5);
108*10275Ssam 	}
109*10275Ssam 	while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) {
110*10275Ssam 		perror("ftpd: bind");
111*10275Ssam 		sleep(5);
112*10275Ssam 	}
113*10275Ssam 	for (;;) {
114*10275Ssam 		int hisaddrlen = sizeof (his_addr);
115*10275Ssam 
116*10275Ssam 		ctrl = accept(s, &his_addr, &hisaddrlen, 0);
117*10275Ssam 		if (ctrl < 0) {
118*10275Ssam 			perror("ftpd: accept");
119*10275Ssam 			sleep(5);
120*10275Ssam 			continue;
121*10275Ssam 		}
122*10275Ssam 		data_dest = his_addr;
123*10275Ssam 		if (fork() == 0) {
124*10275Ssam 			if (logging)
125*10275Ssam 				dolog(&his_addr);
126*10275Ssam 			close(s);
127*10275Ssam 			dup2(ctrl, 0), close(ctrl), dup2(0, 1);
128*10275Ssam 			/* do telnet option negotiation here */
129*10275Ssam 			logged_in = 0;
130*10275Ssam 			data = -1;
131*10275Ssam 			gethostname(hostname, sizeof (hostname));
132*10275Ssam 			reply(220, "%s FTP server (%s) ready.",
133*10275Ssam 				hostname, version);
134*10275Ssam 			for (;;) {
135*10275Ssam 				setjmp(errcatch);
136*10275Ssam 				yyparse();
137*10275Ssam 			}
138*10275Ssam 		}
139*10275Ssam 		close(ctrl);
140*10275Ssam 		while (wait3(status, WNOHANG, 0) > 0)
141*10275Ssam 			continue;
142*10275Ssam 	}
143*10275Ssam }
144*10275Ssam 
145*10275Ssam lostconn()
146*10275Ssam {
147*10275Ssam 
148*10275Ssam 	fatal("Connection closed.");
149*10275Ssam }
150*10275Ssam 
151*10275Ssam pass(passwd)
152*10275Ssam 	char *passwd;
153*10275Ssam {
154*10275Ssam 	char *xpasswd;
155*10275Ssam 
156*10275Ssam 	if (logged_in || pw == NULL) {
157*10275Ssam 		reply(503, "Login with USER first.");
158*10275Ssam 		return;
159*10275Ssam 	}
160*10275Ssam 	if (!guest) {		/* "ftp" is only account allowed no password */
161*10275Ssam 		xpasswd = crypt(passwd, pw->pw_passwd);
162*10275Ssam 		if (strcmp(xpasswd, pw->pw_passwd) != 0) {
163*10275Ssam 			reply(530, "Login incorrect.");
164*10275Ssam 			pw = NULL;
165*10275Ssam 			return;
166*10275Ssam 		}
167*10275Ssam 	}
168*10275Ssam 	home = pw->pw_dir;		/* home dir for globbing */
169*10275Ssam 	setreuid(-1, pw->pw_uid);
170*10275Ssam 	setregid(-1, pw->pw_gid);
171*10275Ssam 	initgroups(pw->pw_name, pw->pw_gid);
172*10275Ssam 	if (chdir(pw->pw_dir)) {
173*10275Ssam 		reply(550, "User %s: can't change directory to $s.",
174*10275Ssam 			pw->pw_name, pw->pw_dir);
175*10275Ssam 		pw = NULL;
176*10275Ssam 		return;
177*10275Ssam 	}
178*10275Ssam 	if (guest && chroot(pw->pw_dir) < 0){
179*10275Ssam 		reply(550, "Can't set guest privileges.");
180*10275Ssam 		pw = NULL;
181*10275Ssam 		return;
182*10275Ssam 	}
183*10275Ssam 	if (!guest)
184*10275Ssam 		reply(230, "User %s logged in.", pw->pw_name);
185*10275Ssam 	else
186*10275Ssam 		reply(230, "Guest login ok, access restrictions apply.");
187*10275Ssam 	logged_in = 1;
188*10275Ssam }
189*10275Ssam 
190*10275Ssam retrieve(cmd, name)
191*10275Ssam 	char *cmd, *name;
192*10275Ssam {
193*10275Ssam 	FILE *fin, *dout;
194*10275Ssam 	struct stat st;
195*10275Ssam 	int (*closefunc)();
196*10275Ssam 
197*10275Ssam 	if (cmd == 0) {
198*10275Ssam 		if (*name == '!')
199*10275Ssam 			fin = popen(name + 1, "r"), closefunc = pclose;
200*10275Ssam 		else
201*10275Ssam 			fin = fopen(name, "r"), closefunc = fclose;
202*10275Ssam 	} else {
203*10275Ssam 		char line[BUFSIZ];
204*10275Ssam 
205*10275Ssam 		sprintf(line, cmd, name);
206*10275Ssam 		fin = popen(line, "r"), closefunc = pclose;
207*10275Ssam 	}
208*10275Ssam 	if (fin == NULL) {
209*10275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
210*10275Ssam 		return;
211*10275Ssam 	}
212*10275Ssam 	st.st_size = 0;
213*10275Ssam 	if (cmd == 0 &&
214*10275Ssam 	    (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
215*10275Ssam 		reply(550, "%s: not a plain file.", name);
216*10275Ssam 		goto done;
217*10275Ssam 	}
218*10275Ssam 	dout = dataconn(name, st.st_size, "w");
219*10275Ssam 	if (dout == NULL)
220*10275Ssam 		goto done;
221*10275Ssam 	if (!send_data(fin, dout) || ferror(dout))
222*10275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
223*10275Ssam 	else
224*10275Ssam 		reply(226, "Transfer complete.");
225*10275Ssam 	if (mode == MODE_S)
226*10275Ssam 		/* indicate EOF by closing connection */
227*10275Ssam 		fclose(dout), data = -1;
228*10275Ssam done:
229*10275Ssam 	(*closefunc)(fin);
230*10275Ssam }
231*10275Ssam 
232*10275Ssam store(name, mode)
233*10275Ssam 	char *name, *mode;
234*10275Ssam {
235*10275Ssam 	FILE *fout, *din;
236*10275Ssam 	int (*closefunc)();
237*10275Ssam 
238*10275Ssam 	if (name[0] == '!')
239*10275Ssam 		fout = popen(&name[1], "w"), closefunc = pclose;
240*10275Ssam 	else
241*10275Ssam 		fout = fopen(name, mode), closefunc = fclose;
242*10275Ssam 	if (fout == NULL) {
243*10275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
244*10275Ssam 		return;
245*10275Ssam 	}
246*10275Ssam 	din = dataconn(name, -1, "r");
247*10275Ssam 	if (din == NULL)
248*10275Ssam 		goto done;
249*10275Ssam 	if (!receive_data(din, fout) || ferror(fout))
250*10275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
251*10275Ssam 	else
252*10275Ssam 		reply(226, "Transfer complete.");
253*10275Ssam 	fclose(din), data = -1;
254*10275Ssam done:
255*10275Ssam 	(*closefunc)(fout);
256*10275Ssam }
257*10275Ssam 
258*10275Ssam FILE *
259*10275Ssam getdatasock(mode)
260*10275Ssam 	char *mode;
261*10275Ssam {
262*10275Ssam 	int retrytime, s;
263*10275Ssam 
264*10275Ssam 	if (data >= 0)
265*10275Ssam 		return (fdopen(data, mode));
266*10275Ssam 	retrytime = 1;
267*10275Ssam 	while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) {
268*10275Ssam 		if (retrytime < 5) {
269*10275Ssam 			sleep(retrytime);
270*10275Ssam 			retrytime <<= 1;
271*10275Ssam 			continue;
272*10275Ssam 		}
273*10275Ssam 		return (NULL);
274*10275Ssam 	}
275*10275Ssam 	retrytime = 1;
276*10275Ssam 	seteuid(0);
277*10275Ssam 	while (bind(s, &data_source, sizeof (data_source), 0) < 0) {
278*10275Ssam 		if (retrytime < 5) {
279*10275Ssam 			sleep(retrytime);
280*10275Ssam 			retrytime <<= 1;
281*10275Ssam 			continue;
282*10275Ssam 		}
283*10275Ssam 		seteuid(0);
284*10275Ssam 		close(s);
285*10275Ssam 		return (NULL);
286*10275Ssam 	}
287*10275Ssam 	seteuid(0);
288*10275Ssam 	return (fdopen(s, mode));
289*10275Ssam }
290*10275Ssam 
291*10275Ssam FILE *
292*10275Ssam dataconn(name, size, mode)
293*10275Ssam 	char *name;
294*10275Ssam 	int size;
295*10275Ssam 	char *mode;
296*10275Ssam {
297*10275Ssam 	char sizebuf[32];
298*10275Ssam 	FILE *file;
299*10275Ssam 
300*10275Ssam 	if (size >= 0)
301*10275Ssam 		sprintf(sizebuf, " (%d bytes)", size);
302*10275Ssam 	else
303*10275Ssam 		(void) strcpy(sizebuf, "");
304*10275Ssam 	if (data >= 0) {
305*10275Ssam 		reply(125, "Using existing data connection for %s%s.",
306*10275Ssam 		    name, sizebuf);
307*10275Ssam 		return (fdopen(data, mode));
308*10275Ssam 	}
309*10275Ssam 	reply(150, "Opening data connection for %s (%s,%d)%s.",
310*10275Ssam 	    name, ntoa(data_dest.sin_addr.s_addr),
311*10275Ssam 	    ntohs(data_dest.sin_port), sizebuf);
312*10275Ssam 	file = getdatasock(mode);
313*10275Ssam 	if (file == NULL) {
314*10275Ssam 		reply(425, "Can't create data socket (%s,%d): %s.",
315*10275Ssam 		    ntoa(data_source.sin_addr),
316*10275Ssam 		    ntohs(data_source.sin_port),
317*10275Ssam 		    sys_errlist[errno]);
318*10275Ssam 		return (NULL);
319*10275Ssam 	}
320*10275Ssam 	data = fileno(file);
321*10275Ssam 	if (connect(data, &data_dest, sizeof (data_dest), 0) < 0) {
322*10275Ssam 		reply(425, "Can't build data connection: %s.",
323*10275Ssam 		    sys_errlist[errno]);
324*10275Ssam 		(void) fclose(file);
325*10275Ssam 		data = -1;
326*10275Ssam 		return (NULL);
327*10275Ssam 	}
328*10275Ssam 	return (file);
329*10275Ssam }
330*10275Ssam 
331*10275Ssam /*
332*10275Ssam  * Tranfer the contents of "instr" to
333*10275Ssam  * "outstr" peer using the appropriate
334*10275Ssam  * encapulation of the date subject
335*10275Ssam  * to Mode, Structure, and Type.
336*10275Ssam  *
337*10275Ssam  * NB: Form isn't handled.
338*10275Ssam  */
339*10275Ssam send_data(instr, outstr)
340*10275Ssam 	FILE *instr, *outstr;
341*10275Ssam {
342*10275Ssam 	register int c;
343*10275Ssam 	int netfd, filefd, cnt;
344*10275Ssam 	char buf[BUFSIZ];
345*10275Ssam 
346*10275Ssam 	switch (type) {
347*10275Ssam 
348*10275Ssam 	case TYPE_A:
349*10275Ssam 		while ((c = getc(instr)) != EOF) {
350*10275Ssam 			if (c == '\n')
351*10275Ssam 				putc('\r', outstr);
352*10275Ssam 			if (putc(c, outstr) == EOF)
353*10275Ssam 				return (1);
354*10275Ssam 		}
355*10275Ssam 		return (0);
356*10275Ssam 
357*10275Ssam 	case TYPE_I:
358*10275Ssam 	case TYPE_L:
359*10275Ssam 		netfd = fileno(outstr);
360*10275Ssam 		filefd = fileno(instr);
361*10275Ssam 
362*10275Ssam 		while ((cnt = read(filefd, buf, sizeof buf)) > 0)
363*10275Ssam 			if (write(netfd, buf, cnt) < 0)
364*10275Ssam 				return (1);
365*10275Ssam 		return (cnt < 0);
366*10275Ssam 	}
367*10275Ssam 	reply(504,"Unimplemented TYPE %d in send_data", type);
368*10275Ssam 	return (1);
369*10275Ssam }
370*10275Ssam 
371*10275Ssam /*
372*10275Ssam  * Transfer data from peer to
373*10275Ssam  * "outstr" using the appropriate
374*10275Ssam  * encapulation of the data subject
375*10275Ssam  * to Mode, Structure, and Type.
376*10275Ssam  *
377*10275Ssam  * N.B.: Form isn't handled.
378*10275Ssam  */
379*10275Ssam receive_data(instr, outstr)
380*10275Ssam 	FILE *instr, *outstr;
381*10275Ssam {
382*10275Ssam 	register int c;
383*10275Ssam 	int cr, escape, eof;
384*10275Ssam 	int netfd, filefd, cnt;
385*10275Ssam 	char buf[BUFSIZ];
386*10275Ssam 
387*10275Ssam 
388*10275Ssam 	switch (type) {
389*10275Ssam 
390*10275Ssam 	case TYPE_I:
391*10275Ssam 	case TYPE_L:
392*10275Ssam 		netfd = fileno(instr);
393*10275Ssam 		netfd = fileno(outstr);
394*10275Ssam 		while ((cnt = read(netfd, buf, sizeof buf)) > 0)
395*10275Ssam 			if (write(filefd, buf, cnt) < 0)
396*10275Ssam 				return (1);
397*10275Ssam 		return (cnt < 0);
398*10275Ssam 
399*10275Ssam 	case TYPE_E:
400*10275Ssam 		reply(504, "TYPE E not implemented.");
401*10275Ssam 		return (1);
402*10275Ssam 
403*10275Ssam 	case TYPE_A:
404*10275Ssam 		cr = 0;
405*10275Ssam 		while ((c = getc(instr)) != EOF) {
406*10275Ssam 			if (cr) {
407*10275Ssam 				if (c != '\r' && c != '\n')
408*10275Ssam 					putc('\r', outstr);
409*10275Ssam 				putc(c, outstr);
410*10275Ssam 				cr = c == '\r';
411*10275Ssam 				continue;
412*10275Ssam 			}
413*10275Ssam 			if (c == '\r') {
414*10275Ssam 				cr = 1;
415*10275Ssam 				continue;
416*10275Ssam 			}
417*10275Ssam 			putc(c, outstr);
418*10275Ssam 		}
419*10275Ssam 		if (cr)
420*10275Ssam 			putc('\r', outstr);
421*10275Ssam 		return (0);
422*10275Ssam 	}
423*10275Ssam 	fatal("Unknown type in receive_data.");
424*10275Ssam 	/*NOTREACHED*/
425*10275Ssam }
426*10275Ssam 
427*10275Ssam fatal(s)
428*10275Ssam 	char *s;
429*10275Ssam {
430*10275Ssam 	reply(451, "Error in server: %s\n", s);
431*10275Ssam 	reply(221, "Closing connection due to server error.");
432*10275Ssam 	exit(0);
433*10275Ssam }
434*10275Ssam 
435*10275Ssam reply(n, s, args)
436*10275Ssam 	int n;
437*10275Ssam 	char *s;
438*10275Ssam {
439*10275Ssam 
440*10275Ssam 	printf("%d ", n);
441*10275Ssam 	_doprnt(s, &args, stdout);
442*10275Ssam 	printf("\r\n");
443*10275Ssam 	fflush(stdout);
444*10275Ssam 	if (debug) {
445*10275Ssam 		fprintf(stderr, "<--- %d ", n);
446*10275Ssam 		_doprnt(s, &args, stderr);
447*10275Ssam 		fprintf(stderr, "\n");
448*10275Ssam 		fflush(stderr);
449*10275Ssam 	}
450*10275Ssam }
451*10275Ssam 
452*10275Ssam lreply(n, s, args)
453*10275Ssam 	int n;
454*10275Ssam 	char *s;
455*10275Ssam {
456*10275Ssam 	printf("%d-", n);
457*10275Ssam 	_doprnt(s, &args, stdout);
458*10275Ssam 	printf("\r\n");
459*10275Ssam 	fflush(stdout);
460*10275Ssam 	if (debug) {
461*10275Ssam 		fprintf(stderr, "<--- %d-", n);
462*10275Ssam 		_doprnt(s, &args, stderr);
463*10275Ssam 		fprintf(stderr, "\n");
464*10275Ssam 	}
465*10275Ssam }
466*10275Ssam 
467*10275Ssam replystr(s)
468*10275Ssam 	char *s;
469*10275Ssam {
470*10275Ssam 	printf("%s\r\n", s);
471*10275Ssam 	fflush(stdout);
472*10275Ssam 	if (debug)
473*10275Ssam 		fprintf(stderr, "<--- %s\n", s);
474*10275Ssam }
475*10275Ssam 
476*10275Ssam ack(s)
477*10275Ssam 	char *s;
478*10275Ssam {
479*10275Ssam 	reply(200, "%s command okay.", s);
480*10275Ssam }
481*10275Ssam 
482*10275Ssam nack(s)
483*10275Ssam 	char *s;
484*10275Ssam {
485*10275Ssam 	reply(502, "%s command not implemented.", s);
486*10275Ssam }
487*10275Ssam 
488*10275Ssam yyerror()
489*10275Ssam {
490*10275Ssam 	reply(500, "Command not understood.");
491*10275Ssam }
492*10275Ssam 
493*10275Ssam delete(name)
494*10275Ssam 	char *name;
495*10275Ssam {
496*10275Ssam 	struct stat st;
497*10275Ssam 
498*10275Ssam 	if (stat(name, &st) < 0) {
499*10275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
500*10275Ssam 		return;
501*10275Ssam 	}
502*10275Ssam 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
503*10275Ssam 		if (rmdir(name) < 0) {
504*10275Ssam 			reply(550, "%s: %s.", name, sys_errlist[errno]);
505*10275Ssam 			return;
506*10275Ssam 		}
507*10275Ssam 		goto done;
508*10275Ssam 	}
509*10275Ssam 	if (unlink(name) < 0) {
510*10275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
511*10275Ssam 		return;
512*10275Ssam 	}
513*10275Ssam done:
514*10275Ssam 	ack("DELE");
515*10275Ssam }
516*10275Ssam 
517*10275Ssam cwd(path)
518*10275Ssam 	char *path;
519*10275Ssam {
520*10275Ssam 
521*10275Ssam 	if (chdir(path) < 0) {
522*10275Ssam 		reply(550, "%s: %s.", path, sys_errlist[errno]);
523*10275Ssam 		return;
524*10275Ssam 	}
525*10275Ssam 	ack("CWD");
526*10275Ssam }
527*10275Ssam 
528*10275Ssam do_mkdir(name)
529*10275Ssam 	char *name;
530*10275Ssam {
531*10275Ssam 
532*10275Ssam 	if (mkdir(name, 0777) < 0) {
533*10275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
534*10275Ssam 		return;
535*10275Ssam 	}
536*10275Ssam 	ack("MKDIR");
537*10275Ssam }
538*10275Ssam 
539*10275Ssam do_rmdir(name)
540*10275Ssam 	char *name;
541*10275Ssam {
542*10275Ssam 
543*10275Ssam 	if (rmdir(name) < 0) {
544*10275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
545*10275Ssam 		return;
546*10275Ssam 	}
547*10275Ssam 	ack("RMDIR");
548*10275Ssam }
549*10275Ssam 
550*10275Ssam do_pwd()
551*10275Ssam {
552*10275Ssam 	char path[1024];
553*10275Ssam 	char *p;
554*10275Ssam 
555*10275Ssam 	if (getwd(path) == NULL) {
556*10275Ssam 		reply(451, "%s.", path);
557*10275Ssam 		return;
558*10275Ssam 	}
559*10275Ssam 	reply(251, "\"%s\" is current directory.", path);
560*10275Ssam }
561*10275Ssam 
562*10275Ssam char *
563*10275Ssam renamefrom(name)
564*10275Ssam 	char *name;
565*10275Ssam {
566*10275Ssam 	struct stat st;
567*10275Ssam 
568*10275Ssam 	if (stat(name, &st) < 0) {
569*10275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
570*10275Ssam 		return ((char *)0);
571*10275Ssam 	}
572*10275Ssam 	ack("RNFR");
573*10275Ssam 	return (name);
574*10275Ssam }
575*10275Ssam 
576*10275Ssam renamecmd(from, to)
577*10275Ssam 	char *from, *to;
578*10275Ssam {
579*10275Ssam 
580*10275Ssam 	if (rename(from, to) < 0) {
581*10275Ssam 		reply(550, "rename: %s.", sys_errlist[errno]);
582*10275Ssam 		return;
583*10275Ssam 	}
584*10275Ssam 	ack("RNTO");
585*10275Ssam }
586*10275Ssam 
587*10275Ssam int guest;
588*10275Ssam /*
589*10275Ssam  * Test pathname for guest-user safety.
590*10275Ssam  */
591*10275Ssam inappropriate_request(name)
592*10275Ssam 	char *name;
593*10275Ssam {
594*10275Ssam 	int bogus = 0, depth = 0, length = strlen(name);
595*10275Ssam 	char *p, *s;
596*10275Ssam 
597*10275Ssam 	if (!guest)
598*10275Ssam 		return (0);
599*10275Ssam 	if (name[0] == '/' || name[0] == '|')
600*10275Ssam 		bogus = 1;
601*10275Ssam 	for (p = name; p < name+length;) {
602*10275Ssam 		s = p;				/* start of token */
603*10275Ssam 		while ( *p && *p!= '/')
604*10275Ssam 			p++;
605*10275Ssam 		*p = 0;
606*10275Ssam 		if (strcmp(s, "..") == 0)
607*10275Ssam 			depth -= 1;		/* backing up */
608*10275Ssam 		else if (strcmp(s, ".") == 0)
609*10275Ssam 			depth += 0;		/* no change */
610*10275Ssam 		else
611*10275Ssam 			depth += 1;		/* descending */
612*10275Ssam 		if (depth < 0) {
613*10275Ssam 			bogus = 1;
614*10275Ssam 			break;
615*10275Ssam 		}
616*10275Ssam 	}
617*10275Ssam 	if (bogus)
618*10275Ssam 		reply(553, "%s: pathname disallowed guest users", name);
619*10275Ssam 	return (bogus);
620*10275Ssam }
621*10275Ssam 
622*10275Ssam /*
623*10275Ssam  * Convert network-format internet address
624*10275Ssam  * to base 256 d.d.d.d representation.
625*10275Ssam  */
626*10275Ssam char *
627*10275Ssam ntoa(in)
628*10275Ssam 	struct in_addr in;
629*10275Ssam {
630*10275Ssam 	static char b[18];
631*10275Ssam 	register char *p;
632*10275Ssam 
633*10275Ssam 	in.s_addr = ntohl(in.s_addr);
634*10275Ssam 	p = (char *)&in;
635*10275Ssam #define	UC(b)	(((int)b)&0xff)
636*10275Ssam 	sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
637*10275Ssam 	return (b);
638*10275Ssam }
639*10275Ssam 
640*10275Ssam dolog(sin)
641*10275Ssam 	struct sockaddr_in *sin;
642*10275Ssam {
643*10275Ssam 	struct hostent *hp = gethostbyaddr(&sin->sin_addr,
644*10275Ssam 		sizeof (struct in_addr), AF_INET);
645*10275Ssam 	char *remotehost;
646*10275Ssam 	time_t t;
647*10275Ssam 
648*10275Ssam 	if (hp)
649*10275Ssam 		remotehost = hp->h_name;
650*10275Ssam 	else
651*10275Ssam 		remotehost = "UNKNOWNHOST";
652*10275Ssam 	t = time(0);
653*10275Ssam 	fprintf(stderr,"FTP %d: connection from %s at %s",
654*10275Ssam 		getpid(), remotehost, ctime(&t));
655*10275Ssam 	fflush(stderr);
656*10275Ssam }
657