opieftpd.c revision 39012
1235783Skib/* opieftpd.c: Main program for an FTP daemon.
2235783Skib
3235783Skib%%% portions-copyright-cmetz-96
4235783SkibPortions of this software are Copyright 1996-1997 by Craig Metz, All Rights
5235783SkibReserved. The Inner Net License Version 2 applies to these portions of
6235783Skibthe software.
7235783SkibYou should have received a copy of the license with this software. If
8235783Skibyou didn't get a copy, you may request one from <license@inner.net>.
9235783Skib
10235783SkibPortions of this software are Copyright 1995 by Randall Atkinson and Dan
11235783SkibMcDonald, All Rights Reserved. All Rights under this copyright are assigned
12235783Skibto the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13235783SkibLicense Agreement applies to this software.
14235783Skib
15235783Skib	History:
16235783Skib
17235783Skib	Modified by cmetz for OPIE 2.31. Merged in some 4.4BSD-Lite changes.
18235783Skib		Merged in a security fix to BSD-derived ftpds.
19235783Skib	Modified by cmetz for OPIE 2.3. Fixed the filename at the top.
20235783Skib		Moved LS_COMMAND here.
21235783Skib	Modified by cmetz for OPIE 2.2. Use FUNCTION definition et al.
22235783Skib                Removed useless strings (I don't think that removing the
23235783Skib                ucb copyright one is a problem -- please let me know if
24235783Skib                I'm wrong). Changed default CMASK to 077. Removed random
25235783Skib                comments. Use ANSI stdargs for reply/lreply if we can,
26235783Skib                added stdargs version of reply/lreply. Don't declare the
27235783Skib                tos variable unless IP_TOS defined. Include stdargs headers
28235783Skib                early. More headers ifdefed. Made everything static.
29235783Skib                Got rid of gethostname() call and use of hostname. Pared
30235783Skib                down status response for places where header files frequently
31235783Skib                cause trouble. Made logging of user logins (ala -l)
32235783Skib                non-optional. Moved reply()/lrepy(). Fixed some prototypes.
33235783Skib	Modified at NRL for OPIE 2.1. Added declaration of envp. Discard
34235783Skib	        result of opiechallenge (allows access control to work).
35235783Skib		Added patches for AIX. Symbol changes for autoconf.
36235783Skib        Modified at NRL for OPIE 2.01. Changed password lookup handling
37235783Skib                to avoid problems with drain-bamaged shadow password packages.
38235783Skib                Properly handle internal state for anonymous FTP. Unlock
39235783Skib                user accounts properly if login fails because of /etc/shells.
40235783Skib                Make sure to close syslog by function to avoid problems with
41235783Skib                drain bamaged syslog implementations.
42235783Skib	Modified at NRL for OPIE 2.0.
43235783Skib	Originally from BSD Net/2.
44235783Skib
45235783Skib	        There is some really, really ugly code in here.
46235783Skib*/
47235783Skib/*
48235783Skib * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
49235783Skib * All rights reserved.
50235783Skib *
51235783Skib * Redistribution and use in source and binary forms, with or without
52235783Skib * modification, are permitted provided that the following conditions
53235783Skib * are met:
54235783Skib * 1. Redistributions of source code must retain the above copyright
55235783Skib *    notice, this list of conditions and the following disclaimer.
56235783Skib * 2. Redistributions in binary form must reproduce the above copyright
57235783Skib *    notice, this list of conditions and the following disclaimer in the
58235783Skib *    documentation and/or other materials provided with the distribution.
59235783Skib * 3. All advertising materials mentioning features or use of this software
60235783Skib *    must display the following acknowledgement:
61235783Skib *      This product includes software developed by the University of
62235783Skib *      California, Berkeley and its contributors.
63235783Skib * 4. Neither the name of the University nor the names of its contributors
64235783Skib *    may be used to endorse or promote products derived from this software
65235783Skib *    without specific prior written permission.
66235783Skib *
67235783Skib * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
68235783Skib * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
69235783Skib * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
70235783Skib * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
71235783Skib * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
72235783Skib * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
73235783Skib * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
74235783Skib * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
75235783Skib * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
76235783Skib * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
77235783Skib * SUCH DAMAGE.
78235783Skib */
79235783Skib
80235783Skib#include "opie_cfg.h"
81235783Skib
82235783Skib#if HAVE_ANSISTDARG
83235783Skib#include <stdarg.h>
84235783Skib#endif /* HAVE_ANSISTDARG */
85235783Skib
86235783Skib/*
87235783Skib * FTP server.
88235783Skib */
89235783Skib
90235783Skib#if HAVE_SYS_PARAM_H
91235783Skib#include <sys/param.h>
92235783Skib#endif /* HAVE_SYS_PARAM_H */
93235783Skib#include <sys/stat.h>
94235783Skib/* #include <sys/ioctl.h> */
95235783Skib#include <sys/socket.h>
96235783Skib#include <sys/wait.h>
97235783Skib#ifdef SYS_FCNTL_H
98235783Skib#include <sys/fcntl.h>
99235783Skib#else
100235783Skib#include <fcntl.h>
101235783Skib#endif	/* SYS_FCNTL_H */
102235783Skib#include <sys/types.h>
103235783Skib
104235783Skib#include <netinet/in.h>
105235783Skib#include <netinet/in_systm.h>
106235783Skib#include <netinet/ip.h>
107235783Skib
108235783Skib#define	FTP_NAMES
109235783Skib#include <arpa/ftp.h>
110235783Skib#include <arpa/inet.h>
111235783Skib#include <arpa/telnet.h>
112235783Skib
113235783Skib#include <signal.h>
114235783Skib#include <dirent.h>
115235783Skib#include <fcntl.h>
116235783Skib#if HAVE_TIME_H
117235783Skib#include <time.h>
118235783Skib#endif /* HAVE_TIME_H */
119235783Skib#if HAVE_PWD_H
120235783Skib#include <pwd.h>
121235783Skib#endif /* HAVE_PWD_H */
122235783Skib#include <setjmp.h>
123235783Skib#include <netdb.h>
124235783Skib#include <errno.h>
125235783Skib#include <syslog.h>
126235783Skib#if HAVE_UNISTD_H
127235783Skib#include <unistd.h>
128235783Skib#endif /* HAVE_UNISTD_H */
129235783Skib#include <stdio.h>
130235783Skib#include <ctype.h>
131235783Skib#include <stdlib.h>
132235783Skib#include <string.h>
133235783Skib#include <grp.h>
134235783Skib
135235783Skib#include "opie.h"
136235783Skib
137235783Skib#if HAVE_SHADOW_H
138235783Skib#include <shadow.h>
139235783Skib#endif /* HAVE_SHADOW_H */
140235783Skib
141235783Skib#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    snprintf(proctitle, sizeof(proctitle), "%s: anonymous/%s", remotehost,
632	passwd);
633    setproctitle(proctitle);
634#endif	/* DOTITLE */
635    syslog(LOG_NOTICE, "ANONYMOUS FTP login from %s with ID %s",
636            remotehost, passwd);
637  } else
638#endif	/* DOANONYMOUS */
639  {
640    reply(230, "User %s logged in.", pw->pw_name);
641
642#if DOTITLE
643    snprintf(proctitle, sizeof(proctitle), "%s: %s", remotehost, pw->pw_name);
644    setproctitle(proctitle);
645#endif	/* DOTITLE */
646    syslog(LOG_INFO, "FTP login from %s with user name %s", remotehost, pw->pw_name);
647  }
648  home = pw->pw_dir;	/* home dir for globbing */
649  umask(defumask);
650  return;
651
652bad:
653  /* Forget all about it... */
654  end_login();
655}
656
657VOIDRET retrieve FUNCTION((cmd, name), char *cmd AND char *name)
658{
659  FILE *fin, *dout;
660  struct stat st;
661  int (*closefunc) ();
662
663  if (cmd == 0) {
664    fin = fopen(name, "r"), closefunc = fclose;
665    st.st_size = 0;
666  } else {
667    char line[BUFSIZ];
668
669    snprintf(line, sizeof(line), cmd, name);
670    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    snprintf(sizebuf, sizeof(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  snprintf(line, sizeof(line), "%s %s", "/bin/ls -lgA", filename);
1072#else /* HAVE_LS_G_FLAG */
1073  snprintf(line, sizeof(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  remotehost[sizeof(remotehost) - 1] = '\0';
1260#if DOTITLE
1261  snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
1262  setproctitle(proctitle);
1263#endif	/* DOTITLE */
1264
1265  t = time((time_t *) 0);
1266  syslog(LOG_INFO, "connection from %s at %s",
1267    remotehost, ctime(&t));
1268}
1269
1270/*
1271 * Record logout in wtmp file
1272 * and exit with supplied status.
1273 */
1274VOIDRET dologout FUNCTION((status), int status)
1275{
1276  disable_signalling();
1277  if (logged_in) {
1278    if (seteuid((uid_t) 0))
1279      syslog(LOG_ERR, "Can't set euid");
1280    opielogwtmp(ttyline, "", "");
1281  }
1282  /* beware of flushing buffers after a SIGPIPE */
1283  _exit(status);
1284}
1285
1286static VOIDRET myoob FUNCTION((input), int input)
1287{
1288  char *cp;
1289
1290  /* only process if transfer occurring */
1291  if (!transflag)
1292    return;
1293  cp = tmpline;
1294  if (getline(cp, 7, stdin) == NULL) {
1295    reply(221, "You could at least say goodbye.");
1296    dologout(0);
1297  }
1298  upper(cp);
1299  if (strcmp(cp, "ABOR\r\n") == 0) {
1300    tmpline[0] = '\0';
1301    reply(426, "Transfer aborted. Data connection closed.");
1302    reply(226, "Abort successful");
1303    longjmp(urgcatch, 1);
1304  }
1305  if (strcmp(cp, "STAT\r\n") == 0) {
1306    if (file_size != (off_t) - 1)
1307      reply(213, "Status: %lu of %lu bytes transferred",
1308	    byte_count, file_size);
1309    else
1310      reply(213, "Status: %lu bytes transferred", byte_count);
1311  }
1312}
1313
1314/*
1315 * Note: a response of 425 is not mentioned as a possible response to
1316 *      the PASV command in RFC959. However, it has been blessed as
1317 *      a legitimate response by Jon Postel in a telephone conversation
1318 *      with Rick Adams on 25 Jan 89.
1319 */
1320VOIDRET passive FUNCTION_NOARGS
1321{
1322  int len;
1323  register char *p, *a;
1324
1325  pdata = socket(AF_INET, SOCK_STREAM, 0);
1326  if (pdata < 0) {
1327    perror_reply(425, "Can't open passive connection");
1328    return;
1329  }
1330  pasv_addr = ctrl_addr;
1331  pasv_addr.sin_port = 0;
1332  if (seteuid((uid_t) 0))
1333    syslog(LOG_ERR, "Can't set euid");
1334  if (bind(pdata, (struct sockaddr *) & pasv_addr, sizeof(pasv_addr)) < 0) {
1335    seteuid((uid_t) pw->pw_uid);
1336    goto pasv_error;
1337  }
1338  if (seteuid((uid_t) pw->pw_uid))
1339    syslog(LOG_ERR, "Can't set euid");
1340  len = sizeof(pasv_addr);
1341  if (getsockname(pdata, (struct sockaddr *) & pasv_addr, &len) < 0)
1342    goto pasv_error;
1343  if (listen(pdata, 1) < 0)
1344    goto pasv_error;
1345  a = (char *) &pasv_addr.sin_addr;
1346  p = (char *) &pasv_addr.sin_port;
1347
1348#define UC(b) (((int) b) & 0xff)
1349
1350  reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1351	UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1352  return;
1353
1354pasv_error:
1355  close(pdata);
1356  pdata = -1;
1357  perror_reply(425, "Can't open passive connection");
1358  return;
1359}
1360
1361/*
1362 * Generate unique name for file with basename "local".
1363 * The file named "local" is already known to exist.
1364 * Generates failure reply on error.
1365 */
1366static char *gunique FUNCTION((local), char *local)
1367{
1368  static char new[MAXPATHLEN+1];
1369  struct stat st;
1370  char *cp = strrchr(local, '/');
1371  int count = 0;
1372
1373  if (cp)
1374    *cp = '\0';
1375  if (stat(cp ? local : ".", &st) < 0) {
1376    perror_reply(553, cp ? local : ".");
1377    return ((char *) 0);
1378  }
1379  if (cp)
1380    *cp = '/';
1381  strcpy(new, local);
1382  cp = new + strlen(new);
1383  *cp++ = '.';
1384  for (count = 1; count < 100; count++) {
1385    snprintf(cp, sizeof(new) - (cp - new), "%d", count);
1386    if (stat(new, &st) < 0)
1387      return (new);
1388  }
1389  reply(452, "Unique file name cannot be created.");
1390  return ((char *) 0);
1391}
1392
1393/*
1394 * Format and send reply containing system error number.
1395 */
1396VOIDRET perror_reply FUNCTION((code, string), int code AND char *string)
1397{
1398  reply(code, "%s: %s.", string, strerror(errno));
1399}
1400
1401static char *onefile[] =
1402{
1403  "",
1404  0
1405};
1406
1407VOIDRET send_file_list FUNCTION((whichfiles), char *whichfiles)
1408{
1409  struct stat st;
1410  DIR *dirp = NULL;
1411  struct dirent *dir;
1412  FILE *dout = NULL;
1413  register char **dirlist, *dirname;
1414  int simple = 0;
1415
1416  if (strpbrk(whichfiles, "~{[*?") != NULL) {
1417    extern char **ftpglob(), *globerr;
1418
1419    globerr = NULL;
1420    dirlist = ftpglob(whichfiles);
1421    if (globerr != NULL) {
1422      reply(550, globerr);
1423      return;
1424    } else
1425      if (dirlist == NULL) {
1426	errno = ENOENT;
1427	perror_reply(550, whichfiles);
1428	return;
1429      }
1430  } else {
1431    onefile[0] = whichfiles;
1432    dirlist = onefile;
1433    simple = 1;
1434  }
1435
1436  if (setjmp(urgcatch)) {
1437    transflag = 0;
1438    return;
1439  }
1440  while (dirname = *dirlist++) {
1441    if (stat(dirname, &st) < 0) {
1442      /* If user typed "ls -l", etc, and the client used NLST, do what the
1443         user meant. */
1444      if (dirname[0] == '-' && *dirlist == NULL &&
1445	  transflag == 0) {
1446	retrieve("/bin/ls %s", dirname);
1447	return;
1448      }
1449      perror_reply(550, whichfiles);
1450      if (dout != NULL) {
1451	fclose(dout);
1452	transflag = 0;
1453	data = -1;
1454	pdata = -1;
1455      }
1456      return;
1457    }
1458    if ((st.st_mode & S_IFMT) == S_IFREG) {
1459      if (dout == NULL) {
1460	dout = dataconn("file list", (off_t) - 1, "w");
1461	if (dout == NULL)
1462	  return;
1463	transflag++;
1464      }
1465      fprintf(dout, "%s%s\n", dirname,
1466	      type == TYPE_A ? "\r" : "");
1467      byte_count += strlen(dirname) + 1;
1468      continue;
1469    } else
1470      if ((st.st_mode & S_IFMT) != S_IFDIR)
1471	continue;
1472
1473    if ((dirp = opendir(dirname)) == NULL)
1474      continue;
1475
1476    while ((dir = readdir(dirp)) != NULL) {
1477      char nbuf[MAXPATHLEN+1];
1478
1479      if (dir->d_name[0] == '.' && (strlen(dir->d_name) == 1))
1480	continue;
1481      if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1482	  (strlen(dir->d_name) == 2))
1483	continue;
1484
1485      snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name);
1486
1487      /* We have to do a stat to insure it's not a directory or special file. */
1488      if (simple || (stat(nbuf, &st) == 0 &&
1489		     (st.st_mode & S_IFMT) == S_IFREG)) {
1490	if (dout == NULL) {
1491	  dout = dataconn("file list", (off_t) - 1, "w");
1492	  if (dout == NULL)
1493	    return;
1494	  transflag++;
1495	}
1496	if (nbuf[0] == '.' && nbuf[1] == '/')
1497	  fprintf(dout, "%s%s\n", &nbuf[2],
1498		  type == TYPE_A ? "\r" : "");
1499	else
1500	  fprintf(dout, "%s%s\n", nbuf,
1501		  type == TYPE_A ? "\r" : "");
1502	byte_count += strlen(nbuf) + 1;
1503      }
1504    }
1505    closedir(dirp);
1506  }
1507
1508  if (dout == NULL)
1509    reply(550, "No files found.");
1510  else
1511    if (ferror(dout) != 0)
1512      perror_reply(550, "Data connection");
1513    else
1514      reply(226, "Transfer complete.");
1515
1516  transflag = 0;
1517  if (dout != NULL)
1518    fclose(dout);
1519  data = -1;
1520  pdata = -1;
1521}
1522
1523#if DOTITLE
1524/*
1525 * clobber argv so ps will show what we're doing.
1526 * (stolen from sendmail)
1527 * warning, since this is usually started from inetd.conf, it
1528 * often doesn't have much of an environment or arglist to overwrite.
1529 */
1530VOIDRET setproctitle FUNCTION((fmt, a, b, c), char *fmt AND int a AND int b AND int c)
1531{
1532  register char *p, *bp, ch;
1533  register int i;
1534  char buf[BUFSIZ];
1535
1536  snprintf(buf, sizeof(buf), fmt, a, b, c);
1537
1538  /* make ps print our process name */
1539  p = Argv[0];
1540  *p++ = '-';
1541
1542  i = strlen(buf);
1543  if (i > LastArgv - p - 2) {
1544    i = LastArgv - p - 2;
1545    buf[i] = '\0';
1546  }
1547  bp = buf;
1548  while (ch = *bp++)
1549    if (ch != '\n' && ch != '\r')
1550      *p++ = ch;
1551  while (p < LastArgv)
1552    *p++ = ' ';
1553}
1554#endif	/* DOTITLE */
1555
1556VOIDRET catchexit FUNCTION_NOARGS
1557{
1558  closelog();
1559}
1560
1561int main FUNCTION((argc, argv, envp), int argc AND char *argv[] AND char *envp[])
1562{
1563  int addrlen, on = 1;
1564  char *cp;
1565#ifdef IP_TOS
1566  int tos;
1567#endif /* IP_TOS */
1568
1569  {
1570  int i;
1571
1572  for (i = sysconf(_SC_OPEN_MAX); i > 2; i--)
1573    close(i);
1574  }
1575
1576  /* LOG_NDELAY sets up the logging connection immediately, necessary for
1577     anonymous ftp's that chroot and can't do it later. */
1578  openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
1579  atexit(catchexit);
1580  addrlen = sizeof(his_addr);
1581  if (getpeername(0, (struct sockaddr *) & his_addr, &addrlen) < 0) {
1582    syslog(LOG_ERR, "getpeername (%s): %m", argv[0]);
1583    exit(1);
1584  }
1585  addrlen = sizeof(ctrl_addr);
1586  if (getsockname(0, (struct sockaddr *) & ctrl_addr, &addrlen) < 0) {
1587    syslog(LOG_ERR, "getsockname (%s): %m", argv[0]);
1588    exit(1);
1589  }
1590#ifdef IP_TOS
1591  tos = IPTOS_LOWDELAY;
1592  if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
1593    syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
1594#endif
1595  data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
1596  debug = 0;
1597#if DOTITLE
1598  /* Save start and extent of argv for setproctitle. */
1599  Argv = argv;
1600  while (*envp)
1601    envp++;
1602  LastArgv = envp[-1] + strlen(envp[-1]);
1603#endif	/* DOTITLE */
1604
1605  argc--, argv++;
1606  while (argc > 0 && *argv[0] == '-') {
1607    for (cp = &argv[0][1]; *cp; cp++)
1608      switch (*cp) {
1609
1610      case 'v':
1611	debug = 1;
1612	break;
1613
1614      case 'd':
1615	debug = 1;
1616	break;
1617
1618      case 'l':
1619	break;
1620
1621      case 't':
1622	timeout = atoi(++cp);
1623	if (maxtimeout < timeout)
1624	  maxtimeout = timeout;
1625	goto nextopt;
1626
1627      case 'T':
1628	maxtimeout = atoi(++cp);
1629	if (timeout > maxtimeout)
1630	  timeout = maxtimeout;
1631	goto nextopt;
1632
1633      case 'u':
1634	{
1635	  int val = 0;
1636
1637	  while (*++cp && *cp >= '0' && *cp <= '9')
1638	    val = val * 8 + *cp - '0';
1639	  if (*cp)
1640	    fprintf(stderr, "ftpd: Bad value for -u\n");
1641	  else
1642	    defumask = val;
1643	  goto nextopt;
1644	}
1645
1646      default:
1647	fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
1648		*cp);
1649	break;
1650      }
1651nextopt:
1652    argc--, argv++;
1653  }
1654  freopen(_PATH_DEVNULL, "w", stderr);
1655  signal(SIGCHLD, SIG_IGN);
1656  enable_signalling();
1657
1658  /* Try to handle urgent data inline */
1659#ifdef SO_OOBINLINE
1660  if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0)
1661    syslog(LOG_ERR, "setsockopt: %m");
1662#endif
1663
1664#ifdef	F_SETOWN
1665  if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
1666    syslog(LOG_ERR, "fcntl F_SETOWN: %m");
1667#endif
1668  dolog(&his_addr);
1669  /* Set up default state */
1670  data = -1;
1671  type = TYPE_A;
1672  form = FORM_N;
1673  stru = STRU_F;
1674  mode = MODE_S;
1675  tmpline[0] = '\0';
1676  af_pwok = opieaccessfile(remotehost);
1677
1678  {
1679  FILE *fd;
1680  char line[128];
1681
1682  /* If logins are disabled, print out the message. */
1683  if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
1684    while (fgets(line, sizeof(line), fd) != NULL) {
1685      if ((cp = strchr(line, '\n')) != NULL)
1686        *cp = '\0';
1687      lreply(530, "%s", line);
1688    }
1689    (void) fflush(stdout);
1690    (void) fclose(fd);
1691    reply(530, "System not available.");
1692    exit(0);
1693  }
1694  if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
1695    while (fgets(line, sizeof(line), fd) != NULL) {
1696      if ((cp = strchr(line, '\n')) != NULL)
1697        *cp = '\0';
1698      lreply(220, "%s", line);
1699    }
1700    (void) fflush(stdout);
1701    (void) fclose(fd);
1702    /* reply(220,) must follow */
1703  }
1704  };
1705
1706  reply(220, "FTP server ready.");
1707
1708  setjmp(errcatch);
1709  for (;;)
1710    yyparse();
1711  /* NOTREACHED */
1712  return 0;
1713}
1714