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