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