ftpd.c revision 1.14 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