home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / libexec / ftpd / ftpd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-07-02  |  31.7 KB  |  1,474 lines

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