xref: /original-bsd/libexec/ftpd/ftpd.c (revision 49ded727)
1 /*
2  * Copyright (c) 1985, 1988, 1990, 1992 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1985, 1988, 1990, 1992 Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)ftpd.c	5.42 (Berkeley) 07/15/92";
16 #endif /* not lint */
17 
18 /*
19  * FTP server.
20  */
21 #include <sys/param.h>
22 #include <sys/stat.h>
23 #include <sys/ioctl.h>
24 #include <sys/socket.h>
25 #include <sys/wait.h>
26 
27 #include <netinet/in.h>
28 #include <netinet/in_systm.h>
29 #include <netinet/ip.h>
30 
31 #define	FTP_NAMES
32 #include <arpa/ftp.h>
33 #include <arpa/inet.h>
34 #include <arpa/telnet.h>
35 
36 #include <signal.h>
37 #include <dirent.h>
38 #include <fcntl.h>
39 #include <time.h>
40 #include <pwd.h>
41 #include <setjmp.h>
42 #include <netdb.h>
43 #include <errno.h>
44 #include <syslog.h>
45 #include <unistd.h>
46 #include <stdio.h>
47 #include <ctype.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include "pathnames.h"
51 #include "extern.h"
52 
53 #if __STDC__
54 #include <stdarg.h>
55 #else
56 #include <varargs.h>
57 #endif
58 
59 extern	off_t restart_point;
60 extern	char *home;		/* pointer to home directory for glob */
61 extern	char cbuf[];
62 extern	char version[];
63 
64 struct	sockaddr_in ctrl_addr;
65 struct	sockaddr_in data_source;
66 struct	sockaddr_in data_dest;
67 struct	sockaddr_in his_addr;
68 struct	sockaddr_in pasv_addr;
69 
70 int	data;
71 jmp_buf	errcatch, urgcatch;
72 int	logged_in;
73 struct	passwd *pw;
74 int	debug;
75 int	timeout = 900;    /* timeout after 15 minutes of inactivity */
76 int	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
77 int	logging;
78 int	guest;
79 int	type;
80 int	form;
81 int	stru;			/* avoid C keyword */
82 int	mode;
83 int	usedefault = 1;		/* for data transfers */
84 int	pdata = -1;		/* for passive mode */
85 int	transflag;
86 off_t	file_size;
87 off_t	byte_count;
88 #if !defined(CMASK) || CMASK == 0
89 #undef CMASK
90 #define CMASK 027
91 #endif
92 int	defumask = CMASK;		/* default umask value */
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 #ifdef SETPROCTITLE
109 char	**Argv = NULL;		/* pointer to argument vector */
110 char	*LastArgv = NULL;	/* end of argv */
111 char	proctitle[BUFSIZ];	/* initial part of title */
112 #endif /* SETPROCTITLE */
113 
114 #define MAXLINE         256
115 
116 #define LOGCMD(cmd, file) \
117 	if (logging > 1) \
118 	    syslog(LOG_INFO,"%s %s%s", cmd, \
119 		*(file) == '/' ? "" : curdir(), file);
120 #define LOGCMD2(cmd, file1, file2) \
121 	 if (logging > 1) \
122 	    syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
123 		*(file1) == '/' ? "" : curdir(), file1, \
124 		*(file2) == '/' ? "" : curdir(), file2);
125 #define LOGBYTES(cmd, file, cnt) \
126 	if (logging > 1) { \
127 		if (cnt == (off_t)-1) \
128 		    syslog(LOG_INFO,"%s %s%s", cmd, \
129 			*(file) == '/' ? "" : curdir(), file); \
130 		else \
131 		    syslog(LOG_INFO, "%s %s%s = %qd bytes", \
132 			cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
133 	}
134 
135 static void	 ack __P((char *));
136 static void	 myoob __P((int));
137 static int	 checkuser __P((char *));
138 static FILE	*dataconn __P((char *, off_t, char *));
139 static void	 dolog __P((struct sockaddr_in *));
140 static char	*curdir __P((void));
141 static void	 end_login __P((void));
142 static FILE	*getdatasock __P((char *));
143 static char	*gunique __P((char *));
144 static void	 lostconn __P((int));
145 static void	 pass __P((char *));
146 static int	 receive_data __P((FILE *, FILE *));
147 static void	 send_data __P((FILE *, FILE *, off_t));
148 static struct passwd *
149 		 sgetpwnam __P((char *));
150 static char	*sgetsave __P((char *));
151 static void	 user __P((char *));
152 
153 static char *
154 curdir()
155 {
156 	static char path[MAXPATHLEN+1+1];	/* path + '/' + '\0' */
157 
158 	if (getcwd(path, sizeof(path)-2) == NULL)
159 		return ("");
160 	if (path[1] != '\0')		/* special case for root dir. */
161 		strcat(path, "/");
162 	/* For guest account, skip / since it's chrooted */
163 	return (guest ? path+1 : path);
164 }
165 
166 int
167 main(argc, argv, envp)
168 	int argc;
169 	char *argv[];
170 	char **envp;
171 {
172 	int addrlen, on = 1, tos;
173 	char *cp, line[MAXLINE];
174 	FILE *fd;
175 
176 	/*
177 	 * LOG_NDELAY sets up the logging connection immediately,
178 	 * necessary for anonymous ftp's that chroot and can't do it later.
179 	 */
180 	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
181 	addrlen = sizeof (his_addr);
182 	if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
183 		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
184 		exit(1);
185 	}
186 	addrlen = sizeof (ctrl_addr);
187 	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
188 		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
189 		exit(1);
190 	}
191 #ifdef IP_TOS
192 	tos = IPTOS_LOWDELAY;
193 	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
194 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
195 #endif
196 	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
197 	debug = 0;
198 #ifdef SETPROCTITLE
199 	/*
200 	 *  Save start and extent of argv for setproctitle.
201 	 */
202 	Argv = argv;
203 	while (*envp)
204 		envp++;
205 	LastArgv = envp[-1] + strlen(envp[-1]);
206 #endif /* SETPROCTITLE */
207 
208 	argc--, argv++;
209 	while (argc > 0 && *argv[0] == '-') {
210 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
211 
212 		case 'v':
213 			debug = 1;
214 			break;
215 
216 		case 'd':
217 			debug = 1;
218 			break;
219 
220 		case 'l':
221 			logging++;	/* > 1 == extra logging */
222 			break;
223 
224 		case 't':
225 			timeout = atoi(++cp);
226 			if (maxtimeout < timeout)
227 				maxtimeout = timeout;
228 			goto nextopt;
229 
230 		case 'T':
231 			maxtimeout = atoi(++cp);
232 			if (timeout > maxtimeout)
233 				timeout = maxtimeout;
234 			goto nextopt;
235 
236 		case 'u':
237 		    {
238 			int val = 0;
239 
240 			while (*++cp && *cp >= '0' && *cp <= '9')
241 				val = val*8 + *cp - '0';
242 			if (*cp)
243 				fprintf(stderr, "ftpd: Bad value for -u\n");
244 			else
245 				defumask = val;
246 			goto nextopt;
247 		    }
248 
249 		default:
250 			fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
251 			     *cp);
252 			break;
253 		}
254 nextopt:
255 		argc--, argv++;
256 	}
257 	(void) freopen(_PATH_DEVNULL, "w", stderr);
258 	(void) signal(SIGPIPE, lostconn);
259 	(void) signal(SIGCHLD, SIG_IGN);
260 	if ((int)signal(SIGURG, myoob) < 0)
261 		syslog(LOG_ERR, "signal: %m");
262 
263 	/* Try to handle urgent data inline */
264 #ifdef SO_OOBINLINE
265 	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
266 		syslog(LOG_ERR, "setsockopt: %m");
267 #endif
268 
269 #ifdef	F_SETOWN
270 	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
271 		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
272 #endif
273 	dolog(&his_addr);
274 	/*
275 	 * Set up default state
276 	 */
277 	data = -1;
278 	type = TYPE_A;
279 	form = FORM_N;
280 	stru = STRU_F;
281 	mode = MODE_S;
282 	tmpline[0] = '\0';
283 
284 	/* If logins are disabled, print out the message. */
285 	if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
286 		while (fgets(line, sizeof (line), fd) != NULL) {
287 			if ((cp = index(line, '\n')) != NULL)
288 				*cp = '\0';
289 			lreply(530, "%s", line);
290 		}
291 		(void) fflush(stdout);
292 		(void) fclose(fd);
293 		reply(530, "System not available.");
294 		exit(0);
295 	}
296 	if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
297 		while (fgets(line, sizeof (line), fd) != NULL) {
298 			if ((cp = index(line, '\n')) != NULL)
299 				*cp = '\0';
300 			lreply(220, "%s", line);
301 		}
302 		(void) fflush(stdout);
303 		(void) fclose(fd);
304 		/* reply(220,) must follow */
305 	}
306 	(void) gethostname(hostname, sizeof (hostname));
307 	reply(220, "%s FTP server (%s) ready.", hostname, version);
308 	(void) setjmp(errcatch);
309 	for (;;)
310 		(void) yyparse();
311 	/* NOTREACHED */
312 }
313 
314 static void
315 lostconn(signo)
316 	int signo;
317 {
318 	if (debug)
319 		syslog(LOG_DEBUG, "lost connection");
320 	dologout(-1);
321 }
322 
323 static char ttyline[20];
324 
325 /*
326  * Helper function for sgetpwnam().
327  */
328 static char *
329 sgetsave(s)
330 	char *s;
331 {
332 	char *new = malloc((unsigned) strlen(s) + 1);
333 
334 	if (new == NULL) {
335 		perror_reply(421, "Local resource failure: malloc");
336 		dologout(1);
337 		/* NOTREACHED */
338 	}
339 	(void) strcpy(new, s);
340 	return (new);
341 }
342 
343 /*
344  * Save the result of a getpwnam.  Used for USER command, since
345  * the data returned must not be clobbered by any other command
346  * (e.g., globbing).
347  */
348 static struct passwd *
349 sgetpwnam(name)
350 	char *name;
351 {
352 	static struct passwd save;
353 	register struct passwd *p;
354 	char *sgetsave();
355 
356 	if ((p = getpwnam(name)) == NULL)
357 		return (p);
358 	if (save.pw_name) {
359 		free(save.pw_name);
360 		free(save.pw_passwd);
361 		free(save.pw_gecos);
362 		free(save.pw_dir);
363 		free(save.pw_shell);
364 	}
365 	save = *p;
366 	save.pw_name = sgetsave(p->pw_name);
367 	save.pw_passwd = sgetsave(p->pw_passwd);
368 	save.pw_gecos = sgetsave(p->pw_gecos);
369 	save.pw_dir = sgetsave(p->pw_dir);
370 	save.pw_shell = sgetsave(p->pw_shell);
371 	return (&save);
372 }
373 
374 static int login_attempts;	/* number of failed login attempts */
375 static int askpasswd;		/* had user command, ask for passwd */
376 static char curname[10];	/* current USER name */
377 
378 /*
379  * USER command.
380  * Sets global passwd pointer pw if named account exists and is acceptable;
381  * sets askpasswd if a PASS command is expected.  If logged in previously,
382  * need to reset state.  If name is "ftp" or "anonymous", the name is not in
383  * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
384  * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
385  * requesting login privileges.  Disallow anyone who does not have a standard
386  * shell as returned by getusershell().  Disallow anyone mentioned in the file
387  * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
388  */
389 void
390 user(name)
391 	char *name;
392 {
393 	register char *cp;
394 	char *shell;
395 
396 	if (logged_in) {
397 		if (guest) {
398 			reply(530, "Can't change user from guest login.");
399 			return;
400 		}
401 		end_login();
402 	}
403 
404 	guest = 0;
405 	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
406 		if (checkuser("ftp") || checkuser("anonymous"))
407 			reply(530, "User %s access denied.", name);
408 		else if ((pw = sgetpwnam("ftp")) != NULL) {
409 			guest = 1;
410 			askpasswd = 1;
411 			reply(331,
412 			    "Guest login ok, type your name as password.");
413 		} else
414 			reply(530, "User %s unknown.", name);
415 		if (!askpasswd && logging)
416 			syslog(LOG_NOTICE,
417 			    "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
418 		return;
419 	}
420 	if (pw = sgetpwnam(name)) {
421 		if ((shell = pw->pw_shell) == NULL || *shell == 0)
422 			shell = _PATH_BSHELL;
423 		while ((cp = getusershell()) != NULL)
424 			if (strcmp(cp, shell) == 0)
425 				break;
426 		endusershell();
427 
428 		if (cp == NULL || checkuser(name)) {
429 			reply(530, "User %s access denied.", name);
430 			if (logging)
431 				syslog(LOG_NOTICE,
432 				    "FTP LOGIN REFUSED FROM %s, %s",
433 				    remotehost, name);
434 			pw = (struct passwd *) NULL;
435 			return;
436 		}
437 	}
438 	if (logging)
439 		strncpy(curname, name, sizeof(curname)-1);
440 	reply(331, "Password required for %s.", name);
441 	askpasswd = 1;
442 	/*
443 	 * Delay before reading passwd after first failed
444 	 * attempt to slow down passwd-guessing programs.
445 	 */
446 	if (login_attempts)
447 		sleep((unsigned) login_attempts);
448 }
449 
450 /*
451  * Check if a user is in the file _PATH_FTPUSERS
452  */
453 static int
454 checkuser(name)
455 	char *name;
456 {
457 	register FILE *fd;
458 	register char *p;
459 	char line[BUFSIZ];
460 	int found = 0;
461 
462 	if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
463 		while (fgets(line, sizeof(line), fd) != NULL)
464 			if ((p = index(line, '\n')) != NULL) {
465 				*p = '\0';
466 				if (line[0] == '#')
467 					continue;
468 				if (strcmp(p, name) == 0) {
469 					found = 1;
470 					break;
471 				}
472 			}
473 		(void) fclose(fd);
474 	}
475 	return (found);
476 }
477 
478 /*
479  * Terminate login as previous user, if any, resetting state;
480  * used when USER command is given or login fails.
481  */
482 static void
483 end_login()
484 {
485 
486 	(void) seteuid((uid_t)0);
487 	if (logged_in)
488 		logwtmp(ttyline, "", "");
489 	pw = NULL;
490 	logged_in = 0;
491 	guest = 0;
492 }
493 
494 void
495 pass(passwd)
496 	char *passwd;
497 {
498 	char *xpasswd, *salt;
499 	FILE *fd;
500 
501 	if (logged_in || askpasswd == 0) {
502 		reply(503, "Login with USER first.");
503 		return;
504 	}
505 	askpasswd = 0;
506 	if (!guest) {		/* "ftp" is only account allowed no password */
507 		if (pw == NULL)
508 			salt = "xx";
509 		else
510 			salt = pw->pw_passwd;
511 		xpasswd = crypt(passwd, salt);
512 		/* The strcmp does not catch null passwords! */
513 		if (pw == NULL || *pw->pw_passwd == '\0' ||
514 		    strcmp(xpasswd, pw->pw_passwd)) {
515 			reply(530, "Login incorrect.");
516 			if (logging)
517 				syslog(LOG_NOTICE,
518 				    "FTP LOGIN FAILED FROM %s, %s",
519 				    remotehost, curname);
520 			pw = NULL;
521 			if (login_attempts++ >= 5) {
522 				syslog(LOG_NOTICE,
523 				    "repeated login failures from %s",
524 				    remotehost);
525 				exit(0);
526 			}
527 			return;
528 		}
529 	}
530 	login_attempts = 0;		/* this time successful */
531 	if (setegid((gid_t)pw->pw_gid) < 0) {
532 		reply(550, "Can't set gid.");
533 		return;
534 	}
535 	(void) initgroups(pw->pw_name, pw->pw_gid);
536 
537 	/* open wtmp before chroot */
538 	(void)sprintf(ttyline, "ftp%d", getpid());
539 	logwtmp(ttyline, pw->pw_name, remotehost);
540 	logged_in = 1;
541 
542 	if (guest) {
543 		/*
544 		 * We MUST do a chdir() after the chroot. Otherwise
545 		 * the old current directory will be accessible as "."
546 		 * outside the new root!
547 		 */
548 		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
549 			reply(550, "Can't set guest privileges.");
550 			goto bad;
551 		}
552 	} else if (chdir(pw->pw_dir) < 0) {
553 		if (chdir("/") < 0) {
554 			reply(530, "User %s: can't change directory to %s.",
555 			    pw->pw_name, pw->pw_dir);
556 			goto bad;
557 		} else
558 			lreply(230, "No directory! Logging in with home=/");
559 	}
560 	if (seteuid((uid_t)pw->pw_uid) < 0) {
561 		reply(550, "Can't set uid.");
562 		goto bad;
563 	}
564 	/*
565 	 * Display a login message, if it exists.
566 	 * N.B. reply(230,) must follow the message.
567 	 */
568 	if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
569 		char *cp, line[MAXLINE];
570 
571 		while (fgets(line, sizeof (line), fd) != NULL) {
572 			if ((cp = index(line, '\n')) != NULL)
573 				*cp = '\0';
574 			lreply(230, "%s", line);
575 		}
576 		(void) fflush(stdout);
577 		(void) fclose(fd);
578 	}
579 	if (guest) {
580 		reply(230, "Guest login ok, access restrictions apply.");
581 #ifdef SETPROCTITLE
582 		sprintf(proctitle, "%s: anonymous/%.*s", remotehost,
583 		    sizeof(proctitle) - sizeof(remotehost) -
584 		    sizeof(": anonymous/"), passwd);
585 		setproctitle(proctitle);
586 #endif /* SETPROCTITLE */
587 		if (logging)
588 			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
589 			    remotehost, passwd);
590 		home = "/";		/* guest home dir for globbing */
591 	} else {
592 		reply(230, "User %s logged in.", pw->pw_name);
593 #ifdef SETPROCTITLE
594 		sprintf(proctitle, "%s: %s", remotehost, pw->pw_name);
595 		setproctitle(proctitle);
596 #endif /* SETPROCTITLE */
597 		if (logging)
598 			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
599 			    remotehost, pw->pw_name);
600 		home = pw->pw_dir;	/* home dir for globbing */
601 	}
602 	(void) umask(defumask);
603 	return;
604 bad:
605 	/* Forget all about it... */
606 	end_login();
607 }
608 
609 void
610 retrieve(cmd, name)
611 	char *cmd, *name;
612 {
613 	FILE *fin, *dout;
614 	struct stat st;
615 	int (*closefunc)();
616 
617 	if (cmd == 0) {
618 		fin = fopen(name, "r"), closefunc = fclose;
619 		st.st_size = 0;
620 	} else {
621 		char line[BUFSIZ];
622 
623 		(void) sprintf(line, cmd, name), name = line;
624 		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
625 		st.st_size = -1;
626 		st.st_blksize = BUFSIZ;
627 	}
628 	if (fin == NULL) {
629 		if (errno != 0) {
630 			perror_reply(550, name);
631 			if (cmd == 0) {
632 				LOGCMD("get", name);
633 			}
634 		}
635 		return;
636 	}
637 	byte_count = -1;
638 	if (cmd == 0 &&
639 	    (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
640 		reply(550, "%s: not a plain file.", name);
641 		goto done;
642 	}
643 	if (restart_point) {
644 		if (type == TYPE_A) {
645 			register int i, n, c;
646 
647 			n = restart_point;
648 			i = 0;
649 			while (i++ < n) {
650 				if ((c=getc(fin)) == EOF) {
651 					perror_reply(550, name);
652 					goto done;
653 				}
654 				if (c == '\n')
655 					i++;
656 			}
657 		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
658 			perror_reply(550, name);
659 			goto done;
660 		}
661 	}
662 	dout = dataconn(name, st.st_size, "w");
663 	if (dout == NULL)
664 		goto done;
665 	send_data(fin, dout, st.st_blksize);
666 	(void) fclose(dout);
667 	data = -1;
668 	pdata = -1;
669 done:
670 	if (cmd == 0)
671 		LOGBYTES("get", name, byte_count);
672 	(*closefunc)(fin);
673 }
674 
675 void
676 store(name, mode, unique)
677 	char *name, *mode;
678 	int unique;
679 {
680 	FILE *fout, *din;
681 	struct stat st;
682 	int (*closefunc)();
683 
684 	if (unique && stat(name, &st) == 0 &&
685 	    (name = gunique(name)) == NULL) {
686 		LOGCMD(*mode == 'w' ? "put" : "append", name);
687 		return;
688 	}
689 
690 	if (restart_point)
691 		mode = "r+w";
692 	fout = fopen(name, mode);
693 	closefunc = fclose;
694 	if (fout == NULL) {
695 		perror_reply(553, name);
696 		LOGCMD(*mode == 'w' ? "put" : "append", name);
697 		return;
698 	}
699 	byte_count = -1;
700 	if (restart_point) {
701 		if (type == TYPE_A) {
702 			register int i, n, c;
703 
704 			n = restart_point;
705 			i = 0;
706 			while (i++ < n) {
707 				if ((c=getc(fout)) == EOF) {
708 					perror_reply(550, name);
709 					goto done;
710 				}
711 				if (c == '\n')
712 					i++;
713 			}
714 			/*
715 			 * We must do this seek to "current" position
716 			 * because we are changing from reading to
717 			 * writing.
718 			 */
719 			if (fseek(fout, 0L, L_INCR) < 0) {
720 				perror_reply(550, name);
721 				goto done;
722 			}
723 		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
724 			perror_reply(550, name);
725 			goto done;
726 		}
727 	}
728 	din = dataconn(name, (off_t)-1, "r");
729 	if (din == NULL)
730 		goto done;
731 	if (receive_data(din, fout) == 0) {
732 		if (unique)
733 			reply(226, "Transfer complete (unique file name:%s).",
734 			    name);
735 		else
736 			reply(226, "Transfer complete.");
737 	}
738 	(void) fclose(din);
739 	data = -1;
740 	pdata = -1;
741 done:
742 	LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
743 	(*closefunc)(fout);
744 }
745 
746 static FILE *
747 getdatasock(mode)
748 	char *mode;
749 {
750 	int s, on = 1, tries;
751 	int t;
752 
753 	if (data >= 0)
754 		return (fdopen(data, mode));
755 	(void) seteuid((uid_t)0);
756 	s = socket(AF_INET, SOCK_STREAM, 0);
757 	if (s < 0)
758 		goto bad;
759 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
760 	    (char *) &on, sizeof (on)) < 0)
761 		goto bad;
762 	/* anchor socket to avoid multi-homing problems */
763 	data_source.sin_family = AF_INET;
764 	data_source.sin_addr = ctrl_addr.sin_addr;
765 	for (tries = 1; ; tries++) {
766 		if (bind(s, (struct sockaddr *)&data_source,
767 		    sizeof (data_source)) >= 0)
768 			break;
769 		if (errno != EADDRINUSE || tries > 10)
770 			goto bad;
771 		sleep(tries);
772 	}
773 	(void) seteuid((uid_t)pw->pw_uid);
774 #ifdef IP_TOS
775 	on = IPTOS_THROUGHPUT;
776 	if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
777 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
778 #endif
779 	return (fdopen(s, mode));
780 bad:
781 	/* Return the real value of errno (close may change it) */
782 	t = errno;
783 	(void) seteuid((uid_t)pw->pw_uid);
784 	(void) close(s);
785 	errno = t;
786 	return (NULL);
787 }
788 
789 static FILE *
790 dataconn(name, size, mode)
791 	char *name;
792 	off_t size;
793 	char *mode;
794 {
795 	char sizebuf[32];
796 	FILE *file;
797 	int retry = 0, tos;
798 
799 	file_size = size;
800 	byte_count = 0;
801 	if (size != (off_t) -1)
802 		(void) sprintf (sizebuf, " (%qd bytes)", size);
803 	else
804 		(void) strcpy(sizebuf, "");
805 	if (pdata >= 0) {
806 		struct sockaddr_in from;
807 		int s, fromlen = sizeof(from);
808 
809 		s = accept(pdata, (struct sockaddr *)&from, &fromlen);
810 		if (s < 0) {
811 			reply(425, "Can't open data connection.");
812 			(void) close(pdata);
813 			pdata = -1;
814 			return(NULL);
815 		}
816 		(void) close(pdata);
817 		pdata = s;
818 #ifdef IP_TOS
819 		tos = IPTOS_LOWDELAY;
820 		(void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
821 		    sizeof(int));
822 #endif
823 		reply(150, "Opening %s mode data connection for '%s'%s.",
824 		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
825 		return(fdopen(pdata, mode));
826 	}
827 	if (data >= 0) {
828 		reply(125, "Using existing data connection for '%s'%s.",
829 		    name, sizebuf);
830 		usedefault = 1;
831 		return (fdopen(data, mode));
832 	}
833 	if (usedefault)
834 		data_dest = his_addr;
835 	usedefault = 1;
836 	file = getdatasock(mode);
837 	if (file == NULL) {
838 		reply(425, "Can't create data socket (%s,%d): %s.",
839 		    inet_ntoa(data_source.sin_addr),
840 		    ntohs(data_source.sin_port), strerror(errno));
841 		return (NULL);
842 	}
843 	data = fileno(file);
844 	while (connect(data, (struct sockaddr *)&data_dest,
845 	    sizeof (data_dest)) < 0) {
846 		if (errno == EADDRINUSE && retry < swaitmax) {
847 			sleep((unsigned) swaitint);
848 			retry += swaitint;
849 			continue;
850 		}
851 		perror_reply(425, "Can't build data connection");
852 		(void) fclose(file);
853 		data = -1;
854 		return (NULL);
855 	}
856 	reply(150, "Opening %s mode data connection for '%s'%s.",
857 	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
858 	return (file);
859 }
860 
861 /*
862  * Tranfer the contents of "instr" to "outstr" peer using the appropriate
863  * encapsulation of the data subject * to Mode, Structure, and Type.
864  *
865  * NB: Form isn't handled.
866  */
867 static void
868 send_data(instr, outstr, blksize)
869 	FILE *instr, *outstr;
870 	off_t blksize;
871 {
872 	register int c, cnt;
873 	register char *buf;
874 	int netfd, filefd;
875 
876 	transflag++;
877 	if (setjmp(urgcatch)) {
878 		transflag = 0;
879 		return;
880 	}
881 	switch (type) {
882 
883 	case TYPE_A:
884 		while ((c = getc(instr)) != EOF) {
885 			byte_count++;
886 			if (c == '\n') {
887 				if (ferror(outstr))
888 					goto data_err;
889 				(void) putc('\r', outstr);
890 			}
891 			(void) putc(c, outstr);
892 		}
893 		fflush(outstr);
894 		transflag = 0;
895 		if (ferror(instr))
896 			goto file_err;
897 		if (ferror(outstr))
898 			goto data_err;
899 		reply(226, "Transfer complete.");
900 		return;
901 
902 	case TYPE_I:
903 	case TYPE_L:
904 		if ((buf = malloc((u_int)blksize)) == NULL) {
905 			transflag = 0;
906 			perror_reply(451, "Local resource failure: malloc");
907 			return;
908 		}
909 		netfd = fileno(outstr);
910 		filefd = fileno(instr);
911 		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
912 		    write(netfd, buf, cnt) == cnt)
913 			byte_count += cnt;
914 		transflag = 0;
915 		(void)free(buf);
916 		if (cnt != 0) {
917 			if (cnt < 0)
918 				goto file_err;
919 			goto data_err;
920 		}
921 		reply(226, "Transfer complete.");
922 		return;
923 	default:
924 		transflag = 0;
925 		reply(550, "Unimplemented TYPE %d in send_data", type);
926 		return;
927 	}
928 
929 data_err:
930 	transflag = 0;
931 	perror_reply(426, "Data connection");
932 	return;
933 
934 file_err:
935 	transflag = 0;
936 	perror_reply(551, "Error on input file");
937 }
938 
939 /*
940  * Transfer data from peer to "outstr" using the appropriate encapulation of
941  * the data subject to Mode, Structure, and Type.
942  *
943  * N.B.: Form isn't handled.
944  */
945 static int
946 receive_data(instr, outstr)
947 	FILE *instr, *outstr;
948 {
949 	register int c;
950 	int cnt, bare_lfs = 0;
951 	char buf[BUFSIZ];
952 
953 	transflag++;
954 	if (setjmp(urgcatch)) {
955 		transflag = 0;
956 		return (-1);
957 	}
958 	switch (type) {
959 
960 	case TYPE_I:
961 	case TYPE_L:
962 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
963 			if (write(fileno(outstr), buf, cnt) != cnt)
964 				goto file_err;
965 			byte_count += cnt;
966 		}
967 		if (cnt < 0)
968 			goto data_err;
969 		transflag = 0;
970 		return (0);
971 
972 	case TYPE_E:
973 		reply(553, "TYPE E not implemented.");
974 		transflag = 0;
975 		return (-1);
976 
977 	case TYPE_A:
978 		while ((c = getc(instr)) != EOF) {
979 			byte_count++;
980 			if (c == '\n')
981 				bare_lfs++;
982 			while (c == '\r') {
983 				if (ferror(outstr))
984 					goto data_err;
985 				if ((c = getc(instr)) != '\n') {
986 					(void) putc ('\r', outstr);
987 					if (c == '\0' || c == EOF)
988 						goto contin2;
989 				}
990 			}
991 			(void) putc(c, outstr);
992 	contin2:	;
993 		}
994 		fflush(outstr);
995 		if (ferror(instr))
996 			goto data_err;
997 		if (ferror(outstr))
998 			goto file_err;
999 		transflag = 0;
1000 		if (bare_lfs) {
1001 			lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
1002 			printf("   File may not have transferred correctly.\r\n");
1003 		}
1004 		return (0);
1005 	default:
1006 		reply(550, "Unimplemented TYPE %d in receive_data", type);
1007 		transflag = 0;
1008 		return (-1);
1009 	}
1010 
1011 data_err:
1012 	transflag = 0;
1013 	perror_reply(426, "Data Connection");
1014 	return (-1);
1015 
1016 file_err:
1017 	transflag = 0;
1018 	perror_reply(452, "Error writing file");
1019 	return (-1);
1020 }
1021 
1022 void
1023 statfilecmd(filename)
1024 	char *filename;
1025 {
1026 	char line[BUFSIZ];
1027 	FILE *fin;
1028 	int c;
1029 
1030 	(void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
1031 	fin = ftpd_popen(line, "r");
1032 	lreply(211, "status of %s:", filename);
1033 	while ((c = getc(fin)) != EOF) {
1034 		if (c == '\n') {
1035 			if (ferror(stdout)){
1036 				perror_reply(421, "control connection");
1037 				(void) ftpd_pclose(fin);
1038 				dologout(1);
1039 				/* NOTREACHED */
1040 			}
1041 			if (ferror(fin)) {
1042 				perror_reply(551, filename);
1043 				(void) ftpd_pclose(fin);
1044 				return;
1045 			}
1046 			(void) putc('\r', stdout);
1047 		}
1048 		(void) putc(c, stdout);
1049 	}
1050 	(void) ftpd_pclose(fin);
1051 	reply(211, "End of Status");
1052 }
1053 
1054 void
1055 statcmd()
1056 {
1057 	struct sockaddr_in *sin;
1058 	u_char *a, *p;
1059 
1060 	lreply(211, "%s FTP server status:", hostname, version);
1061 	printf("     %s\r\n", version);
1062 	printf("     Connected to %s", remotehost);
1063 	if (!isdigit(remotehost[0]))
1064 		printf(" (%s)", inet_ntoa(his_addr.sin_addr));
1065 	printf("\r\n");
1066 	if (logged_in) {
1067 		if (guest)
1068 			printf("     Logged in anonymously\r\n");
1069 		else
1070 			printf("     Logged in as %s\r\n", pw->pw_name);
1071 	} else if (askpasswd)
1072 		printf("     Waiting for password\r\n");
1073 	else
1074 		printf("     Waiting for user name\r\n");
1075 	printf("     TYPE: %s", typenames[type]);
1076 	if (type == TYPE_A || type == TYPE_E)
1077 		printf(", FORM: %s", formnames[form]);
1078 	if (type == TYPE_L)
1079 #if NBBY == 8
1080 		printf(" %d", NBBY);
1081 #else
1082 		printf(" %d", bytesize);	/* need definition! */
1083 #endif
1084 	printf("; STRUcture: %s; transfer MODE: %s\r\n",
1085 	    strunames[stru], modenames[mode]);
1086 	if (data != -1)
1087 		printf("     Data connection open\r\n");
1088 	else if (pdata != -1) {
1089 		printf("     in Passive mode");
1090 		sin = &pasv_addr;
1091 		goto printaddr;
1092 	} else if (usedefault == 0) {
1093 		printf("     PORT");
1094 		sin = &data_dest;
1095 printaddr:
1096 		a = (u_char *) &sin->sin_addr;
1097 		p = (u_char *) &sin->sin_port;
1098 #define UC(b) (((int) b) & 0xff)
1099 		printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1100 			UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1101 #undef UC
1102 	} else
1103 		printf("     No data connection\r\n");
1104 	reply(211, "End of status");
1105 }
1106 
1107 void
1108 fatal(s)
1109 	char *s;
1110 {
1111 	reply(451, "Error in server: %s\n", s);
1112 	reply(221, "Closing connection due to server error.");
1113 	dologout(0);
1114 	/* NOTREACHED */
1115 }
1116 
1117 void
1118 #if __STDC__
1119 reply(int n, const char *fmt, ...)
1120 #else
1121 reply(n, fmt, va_alist)
1122 	int n;
1123 	char *fmt;
1124         va_dcl
1125 #endif
1126 {
1127 	va_list ap;
1128 #if __STDC__
1129 	va_start(ap, fmt);
1130 #else
1131 	va_start(ap);
1132 #endif
1133 	(void)printf("%d ", n);
1134 	(void)vprintf(fmt, ap);
1135 	(void)printf("\r\n");
1136 	(void)fflush(stdout);
1137 	if (debug) {
1138 		syslog(LOG_DEBUG, "<--- %d ", n);
1139 		vsyslog(LOG_DEBUG, fmt, ap);
1140 	}
1141 }
1142 
1143 void
1144 #if __STDC__
1145 lreply(int n, const char *fmt, ...)
1146 #else
1147 lreply(n, fmt, va_alist)
1148 	int n;
1149 	char *fmt;
1150         va_dcl
1151 #endif
1152 {
1153 	va_list ap;
1154 #if __STDC__
1155 	va_start(ap, fmt);
1156 #else
1157 	va_start(ap);
1158 #endif
1159 	(void)printf("%d- ", n);
1160 	(void)vprintf(fmt, ap);
1161 	(void)printf("\r\n");
1162 	(void)fflush(stdout);
1163 	if (debug) {
1164 		syslog(LOG_DEBUG, "<--- %d- ", n);
1165 		vsyslog(LOG_DEBUG, fmt, ap);
1166 	}
1167 }
1168 
1169 static void
1170 ack(s)
1171 	char *s;
1172 {
1173 	reply(250, "%s command successful.", s);
1174 }
1175 
1176 void
1177 nack(s)
1178 	char *s;
1179 {
1180 	reply(502, "%s command not implemented.", s);
1181 }
1182 
1183 /* ARGSUSED */
1184 char *
1185 yyerror(s)
1186 	char *s;
1187 {
1188 	char *cp;
1189 
1190 	if (cp = index(cbuf,'\n'))
1191 		*cp = '\0';
1192 	reply(500, "'%s': command not understood.", cbuf);
1193 }
1194 
1195 void
1196 delete(name)
1197 	char *name;
1198 {
1199 	struct stat st;
1200 
1201 	LOGCMD("delete", name);
1202 	if (stat(name, &st) < 0) {
1203 		perror_reply(550, name);
1204 		return;
1205 	}
1206 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
1207 		if (rmdir(name) < 0) {
1208 			perror_reply(550, name);
1209 			return;
1210 		}
1211 		goto done;
1212 	}
1213 	if (unlink(name) < 0) {
1214 		perror_reply(550, name);
1215 		return;
1216 	}
1217 done:
1218 	ack("DELE");
1219 }
1220 
1221 void
1222 cwd(path)
1223 	char *path;
1224 {
1225 	if (chdir(path) < 0)
1226 		perror_reply(550, path);
1227 	else
1228 		ack("CWD");
1229 }
1230 
1231 void
1232 makedir(name)
1233 	char *name;
1234 {
1235 	LOGCMD("mkdir", name);
1236 	if (mkdir(name, 0777) < 0)
1237 		perror_reply(550, name);
1238 	else
1239 		reply(257, "MKD command successful.");
1240 }
1241 
1242 void
1243 removedir(name)
1244 	char *name;
1245 {
1246 	LOGCMD("rmdir", name);
1247 	if (rmdir(name) < 0)
1248 		perror_reply(550, name);
1249 	else
1250 		ack("RMD");
1251 }
1252 
1253 void
1254 pwd()
1255 {
1256 	char path[MAXPATHLEN + 1];
1257 
1258 	if (getwd(path) == (char *)NULL)
1259 		reply(550, "%s.", path);
1260 	else
1261 		reply(257, "\"%s\" is current directory.", path);
1262 }
1263 
1264 char *
1265 renamefrom(name)
1266 	char *name;
1267 {
1268 	struct stat st;
1269 
1270 	if (stat(name, &st) < 0) {
1271 		perror_reply(550, name);
1272 		return ((char *)0);
1273 	}
1274 	reply(350, "File exists, ready for destination name");
1275 	return (name);
1276 }
1277 
1278 void
1279 renamecmd(from, to)
1280 	char *from, *to;
1281 {
1282 	LOGCMD2("rename", from, to);
1283 	if (rename(from, to) < 0)
1284 		perror_reply(550, "rename");
1285 	else
1286 		ack("RNTO");
1287 }
1288 
1289 static void
1290 dolog(sin)
1291 	struct sockaddr_in *sin;
1292 {
1293 	struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
1294 		sizeof (struct in_addr), AF_INET);
1295 
1296 	if (hp)
1297 		(void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
1298 	else
1299 		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
1300 		    sizeof (remotehost));
1301 #ifdef SETPROCTITLE
1302 	sprintf(proctitle, "%s: connected", remotehost);
1303 	setproctitle(proctitle);
1304 #endif /* SETPROCTITLE */
1305 
1306 	if (logging)
1307 		syslog(LOG_INFO, "connection from %s", remotehost);
1308 }
1309 
1310 /*
1311  * Record logout in wtmp file
1312  * and exit with supplied status.
1313  */
1314 void
1315 dologout(status)
1316 	int status;
1317 {
1318 	if (logged_in) {
1319 		(void) seteuid((uid_t)0);
1320 		logwtmp(ttyline, "", "");
1321 	}
1322 	/* beware of flushing buffers after a SIGPIPE */
1323 	_exit(status);
1324 }
1325 
1326 static void
1327 myoob(signo)
1328 	int signo;
1329 {
1330 	char *cp;
1331 
1332 	/* only process if transfer occurring */
1333 	if (!transflag)
1334 		return;
1335 	cp = tmpline;
1336 	if (getline(cp, 7, stdin) == NULL) {
1337 		reply(221, "You could at least say goodbye.");
1338 		dologout(0);
1339 	}
1340 	upper(cp);
1341 	if (strcmp(cp, "ABOR\r\n") == 0) {
1342 		tmpline[0] = '\0';
1343 		reply(426, "Transfer aborted. Data connection closed.");
1344 		reply(226, "Abort successful");
1345 		longjmp(urgcatch, 1);
1346 	}
1347 	if (strcmp(cp, "STAT\r\n") == 0) {
1348 		if (file_size != (off_t) -1)
1349 			reply(213, "Status: %qd of %qd bytes transferred",
1350 			    byte_count, file_size);
1351 		else
1352 			reply(213, "Status: %qd bytes transferred", byte_count);
1353 	}
1354 }
1355 
1356 /*
1357  * Note: a response of 425 is not mentioned as a possible response to
1358  *	the PASV command in RFC959. However, it has been blessed as
1359  *	a legitimate response by Jon Postel in a telephone conversation
1360  *	with Rick Adams on 25 Jan 89.
1361  */
1362 void
1363 passive()
1364 {
1365 	int len;
1366 	register char *p, *a;
1367 
1368 	pdata = socket(AF_INET, SOCK_STREAM, 0);
1369 	if (pdata < 0) {
1370 		perror_reply(425, "Can't open passive connection");
1371 		return;
1372 	}
1373 	pasv_addr = ctrl_addr;
1374 	pasv_addr.sin_port = 0;
1375 	(void) seteuid((uid_t)0);
1376 	if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
1377 		(void) seteuid((uid_t)pw->pw_uid);
1378 		goto pasv_error;
1379 	}
1380 	(void) seteuid((uid_t)pw->pw_uid);
1381 	len = sizeof(pasv_addr);
1382 	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
1383 		goto pasv_error;
1384 	if (listen(pdata, 1) < 0)
1385 		goto pasv_error;
1386 	a = (char *) &pasv_addr.sin_addr;
1387 	p = (char *) &pasv_addr.sin_port;
1388 
1389 #define UC(b) (((int) b) & 0xff)
1390 
1391 	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1392 		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1393 	return;
1394 
1395 pasv_error:
1396 	(void) close(pdata);
1397 	pdata = -1;
1398 	perror_reply(425, "Can't open passive connection");
1399 	return;
1400 }
1401 
1402 /*
1403  * Generate unique name for file with basename "local".
1404  * The file named "local" is already known to exist.
1405  * Generates failure reply on error.
1406  */
1407 static char *
1408 gunique(local)
1409 	char *local;
1410 {
1411 	static char new[MAXPATHLEN];
1412 	struct stat st;
1413 	int count;
1414 	char *cp;
1415 
1416 	cp = rindex(local, '/');
1417 	if (cp)
1418 		*cp = '\0';
1419 	if (stat(cp ? local : ".", &st) < 0) {
1420 		perror_reply(553, cp ? local : ".");
1421 		return((char *) 0);
1422 	}
1423 	if (cp)
1424 		*cp = '/';
1425 	(void) strcpy(new, local);
1426 	cp = new + strlen(new);
1427 	*cp++ = '.';
1428 	for (count = 1; count < 100; count++) {
1429 		(void)sprintf(cp, "%d", count);
1430 		if (stat(new, &st) < 0)
1431 			return(new);
1432 	}
1433 	reply(452, "Unique file name cannot be created.");
1434 	return(NULL);
1435 }
1436 
1437 /*
1438  * Format and send reply containing system error number.
1439  */
1440 void
1441 perror_reply(code, string)
1442 	int code;
1443 	char *string;
1444 {
1445 	reply(code, "%s: %s.", string, strerror(errno));
1446 }
1447 
1448 static char *onefile[] = {
1449 	"",
1450 	0
1451 };
1452 
1453 void
1454 send_file_list(whichfiles)
1455 	char *whichfiles;
1456 {
1457 	struct stat st;
1458 	DIR *dirp = NULL;
1459 	struct dirent *dir;
1460 	FILE *dout = NULL;
1461 	register char **dirlist, *dirname;
1462 	int simple = 0;
1463 
1464 	if (strpbrk(whichfiles, "~{[*?") != NULL) {
1465 		extern char *globerr;
1466 
1467 		globerr = NULL;
1468 		dirlist = ftpglob(whichfiles);
1469 		if (globerr != NULL) {
1470 			reply(550, globerr);
1471 			return;
1472 		} else if (dirlist == NULL) {
1473 			errno = ENOENT;
1474 			perror_reply(550, whichfiles);
1475 			return;
1476 		}
1477 	} else {
1478 		onefile[0] = whichfiles;
1479 		dirlist = onefile;
1480 		simple = 1;
1481 	}
1482 
1483 	if (setjmp(urgcatch)) {
1484 		transflag = 0;
1485 		return;
1486 	}
1487 	while (dirname = *dirlist++) {
1488 		if (stat(dirname, &st) < 0) {
1489 			/*
1490 			 * If user typed "ls -l", etc, and the client
1491 			 * used NLST, do what the user meant.
1492 			 */
1493 			if (dirname[0] == '-' && *dirlist == NULL &&
1494 			    transflag == 0) {
1495 				retrieve("/bin/ls %s", dirname);
1496 				return;
1497 			}
1498 			perror_reply(550, whichfiles);
1499 			if (dout != NULL) {
1500 				(void) fclose(dout);
1501 				transflag = 0;
1502 				data = -1;
1503 				pdata = -1;
1504 			}
1505 			return;
1506 		}
1507 
1508 		if ((st.st_mode&S_IFMT) == S_IFREG) {
1509 			if (dout == NULL) {
1510 				dout = dataconn("file list", (off_t)-1, "w");
1511 				if (dout == NULL)
1512 					return;
1513 				transflag++;
1514 			}
1515 			fprintf(dout, "%s%s\n", dirname,
1516 				type == TYPE_A ? "\r" : "");
1517 			byte_count += strlen(dirname) + 1;
1518 			continue;
1519 		} else if ((st.st_mode&S_IFMT) != S_IFDIR)
1520 			continue;
1521 
1522 		if ((dirp = opendir(dirname)) == NULL)
1523 			continue;
1524 
1525 		while ((dir = readdir(dirp)) != NULL) {
1526 			char nbuf[MAXPATHLEN];
1527 
1528 			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
1529 				continue;
1530 			if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1531 			    dir->d_namlen == 2)
1532 				continue;
1533 
1534 			sprintf(nbuf, "%s/%s", dirname, dir->d_name);
1535 
1536 			/*
1537 			 * We have to do a stat to insure it's
1538 			 * not a directory or special file.
1539 			 */
1540 			if (simple || (stat(nbuf, &st) == 0 &&
1541 			    (st.st_mode&S_IFMT) == S_IFREG)) {
1542 				if (dout == NULL) {
1543 					dout = dataconn("file list", (off_t)-1,
1544 						"w");
1545 					if (dout == NULL)
1546 						return;
1547 					transflag++;
1548 				}
1549 				if (nbuf[0] == '.' && nbuf[1] == '/')
1550 					fprintf(dout, "%s%s\n", &nbuf[2],
1551 						type == TYPE_A ? "\r" : "");
1552 				else
1553 					fprintf(dout, "%s%s\n", nbuf,
1554 						type == TYPE_A ? "\r" : "");
1555 				byte_count += strlen(nbuf) + 1;
1556 			}
1557 		}
1558 		(void) closedir(dirp);
1559 	}
1560 
1561 	if (dout == NULL)
1562 		reply(550, "No files found.");
1563 	else if (ferror(dout) != 0)
1564 		perror_reply(550, "Data connection");
1565 	else
1566 		reply(226, "Transfer complete.");
1567 
1568 	transflag = 0;
1569 	if (dout != NULL)
1570 		(void) fclose(dout);
1571 	data = -1;
1572 	pdata = -1;
1573 }
1574 
1575 #ifdef SETPROCTITLE
1576 /*
1577  * Clobber argv so ps will show what we're doing.  (Stolen from sendmail.)
1578  * Warning, since this is usually started from inetd.conf, it often doesn't
1579  * have much of an environment or arglist to overwrite.
1580  */
1581 void
1582 #if __STDC__
1583 setproctitle(const char *fmt, ...)
1584 #else
1585 setproctitle(fmt, va_alist)
1586 	char *fmt;
1587         va_dcl
1588 #endif
1589 {
1590 	register char *p, *bp, ch;
1591 	register int i;
1592 	va_list ap;
1593 	char buf[BUFSIZ];
1594 #if __STDC__
1595 	va_start(ap, fmt);
1596 #else
1597 	va_start(ap);
1598 #endif
1599 	(void)vsnprintf(buf, sizeof(buf), fmt, ap);
1600 
1601 	/* make ps print our process name */
1602 	p = Argv[0];
1603 	*p++ = '-';
1604 
1605 	i = strlen(buf);
1606 	if (i > LastArgv - p - 2) {
1607 		i = LastArgv - p - 2;
1608 		buf[i] = '\0';
1609 	}
1610 	bp = buf;
1611 	while (ch = *bp++)
1612 		if (ch != '\n' && ch != '\r')
1613 			*p++ = ch;
1614 	while (p < LastArgv)
1615 		*p++ = ' ';
1616 }
1617 #endif /* SETPROCTITLE */
1618