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