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