xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 36550)
1 /*
2  * Copyright (c) 1985, 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1985, 1988 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)ftpd.c	5.23 (Berkeley) 01/15/89";
26 #endif /* not lint */
27 
28 /*
29  * FTP server.
30  */
31 #include <sys/param.h>
32 #include <sys/stat.h>
33 #include <sys/ioctl.h>
34 #include <sys/socket.h>
35 #include <sys/file.h>
36 #include <sys/wait.h>
37 
38 #include <netinet/in.h>
39 
40 #include <arpa/ftp.h>
41 #include <arpa/inet.h>
42 #include <arpa/telnet.h>
43 
44 #include <stdio.h>
45 #include <signal.h>
46 #include <pwd.h>
47 #include <setjmp.h>
48 #include <netdb.h>
49 #include <errno.h>
50 #include <strings.h>
51 #include <syslog.h>
52 #include <varargs.h>
53 
54 /*
55  * File containing login names
56  * NOT to be used on this machine.
57  * Commonly used to disallow uucp.
58  */
59 #define	FTPUSERS	"/etc/ftpusers"
60 
61 extern	int errno;
62 extern	char *sys_errlist[];
63 extern	int sys_nerr;
64 extern	char *crypt();
65 extern	char version[];
66 extern	char *home;		/* pointer to home directory for glob */
67 extern	FILE *ftpd_popen(), *fopen(), *freopen();
68 extern	int  ftpd_pclose(), fclose();
69 extern	char *getline();
70 extern	char cbuf[];
71 
72 struct	sockaddr_in ctrl_addr;
73 struct	sockaddr_in data_source;
74 struct	sockaddr_in data_dest;
75 struct	sockaddr_in his_addr;
76 
77 int	data;
78 jmp_buf	errcatch, urgcatch;
79 int	logged_in;
80 struct	passwd *pw;
81 int	debug;
82 int	timeout = 900;    /* timeout after 15 minutes of inactivity */
83 int	logging;
84 int	guest;
85 int	type;
86 int	form;
87 int	stru;			/* avoid C keyword */
88 int	mode;
89 int	usedefault = 1;		/* for data transfers */
90 int	pdata = -1;		/* for passive mode */
91 int	transflag;
92 char	tmpline[7];
93 char	hostname[MAXHOSTNAMELEN];
94 char	remotehost[MAXHOSTNAMELEN];
95 
96 /*
97  * Timeout intervals for retrying connections
98  * to hosts that don't accept PORT cmds.  This
99  * is a kludge, but given the problems with TCP...
100  */
101 #define	SWAITMAX	90	/* wait at most 90 seconds */
102 #define	SWAITINT	5	/* interval between retries */
103 
104 int	swaitmax = SWAITMAX;
105 int	swaitint = SWAITINT;
106 
107 int	lostconn();
108 int	myoob();
109 FILE	*getdatasock(), *dataconn();
110 
111 main(argc, argv)
112 	int argc;
113 	char *argv[];
114 {
115 	int addrlen, on = 1;
116 	char *cp;
117 
118 	addrlen = sizeof (his_addr);
119 	if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
120 		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
121 		exit(1);
122 	}
123 	addrlen = sizeof (ctrl_addr);
124 	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
125 		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
126 		exit(1);
127 	}
128 	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
129 	debug = 0;
130 	openlog("ftpd", LOG_PID, LOG_DAEMON);
131 	argc--, argv++;
132 	while (argc > 0 && *argv[0] == '-') {
133 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
134 
135 		case 'v':
136 			debug = 1;
137 			break;
138 
139 		case 'd':
140 			debug = 1;
141 			break;
142 
143 		case 'l':
144 			logging = 1;
145 			break;
146 
147 		case 't':
148 			timeout = atoi(++cp);
149 			goto nextopt;
150 
151 		default:
152 			fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
153 			     *cp);
154 			break;
155 		}
156 nextopt:
157 		argc--, argv++;
158 	}
159 	(void) freopen("/dev/null", "w", stderr);
160 	(void) signal(SIGPIPE, lostconn);
161 	(void) signal(SIGCHLD, SIG_IGN);
162 	if ((int)signal(SIGURG, myoob) < 0)
163 		syslog(LOG_ERR, "signal: %m");
164 
165 	/* handle urgent data inline */
166 	/* Sequent defines this, but it doesn't work */
167 #ifdef SO_OOBINLINE
168 	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
169 		syslog(LOG_ERR, "setsockopt: %m");
170 #endif
171 	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
172 		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
173 	dolog(&his_addr);
174 	/* do telnet option negotiation here */
175 	/*
176 	 * Set up default state
177 	 */
178 	data = -1;
179 	type = TYPE_A;
180 	form = FORM_N;
181 	stru = STRU_F;
182 	mode = MODE_S;
183 	tmpline[0] = '\0';
184 	(void) gethostname(hostname, sizeof (hostname));
185 	reply(220, "%s FTP server (%s) ready.", hostname, version);
186 	(void) setjmp(errcatch);
187 	for (;;)
188 		(void) yyparse();
189 }
190 
191 lostconn()
192 {
193 
194 	if (debug)
195 		syslog(LOG_DEBUG, "lost connection");
196 	dologout(-1);
197 }
198 
199 static char ttyline[20];
200 
201 /*
202  * Helper function for sgetpwnam().
203  */
204 char *
205 sgetsave(s)
206 	char *s;
207 {
208 	char *malloc();
209 	char *new = malloc((unsigned) strlen(s) + 1);
210 
211 	if (new == NULL) {
212 		reply(553, "Local resource failure: malloc");
213 		dologout(1);
214 	}
215 	(void) strcpy(new, s);
216 	return (new);
217 }
218 
219 /*
220  * Save the result of a getpwnam.  Used for USER command, since
221  * the data returned must not be clobbered by any other command
222  * (e.g., globbing).
223  */
224 struct passwd *
225 sgetpwnam(name)
226 	char *name;
227 {
228 	static struct passwd save;
229 	register struct passwd *p;
230 	char *sgetsave();
231 
232 	if ((p = getpwnam(name)) == NULL)
233 		return (p);
234 	if (save.pw_name) {
235 		free(save.pw_name);
236 		free(save.pw_passwd);
237 		free(save.pw_comment);
238 		free(save.pw_gecos);
239 		free(save.pw_dir);
240 		free(save.pw_shell);
241 	}
242 	save = *p;
243 	save.pw_name = sgetsave(p->pw_name);
244 	save.pw_passwd = sgetsave(p->pw_passwd);
245 	save.pw_comment = sgetsave(p->pw_comment);
246 	save.pw_gecos = sgetsave(p->pw_gecos);
247 	save.pw_dir = sgetsave(p->pw_dir);
248 	save.pw_shell = sgetsave(p->pw_shell);
249 	return (&save);
250 }
251 
252 int login_attempts;		/* number of failed login attempts */
253 int askpasswd;			/* had user command, ask for passwd */
254 
255 /*
256  * USER command.
257  * Sets global passwd pointer pw if named account exists
258  * and is acceptable; sets askpasswd if a PASS command is
259  * expected. If logged in previously, need to reset state.
260  * If name is "ftp" or "anonymous" and ftp account exists,
261  * set guest and pw, then just return.
262  * If account doesn't exist, ask for passwd anyway.
263  * Otherwise, check user requesting login privileges.
264  * Disallow anyone who does not have a standard
265  * shell returned by getusershell() (/etc/shells).
266  * Disallow anyone mentioned in the file FTPUSERS
267  * to allow people such as root and uucp to be avoided.
268  */
269 user(name)
270 	char *name;
271 {
272 	register char *cp;
273 	FILE *fd;
274 	char *shell;
275 	char line[BUFSIZ], *index(), *getusershell();
276 
277 	if (logged_in) {
278 		if (guest) {
279 			reply(530, "Can't change user from guest login.");
280 			return;
281 		}
282 		end_login();
283 	}
284 
285 	guest = 0;
286 	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
287 		if ((pw = sgetpwnam("ftp")) != NULL) {
288 			guest = 1;
289 			askpasswd = 1;
290 			reply(331, "Guest login ok, send ident as password.");
291 		} else
292 			reply(530, "User %s unknown.", name);
293 		return;
294 	}
295 	if (pw = sgetpwnam(name)) {
296 		if ((shell = pw->pw_shell) == NULL || *shell == 0)
297 			shell = "/bin/sh";
298 		while ((cp = getusershell()) != NULL)
299 			if (strcmp(cp, shell) == 0)
300 				break;
301 		endusershell();
302 		if (cp == NULL) {
303 			reply(530, "User %s access denied.", name);
304 			syslog(LOG_ERR, "FTP LOGIN REFUSED FROM %s, %s",
305 			    remotehost, name);
306 			pw = (struct passwd *) NULL;
307 			return;
308 		}
309 		if ((fd = fopen(FTPUSERS, "r")) != NULL) {
310 		    while (fgets(line, sizeof (line), fd) != NULL) {
311 			if ((cp = index(line, '\n')) != NULL)
312 				*cp = '\0';
313 			if (strcmp(line, name) == 0) {
314 				reply(530, "User %s access denied.", name);
315 				syslog(LOG_ERR, "FTP LOGIN REFUSED FROM %s, %s",
316 				    remotehost, name);
317 				pw = (struct passwd *) NULL;
318 				return;
319 			}
320 		    }
321 		}
322 		(void) fclose(fd);
323 	}
324 	reply(331, "Password required for %s.", name);
325 	askpasswd = 1;
326 	/*
327 	 * Delay before reading passwd after first failed
328 	 * attempt to slow down passwd-guessing programs.
329 	 */
330 	if (login_attempts)
331 		sleep((unsigned) login_attempts);
332 }
333 
334 /*
335  * Terminate login as previous user, if any, resetting state;
336  * used when USER command is given or login fails.
337  */
338 end_login()
339 {
340 
341 	(void) seteuid((uid_t)0);
342 	if (logged_in)
343 		logwtmp(ttyline, "", "");
344 	pw = NULL;
345 	logged_in = 0;
346 	guest = 0;
347 }
348 
349 pass(passwd)
350 	char *passwd;
351 {
352 	char *xpasswd, *salt;
353 
354 	if (logged_in || askpasswd == 0) {
355 		reply(503, "Login with USER first.");
356 		return;
357 	}
358 	askpasswd = 0;
359 	if (!guest) {		/* "ftp" is only account allowed no password */
360 		if (pw == NULL)
361 			salt = "xx";
362 		else
363 			salt = pw->pw_passwd;
364 		xpasswd = crypt(passwd, salt);
365 		/* The strcmp does not catch null passwords! */
366 		if (pw == NULL || *pw->pw_passwd == '\0' ||
367 		    strcmp(xpasswd, pw->pw_passwd)) {
368 			reply(530, "Login incorrect.");
369 			pw = NULL;
370 			if (login_attempts++ >= 5) {
371 				syslog(LOG_ERR,
372 				    "repeated login failures from %s",
373 				    remotehost);
374 				exit(0);
375 			}
376 			return;
377 		}
378 	}
379 	login_attempts = 0;		/* this time successful */
380 	(void) setegid((gid_t)pw->pw_gid);
381 	(void) initgroups(pw->pw_name, pw->pw_gid);
382 
383 	/* open wtmp before chroot */
384 	(void)sprintf(ttyline, "ftp%d", getpid());
385 	logwtmp(ttyline, pw->pw_name, remotehost);
386 	logged_in = 1;
387 
388 	if (guest) {
389 		if (chroot(pw->pw_dir) < 0) {
390 			reply(550, "Can't set guest privileges.");
391 			goto bad;
392 		}
393 	}
394 	else if (chdir(pw->pw_dir))
395 		if (chdir("/")) {
396 			reply(530, "User %s: can't change directory to %s.",
397 			    pw->pw_name, pw->pw_dir);
398 			goto bad;
399 		}
400 		else
401 			lreply(230, "No directory! Logging in with home=/");
402 	if (seteuid((uid_t)pw->pw_uid) < 0) {
403 		reply(550, "Can't set uid.");
404 		goto bad;
405 	}
406 	if (guest) {
407 		reply(230, "Guest login ok, access restrictions apply.");
408 		syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
409 		    remotehost, passwd);
410 	} else {
411 		reply(230, "User %s logged in.", pw->pw_name);
412 		syslog(LOG_INFO, "FTP LOGIN FROM %s, %s",
413 		    remotehost, pw->pw_name);
414 	}
415 	home = pw->pw_dir;		/* home dir for globbing */
416 	return;
417 bad:
418 	/* Forget all about it... */
419 	end_login();
420 }
421 
422 retrieve(cmd, name)
423 	char *cmd, *name;
424 {
425 	FILE *fin, *dout;
426 	struct stat st;
427 	int (*closefunc)(), tmp;
428 
429 	if (cmd == 0)
430 		fin = fopen(name, "r"), closefunc = fclose;
431 	else {
432 		char line[BUFSIZ];
433 
434 		(void) sprintf(line, cmd, name), name = line;
435 		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
436 	}
437 	if (fin == NULL) {
438 		if (errno != 0)
439 			perror_reply(550, name);
440 		return;
441 	}
442 	st.st_size = 0;
443 	if (cmd == 0 &&
444 	    (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
445 		reply(550, "%s: not a plain file.", name);
446 		goto done;
447 	}
448 	dout = dataconn(name, st.st_size, "w");
449 	if (dout == NULL)
450 		goto done;
451 	if ((tmp = send_data(fin, dout, st.st_blksize)) > 0 ||
452 	    ferror(dout) > 0) {
453 		perror_reply(550, name);
454 	}
455 	else if (tmp == 0) {
456 		reply(226, "Transfer complete.");
457 	}
458 	(void) fclose(dout);
459 	data = -1;
460 	pdata = -1;
461 done:
462 	(*closefunc)(fin);
463 }
464 
465 store(name, mode, unique)
466 	char *name, *mode;
467 	int unique;
468 {
469 	FILE *fout, *din;
470 	struct stat st;
471 	int (*closefunc)(), tmp;
472 	char *gunique();
473 
474 	if (unique && stat(name, &st) == 0 &&
475 	    (name = gunique(name)) == NULL)
476 		return;
477 
478 	fout = fopen(name, mode), closefunc = fclose;
479 	if (fout == NULL) {
480 		perror_reply(553, name);
481 		return;
482 	}
483 	din = dataconn(name, (off_t)-1, "r");
484 	if (din == NULL)
485 		goto done;
486 	if ((tmp = receive_data(din, fout)) > 0)
487 		perror_reply(552, name);
488 	else if (tmp == 0) {
489 		if (ferror(fout) > 0)
490 			perror_reply(552, name);
491 		else if (unique)
492 			reply(226, "Transfer complete (unique file name:%s).",
493 			    name);
494 		else
495 			reply(226, "Transfer complete.");
496 	}
497 	(void) fclose(din);
498 	data = -1;
499 	pdata = -1;
500 done:
501 	(*closefunc)(fout);
502 }
503 
504 FILE *
505 getdatasock(mode)
506 	char *mode;
507 {
508 	int s, on = 1;
509 
510 	if (data >= 0)
511 		return (fdopen(data, mode));
512 	s = socket(AF_INET, SOCK_STREAM, 0);
513 	if (s < 0)
514 		return (NULL);
515 	(void) seteuid((uid_t)0);
516 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0)
517 		goto bad;
518 	/* anchor socket to avoid multi-homing problems */
519 	data_source.sin_family = AF_INET;
520 	data_source.sin_addr = ctrl_addr.sin_addr;
521 	if (bind(s, (struct sockaddr *)&data_source, sizeof (data_source)) < 0)
522 		goto bad;
523 	(void) seteuid((uid_t)pw->pw_uid);
524 	return (fdopen(s, mode));
525 bad:
526 	(void) seteuid((uid_t)pw->pw_uid);
527 	(void) close(s);
528 	return (NULL);
529 }
530 
531 FILE *
532 dataconn(name, size, mode)
533 	char *name;
534 	off_t size;
535 	char *mode;
536 {
537 	char sizebuf[32];
538 	FILE *file;
539 	int retry = 0;
540 
541 	if (size != (off_t) -1)
542 		(void) sprintf (sizebuf, " (%ld bytes)", size);
543 	else
544 		(void) strcpy(sizebuf, "");
545 	if (pdata >= 0) {
546 		struct sockaddr_in from;
547 		int s, fromlen = sizeof(from);
548 
549 		s = accept(pdata, (struct sockaddr *)&from, &fromlen);
550 		if (s < 0) {
551 			reply(425, "Can't open data connection.");
552 			(void) close(pdata);
553 			pdata = -1;
554 			return(NULL);
555 		}
556 		(void) close(pdata);
557 		pdata = s;
558 		reply(150, "Opening %s mode data connection for %s%s.",
559 		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
560 		return(fdopen(pdata, mode));
561 	}
562 	if (data >= 0) {
563 		reply(125, "Using existing data connection for %s%s.",
564 		    name, sizebuf);
565 		usedefault = 1;
566 		return (fdopen(data, mode));
567 	}
568 	if (usedefault)
569 		data_dest = his_addr;
570 	usedefault = 1;
571 	file = getdatasock(mode);
572 	if (file == NULL) {
573 		reply(425, "Can't create data socket (%s,%d): %s.",
574 		    inet_ntoa(data_source.sin_addr),
575 		    ntohs(data_source.sin_port),
576 		    errno < sys_nerr ? sys_errlist[errno] : "unknown error");
577 		return (NULL);
578 	}
579 	data = fileno(file);
580 	while (connect(data, (struct sockaddr *)&data_dest,
581 	    sizeof (data_dest)) < 0) {
582 		if (errno == EADDRINUSE && retry < swaitmax) {
583 			sleep((unsigned) swaitint);
584 			retry += swaitint;
585 			continue;
586 		}
587 		perror_reply(425, "Can't build data connection");
588 		(void) fclose(file);
589 		data = -1;
590 		return (NULL);
591 	}
592 	reply(150, "Opening %s mode data connection for %s%s.",
593 	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
594 	return (file);
595 }
596 
597 /*
598  * Tranfer the contents of "instr" to
599  * "outstr" peer using the appropriate
600  * encapulation of the date subject
601  * to Mode, Structure, and Type.
602  *
603  * NB: Form isn't handled.
604  */
605 send_data(instr, outstr, blksize)
606 	FILE *instr, *outstr;
607 	off_t blksize;
608 {
609 	register int c, cnt;
610 	register char *buf;
611 	int netfd, filefd;
612 
613 	transflag++;
614 	if (setjmp(urgcatch)) {
615 		transflag = 0;
616 		return(-1);
617 	}
618 	switch (type) {
619 
620 	case TYPE_A:
621 		while ((c = getc(instr)) != EOF) {
622 			if (c == '\n') {
623 				if (ferror (outstr)) {
624 					transflag = 0;
625 					return (1);
626 				}
627 				(void) putc('\r', outstr);
628 			}
629 			(void) putc(c, outstr);
630 		}
631 		transflag = 0;
632 		if (ferror (instr) || ferror (outstr)) {
633 			return (1);
634 		}
635 		return (0);
636 
637 	case TYPE_I:
638 	case TYPE_L:
639 		if ((buf = malloc((u_int)blksize)) == NULL) {
640 			transflag = 0;
641 			return (1);
642 		}
643 		netfd = fileno(outstr);
644 		filefd = fileno(instr);
645 		while ((cnt = read(filefd, buf, sizeof(buf))) > 0 &&
646 		    write(netfd, buf, cnt) == cnt);
647 		transflag = 0;
648 		(void)free(buf);
649 		return (cnt != 0);
650 	}
651 	reply(550, "Unimplemented TYPE %d in send_data", type);
652 	transflag = 0;
653 	return (-1);
654 }
655 
656 /*
657  * Transfer data from peer to
658  * "outstr" using the appropriate
659  * encapulation of the data subject
660  * to Mode, Structure, and Type.
661  *
662  * N.B.: Form isn't handled.
663  */
664 receive_data(instr, outstr)
665 	FILE *instr, *outstr;
666 {
667 	register int c;
668 	int cnt;
669 	char buf[BUFSIZ];
670 
671 
672 	transflag++;
673 	if (setjmp(urgcatch)) {
674 		transflag = 0;
675 		return(-1);
676 	}
677 	switch (type) {
678 
679 	case TYPE_I:
680 	case TYPE_L:
681 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
682 			if (write(fileno(outstr), buf, cnt) < 0) {
683 				transflag = 0;
684 				return (1);
685 			}
686 		}
687 		transflag = 0;
688 		return (cnt < 0);
689 
690 	case TYPE_E:
691 		reply(553, "TYPE E not implemented.");
692 		transflag = 0;
693 		return (-1);
694 
695 	case TYPE_A:
696 		while ((c = getc(instr)) != EOF) {
697 			while (c == '\r') {
698 				if (ferror (outstr)) {
699 					transflag = 0;
700 					return (1);
701 				}
702 				if ((c = getc(instr)) != '\n')
703 					(void) putc ('\r', outstr);
704 			/*	if (c == '\0')			*/
705 			/*		continue;		*/
706 			}
707 			(void) putc (c, outstr);
708 		}
709 		transflag = 0;
710 		if (ferror (instr) || ferror (outstr))
711 			return (1);
712 		return (0);
713 	}
714 	transflag = 0;
715 	fatal("Unknown type in receive_data.");
716 	/*NOTREACHED*/
717 }
718 
719 fatal(s)
720 	char *s;
721 {
722 	reply(451, "Error in server: %s\n", s);
723 	reply(221, "Closing connection due to server error.");
724 	dologout(0);
725 }
726 
727 /* VARARGS2 */
728 reply(n, fmt, p0, p1, p2, p3, p4, p5)
729 	int n;
730 	char *fmt;
731 {
732 	printf("%d ", n);
733 	printf(fmt, p0, p1, p2, p3, p4, p5);
734 	printf("\r\n");
735 	(void)fflush(stdout);
736 	if (debug) {
737 		syslog(LOG_DEBUG, "<--- %d ", n);
738 		syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
739 	}
740 }
741 
742 /* VARARGS2 */
743 lreply(n, fmt, p0, p1, p2, p3, p4, p5)
744 	int n;
745 	char *fmt;
746 {
747 	printf("%d- ", n);
748 	printf(fmt, p0, p1, p2, p3, p4, p5);
749 	printf("\r\n");
750 	(void)fflush(stdout);
751 	if (debug) {
752 		syslog(LOG_DEBUG, "<--- %d- ", n);
753 		syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
754 	}
755 }
756 
757 ack(s)
758 	char *s;
759 {
760 	reply(250, "%s command successful.", s);
761 }
762 
763 nack(s)
764 	char *s;
765 {
766 	reply(502, "%s command not implemented.", s);
767 }
768 
769 /* ARGSUSED */
770 yyerror(s)
771 	char *s;
772 {
773 	char *cp;
774 
775 	cp = index(cbuf,'\n');
776 	*cp = '\0';
777 	reply(500, "'%s': command not understood.",cbuf);
778 }
779 
780 delete(name)
781 	char *name;
782 {
783 	struct stat st;
784 
785 	if (stat(name, &st) < 0) {
786 		perror_reply(550, name);
787 		return;
788 	}
789 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
790 		if (rmdir(name) < 0) {
791 			perror_reply(550, name);
792 			return;
793 		}
794 		goto done;
795 	}
796 	if (unlink(name) < 0) {
797 		perror_reply(550, name);
798 		return;
799 	}
800 done:
801 	ack("DELE");
802 }
803 
804 cwd(path)
805 	char *path;
806 {
807 
808 	if (chdir(path) < 0) {
809 		perror_reply(550, path);
810 		return;
811 	}
812 	ack("CWD");
813 }
814 
815 makedir(name)
816 	char *name;
817 {
818 	if (mkdir(name, 0777) < 0)
819 		perror_reply(550, name);
820 	else
821 		reply(257, "MKD command successful.");
822 }
823 
824 removedir(name)
825 	char *name;
826 {
827 
828 	if (rmdir(name) < 0) {
829 		perror_reply(550, name);
830 		return;
831 	}
832 	ack("RMD");
833 }
834 
835 pwd()
836 {
837 	char path[MAXPATHLEN + 1];
838 	extern char *getwd();
839 
840 	if (getwd(path) == (char *)NULL) {
841 		reply(550, "%s.", path);
842 		return;
843 	}
844 	reply(257, "\"%s\" is current directory.", path);
845 }
846 
847 char *
848 renamefrom(name)
849 	char *name;
850 {
851 	struct stat st;
852 
853 	if (stat(name, &st) < 0) {
854 		perror_reply(550, name);
855 		return ((char *)0);
856 	}
857 	reply(350, "File exists, ready for destination name");
858 	return (name);
859 }
860 
861 renamecmd(from, to)
862 	char *from, *to;
863 {
864 
865 	if (rename(from, to) < 0) {
866 		perror_reply(550, "rename");
867 		return;
868 	}
869 	ack("RNTO");
870 }
871 
872 dolog(sin)
873 	struct sockaddr_in *sin;
874 {
875 	struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
876 		sizeof (struct in_addr), AF_INET);
877 	time_t t, time();
878 	extern char *ctime();
879 
880 	if (hp)
881 		(void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
882 	else
883 		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
884 		    sizeof (remotehost));
885 	if (!logging)
886 		return;
887 	t = time((time_t *) 0);
888 	syslog(LOG_INFO, "connection from %s at %s",
889 	    remotehost, ctime(&t));
890 }
891 
892 /*
893  * Record logout in wtmp file
894  * and exit with supplied status.
895  */
896 dologout(status)
897 	int status;
898 {
899 	if (logged_in) {
900 		(void) seteuid((uid_t)0);
901 		logwtmp(ttyline, "", "");
902 	}
903 	/* beware of flushing buffers after a SIGPIPE */
904 	_exit(status);
905 }
906 
907 myoob()
908 {
909 	char *cp;
910 
911 	/* only process if transfer occurring */
912 	if (!transflag)
913 		return;
914 	cp = tmpline;
915 	if (getline(cp, 7, stdin) == NULL) {
916 		reply(221, "You could at least say goodbye.");
917 		dologout(0);
918 	}
919 	upper(cp);
920 	if (strcmp(cp, "ABOR\r\n"))
921 		return;
922 	tmpline[0] = '\0';
923 	reply(426,"Transfer aborted. Data connection closed.");
924 	reply(226,"Abort successful");
925 	longjmp(urgcatch, 1);
926 }
927 
928 /*
929  * Note: The 530 reply codes could be 4xx codes, except nothing is
930  * given in the state tables except 421 which implies an exit.  (RFC959)
931  */
932 passive()
933 {
934 	int len;
935 	struct sockaddr_in tmp;
936 	register char *p, *a;
937 
938 	pdata = socket(AF_INET, SOCK_STREAM, 0);
939 	if (pdata < 0) {
940 		reply(530, "Can't open passive connection");
941 		return;
942 	}
943 	tmp = ctrl_addr;
944 	tmp.sin_port = 0;
945 	(void) seteuid((uid_t)0);
946 	if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) {
947 		(void) seteuid((uid_t)pw->pw_uid);
948 		(void) close(pdata);
949 		pdata = -1;
950 		reply(530, "Can't open passive connection");
951 		return;
952 	}
953 	(void) seteuid((uid_t)pw->pw_uid);
954 	len = sizeof(tmp);
955 	if (getsockname(pdata, (struct sockaddr *) &tmp, &len) < 0) {
956 		(void) close(pdata);
957 		pdata = -1;
958 		reply(530, "Can't open passive connection");
959 		return;
960 	}
961 	if (listen(pdata, 1) < 0) {
962 		(void) close(pdata);
963 		pdata = -1;
964 		reply(530, "Can't open passive connection");
965 		return;
966 	}
967 	a = (char *) &tmp.sin_addr;
968 	p = (char *) &tmp.sin_port;
969 
970 #define UC(b) (((int) b) & 0xff)
971 
972 	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
973 		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
974 }
975 
976 /*
977  * Generate unique name for file with basename "local".
978  * The file named "local" is already known to exist.
979  * Generates failure reply on error.
980  */
981 char *
982 gunique(local)
983 	char *local;
984 {
985 	static char new[MAXPATHLEN];
986 	struct stat st;
987 	char *cp = rindex(local, '/');
988 	int d, count=0;
989 
990 	if (cp)
991 		*cp = '\0';
992 	d = stat(cp ? local : ".", &st);
993 	if (cp)
994 		*cp = '/';
995 	if (d < 0) {
996 		perror_reply(553, local);
997 		return((char *) 0);
998 	}
999 	(void) strcpy(new, local);
1000 	cp = new + strlen(new);
1001 	*cp++ = '.';
1002 	for (count = 1; count < 100; count++) {
1003 		(void) sprintf(cp, "%d", count);
1004 		if (stat(new, &st) < 0)
1005 			return(new);
1006 	}
1007 	reply(452, "Unique file name cannot be created.");
1008 	return((char *) 0);
1009 }
1010 
1011 /*
1012  * Format and send reply containing system error number.
1013  */
1014 perror_reply(code, string)
1015 	int code;
1016 	char *string;
1017 {
1018 
1019 	if (errno < sys_nerr)
1020 		reply(code, "%s: %s.", string, sys_errlist[errno]);
1021 	else
1022 		reply(code, "%s: unknown error %d.", string, errno);
1023 }
1024