ftpd.c revision 1.17 1 /* $NetBSD: ftpd.c,v 1.17 1997/03/28 21:55:41 cjs 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.5 (Berkeley) 4/28/95";
45 #else
46 static char rcsid[] = "$NetBSD: ftpd.c,v 1.17 1997/03/28 21:55:41 cjs 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_len = sizeof(struct sockaddr_in);
830 data_source.sin_family = AF_INET;
831 data_source.sin_addr = ctrl_addr.sin_addr;
832 for (tries = 1; ; tries++) {
833 if (bind(s, (struct sockaddr *)&data_source,
834 sizeof(data_source)) >= 0)
835 break;
836 if (errno != EADDRINUSE || tries > 10)
837 goto bad;
838 sleep(tries);
839 }
840 (void) seteuid((uid_t)pw->pw_uid);
841 #ifdef IP_TOS
842 on = IPTOS_THROUGHPUT;
843 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
844 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
845 #endif
846 return (fdopen(s, mode));
847 bad:
848 /* Return the real value of errno (close may change it) */
849 t = errno;
850 (void) seteuid((uid_t)pw->pw_uid);
851 (void) close(s);
852 errno = t;
853 return (NULL);
854 }
855
856 static FILE *
857 dataconn(name, size, mode)
858 char *name;
859 off_t size;
860 char *mode;
861 {
862 char sizebuf[32];
863 FILE *file;
864 int retry = 0, tos;
865
866 file_size = size;
867 byte_count = 0;
868 if (size != (off_t) -1)
869 (void) sprintf(sizebuf, " (%qd bytes)", size);
870 else
871 (void) strcpy(sizebuf, "");
872 if (pdata >= 0) {
873 struct sockaddr_in from;
874 int s, fromlen = sizeof(from);
875
876 s = accept(pdata, (struct sockaddr *)&from, &fromlen);
877 if (s < 0) {
878 reply(425, "Can't open data connection.");
879 (void) close(pdata);
880 pdata = -1;
881 return (NULL);
882 }
883 (void) close(pdata);
884 pdata = s;
885 #ifdef IP_TOS
886 tos = IPTOS_THROUGHPUT;
887 (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
888 sizeof(int));
889 #endif
890 reply(150, "Opening %s mode data connection for '%s'%s.",
891 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
892 return (fdopen(pdata, mode));
893 }
894 if (data >= 0) {
895 reply(125, "Using existing data connection for '%s'%s.",
896 name, sizebuf);
897 usedefault = 1;
898 return (fdopen(data, mode));
899 }
900 if (usedefault)
901 data_dest = his_addr;
902 usedefault = 1;
903 file = getdatasock(mode);
904 if (file == NULL) {
905 reply(425, "Can't create data socket (%s,%d): %s.",
906 inet_ntoa(data_source.sin_addr),
907 ntohs(data_source.sin_port), strerror(errno));
908 return (NULL);
909 }
910 data = fileno(file);
911 while (connect(data, (struct sockaddr *)&data_dest,
912 sizeof(data_dest)) < 0) {
913 if (errno == EADDRINUSE && retry < swaitmax) {
914 sleep((unsigned) swaitint);
915 retry += swaitint;
916 continue;
917 }
918 perror_reply(425, "Can't build data connection");
919 (void) fclose(file);
920 data = -1;
921 return (NULL);
922 }
923 reply(150, "Opening %s mode data connection for '%s'%s.",
924 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
925 return (file);
926 }
927
928 /*
929 * Tranfer the contents of "instr" to "outstr" peer using the appropriate
930 * encapsulation of the data subject * to Mode, Structure, and Type.
931 *
932 * NB: Form isn't handled.
933 */
934 static void
935 send_data(instr, outstr, blksize)
936 FILE *instr, *outstr;
937 off_t blksize;
938 {
939 int c, cnt, filefd, netfd;
940 char *buf;
941
942 transflag++;
943 if (setjmp(urgcatch)) {
944 transflag = 0;
945 return;
946 }
947 switch (type) {
948
949 case TYPE_A:
950 while ((c = getc(instr)) != EOF) {
951 byte_count++;
952 if (c == '\n') {
953 if (ferror(outstr))
954 goto data_err;
955 (void) putc('\r', outstr);
956 }
957 (void) putc(c, outstr);
958 }
959 fflush(outstr);
960 transflag = 0;
961 if (ferror(instr))
962 goto file_err;
963 if (ferror(outstr))
964 goto data_err;
965 reply(226, "Transfer complete.");
966 return;
967
968 case TYPE_I:
969 case TYPE_L:
970 if ((buf = malloc((u_int)blksize)) == NULL) {
971 transflag = 0;
972 perror_reply(451, "Local resource failure: malloc");
973 return;
974 }
975 netfd = fileno(outstr);
976 filefd = fileno(instr);
977 while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
978 write(netfd, buf, cnt) == cnt)
979 byte_count += cnt;
980 transflag = 0;
981 (void)free(buf);
982 if (cnt != 0) {
983 if (cnt < 0)
984 goto file_err;
985 goto data_err;
986 }
987 reply(226, "Transfer complete.");
988 return;
989 default:
990 transflag = 0;
991 reply(550, "Unimplemented TYPE %d in send_data", type);
992 return;
993 }
994
995 data_err:
996 transflag = 0;
997 perror_reply(426, "Data connection");
998 return;
999
1000 file_err:
1001 transflag = 0;
1002 perror_reply(551, "Error on input file");
1003 }
1004
1005 /*
1006 * Transfer data from peer to "outstr" using the appropriate encapulation of
1007 * the data subject to Mode, Structure, and Type.
1008 *
1009 * N.B.: Form isn't handled.
1010 */
1011 static int
1012 receive_data(instr, outstr)
1013 FILE *instr, *outstr;
1014 {
1015 int c;
1016 int cnt, bare_lfs = 0;
1017 char buf[BUFSIZ];
1018
1019 transflag++;
1020 if (setjmp(urgcatch)) {
1021 transflag = 0;
1022 return (-1);
1023 }
1024 switch (type) {
1025
1026 case TYPE_I:
1027 case TYPE_L:
1028 while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
1029 if (write(fileno(outstr), buf, cnt) != cnt)
1030 goto file_err;
1031 byte_count += cnt;
1032 }
1033 if (cnt < 0)
1034 goto data_err;
1035 transflag = 0;
1036 return (0);
1037
1038 case TYPE_E:
1039 reply(553, "TYPE E not implemented.");
1040 transflag = 0;
1041 return (-1);
1042
1043 case TYPE_A:
1044 while ((c = getc(instr)) != EOF) {
1045 byte_count++;
1046 if (c == '\n')
1047 bare_lfs++;
1048 while (c == '\r') {
1049 if (ferror(outstr))
1050 goto data_err;
1051 if ((c = getc(instr)) != '\n') {
1052 (void) putc ('\r', outstr);
1053 if (c == '\0' || c == EOF)
1054 goto contin2;
1055 }
1056 }
1057 (void) putc(c, outstr);
1058 contin2: ;
1059 }
1060 fflush(outstr);
1061 if (ferror(instr))
1062 goto data_err;
1063 if (ferror(outstr))
1064 goto file_err;
1065 transflag = 0;
1066 if (bare_lfs) {
1067 lreply(226,
1068 "WARNING! %d bare linefeeds received in ASCII mode",
1069 bare_lfs);
1070 (void)printf(" File may not have transferred correctly.\r\n");
1071 }
1072 return (0);
1073 default:
1074 reply(550, "Unimplemented TYPE %d in receive_data", type);
1075 transflag = 0;
1076 return (-1);
1077 }
1078
1079 data_err:
1080 transflag = 0;
1081 perror_reply(426, "Data Connection");
1082 return (-1);
1083
1084 file_err:
1085 transflag = 0;
1086 perror_reply(452, "Error writing file");
1087 return (-1);
1088 }
1089
1090 void
1091 statfilecmd(filename)
1092 char *filename;
1093 {
1094 FILE *fin;
1095 int c;
1096 char line[LINE_MAX];
1097
1098 (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
1099 fin = ftpd_popen(line, "r");
1100 lreply(211, "status of %s:", filename);
1101 while ((c = getc(fin)) != EOF) {
1102 if (c == '\n') {
1103 if (ferror(stdout)){
1104 perror_reply(421, "control connection");
1105 (void) ftpd_pclose(fin);
1106 dologout(1);
1107 /* NOTREACHED */
1108 }
1109 if (ferror(fin)) {
1110 perror_reply(551, filename);
1111 (void) ftpd_pclose(fin);
1112 return;
1113 }
1114 (void) putc('\r', stdout);
1115 }
1116 (void) putc(c, stdout);
1117 }
1118 (void) ftpd_pclose(fin);
1119 reply(211, "End of Status");
1120 }
1121
1122 void
1123 statcmd()
1124 {
1125 struct sockaddr_in *sin;
1126 u_char *a, *p;
1127
1128 lreply(211, "%s FTP server status:", hostname, version);
1129 printf(" %s\r\n", version);
1130 printf(" Connected to %s", remotehost);
1131 if (!isdigit(remotehost[0]))
1132 printf(" (%s)", inet_ntoa(his_addr.sin_addr));
1133 printf("\r\n");
1134 if (logged_in) {
1135 if (guest)
1136 printf(" Logged in anonymously\r\n");
1137 else
1138 printf(" Logged in as %s\r\n", pw->pw_name);
1139 } else if (askpasswd)
1140 printf(" Waiting for password\r\n");
1141 else
1142 printf(" Waiting for user name\r\n");
1143 printf(" TYPE: %s", typenames[type]);
1144 if (type == TYPE_A || type == TYPE_E)
1145 printf(", FORM: %s", formnames[form]);
1146 if (type == TYPE_L)
1147 #if NBBY == 8
1148 printf(" %d", NBBY);
1149 #else
1150 printf(" %d", bytesize); /* need definition! */
1151 #endif
1152 printf("; STRUcture: %s; transfer MODE: %s\r\n",
1153 strunames[stru], modenames[mode]);
1154 if (data != -1)
1155 printf(" Data connection open\r\n");
1156 else if (pdata != -1) {
1157 printf(" in Passive mode");
1158 sin = &pasv_addr;
1159 goto printaddr;
1160 } else if (usedefault == 0) {
1161 printf(" PORT");
1162 sin = &data_dest;
1163 printaddr:
1164 a = (u_char *) &sin->sin_addr;
1165 p = (u_char *) &sin->sin_port;
1166 #define UC(b) (((int) b) & 0xff)
1167 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1168 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1169 #undef UC
1170 } else
1171 printf(" No data connection\r\n");
1172 reply(211, "End of status");
1173 }
1174
1175 void
1176 fatal(s)
1177 char *s;
1178 {
1179
1180 reply(451, "Error in server: %s\n", s);
1181 reply(221, "Closing connection due to server error.");
1182 dologout(0);
1183 /* NOTREACHED */
1184 }
1185
1186 void
1187 #if __STDC__
1188 reply(int n, const char *fmt, ...)
1189 #else
1190 reply(n, fmt, va_alist)
1191 int n;
1192 char *fmt;
1193 va_dcl
1194 #endif
1195 {
1196 va_list ap;
1197 #if __STDC__
1198 va_start(ap, fmt);
1199 #else
1200 va_start(ap);
1201 #endif
1202 (void)printf("%d ", n);
1203 (void)vprintf(fmt, ap);
1204 (void)printf("\r\n");
1205 (void)fflush(stdout);
1206 if (debug) {
1207 syslog(LOG_DEBUG, "<--- %d ", n);
1208 vsyslog(LOG_DEBUG, fmt, ap);
1209 }
1210 }
1211
1212 void
1213 #if __STDC__
1214 lreply(int n, const char *fmt, ...)
1215 #else
1216 lreply(n, fmt, va_alist)
1217 int n;
1218 char *fmt;
1219 va_dcl
1220 #endif
1221 {
1222 va_list ap;
1223 #if __STDC__
1224 va_start(ap, fmt);
1225 #else
1226 va_start(ap);
1227 #endif
1228 (void)printf("%d- ", n);
1229 (void)vprintf(fmt, ap);
1230 (void)printf("\r\n");
1231 (void)fflush(stdout);
1232 if (debug) {
1233 syslog(LOG_DEBUG, "<--- %d- ", n);
1234 vsyslog(LOG_DEBUG, fmt, ap);
1235 }
1236 }
1237
1238 static void
1239 ack(s)
1240 char *s;
1241 {
1242
1243 reply(250, "%s command successful.", s);
1244 }
1245
1246 void
1247 nack(s)
1248 char *s;
1249 {
1250
1251 reply(502, "%s command not implemented.", s);
1252 }
1253
1254 /* ARGSUSED */
1255 void
1256 yyerror(s)
1257 char *s;
1258 {
1259 char *cp;
1260
1261 if (cp = strchr(cbuf,'\n'))
1262 *cp = '\0';
1263 reply(500, "'%s': command not understood.", cbuf);
1264 }
1265
1266 void
1267 delete(name)
1268 char *name;
1269 {
1270 struct stat st;
1271
1272 LOGCMD("delete", name);
1273 if (stat(name, &st) < 0) {
1274 perror_reply(550, name);
1275 return;
1276 }
1277 if ((st.st_mode&S_IFMT) == S_IFDIR) {
1278 if (rmdir(name) < 0) {
1279 perror_reply(550, name);
1280 return;
1281 }
1282 goto done;
1283 }
1284 if (unlink(name) < 0) {
1285 perror_reply(550, name);
1286 return;
1287 }
1288 done:
1289 ack("DELE");
1290 }
1291
1292 void
1293 cwd(path)
1294 char *path;
1295 {
1296
1297 if (chdir(path) < 0)
1298 perror_reply(550, path);
1299 else
1300 ack("CWD");
1301 }
1302
1303 void
1304 makedir(name)
1305 char *name;
1306 {
1307
1308 LOGCMD("mkdir", name);
1309 if (mkdir(name, 0777) < 0)
1310 perror_reply(550, name);
1311 else
1312 reply(257, "MKD command successful.");
1313 }
1314
1315 void
1316 removedir(name)
1317 char *name;
1318 {
1319
1320 LOGCMD("rmdir", name);
1321 if (rmdir(name) < 0)
1322 perror_reply(550, name);
1323 else
1324 ack("RMD");
1325 }
1326
1327 void
1328 pwd()
1329 {
1330 char path[MAXPATHLEN + 1];
1331
1332 if (getwd(path) == (char *)NULL)
1333 reply(550, "%s.", path);
1334 else
1335 reply(257, "\"%s\" is current directory.", path);
1336 }
1337
1338 char *
1339 renamefrom(name)
1340 char *name;
1341 {
1342 struct stat st;
1343
1344 if (stat(name, &st) < 0) {
1345 perror_reply(550, name);
1346 return ((char *)0);
1347 }
1348 reply(350, "File exists, ready for destination name");
1349 return (name);
1350 }
1351
1352 void
1353 renamecmd(from, to)
1354 char *from, *to;
1355 {
1356
1357 LOGCMD2("rename", from, to);
1358 if (rename(from, to) < 0)
1359 perror_reply(550, "rename");
1360 else
1361 ack("RNTO");
1362 }
1363
1364 static void
1365 dolog(sin)
1366 struct sockaddr_in *sin;
1367 {
1368 struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
1369 sizeof(struct in_addr), AF_INET);
1370
1371 if (hp)
1372 (void) strncpy(remotehost, hp->h_name, sizeof(remotehost));
1373 else
1374 (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
1375 sizeof(remotehost));
1376 #ifdef HASSETPROCTITLE
1377 snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
1378 setproctitle(proctitle);
1379 #endif /* HASSETPROCTITLE */
1380
1381 if (logging)
1382 syslog(LOG_INFO, "connection from %s", remotehost);
1383 }
1384
1385 /*
1386 * Record logout in wtmp file
1387 * and exit with supplied status.
1388 */
1389 void
1390 dologout(status)
1391 int status;
1392 {
1393 /*
1394 * Prevent reception of SIGURG from resulting in a resumption
1395 * back to the main program loop.
1396 */
1397 transflag = 0;
1398
1399 if (logged_in) {
1400 (void) seteuid((uid_t)0);
1401 logwtmp(ttyline, "", "");
1402 #if defined(KERBEROS)
1403 if (!notickets && krbtkfile_env)
1404 unlink(krbtkfile_env);
1405 #endif
1406 }
1407 /* beware of flushing buffers after a SIGPIPE */
1408 _exit(status);
1409 }
1410
1411 static void
1412 myoob(signo)
1413 int signo;
1414 {
1415 char *cp;
1416
1417 /* only process if transfer occurring */
1418 if (!transflag)
1419 return;
1420 cp = tmpline;
1421 if (getline(cp, 7, stdin) == NULL) {
1422 reply(221, "You could at least say goodbye.");
1423 dologout(0);
1424 }
1425 upper(cp);
1426 if (strcmp(cp, "ABOR\r\n") == 0) {
1427 tmpline[0] = '\0';
1428 reply(426, "Transfer aborted. Data connection closed.");
1429 reply(226, "Abort successful");
1430 longjmp(urgcatch, 1);
1431 }
1432 if (strcmp(cp, "STAT\r\n") == 0) {
1433 if (file_size != (off_t) -1)
1434 reply(213, "Status: %qd of %qd bytes transferred",
1435 byte_count, file_size);
1436 else
1437 reply(213, "Status: %qd bytes transferred", byte_count);
1438 }
1439 }
1440
1441 /*
1442 * Note: a response of 425 is not mentioned as a possible response to
1443 * the PASV command in RFC959. However, it has been blessed as
1444 * a legitimate response by Jon Postel in a telephone conversation
1445 * with Rick Adams on 25 Jan 89.
1446 */
1447 void
1448 passive()
1449 {
1450 int len;
1451 char *p, *a;
1452
1453 pdata = socket(AF_INET, SOCK_STREAM, 0);
1454 if (pdata < 0) {
1455 perror_reply(425, "Can't open passive connection");
1456 return;
1457 }
1458 pasv_addr = ctrl_addr;
1459 pasv_addr.sin_port = 0;
1460 (void) seteuid((uid_t)0);
1461 if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
1462 (void) seteuid((uid_t)pw->pw_uid);
1463 goto pasv_error;
1464 }
1465 (void) seteuid((uid_t)pw->pw_uid);
1466 len = sizeof(pasv_addr);
1467 if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
1468 goto pasv_error;
1469 if (listen(pdata, 1) < 0)
1470 goto pasv_error;
1471 a = (char *) &pasv_addr.sin_addr;
1472 p = (char *) &pasv_addr.sin_port;
1473
1474 #define UC(b) (((int) b) & 0xff)
1475
1476 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1477 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1478 return;
1479
1480 pasv_error:
1481 (void) close(pdata);
1482 pdata = -1;
1483 perror_reply(425, "Can't open passive connection");
1484 return;
1485 }
1486
1487 /*
1488 * Generate unique name for file with basename "local".
1489 * The file named "local" is already known to exist.
1490 * Generates failure reply on error.
1491 */
1492 static char *
1493 gunique(local)
1494 char *local;
1495 {
1496 static char new[MAXPATHLEN];
1497 struct stat st;
1498 int count;
1499 char *cp;
1500
1501 cp = strrchr(local, '/');
1502 if (cp)
1503 *cp = '\0';
1504 if (stat(cp ? local : ".", &st) < 0) {
1505 perror_reply(553, cp ? local : ".");
1506 return ((char *) 0);
1507 }
1508 if (cp)
1509 *cp = '/';
1510 (void) strcpy(new, local);
1511 cp = new + strlen(new);
1512 *cp++ = '.';
1513 for (count = 1; count < 100; count++) {
1514 (void)sprintf(cp, "%d", count);
1515 if (stat(new, &st) < 0)
1516 return (new);
1517 }
1518 reply(452, "Unique file name cannot be created.");
1519 return (NULL);
1520 }
1521
1522 /*
1523 * Format and send reply containing system error number.
1524 */
1525 void
1526 perror_reply(code, string)
1527 int code;
1528 char *string;
1529 {
1530
1531 reply(code, "%s: %s.", string, strerror(errno));
1532 }
1533
1534 static char *onefile[] = {
1535 "",
1536 0
1537 };
1538
1539 void
1540 send_file_list(whichf)
1541 char *whichf;
1542 {
1543 struct stat st;
1544 DIR *dirp = NULL;
1545 struct dirent *dir;
1546 FILE *dout = NULL;
1547 char **dirlist, *dirname;
1548 int simple = 0;
1549 int freeglob = 0;
1550 glob_t gl;
1551
1552 if (strpbrk(whichf, "~{[*?") != NULL) {
1553 int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
1554
1555 memset(&gl, 0, sizeof(gl));
1556 freeglob = 1;
1557 if (glob(whichf, flags, 0, &gl)) {
1558 reply(550, "not found");
1559 goto out;
1560 } else if (gl.gl_pathc == 0) {
1561 errno = ENOENT;
1562 perror_reply(550, whichf);
1563 goto out;
1564 }
1565 dirlist = gl.gl_pathv;
1566 } else {
1567 onefile[0] = whichf;
1568 dirlist = onefile;
1569 simple = 1;
1570 }
1571
1572 if (setjmp(urgcatch)) {
1573 transflag = 0;
1574 goto out;
1575 }
1576 while (dirname = *dirlist++) {
1577 if (stat(dirname, &st) < 0) {
1578 /*
1579 * If user typed "ls -l", etc, and the client
1580 * used NLST, do what the user meant.
1581 */
1582 if (dirname[0] == '-' && *dirlist == NULL &&
1583 transflag == 0) {
1584 retrieve("/bin/ls %s", dirname);
1585 goto out;
1586 }
1587 perror_reply(550, whichf);
1588 if (dout != NULL) {
1589 (void) fclose(dout);
1590 transflag = 0;
1591 data = -1;
1592 pdata = -1;
1593 }
1594 goto out;
1595 }
1596
1597 if (S_ISREG(st.st_mode)) {
1598 if (dout == NULL) {
1599 dout = dataconn("file list", (off_t)-1, "w");
1600 if (dout == NULL)
1601 goto out;
1602 transflag++;
1603 }
1604 fprintf(dout, "%s%s\n", dirname,
1605 type == TYPE_A ? "\r" : "");
1606 byte_count += strlen(dirname) + 1;
1607 continue;
1608 } else if (!S_ISDIR(st.st_mode))
1609 continue;
1610
1611 if ((dirp = opendir(dirname)) == NULL)
1612 continue;
1613
1614 while ((dir = readdir(dirp)) != NULL) {
1615 char nbuf[MAXPATHLEN];
1616
1617 if (dir->d_name[0] == '.' && dir->d_namlen == 1)
1618 continue;
1619 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1620 dir->d_namlen == 2)
1621 continue;
1622
1623 sprintf(nbuf, "%s/%s", dirname, dir->d_name);
1624
1625 /*
1626 * We have to do a stat to insure it's
1627 * not a directory or special file.
1628 */
1629 if (simple || (stat(nbuf, &st) == 0 &&
1630 S_ISREG(st.st_mode))) {
1631 if (dout == NULL) {
1632 dout = dataconn("file list", (off_t)-1,
1633 "w");
1634 if (dout == NULL)
1635 goto out;
1636 transflag++;
1637 }
1638 if (nbuf[0] == '.' && nbuf[1] == '/')
1639 fprintf(dout, "%s%s\n", &nbuf[2],
1640 type == TYPE_A ? "\r" : "");
1641 else
1642 fprintf(dout, "%s%s\n", nbuf,
1643 type == TYPE_A ? "\r" : "");
1644 byte_count += strlen(nbuf) + 1;
1645 }
1646 }
1647 (void) closedir(dirp);
1648 }
1649
1650 if (dout == NULL)
1651 reply(550, "No files found.");
1652 else if (ferror(dout) != 0)
1653 perror_reply(550, "Data connection");
1654 else
1655 reply(226, "Transfer complete.");
1656
1657 transflag = 0;
1658 if (dout != NULL)
1659 (void) fclose(dout);
1660 data = -1;
1661 pdata = -1;
1662 out:
1663 if (freeglob) {
1664 freeglob = 0;
1665 globfree(&gl);
1666 }
1667 }
1668