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