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