Changeset 38371dbe in rtems for cpukit/ftpd


Ignore:
Timestamp:
Jan 24, 2001, 7:20:24 PM (20 years ago)
Author:
Joel Sherrill <joel.sherrill@…>
Branches:
4.10, 4.11, 4.8, 4.9, 5, master
Children:
4a80bf2b
Parents:
e8a7a46
Message:

2001-01-24 Sergei Organov <osv@…>

  • rtems_servers/ftpd.c, rtems_servers/ftpd.h: Major enhancements as listed below:
    • Timeouts on sockets implemented. 'idle' field added to configuration. No timeout by default to keep backward compatibility. Note: SITE IDLE command not implemented yet.
    • Basic global access control implemented. 'access' field added to configuration. No access limitations by default to keep backward compatibility.
    • Anchor data socket for active mode (using self IP and port 20.)
    • Fixed default data port support (still not tested).
    • Don't allow IP address different from originating host in PORT command to improve security.
    • Fixed bug in MDTM command.
    • Check for correctness of parsing of argument in command_port().
    • Fixed squeeze_path() to don't allow names like 'NAME/smth' where 'NAME' is not a directory.
    • Command parsing a little bit improved: command names are now converted to upper-case to be more compatible with RFC (command names are not case-sensitive.)
    • Reformat comments so that they have RTEMS look-and-feel.
    • Fixed DELE, SITE CHMOD, RMD, MKD broken by previous changes
    • True ASCII mode implemented (doesn't work for hooks and /dev/null)
    • Passive mode implemented, PASV command added.
    • Default port for data connection could be used (untested, can't find ftp client that doesn't send PORT command)
    • SYST reply changed to UNIX, as former RTEMS isn't registered name.
    • Reply codes reviewed and fixed.
Location:
cpukit/ftpd
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • cpukit/ftpd/ftpd.c

    re8a7a46 r38371dbe  
    11/* FIXME: 1. Parse command is a hack.  We can do better.
    22 *        2. Some sort of access control?
    3  *        3. OSV: Timeouts on sockets.
    4  *        4. OSV: hooks support seems to be a joke, as it requires storing of
    5  *           entire input file in memory. Seem to be better to change it to
     3 *        3. OSV: hooks support seems to be bad, as it requires storing of
     4 *           entire input file in memory.  Seem to be better to change it to
    65 *           something more reasonable, like having
    76 *           'hook_write(void const *buf, int count)' routine that will be
    87 *           called multiple times while file is being received.
    9  *        5. OSV: Remove hack with "/dev/null"?
    10  *        6. OSV: data_addr field of SessionInfo structure is not initialized.
    11  *           This will lead to undefined behavior if FTP client doesn't send
    12  *           PORT command. Seems we need to implement usage of default port
    13  *           (ftp port - 1). I didn't find any FTP client that doesn't send
    14  *           PORT command though.
    15  *        7. OSV: while server claims TYPE ASCII command succeeds (to make
    16  *           happy some clients), ASCII mode isn't actually implemented.
    17  *        8. Passive Mode?
     8 *        4. OSV: Remove hack with "/dev/null"?
    189 *
    1910 *  FTP Server Daemon
     
    2213 *
    2314 *  Changed by:   Sergei Organov <osv@javad.ru> (OSV)
    24  *    Changes:
    25  *      - use pool of pre-created threads to handle sessions
    26  *      - LIST output now similar to what "/bin/ls -al" would output, thus
     15 *
     16 *  Changes:
     17 *
     18 *    2001-01-22        Sergei Organov <osv@javad.ru>
     19 *
     20 *      * Timeouts on sockets implemented. 'idle' field added to
     21 *        configuration. No timeout by default to keep backward compatibility.
     22 *        Note: SITE IDLE command not implemented yet.
     23 *      * Basic global access control implemented. 'access' field added to
     24 *        configuration. No access limitations by default to keep backward
     25 *        compatibility.
     26 *
     27 *    2001-01-17        Sergei Organov <osv@javad.ru>
     28 *
     29 *      * Anchor data socket for active mode (using self IP and port 20.)
     30 *      * Fixed default data port support (still not tested).
     31 *      * Don't allow IP address different from originating host in
     32 *        PORT command to improve security.
     33 *      * Fixed bug in MDTM command.
     34 *      * Check for correctness of parsing of argument in command_port().
     35 *      * Fixed squeeze_path() to don't allow names like 'NAME/smth' where
     36 *        'NAME' is not a directory.
     37 *      * Command parsing a little bit improved: command names are now
     38 *        converted to upper-case to be more compatible with RFC (command
     39 *        names are not case-sensitive.)
     40 *      * Reformat comments so that they have RTEMS look-and-feel.
     41 *
     42 *    2001-01-16        Sergei Organov <osv@javad.ru>
     43 *
     44 *      * Fixed DELE, SITE CHMOD, RMD, MKD broken by previous changes
     45 *      * True ASCII mode implemented (doesn't work for hooks and /dev/null)
     46 *      * Passive mode implemented, PASV command added.
     47 *      * Default port for data connection could be used (untested, can't find
     48 *        ftp client that doesn't send PORT command)
     49 *      * SYST reply changed to UNIX, as former RTEMS isn't registered name.
     50 *      * Reply codes reviewed and fixed.
     51 *
     52 *    2001-01-08        Sergei Organov <osv@javad.ru>
     53 *
     54 *      * use pool of pre-created threads to handle sessions
     55 *      * LIST output now similar to what "/bin/ls -al" would output, thus
    2756 *        FTP clients could parse it.
    28  *      - LIST NAME now works (both for files and directories)
    29  *      - keep track of CWD for every session separately
    30  *      - ability to specify root directory name in configuration table
    31  *      - options sent in commands are ignored, thus LIST -al FILE works
    32  *      - added support for NLST, CDUP and MDTM commands
    33  *      - buffers are allocated on stack instead of heap where possible
    34  *      - drop using of task notepad to pass parameters - use function
     57 *      * LIST NAME now works (both for files and directories)
     58 *      * keep track of CWD for every session separately
     59 *      * ability to specify root directory name in configuration table
     60 *      * options sent in commands are ignored, thus LIST -al FILE works
     61 *      * added support for NLST, CDUP and MDTM commands
     62 *      * buffers are allocated on stack instead of heap where possible
     63 *      * drop using of task notepad to pass parameters - use function
    3564 *        arguments instead
    36  *      - various bug-fixes, e.g., use of PF_INET in socket() instead of
     65 *      * various bug-fixes, e.g., use of PF_INET in socket() instead of
    3766 *        AF_INET, use snprintf() instead of sprintf() everywhere for safety,
    3867 *        etc.
     
    4170 */
    4271
    43 /**************************************************************************
    44  *                                 ftpd.c                                 *
    45  **************************************************************************
    46  * Description:                                                           *
    47  *                                                                        *
    48  *    This file contains the daemon which services requests that appear   *
    49  *    on the FTP port.  This server is compatible with FTP, but it        *
    50  *    also provides 'hooks' to make it usable in situations where files   *
    51  *    are not used/necessary.  Once the server is started, it runs        *
    52  *    forever.                                                            *
    53  *                                                                        *
    54  *                                                                        *
    55  *    Organization:                                                       *
    56  *                                                                        *
    57  *       The FTP daemon is started upon boot along with a (configurable)  *
    58  *       number of tasks to handle sessions. It runs all the time and     *
    59  *       waits for connections on the known FTP port (21).  When          *
    60  *       a connection is made, it wakeups a 'session' task.  That         *
    61  *       session then interacts with the remote host.  When the session   *
    62  *       is complete, the session task goes to sleep.  The daemon still   *
    63  *       runs, however.                                                   *
    64  *                                                                        *
    65  *                                                                        *
    66  * Supported commands are:                                                *
    67  *                                                                        *
    68  * RETR xxx     - Sends a file from the client.                           *
    69  * STOR xxx     - Receives a file from the client.  xxx = filename.       *
    70  * LIST xxx     - Sends a file list to the client.                        *
    71  * NLST xxx     - Sends a file list to the client.                        *
    72  * USER         - Does nothing.                                           *
    73  * PASS         - Does nothing.                                           *
    74  * SYST         - Replies with the system type (`RTEMS').                 *
    75  * DELE xxx     - Delete file xxx.                                        *
    76  * MKD xxx      - Create directory xxx.                                   *
    77  * RMD xxx      - Remove directory xxx.                                   *
    78  * PWD          - Print working directory.                                *
    79  * CWD xxx      - Change working directory.                               *
    80  * CDUP         - Change to upper directory.                              *
    81  * SITE CHMOD xxx yyy - Change permissions on file yyy to xxx.            *
    82  * PORT a,b,c,d,x,y   - Setup for a data port to IP address a.b.c.d       *
    83  *                      and port (x*256 + y).                             *
    84  * MDTM xxx     - Send date/time to the client. xxx = filename.           *
    85  *                                                                        *
    86  *                                                                        *
    87  * The public routines contained in this file are:                        *
    88  *                                                                        *
    89  *    rtems_initialize_ftpd - Initializes and starts the server daemon,   *
    90  *                            then returns to its caller.                 *
    91  *                                                                        *
    92  *------------------------------------------------------------------------*
    93  * Jake Janovetz                                                          *
    94  * University of Illinois                                                 *
    95  * 1406 West Green Street                                                 *
    96  * Urbana IL  61801                                                       *
    97  **************************************************************************
    98  * Change History:                                                        *
    99  *  12/01/97   - Creation (JWJ)                                           *
    100  *  2001-01-08 - Changes by OSV                                           *
     72/*************************************************************************
     73 *                                 ftpd.c
     74 *************************************************************************
     75 * Description:
     76 *
     77 *    This file contains the daemon which services requests that appear
     78 *    on the FTP port.  This server is compatible with FTP, but it
     79 *    also provides 'hooks' to make it usable in situations where files
     80 *    are not used/necessary.  Once the server is started, it runs
     81 *    forever.
     82 *
     83 *
     84 *    Organization:
     85 *
     86 *       The FTP daemon is started upon boot along with a (configurable)
     87 *       number of tasks to handle sessions.  It runs all the time and
     88 *       waits for connections on the known FTP port (21).  When
     89 *       a connection is made, it wakes-up a 'session' task.  That
     90 *       session then interacts with the remote host.  When the session
     91 *       is complete, the session task goes to sleep.  The daemon still
     92 *       runs, however.
     93 *
     94 *
     95 * Supported commands are:
     96 *
     97 * RETR xxx     - Sends a file from the client.
     98 * STOR xxx     - Receives a file from the client.  xxx = filename.
     99 * LIST xxx     - Sends a file list to the client.
     100 * NLST xxx     - Sends a file list to the client.
     101 * USER         - Does nothing.
     102 * PASS         - Does nothing.
     103 * SYST         - Replies with the system type (`RTEMS').
     104 * DELE xxx     - Delete file xxx.
     105 * MKD xxx      - Create directory xxx.
     106 * RMD xxx      - Remove directory xxx.
     107 * PWD          - Print working directory.
     108 * CWD xxx      - Change working directory.
     109 * CDUP         - Change to upper directory.
     110 * SITE CHMOD xxx yyy - Change permissions on file yyy to xxx.
     111 * PORT a,b,c,d,x,y   - Setup for a data port to IP address a.b.c.d
     112 *                      and port (x*256 + y).
     113 * MDTM xxx     - Send file modification date/time to the client.
     114 *                xxx = filename.
     115 * PASV         - Use passive mode data connection.
     116 *
     117 *
     118 * The public routines contained in this file are:
     119 *
     120 *    rtems_initialize_ftpd - Initializes and starts the server daemon,
     121 *                            then returns to its caller.
     122 *
     123 *------------------------------------------------------------------------
     124 * Jake Janovetz
     125 * University of Illinois
     126 * 1406 West Green Street
     127 * Urbana IL  61801
     128 *************************************************************************
     129 * Change History:
     130 *  12/01/97   - Creation (JWJ)
     131 *  2001-01-08 - Changes by OSV
    101132 *************************************************************************/
    102133
    103 /**************************************************************************
     134/*************************************************************************
    104135 * Meanings of first and second digits of reply codes:
    105136 *
     
    109140 *         expect another reply before sending another command.
    110141 *  2yz    Positive completion reply.  A new command can be sent.
    111  *  3yz    Positive intermediate reply.  The command has been accpeted
     142 *  3yz    Positive intermediate reply.  The command has been accepted
    112143 *         but another command must be sent.
    113144 *  4yz    Transient negative completion reply.  The requested action did
     
    134165#include <dirent.h>
    135166#include <errno.h>
     167#include <ctype.h>
    136168
    137169#include <rtems.h>
     
    158190#define FTPD_SERVER_MESSAGE  "RTEMS FTP server (Version 1.1-JWJ) ready."
    159191
    160 #define FTPD_SYSTYPE "RTEMS"
     192#define FTPD_SYSTYPE "UNIX Type: L8"
    161193
    162194/* Seem to be unused */
     
    188220FILE *fdopen (int fildes, const char *mode);
    189221
    190 /*
     222/*PAGE
    191223 * SessionInfo structure.
    192224 *
     
    195227typedef struct
    196228{
    197   struct sockaddr_in  data_addr;   /* Data address for PORT commands */
     229  struct sockaddr_in  ctrl_addr;   /* Control connection self address */
     230  struct sockaddr_in  data_addr;   /* Data address set by PORT command */
     231  struct sockaddr_in  def_addr;    /* Default address for data */
     232  int                 use_default; /* 1 - use default address for data */
    198233  FILE                *ctrl_fp;    /* File pointer for control connection */
    199   int                 socket;      /* Socket descriptor for ctrl connection */
     234  int                 ctrl_socket; /* Socket for ctrl connection */
     235  int                 pasv_socket; /* Socket for PASV connection */
     236  int                 data_socket; /* Socket for data connection */
     237  int                 idle;        /* Timeout in seconds */
    200238  char                cwd[FTPD_BUFSIZE];  /* Current working directory */
    201   /* Login -- future use -- */
    202239  int                 xfer_mode;   /* Transfer mode (ASCII/binary) */
    203240  rtems_id            tid;         /* Task id */
     
    225262
    226263/*
    227  * Root node for FTPD without trailing slash. Even '/' node is denoted as
     264 * Root node for FTPD without trailing slash.  Even '/' node is denoted as
    228265 * empty string here.
    229266 */
    230 static char root[FTPD_BUFSIZE];
    231 
     267static char ftpd_root[FTPD_BUFSIZE];
     268
     269/*
     270 * Default idle timeout for sockets in seconds.
     271 */
     272static int ftpd_timeout = 0;
     273
     274/*
     275 * Global access flags.
     276 */
     277static int ftpd_access = 0;
    232278
    233279/*PAGE
     
    235281 * serr
    236282 *
    237  * Return errror string corresponding to current 'errno'.
     283 * Return error string corresponding to current 'errno'.
    238284 *
    239285 */
     
    244290}
    245291
    246 
    247 /*
     292/*PAGE
     293 *
     294 * Utility routines for access control.
     295 *
     296 */
     297
     298static int
     299can_read(void)
     300{
     301  return (ftpd_access & FTPD_NO_READ) == 0;
     302}
     303
     304static int
     305can_write(void)
     306{
     307  return (ftpd_access & FTPD_NO_WRITE) == 0;
     308}
     309
     310/*PAGE
     311 *
    248312 *  Utility routines to manage root directory and session local
    249313 *  current working directory.
    250  */
    251 
     314 *
     315 */
     316
     317
     318/*PAGE
     319 *
     320 * is_dir
     321 *
     322 * Return 1 if file with given 'name' exists and is directory, 0 otherwise.
     323 *
     324 */
     325static int
     326is_dir(char const* name)
     327{
     328  struct stat s;
     329  int res = stat(name, &s) == 0 && S_ISDIR(s.st_mode);
     330  return res;
     331}
    252332
    253333/*PAGE
     
    256336 *
    257337 * Squeezes path according to OS rules, i.e., eliminates /./, /../, and //
    258  * from the path. Does nothing if the path is relative, i.e. doesn't begin
    259  * with '/'. The trailing slash is always removed, even when alone, i.e. "/"
     338 * from the path.  Does nothing if the path is relative, i.e. doesn't begin
     339 * with '/'.  The trailing slash is always removed, even when alone, i.e. "/"
    260340 * will be "" after squeeze.
    261341 *
    262342 * Input parameters:
    263343 *   path - the path to be squeezed
     344 *   full - full file name or NULL (assumed that it points to the beginning of
     345 *          buffer, and 'path' also points somewhere into the same buffer). Is
     346 *          used to check if intermediate names are directories.
    264347 *
    265348 * Output parameters:
    266349 *   path - squeezed path
    267  *
    268  */
    269 static void
    270 squeeze_path(char* path)
     350 *   returns 1 on success, 0 on failure (if any name that supposed to denote
     351 *     directory is not a directory).
     352 *
     353 */
     354static int
     355squeeze_path(char* path, char* full)
    271356{
    272357  if(path[0] == '/')
     
    280365      e = strchr(s, '/');
    281366      if(e)
    282         ++e;
     367      {
     368        char c = *e;
     369        *e = '\0';
     370        if(full && !is_dir(full))
     371        {
     372          *e = c;
     373          return 0;
     374        }
     375        *e++ = c;
     376      }
    283377      else
    284378        e = s + rest + 1;
     
    309403    }
    310404    if(e[-2] == '/')
     405    {
    311406      e[-2] = '\0';
    312   }
     407      if(full && !is_dir(full))
     408        return 0;
     409    }
     410  }
     411  return 1;
    313412}
    314413
     
    337436  char* res = NULL;
    338437
    339   int rlen = strlen(root);
     438  int rlen = strlen(ftpd_root);
    340439  int clen = strlen(cwd);
    341440  int nlen = strlen(name);
     
    352451  {
    353452    char* b = buf;
    354     memcpy(b, root, rlen); b += rlen;
     453    memcpy(b, ftpd_root, rlen); b += rlen;
    355454    if (name[0] != '/')
    356455    {
     
    368467    while(rlen-- > 0 && res[-1] == '/')
    369468      --res;
    370     squeeze_path(res);
     469    if(!squeeze_path(res, buf))
     470      res = NULL;
    371471  }
    372472
     
    374474}
    375475
    376 /*
     476/*PAGE
     477 *
    377478 * Task pool management routines
     479 *
    378480 */
    379481
     
    413515}
    414516
    415 /* Forward declare */
    416 static void session(rtems_task_argument arg);
    417 
    418517/*PAGE
    419518 *
     
    430529 *
    431530 */
     531static void session(rtems_task_argument arg); /* Forward declare */
     532
    432533static int
    433534task_pool_init(int count, rtems_task_priority priority)
     
    503604    task_pool.queue[i] = task_pool.info + i;
    504605    info->ctrl_fp = NULL;
    505     info->socket  = -1;
     606    info->ctrl_socket  = -1;
    506607    if (++id > 'z')
    507608      id = 'a';
     
    537638    if(++task_pool.head >= task_pool.count)
    538639      task_pool.head = 0;
    539     info->socket = -1;
     640    info->ctrl_socket = -1;
    540641    info->ctrl_fp = NULL;
    541642    rtems_semaphore_release(task_pool.mutex);
     
    574675/*PAGE
    575676 *
    576  * close_socket
    577  *
    578  * Close socket.
    579  *
    580  * Input parameters:
    581  *   s - socket descriptor to be closed.
    582  *
    583  * Output parameters:
    584  *   returns 1 on success, 0 on failure
    585  *
    586  */
    587 static int
    588 close_socket(int s)
    589 {
    590   if (0 <= s)
    591   {
    592     if (0 != close(s))
    593     {
    594       shutdown(s, 2);
    595       if (0 != close(s))
    596         return 0;
    597     }
    598   }
    599   return 1;
    600 }
    601 
    602 /*PAGE
    603  *
    604  * close_stream
    605  *
    606  * Close data stream of session.
    607  *
    608  * Input parameters:
    609  *   info - corresponding SessionInfo structure
     677 * Function: send_reply
     678 *
     679 *
     680 *    This procedure sends a reply to the client via the control
     681 *    connection.
     682 *
     683 *
     684 * Input parameters:
     685 *   code  - 3-digit reply code.
     686 *   text  - Reply text.
    610687 *
    611688 * Output parameters:
    612689 *   NONE
    613  *
    614  */
    615 static void
    616 close_stream(FTPD_SessionInfo_t* info)
    617 {
    618   if (NULL != info->ctrl_fp)
    619   {
    620     if (0 != fclose(info->ctrl_fp))
    621     {
    622       syslog(LOG_ERR, "ftpd: Could not close control stream: %s", serr());
    623     }
    624     else
    625       info->socket = -1;
    626   }
    627 
    628   if (!close_socket(info->socket))
    629     syslog(LOG_ERR, "ftpd: Could not close control socket: %s", serr());
    630 
    631   info->ctrl_fp = NULL;
    632   info->socket = -1;
    633 }
    634 
    635 
    636 /**************************************************************************
    637  * Function: send_reply                                                   *
    638  **************************************************************************
    639  * Description:                                                           *
    640  *                                                                        *
    641  *    This procedure sends a reply to the client via the control          *
    642  *    connection.                                                         *
    643  *                                                                        *
    644  *                                                                        *
    645  * Inputs:                                                                *
    646  *                                                                        *
    647  *    int  code  - The 3-digit reply code.                                *
    648  *    char *text - Reply text.                                            *
    649  *                                                                        *
    650  * Output:                                                                *
    651  *                                                                        *
    652  *    none                                                                *
    653  *                                                                        *
    654  *************************************************************************/
     690 */
    655691static void
    656692send_reply(FTPD_SessionInfo_t  *info, int code, char *text)
    657693{
    658   char const* s;
    659 
    660 
    661   /***********************************************************************
    662    * code must be a 3-digit number.
    663    **********************************************************************/
    664   if ((code < 100) || (code > 999))
    665   {
    666     syslog(LOG_ERR, "ftpd: Code not 3-digits.");
    667     return;
    668   }
    669 
    670   /***********************************************************************
    671    * If a text reply exists, add it to the reply data.
    672    **********************************************************************/
    673   s  = (info->xfer_mode == TYPE_A) ? "\r" : "";
     694  char const* s = (info->xfer_mode == TYPE_A) ? "\r" : "";
     695  /* If a text reply exists, add it to the reply data. */
    674696  if (text != NULL)
    675697    fprintf(info->ctrl_fp, "%d %.70s%s\n", code, text, s);
     
    682704/*PAGE
    683705 *
     706 * close_socket
     707 *
     708 * Close socket.
     709 *
     710 * Input parameters:
     711 *   s - socket descriptor.
     712 *   seconds - number of seconds the timeout should be,
     713 *             if >= 0 - infinite timeout (no timeout).
     714 *
     715 * Output parameters:
     716 *   returns 1 on success, 0 on failure.
     717 */
     718static int
     719set_socket_timeout(int s, int seconds)
     720{
     721  int res = 0;
     722  struct timeval tv;
     723  int len = sizeof(tv);
     724
     725  if(seconds < 0)
     726    seconds = 0;
     727  tv.tv_usec = 0;
     728  tv.tv_sec  = seconds;
     729  if(0 != setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, len))
     730    syslog(LOG_ERR, "ftpd: Can't set send timeout on socket: %s.", serr());
     731  else if(0 != setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, len))
     732    syslog(LOG_ERR, "ftpd: Can't set receive timeout on socket: %s.", serr());
     733  else
     734    res = 1;
     735  return res;
     736}
     737
     738/*PAGE
     739 *
     740 * close_socket
     741 *
     742 * Close socket.
     743 *
     744 * Input parameters:
     745 *   s - socket descriptor to be closed.
     746 *
     747 * Output parameters:
     748 *   returns 1 on success, 0 on failure
     749 */
     750static int
     751close_socket(int s)
     752{
     753  if (0 <= s)
     754  {
     755    if (0 != close(s))
     756    {
     757      shutdown(s, 2);
     758      if (0 != close(s))
     759        return 0;
     760    }
     761  }
     762  return 1;
     763}
     764
     765/*PAGE
     766 *
     767 * data_socket
     768 *
     769 * Create data socket for session.
     770 *
     771 * Input parameters:
     772 *   info - corresponding SessionInfo structure
     773 *
     774 * Output parameters:
     775 *   returns socket descriptor, or -1 if failure
     776 *
     777 */
     778static int
     779data_socket(FTPD_SessionInfo_t *info)
     780{
     781  int s = info->pasv_socket;
     782  if(0 > s)
     783  {
     784    int on = 1;
     785    s = socket(PF_INET, SOCK_STREAM, 0);
     786    if(0 > s)
     787      send_reply(info, 425, "Can't create data socket.");
     788    else if(0 > setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
     789    {
     790      close_socket(s);
     791      s = -1;
     792    }
     793    else
     794    {
     795      struct sockaddr_in data_source;
     796      int tries;
     797
     798      /* anchor socket to avoid multi-homing problems */
     799      data_source = info->ctrl_addr;
     800      data_source.sin_port = htons(20); /* ftp-data port */
     801      for(tries = 1; tries < 10; ++tries)
     802      {
     803        if(bind(s, (struct sockaddr *)&data_source, sizeof(data_source)) >= 0)
     804          break;
     805        if (errno != EADDRINUSE)
     806          tries = 10;
     807        else
     808          rtems_task_wake_after(tries * 10);
     809      }
     810      if(tries >= 10)
     811      {
     812        send_reply(info, 425, "Can't bind data socket.");
     813        close_socket(s);
     814        s = -1;
     815      }
     816      else
     817      {
     818        struct sockaddr_in *data_dest =
     819          (info->use_default) ? &info->def_addr : &info->data_addr;
     820        if(0 > connect(s, (struct sockaddr *)data_dest, sizeof(*data_dest)))
     821        {
     822          send_reply(info, 425, "Can't connect data socket.");
     823          close_socket(s);
     824          s = -1;
     825        }
     826      }
     827    }
     828  }
     829  info->data_socket = s;
     830  info->use_default = 1;
     831  if(s >= 0)
     832    set_socket_timeout(s, info->idle);
     833  return s;
     834}
     835
     836/*PAGE
     837 *
     838 * close_data_socket
     839 *
     840 * Close data socket for session.
     841 *
     842 * Input parameters:
     843 *   info - corresponding SessionInfo structure
     844 *
     845 * Output parameters:
     846 *   NONE
     847 *
     848 */
     849static void
     850close_data_socket(FTPD_SessionInfo_t *info)
     851{
     852  int s = info->pasv_socket;
     853  if(0 > s)
     854    s = info->data_socket;
     855  if(!close_socket(s))
     856    syslog(LOG_ERR, "ftpd: Error closing data socket.");
     857  info->data_socket = -1;
     858  info->pasv_socket = -1;
     859  info->use_default = 1;
     860}
     861
     862/*PAGE
     863 *
     864 * close_stream
     865 *
     866 * Close control stream of session.
     867 *
     868 * Input parameters:
     869 *   info - corresponding SessionInfo structure
     870 *
     871 * Output parameters:
     872 *   NONE
     873 *
     874 */
     875static void
     876close_stream(FTPD_SessionInfo_t* info)
     877{
     878  if (NULL != info->ctrl_fp)
     879  {
     880    if (0 != fclose(info->ctrl_fp))
     881    {
     882      syslog(LOG_ERR, "ftpd: Could not close control stream: %s", serr());
     883    }
     884    else
     885      info->ctrl_socket = -1;
     886  }
     887
     888  if (!close_socket(info->ctrl_socket))
     889    syslog(LOG_ERR, "ftpd: Could not close control socket: %s", serr());
     890
     891  info->ctrl_fp = NULL;
     892  info->ctrl_socket = -1;
     893}
     894
     895
     896/*PAGE
     897 *
    684898 * send_mode_reply
    685899 *
     
    704918/*PAGE
    705919 *
    706  * data_socket
    707  *
    708  * Create data socket for session.
     920 * command_retrieve
     921 *
     922 * Perform the "RETR" command (send file to client).
    709923 *
    710924 * Input parameters:
    711925 *   info - corresponding SessionInfo structure
    712  *
    713  * Output parameters:
    714  *   returns socket descriptor, or -1 if failure
    715  *
    716  */
    717 static int
    718 data_socket(FTPD_SessionInfo_t *info)
    719 {
    720   int s = socket(PF_INET, SOCK_STREAM, 0);
    721   if(0 > s)
    722     send_reply(info, 420, "Server error - could not create socket.");
    723   else if(0 > connect(s, (struct sockaddr *)&info->data_addr,
    724     sizeof(struct sockaddr)))
    725   {
    726     send_reply(info, 420, "Server error - could not connect socket.");
    727     close_socket(s);
    728     s = -1;
    729   }
    730   return s;
    731 }
    732 
    733 /**************************************************************************
    734  * Function: command_retrieve                                             *
    735  **************************************************************************
    736  * Description:                                                           *
    737  *                                                                        *
    738  *    This performs the "RETR" command.  A data connection must already   *
    739  *    be open (via the "PORT" command.)  Here, we send the data to the    *
    740  *    connection.                                                         *
    741  *                                                                        *
    742  *                                                                        *
    743  * Inputs:                                                                *
    744  *                                                                        *
    745  *    char *filename   - Source filename.                                 *
    746  *                                                                        *
    747  * Output:                                                                *
    748  *                                                                        *
    749  *    int  - 0 for reply sent.                                            *
    750  *           1 for no reply sent.                                         *
    751  *                                                                        *
    752  *************************************************************************/
    753 static int
    754 command_retrieve(FTPD_SessionInfo_t  *info, char *filename)
     926 *   char *filename  - source filename.
     927 *
     928 * Output parameters:
     929 *   NONE
     930 *
     931 */
     932static void
     933command_retrieve(FTPD_SessionInfo_t  *info, char const *filename)
    755934{
    756935  int                 s = -1;
    757   int                 n;
    758936  int                 fd = -1;
    759937  char                buf[FTPD_DATASIZE];
    760938  int                 res = 0;
    761 
    762   char const* r = make_path(buf, info->cwd, filename);
     939  char const* r = NULL;
     940
     941  if(!can_read())
     942  {
     943    send_reply(info, 550, "Access denied.");
     944    return;
     945  }
     946
     947  r = make_path(buf, info->cwd, filename);
    763948
    764949  if (NULL == r || 0 > (fd = open(buf, O_RDONLY)))
    765950  {
    766     send_reply(info, 450, "Error opening file.");
    767     return(res);
     951    send_reply(info, 550, "Error opening file.");
     952    return;
    768953  }
    769954
    770955  send_mode_reply(info);
    771956
    772   /***********************************************************************
    773    * Connect to the data connection (PORT made in an earlier PORT call).
    774    **********************************************************************/
    775957  s = data_socket(info);
     958
    776959  if (0 <= s)
    777960  {
    778     /***********************************************************************
    779      * Send the data over the ether.
    780      **********************************************************************/
    781     while ((n = read(fd, buf, FTPD_DATASIZE)) > 0)
    782     {
    783       send(s, buf, n, 0);
     961    int n = -1;
     962
     963    if(info->xfer_mode == TYPE_I)
     964    {
     965      while ((n = read(fd, buf, FTPD_DATASIZE)) > 0)
     966      {
     967        if(send(s, buf, n, 0) != n)
     968          break;
     969      }
     970    }
     971    else if (info->xfer_mode == TYPE_A)
     972    {
     973      int rest = 0;
     974      while (rest == 0 && (n = read(fd, buf, FTPD_DATASIZE)) > 0)
     975      {
     976        char const* e = buf;
     977        char const* b;
     978        int i;
     979        rest = n;
     980        do
     981        {
     982          char lf = '\0';
     983
     984          b = e;
     985          for(i = 0; i < rest; ++i, ++e)
     986          {
     987            if(*e == '\n')
     988            {
     989              lf = '\n';
     990              break;
     991            }
     992          }
     993          if(send(s, b, i, 0) != i)
     994            break;
     995          if(lf == '\n')
     996          {
     997            if(send(s, "\r\n", 2, 0) != 2)
     998              break;
     999            ++e;
     1000            ++i;
     1001          }
     1002        }
     1003        while((rest -= i) > 0);
     1004      }
    7841005    }
    7851006
     
    7941015  }
    7951016
    796   if (0 == res)
    797     send_reply(info, 450, "Retrieve failed.");
    798   else
    799     send_reply(info, 210, "File sent successfully.");
    800 
    8011017  if (-1 != fd)
    8021018    close(fd);
    8031019
    804   if (!close_socket(s))
    805     syslog(LOG_ERR, "ftpd: Error closing data socket");
    806 
    807   return(res);
    808 }
    809 
    810 
    811 /**************************************************************************
    812  * Function: command_store                                                *
    813  **************************************************************************
    814  * Description:                                                           *
    815  *                                                                        *
    816  *    This performs the "STOR" command.  A data connection must already   *
    817  *    be open (via the "PORT" command.)  Here, we get the data from the   *
    818  *    connection and figure out what to do with it.                       *
    819  *                                                                        *
    820  *                                                                        *
    821  * Inputs:                                                                *
    822  *                                                                        *
    823  *    char *filename   - Destination filename.                            *
    824  *                                                                        *
    825  * Output:                                                                *
    826  *                                                                        *
    827  *    int  - 0 for success.                                               *
    828  *           1 for failure.                                               *
    829  *                                                                        *
    830  *************************************************************************/
    831 static int
    832 command_store(FTPD_SessionInfo_t *info, char *filename)
     1020  if (0 == res)
     1021    send_reply(info, 451, "File read error.");
     1022  else
     1023    send_reply(info, 226, "Transfer complete.");
     1024
     1025  close_data_socket(info);
     1026
     1027  return;
     1028}
     1029
     1030
     1031/*PAGE
     1032 *
     1033 * command_store
     1034 *
     1035 * Performs the "STOR" command (receive data from client).
     1036 *
     1037 * Input parameters:
     1038 *   info - corresponding SessionInfo structure
     1039 *   char *filename   - Destination filename.
     1040 *
     1041 * Output parameters:
     1042 *   NONE
     1043 */
     1044static void
     1045command_store(FTPD_SessionInfo_t *info, char const *filename)
    8331046{
    8341047  int                    s;
     
    8371050  struct rtems_ftpd_hook *usehook = NULL;
    8381051  char                   buf[FTPD_DATASIZE];
     1052  int                    res = 1;
     1053  int                    bare_lfs = 0;
    8391054
    8401055  int null = !strcmp("/dev/null", filename);
    8411056
     1057  if(!can_write())
     1058  {
     1059    send_reply(info, 550, "Access denied.");
     1060    return;
     1061  }
     1062
    8421063  if(!null)
    8431064  {
    8441065    if (NULL == make_path(buf, info->cwd, filename))
    8451066    {
    846       send_reply(info, 450, "Error creating file.");
    847       return(0);
     1067      send_reply(info, 550, "Error creating file.");
     1068      return;
    8481069    }
    8491070  }
     
    8531074  s = data_socket(info);
    8541075  if(0 > s)
    855   {
    856     return(1);
    857   }
    858 
    859 
    860   /***********************************************************************
    861    * File: "/dev/null" just throws the data away.
    862    * Otherwise, search our list of hooks to see if we need to do something
    863    *   special.
    864    * OSV: FIXME: this is hack. Using /dev/null filesystem entry would be
    865    *             better. However, it's not clear how to handle root directory
    866    *             other than '/' then.
    867    **********************************************************************/
     1076    return;
     1077
     1078
    8681079  if (null)
    8691080  {
    870     while ((n = read(s, buf, FTPD_DATASIZE)) > 0);
     1081    /* File "/dev/null" just throws data away.
     1082     *  FIXME: this is hack.  Using /dev/null filesystem entry would be
     1083     *  better.  However, it's not clear how to handle root directory other
     1084     *  than '/' then.
     1085     */
     1086    while ((n = recv(s, buf, FTPD_DATASIZE, 0)) > 0)
     1087      ;
    8711088  }
    8721089  else if (rtems_ftpd_configuration.hooks != NULL)
    8731090  {
     1091
     1092    /* Search our list of hooks to see if we need to do something special. */
    8741093    struct rtems_ftpd_hook *hook;
    8751094    int i;
     
    8921111    /*
    8931112     * OSV: FIXME: Small buffer could be used and hook routine
    894      * called multiple times instead. Alternatively, the support could be
     1113     * called multiple times instead.  Alternatively, the support could be
    8951114     * removed entirely in favor of configuring RTEMS pseudo-device with
    8961115     * given name.
     
    9001119    size_t filesize = rtems_ftpd_configuration.max_hook_filesize + 1;
    9011120
    902     /***********************************************************************
     1121    /*
    9031122     * Allocate space for our "file".
    904      **********************************************************************/
     1123     */
    9051124    bigBufr = (char *)malloc(filesize);
    9061125    if (bigBufr == NULL)
    9071126    {
    908       send_reply(info, 440, "Server error - malloc fail.");
    909       return(1);
    910     }
    911 
    912     /***********************************************************************
     1127      send_reply(info, 451, "Local resource failure: malloc.");
     1128      close_data_socket(info);
     1129      return;
     1130    }
     1131
     1132    /*
    9131133     * Retrieve the file into our buffer space.
    914      **********************************************************************/
     1134     */
    9151135    size = 0;
    916     while ((n = read(s, bigBufr + size, filesize - size)) > 0)
     1136    while ((n = recv(s, bigBufr + size, filesize - size, 0)) > 0)
    9171137    {
    9181138      size += n;
     
    9201140    if (size >= filesize)
    9211141    {
    922       send_reply(info, 440, "Server error - Buffer size exceeded.");
     1142      send_reply(info, 451, "File too long: buffer size exceeded.");
    9231143      free(bigBufr);
    924       close_socket(s);
    925       return(1);
    926     }
    927     close_socket(s);
    928 
    929     /***********************************************************************
     1144      close_data_socket(info);
     1145      return;
     1146    }
     1147
     1148    /*
    9301149     * Call our hook.
    931      **********************************************************************/
    932     if ((usehook->hook_function)(bigBufr, size) == 0)
    933     {
    934       send_reply(info, 210, "File transferred successfully.");
    935     }
    936     else
    937     {
    938       send_reply(info, 440, "File transfer failed.");
    939     }
     1150     */
     1151    res = (usehook->hook_function)(bigBufr, size) == 0;
    9401152    free(bigBufr);
     1153    if(!res)
     1154    {
     1155      send_reply(info, 451, "File processing failed.");
     1156      close_data_socket(info);
     1157      return;
     1158    }
    9411159  }
    9421160  else
    9431161  {
     1162    /* Data transfer to regular file. */
    9441163    int fd =
    9451164      creat(buf, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
     
    9471166    if (0 > fd)
    9481167    {
    949       send_reply(info, 450, "Error creating file.");
    950       close_socket(s);
    951       return(0);
    952     }
    953 
    954     while ((n = read(s, buf, FTPD_DATASIZE)) > 0)
    955     {
    956       if (0 > write(fd, buf, n))
    957       {
    958         send_reply(info, 450, "Error during write.");
    959         close(fd);
    960         close_socket(s);
    961         return(1);
    962       }
    963     }
    964     if (0 > close(fd))
    965     {
    966       send_reply(info, 450, "Error during write.");
    967       close_socket(s);
    968       return(1);
    969     }
    970     close_socket(s);
     1168      send_reply(info, 550, "Error creating file.");
     1169      close_data_socket(info);
     1170      return;
     1171    }
     1172
     1173    if(info->xfer_mode == TYPE_I)
     1174    {
     1175      while ((n = recv(s, buf, FTPD_DATASIZE, 0)) > 0)
     1176      {
     1177        if (write(fd, buf, n) != n)
     1178        {
     1179          res = 0;
     1180          break;
     1181        }
     1182      }
     1183    }
     1184    else if(info->xfer_mode == TYPE_A)
     1185    {
     1186      int rest = 0;
     1187      int pended_cr = 0;
     1188      while (res && rest == 0 && (n = recv(s, buf, FTPD_DATASIZE, 0)) > 0)
     1189      {
     1190        char const* e = buf;
     1191        char const* b;
     1192        int i;
     1193
     1194        rest = n;
     1195        if(pended_cr && *e != '\n')
     1196        {
     1197          char const lf = '\r';
     1198          pended_cr = 0;
     1199          if(write(fd, &lf, 1) != 1)
     1200          {
     1201            res = 0;
     1202            break;
     1203          }
     1204        }
     1205        do
     1206        {
     1207          int count;
     1208          int sub = 0;
     1209
     1210          b = e;
     1211          for(i = 0; i < rest; ++i, ++e)
     1212          {
     1213            int pcr = pended_cr;
     1214            pended_cr = 0;
     1215            if(*e == '\r')
     1216            {
     1217              pended_cr = 1;
     1218            }
     1219            else if(*e == '\n')
     1220            {
     1221              if(pcr)
     1222              {
     1223                sub = 2;
     1224                ++i;
     1225                ++e;
     1226                break;
     1227              }
     1228              ++bare_lfs;
     1229            }
     1230          }
     1231          if(res == 0)
     1232            break;
     1233          count = i - sub - pended_cr;
     1234          if(count > 0 && write(fd, b, count) != count)
     1235          {
     1236            res = 0;
     1237            break;
     1238          }
     1239          if(sub == 2 && write(fd, e - 1, 1) != 1)
     1240            res = 0;
     1241        }
     1242        while((rest -= i) > 0);
     1243      }
     1244    }
     1245
     1246    if (0 > close(fd) || res == 0)
     1247    {
     1248      send_reply(info, 452, "Error writing file.");
     1249      close_data_socket(info);
     1250      return;
     1251    }
     1252  }
     1253
     1254  if (bare_lfs > 0)
     1255  {
     1256    snprintf(buf, FTPD_BUFSIZE,
     1257      "Transfer complete. WARNING! %d bare linefeeds received in ASCII mode.",
     1258      bare_lfs);
     1259    send_reply(info, 226, buf);
     1260  }
     1261  else
    9711262    send_reply(info, 226, "Transfer complete.");
    972   }
    973 
    974   return(0);
     1263  close_data_socket(info);
     1264
    9751265}
    9761266
     
    9841274 * Input parameters:
    9851275 *   s - socket descriptor to send data to
    986  *   wide - if 0, send only file name. If not 0, send 'stat' info as well in
     1276 *   wide - if 0, send only file name.  If not 0, send 'stat' info as well in
    9871277 *          "ls -l" format.
    9881278 *   curTime - current time
     
    9941284 *
    9951285 * Output parameters:
    996  *   NONE
    997  *
    998  */
    999 static void
     1286 *   returns 0 on failure, 1 on success
     1287 *
     1288 */
     1289static int
    10001290send_dirline(int s, int wide, time_t curTime, char const* path,
    10011291  char const* add, char const* fname, char* buf)
     
    10191309        buf[plen++] = '/';
    10201310        if(plen >= FTPD_BUFSIZE)
    1021           return;
     1311          return 0;
    10221312        buf[plen] = '\0';
    10231313      }
    10241314    }
    10251315    if(plen + alen >= FTPD_BUFSIZE)
    1026       return;
     1316      return 0;
    10271317    strcpy(buf + plen, add);
    10281318
     
    10611351      );
    10621352
    1063       send(s, buf, len, 0);
     1353      if(send(s, buf, len, 0) != len)
     1354        return 0;
    10641355    }
    10651356  }
     
    10671358  {
    10681359    int len = snprintf(buf, FTPD_BUFSIZE, "%s\r\n", fname);
    1069     send(s, buf, len, 0);
    1070   }
    1071 }
    1072 
    1073 /**************************************************************************
    1074  * Function: command_list                                      *
    1075  **************************************************************************
    1076  * Description:                                                           *
    1077  *                                                                        *
    1078  *    Sends a file list through a data connection.  The data              *
    1079  *    connection must have already been opened with the "PORT" command.   *
    1080  *                                                                        *
    1081  *                                                                        *
    1082  * Inputs:                                                                *
    1083  *                                                                        *
    1084  *    char *fname  - File (or directory) to list.                         *
    1085  *                                                                        *
    1086  * Output:                                                                *
    1087  *                                                                        *
    1088  *    none                                                                *
    1089  *                                                                        *
    1090  *************************************************************************/
     1360    if(send(s, buf, len, 0) != len)
     1361      return 0;
     1362  }
     1363  return 1;
     1364}
     1365
     1366/*PAGE
     1367 *
     1368 * command_list
     1369 *
     1370 * Send file list to client.
     1371 *
     1372 * Input parameters:
     1373 *   info - corresponding SessionInfo structure
     1374 *   char *fname  - File (or directory) to list.
     1375 *
     1376 * Output parameters:
     1377 *   NONE
     1378 */
    10911379static void
    10921380command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
     
    11011389  char const* res;
    11021390  char const* cwd = info->cwd;
     1391  int sc = 1;
    11031392
    11041393  send_reply(info, 150, "Opening ASCII mode data connection for LIST.");
     
    11311420  {
    11321421    time(&curTime);
    1133     if(!dirp)
    1134       send_dirline(s, wide, curTime, path, "", fname, buf);
     1422    if(!dirp && *fname)
     1423      sc = sc && send_dirline(s, wide, curTime, path, "", fname, buf);
    11351424    else {
    11361425      /* FIXME: need "." and ".." only when '-a' option is given */
    1137       send_dirline(s, wide, curTime, path, "", ".", buf);
    1138       if(!strcmp(path, root))
    1139         send_dirline(s, wide, curTime, path, "", "..", buf);
    1140       else
    1141         send_dirline(s, wide, curTime, path, "..", "..", buf);
    1142       while ((dp = readdir(dirp)) != NULL)
    1143         send_dirline(s, wide, curTime, path, dp->d_name, dp->d_name, buf);
     1426      sc = sc && send_dirline(s, wide, curTime, path, "", ".", buf);
     1427      sc = sc && send_dirline(s, wide, curTime, path,
     1428        (strcmp(path, ftpd_root) ? ".." : ""), "..", buf);
     1429      while (sc && (dp = readdir(dirp)) != NULL)
     1430        sc = sc &&
     1431          send_dirline(s, wide, curTime, path, dp->d_name, dp->d_name, buf);
    11441432    }
    11451433  }
     
    11471435  if(dirp)
    11481436    closedir(dirp);
    1149   close_socket(s);
    1150   send_reply(info, 226, "Transfer complete.");
     1437  close_data_socket(info);
     1438
     1439  if(sc)
     1440    send_reply(info, 226, "Transfer complete.");
     1441  else
     1442    send_reply(info, 426, "Connection aborted.");
    11511443}
    11521444
     
    11561448 * rtems_ftpd_cwd
    11571449 *
    1158  * Change current working directory. We use 'chdir' here only to validate the
    1159  * new directory. We keep track of current working directory ourselves because
     1450 * Change current working directory.  We use 'chdir' here only to validate the
     1451 * new directory.  We keep track of current working directory ourselves because
    11601452 * current working directory in RTEMS isn't thread local, but we need it to be
    11611453 * session local.
     
    11911483 * command_mdtm
    11921484 *
    1193  * Handle FTP MDTM command
     1485 * Handle FTP MDTM command (send file modification time to client)/
    11941486 *
    11951487 * Input parameters:
     
    11991491 * Output parameters:
    12001492 *   info->cwd is set to new CWD value.
    1201  *
    12021493 */
    12031494static void
     
    12071498  char buf[FTPD_BUFSIZE];
    12081499
    1209   if(*fname == '\0')
    1210     fname = ".";
    1211 
    1212   if (stat(fname, &stbuf) < 0)
     1500  if (NULL == make_path(buf, info->cwd, fname) || 0 > stat(buf, &stbuf))
    12131501  {
    12141502    snprintf(buf, FTPD_BUFSIZE, "%s: %s.", fname, serr());
     
    12261514}
    12271515
    1228 /**************************************************************************
    1229  * Function: command_port                                                 *
    1230  **************************************************************************
    1231  * Description:                                                           *
    1232  *                                                                        *
    1233  *    This procedure opens up a data port given the IP address of the     *
    1234  *    remote machine and the port on the remote machine.  This connection *
    1235  *    will then be used to transfer data between the hosts.               *
    1236  *                                                                        *
    1237  *                                                                        *
    1238  * Inputs:                                                                *
    1239  *                                                                        *
    1240  *    char *bufr - Arguments to the "PORT" command.                       *
    1241  *                                                                        *
    1242  *                                                                        *
    1243  * Output:                                                                *
    1244  *                                                                        *
    1245  *    none                                                                *
    1246  *                                                                        *
    1247  *************************************************************************/
     1516/*PAGE
     1517 *
     1518 * command_port
     1519 *
     1520 * This procedure fills address for data connection given the IP address and
     1521 * port of the remote machine.
     1522 *
     1523 * Input parameters:
     1524 *   info - corresponding SessionInfo structure
     1525 *   args - arguments to the "PORT" command.
     1526 *
     1527 * Output parameters:
     1528 *   info->data_addr is set according to arguments of the PORT command.
     1529 *   info->use_default is set to 0 on success, 1 on failure.
     1530 */
    12481531static void
    1249 command_port(FTPD_SessionInfo_t *info, char *bufr)
    1250 {
    1251   char                *ip;
    1252   char                *port;
    1253   int                 ip0, ip1, ip2, ip3, port0, port1;
    1254 
    1255   sscanf(bufr, "%d,%d,%d,%d,%d,%d", &ip0, &ip1, &ip2, &ip3, &port0, &port1);
    1256   ip = (char *)&(info->data_addr.sin_addr);
    1257   ip[0] = ip0 & 0xff;
    1258   ip[1] = ip1 & 0xff;
    1259   ip[2] = ip2 & 0xff;
    1260   ip[3] = ip3 & 0xff;
    1261   port = (char *)&(info->data_addr.sin_port);
    1262   port[0] = port0 & 0xff;
    1263   port[1] = port1 & 0xff;
    1264   info->data_addr.sin_family = AF_INET;
     1532command_port(FTPD_SessionInfo_t *info, char const *args)
     1533{
     1534  enum { NUM_FIELDS = 6 };
     1535  unsigned int a[NUM_FIELDS];
     1536  int n;
     1537
     1538  close_data_socket(info);
     1539
     1540  n = sscanf(args, "%u,%u,%u,%u,%u,%u", a+0, a+1, a+2, a+3, a+4, a+5);
     1541  if(NUM_FIELDS == n)
     1542  {
     1543    int i;
     1544    unsigned8 b[NUM_FIELDS];
     1545
     1546    for(i = 0; i < NUM_FIELDS; ++i)
     1547    {
     1548      if(a[i] > 255)
     1549        break;
     1550      b[i] = (unsigned8)a[i];
     1551    }
     1552
     1553    if(i == NUM_FIELDS)
     1554    {
     1555      /* Note: while it contradicts with RFC959, we don't allow PORT command
     1556       * to specify IP address different than those of the originating client
     1557       * for the sake of safety. */
     1558      unsigned32 const *ip   = (unsigned32 *)b;
     1559      if(*ip == info->def_addr.sin_addr.s_addr)
     1560      {
     1561        info->data_addr.sin_addr.s_addr = *ip;
     1562        info->data_addr.sin_port        = *(unsigned16 *)(b + 4);
     1563        info->data_addr.sin_family      = AF_INET;
     1564        memset(info->data_addr.sin_zero, 0, sizeof(info->data_addr.sin_zero));
     1565
     1566        info->use_default = 0;
     1567        send_reply(info, 200, "PORT command successful.");
     1568        return; /* success */
     1569      }
     1570      else
     1571      {
     1572        send_reply(info, 425, "Address doesn't match peer's IP.");
     1573        return;
     1574      }
     1575    }
     1576  }
     1577  send_reply(info, 501, "Syntax error.");
     1578}
     1579
     1580
     1581/*PAGE
     1582 *
     1583 * command_pasv
     1584 *
     1585 * Handle FTP PASV command.
     1586 * Open socket, listen for and accept connection on it.
     1587 *
     1588 * Input parameters:
     1589 *   info - corresponding SessionInfo structure
     1590 *
     1591 * Output parameters:
     1592 *   info->pasv_socket is set to the descriptor of the data socket
     1593 */
     1594static void
     1595command_pasv(FTPD_SessionInfo_t *info)
     1596{
     1597  int s = -1;
     1598  int err = 1;
     1599
     1600  close_data_socket(info);
     1601
     1602  s = socket(PF_INET, SOCK_STREAM, 0);
     1603  if (s < 0)
     1604    syslog(LOG_ERR, "ftpd: Error creating PASV socket: %s", serr());
     1605  else
     1606  {
     1607    struct sockaddr_in addr;
     1608    int addrLen = sizeof(addr);
     1609
     1610    addr = info->ctrl_addr;
     1611    addr.sin_port = htons(0);
     1612
     1613    if (0 > bind(s, (struct sockaddr *)&addr, addrLen))
     1614      syslog(LOG_ERR, "ftpd: Error binding PASV socket: %s", serr());
     1615    else if (0 > listen(s, 1))
     1616      syslog(LOG_ERR, "ftpd: Error listening on PASV socket: %s", serr());
     1617    else
     1618    {
     1619      char buf[FTPD_BUFSIZE];
     1620      unsigned char const *ip, *p;
     1621
     1622      getsockname(s, (struct sockaddr *)&addr, &addrLen);
     1623      ip = (unsigned char const*)&(addr.sin_addr);
     1624      p  = (unsigned char const*)&(addr.sin_port);
     1625      snprintf(buf, FTPD_BUFSIZE, "Entering passive mode (%u,%u,%u,%u,%u,%u).",
     1626        ip[0], ip[1], ip[2], ip[3], p[0], p[1]);
     1627      send_reply(info, 227, buf);
     1628
     1629      info->pasv_socket = accept(s, (struct sockaddr *)&addr, &addrLen);
     1630      close_socket(s);
     1631      if (0 > info->pasv_socket)
     1632        syslog(LOG_ERR, "ftpd: Error accepting PASV connection: %s", serr());
     1633      else
     1634        err = 0;
     1635    }
     1636  }
     1637  if(err)
     1638  {
     1639    /* (OSV) The note is from FreeBSD FTPD.
     1640     * Note: a response of 425 is not mentioned as a possible response to
     1641     * the PASV command in RFC959.  However, it has been blessed as a
     1642     * legitimate response by Jon Postel in a telephone conversation
     1643     * with Rick Adams on 25 Jan 89. */
     1644    send_reply(info, 425, "Can't open passive connection.");
     1645    close_socket(s);
     1646  }
    12651647}
    12661648
     
    12771659 * Output parameters:
    12781660 *   p  - is changed to point to first non-option argument
    1279  *
    12801661 */
    12811662static void
     
    12831664{
    12841665  char* buf = *p;
     1666  char* last = NULL;
    12851667  while(1) {
    1286     while(*buf == ' ')
     1668    while(isspace(*buf))
    12871669      ++buf;
    12881670    if(*buf == '-') {
    12891671      if(*++buf == '-') { /* `--' should terminate options */
     1672        if(isspace(*++buf)) {
     1673          last = buf;
     1674          do ++buf;
     1675          while(isspace(*buf));
     1676          break;
     1677        }
     1678      }
     1679      while(*buf && !isspace(*buf))
    12901680        ++buf;
    1291         while(*buf == ' ')
    1292           ++buf;
    1293         break;
    1294       }
    1295       while(*buf != ' ' && *buf != '\0')
    1296         ++buf;
     1681      last = buf;
    12971682    }
    12981683    else
    12991684      break;
    13001685  }
     1686  if(last)
     1687    *last = '\0';
    13011688  *p = buf;
    13021689}
    13031690
    1304 /**************************************************************************
    1305  * Function: parse_command                                     *
    1306  **************************************************************************
    1307  * Description:                                                           *
    1308  *                                                                        *
    1309  *    Here, we parse the commands that have come through the control      *
    1310  *    connection.                                                         *
    1311  *                                                                        *
    1312  * FIXME: This section is somewhat of a hack.  We should have a better    *
    1313  *        way to parse commands.                                          *
    1314  *                                                                        *
    1315  * Inputs:                                                                *
    1316  *                                                                        *
    1317  *    char *bufr     - Pointer to the buffer which contains the command   *
    1318  *                     text.                                              *
    1319  *                                                                        *
    1320  * Output:                                                                *
    1321  *                                                                        *
    1322  *    none                                                                *
    1323  *                                                                        *
    1324  *************************************************************************/
     1691/*PAGE
     1692 *
     1693 * split_command
     1694 *
     1695 * Split command into command itself, options, and arguments. Command itself
     1696 * is converted to upper case.
     1697 *
     1698 * Input parameters:
     1699 *   buf - initial command string
     1700 *
     1701 * Output parameter:
     1702 *   buf  - is modified by inserting '\0' at ends of split entities
     1703 *   cmd  - upper-cased command code
     1704 *   opts - string containing all the options
     1705 *   args - string containing all the arguments
     1706 */
     1707void
     1708split_command(char *buf, char **cmd, char **opts, char **args)
     1709{
     1710  char* eoc;
     1711  char* p = buf;
     1712  while(isspace(*p))
     1713    ++p;
     1714  *cmd = p;
     1715  while(*p && !isspace(*p))
     1716  {
     1717    *p = toupper(*p);
     1718    ++p;
     1719  }
     1720  eoc = p;
     1721  if(*p)
     1722    *p++ = '\0';
     1723  while(isspace(*p))
     1724    ++p;
     1725  *opts = p;
     1726  skip_options(&p);
     1727  *args = p;
     1728  if(*opts == p)
     1729    *opts = eoc;
     1730  while(*p && *p != '\r' && *p != '\n')
     1731    ++p;
     1732  if(*p)
     1733    *p++ = '\0';
     1734}
     1735
     1736/*PAGE
     1737 *
     1738 * exec_command
     1739 *
     1740 * Parse and execute FTP command.
     1741 *
     1742 * FIXME: This section is somewhat of a hack.  We should have a better
     1743 *        way to parse commands.
     1744 *
     1745 * Input parameters:
     1746 *   info - corresponding SessionInfo structure
     1747 *   cmd  - command to be executed (upper-case)
     1748 *   args - arguments of the command
     1749 *   buf  - beginning of buffer where 'cmd' and 'args' reside.
     1750 *
     1751 * Output parameters:
     1752 *    NONE
     1753 */
    13251754static void
    1326 parse_command(FTPD_SessionInfo_t *info, char *bufr)
     1755exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args, char* buf)
    13271756{
    13281757  char fname[FTPD_BUFSIZE];
    1329 
    1330   if (!strncmp("PORT", bufr, 4))
    1331   {
    1332     send_reply(info, 200, "PORT command successful.");
    1333     command_port(info, &bufr[5]);
    1334   }
    1335   else if (!strncmp("RETR", bufr, 4))
    1336   {
    1337     sscanf(&bufr[5], "%254s", fname);
     1758  int wrong_command = 0;
     1759
     1760  if (!strcmp("PORT", cmd))
     1761  {
     1762    command_port(info, args);
     1763  }
     1764  else if (!strcmp("PASV", cmd))
     1765  {
     1766    command_pasv(info);
     1767  }
     1768  else if (!strcmp("RETR", cmd))
     1769  {
     1770    sscanf(args, "%254s", fname);
    13381771    command_retrieve(info, fname);
    13391772  }
    1340   else if (!strncmp("STOR", bufr, 4))
    1341   {
    1342     sscanf(&bufr[5], "%254s", fname);
     1773  else if (!strcmp("STOR", cmd))
     1774  {
     1775    sscanf(args, "%254s", fname);
    13431776    command_store(info, fname);
    13441777  }
    1345   else if (!strncmp("LIST", bufr, 4))
    1346   {
    1347     bufr += 4;
    1348     skip_options(&bufr);
    1349     sscanf(bufr, "%254s", fname);
     1778  else if (!strcmp("LIST", cmd))
     1779  {
     1780    sscanf(args, "%254s", fname);
    13501781    command_list(info, fname, 1);
    13511782  }
    1352   else if (!strncmp("NLST", bufr, 4))
    1353   {
    1354     bufr += 4;
    1355     skip_options(&bufr);
    1356     sscanf(bufr, "%254s", fname);
     1783  else if (!strcmp("NLST", cmd))
     1784  {
     1785    sscanf(args, "%254s", fname);
    13571786    command_list(info, fname, 0);
    13581787  }
    1359   else if (!strncmp("MDTM", bufr, 4))
    1360   {
    1361     bufr += 4;
    1362     skip_options(&bufr);
    1363     sscanf(bufr, "%254s", fname);
     1788  else if (!strcmp("MDTM", cmd))
     1789  {
     1790    sscanf(args, "%254s", fname);
    13641791    command_mdtm(info, fname);
    13651792  }
    1366   else if (!strncmp("USER", bufr, 4))
    1367   {
    1368     send_reply(info, 230, "User logged in.");
    1369   }
    1370   else if (!strncmp("SYST", bufr, 4))
    1371   {
    1372     send_reply(info, 240, FTPD_SYSTYPE);
    1373   }
    1374   else if (!strncmp("TYPE", bufr, 4))
    1375   {
    1376     if (bufr[5] == 'I')
     1793  else if (!strcmp("SYST", cmd))
     1794  {
     1795    send_reply(info, 215, FTPD_SYSTYPE);
     1796  }
     1797  else if (!strcmp("TYPE", cmd))
     1798  {
     1799    if (args[0] == 'I')
    13771800    {
    13781801      info->xfer_mode = TYPE_I;
    13791802      send_reply(info, 200, "Type set to I.");
    13801803    }
    1381     else if (bufr[5] == 'A')
    1382     {
    1383       /* FIXME: ASCII mode isn't actually supported yet. */
     1804    else if (args[0] == 'A')
     1805    {
    13841806      info->xfer_mode = TYPE_A;
    13851807      send_reply(info, 200, "Type set to A.");
     
    13911813    }
    13921814  }
    1393   else if (!strncmp("PASS", bufr, 4))
     1815  else if (!strcmp("USER", cmd) || !strcmp("PASS", cmd))
    13941816  {
    13951817    send_reply(info, 230, "User logged in.");
    13961818  }
    1397   else if (!strncmp("DELE", bufr, 4))
    1398   {
    1399     sscanf(&bufr[4], "%254s", fname);
    1400     if (unlink(fname) == 0)
     1819  else if (!strcmp("DELE", cmd))
     1820  {
     1821    if(!can_write())
     1822    {
     1823      send_reply(info, 550, "Access denied.");
     1824    }
     1825    else if (
     1826      1 == sscanf(args, "%254s", fname) &&
     1827      NULL != make_path(buf, info->cwd, fname) &&
     1828      unlink(buf) == 0)
    14011829    {
    14021830      send_reply(info, 257, "DELE successful.");
     
    14071835    }
    14081836  }
    1409   else if (!strncmp("SITE CHMOD", bufr, 10))
    1410   {
    1411     int mask;
    1412 
    1413     sscanf(&bufr[11], "%o %254s", &mask, fname);
    1414     if (chmod(fname, (mode_t)mask) == 0)
    1415     {
    1416       send_reply(info, 257, "CHMOD successful.");
     1837  else if (!strcmp("SITE", cmd))
     1838  {
     1839    char* opts;
     1840    split_command(args, &cmd, &opts, &args);
     1841    if(!strcmp("CHMOD", cmd))
     1842    {
     1843      int mask;
     1844
     1845      if(!can_write())
     1846      {
     1847        send_reply(info, 550, "Access denied.");
     1848      }
     1849      else if(
     1850        2 == sscanf(args, "%o %254s", &mask, fname) &&
     1851        NULL != make_path(buf, info->cwd, fname) &&
     1852        chmod(buf, (mode_t)mask) == 0)
     1853      {
     1854        send_reply(info, 257, "CHMOD successful.");
     1855      }
     1856      else
     1857      {
     1858        send_reply(info, 550, "CHMOD failed.");
     1859      }
    14171860    }
    14181861    else
    1419     {
    1420       send_reply(info, 550, "CHMOD failed.");
    1421     }
    1422   }
    1423   else if (!strncmp("RMD", bufr, 3))
    1424   {
    1425     sscanf(&bufr[4], "%254s", fname);
    1426     if (rmdir(fname) == 0)
     1862      wrong_command = 1;
     1863  }
     1864  else if (!strcmp("RMD", cmd))
     1865  {
     1866    if(!can_write())
     1867    {
     1868      send_reply(info, 550, "Access denied.");
     1869    }
     1870    else if (
     1871      1 == sscanf(args, "%254s", fname) &&
     1872      NULL != make_path(buf, info->cwd, fname) &&
     1873      rmdir(buf) == 0)
    14271874    {
    14281875      send_reply(info, 257, "RMD successful.");
     
    14331880    }
    14341881  }
    1435   else if (!strncmp("MKD", bufr, 3))
    1436   {
    1437     sscanf(&bufr[4], "%254s", fname);
    1438     if (mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
     1882  else if (!strcmp("MKD", cmd))
     1883  {
     1884    if(!can_write())
     1885    {
     1886      send_reply(info, 550, "Access denied.");
     1887    }
     1888    else if (
     1889      1 == sscanf(args, "%254s", fname) &&
     1890      NULL != make_path(buf, info->cwd, fname) &&
     1891      mkdir(buf, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
    14391892    {
    14401893      send_reply(info, 257, "MKD successful.");
     
    14451898    }
    14461899  }
    1447   else if (!strncmp("CWD", bufr, 3))
    1448   {
    1449     sscanf(&bufr[4], "%254s", fname);
     1900  else if (!strcmp("CWD", cmd))
     1901  {
     1902    sscanf(args, "%254s", fname);
    14501903    rtems_ftpd_cwd(info, fname);
    14511904  }
    1452   else if (!strncmp("CDUP", bufr, 4))
     1905  else if (!strcmp("CDUP", cmd))
    14531906  {
    14541907    rtems_ftpd_cwd(info, "..");
    14551908  }
    1456   else if (!strncmp("PWD", bufr, 3))
     1909  else if (!strcmp("PWD", cmd))
    14571910  {
    14581911    char const* cwd = "/";
    14591912    if(info->cwd[0])
    14601913      cwd = info->cwd;
    1461     snprintf(bufr, FTPD_BUFSIZE,
     1914    snprintf(buf, FTPD_BUFSIZE,
    14621915      "\"%s\" is the current directory.", cwd);
    1463     send_reply(info, 250, bufr);
     1916    send_reply(info, 250, buf);
    14641917  }
    14651918  else
    1466   {
    1467     send_reply(info, 500, "Unrecognized/unsupported command.");
    1468   }
    1469 }
    1470 
    1471 
    1472 /**************************************************************************
    1473  * Function: session                                           *
    1474  **************************************************************************
    1475  * Description:                                                           *
    1476  *                                                                        *
    1477  *    This task is started when the FTP daemon gets a service request     *
    1478  *    from a remote machine.  Here, we watch for commands that will       *
    1479  *    come through the "control" connection.  These commands are then     *
    1480  *    parsed and executed until the connection is closed, either          *
    1481  *    unintentionally or intentionally with the "QUIT" command.           *
    1482  *                                                                        *
    1483  *                                                                        *
    1484  * Inputs:                                                                *
    1485  *                                                                        *
    1486  *    rtems_task_argument arg - The daemon task passes the socket         *
    1487  *                              which serves as the control connection.   *
    1488  *                                                                        *
    1489  * Output:                                                                *
    1490  *                                                                        *
    1491  *    none                                                                *
    1492  *                                                                        *
    1493  *************************************************************************/
     1919    wrong_command = 1;
     1920
     1921  if(wrong_command)
     1922    send_reply(info, 500, "Command not understood.");
     1923}
     1924
     1925
     1926/*PAGE
     1927 *
     1928 * session
     1929 *
     1930 * This task handles single session.  It is waked up when the FTP daemon gets a
     1931 * service request from a remote machine.  Here, we watch for commands that
     1932 * will come through the control connection.  These commands are then parsed
     1933 * and executed until the connection is closed, either unintentionally or
     1934 * intentionally with the "QUIT" command.
     1935 *
     1936 * Input parameters:
     1937 *   arg - pointer to corresponding SessionInfo.
     1938 *
     1939 * Output parameters:
     1940 *   NONE
     1941 */
    14941942static void
    14951943session(rtems_task_argument arg)
    14961944{
    1497   char cmd[FTPD_BUFSIZE];
    1498   FTPD_SessionInfo_t  *info = (FTPD_SessionInfo_t  *)arg;
    1499   rtems_event_set set;
    1500 
    1501   while(1) {
     1945  FTPD_SessionInfo_t  *const info = (FTPD_SessionInfo_t  *)arg;
     1946
     1947  while(1)
     1948  {
     1949    rtems_event_set set;
     1950
    15021951    rtems_event_receive(FTPD_RTEMS_EVENT, RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT,
    15031952      &set);
     
    15051954    send_reply(info, 220, FTPD_SERVER_MESSAGE);
    15061955
    1507     info->cwd[0] = 0;
    1508     info->xfer_mode = TYPE_I;
    1509 
    15101956    while (1)
    15111957    {
    1512       if (fgets(cmd, FTPD_BUFSIZE, info->ctrl_fp) == NULL)
     1958      char buf[FTPD_BUFSIZE];
     1959      char *cmd, *opts, *args;
     1960
     1961      if (fgets(buf, FTPD_BUFSIZE, info->ctrl_fp) == NULL)
    15131962      {
    15141963        syslog(LOG_INFO, "ftpd: Connection aborted.");
     
    15161965      }
    15171966
    1518       if (!strncmp("QUIT", cmd, 4))
     1967      split_command(buf, &cmd, &opts, &args);
     1968
     1969      if (!strcmp("QUIT", cmd))
    15191970      {
    15201971        send_reply(info, 221, "Goodbye.");
     
    15231974      else
    15241975      {
    1525         parse_command(info, cmd);
     1976        exec_command(info, cmd, args, buf);
    15261977      }
    15271978    }
    15281979
    15291980    /* Close connection and put ourselves back into the task pool. */
     1981    close_data_socket(info);
    15301982    close_stream(info);
    15311983    task_pool_release(info);
     
    15341986
    15351987
    1536 /**************************************************************************
    1537  * Function: daemon                                            *
    1538  **************************************************************************
    1539  * Description:                                                           *
    1540  *                                                                        *
    1541  *    This task runs in the background forever.  It waits for service     *
    1542  *    requests on the FTP port (port 21).  When a request is received,    *
    1543  *    it opens a new session to handle those requests until the           *
    1544  *    connection is closed.                                               *
    1545  *                                                                        *
    1546  *                                                                        *
    1547  * Inputs:                                                                *
    1548  *                                                                        *
    1549  *    none                                                                *
    1550  *                                                                        *
    1551  * Output:                                                                *
    1552  *                                                                        *
    1553  *    none                                                                *
    1554  *                                                                        *
    1555  *************************************************************************/
     1988/*PAGE
     1989 *
     1990 * daemon
     1991 *
     1992 * This task runs forever.  It waits for service requests on the FTP port
     1993 * (port 21 by default).  When a request is received, it opens a new session
     1994 * to handle those requests until the connection is closed.
     1995 *
     1996 * Input parameters:
     1997 *   NONE
     1998 *
     1999 * Output parameters:
     2000 *   NONE
     2001 */
    15562002static void
    15572003daemon()
     
    15592005  int                 s;
    15602006  int                 addrLen;
    1561   struct sockaddr_in  remoteAddr;
    1562   struct sockaddr_in  localAddr;
    1563   char                sessionID;
     2007  struct sockaddr_in  addr;
    15642008  FTPD_SessionInfo_t  *info = NULL;
    15652009
    1566 
    1567   sessionID = 'a';
    15682010
    15692011  s = socket(PF_INET, SOCK_STREAM, 0);
     
    15712013    syslog(LOG_ERR, "ftpd: Error creating socket: %s", serr());
    15722014
    1573   localAddr.sin_family      = AF_INET;
    1574   localAddr.sin_port        = htons(rtems_ftpd_configuration.port);
    1575   localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    1576   memset(localAddr.sin_zero, '\0', sizeof(localAddr.sin_zero));
    1577 
    1578   if (0 > bind(s, (struct sockaddr *)&localAddr, sizeof(localAddr)))
     2015  addr.sin_family      = AF_INET;
     2016  addr.sin_port        = htons(rtems_ftpd_configuration.port);
     2017  addr.sin_addr.s_addr = htonl(INADDR_ANY);
     2018  memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
     2019
     2020  if (0 > bind(s, (struct sockaddr *)&addr, sizeof(addr)))
    15792021    syslog(LOG_ERR, "ftpd: Error binding control socket: %s", serr());
    1580 
    1581   if (0 > listen(s, 1))
     2022  else if (0 > listen(s, 1))
    15822023    syslog(LOG_ERR, "ftpd: Error listening on control socket: %s", serr());
    1583 
    1584   while (1)
     2024  else while (1)
    15852025  {
    15862026    int ss;
    1587     addrLen = sizeof(remoteAddr);
    1588     ss = accept(s, (struct sockaddr *)&remoteAddr, &addrLen);
     2027    addrLen = sizeof(addr);
     2028    ss = accept(s, (struct sockaddr *)&addr, &addrLen);
    15892029    if (0 > ss)
    1590     {
    15912030      syslog(LOG_ERR, "ftpd: Error accepting control connection: %s", serr());
    1592     }
     2031    else if(!set_socket_timeout(ss, ftpd_timeout))
     2032      close_socket(ss);
    15932033    else
    15942034    {
     
    16002040      else
    16012041      {
    1602         info->socket = ss;
    1603         if ((info->ctrl_fp = fdopen(info->socket, "r+")) == NULL)
     2042        info->ctrl_socket = ss;
     2043        if ((info->ctrl_fp = fdopen(info->ctrl_socket, "r+")) == NULL)
    16042044        {
    16052045          syslog(LOG_ERR, "ftpd: fdopen() on socket failed: %s", serr());
     
    16092049        else
    16102050        {
    1611           /* Wakeup the session task. The task will call task_pool_release
    1612              after it closes connection. */
    1613           rtems_event_send(info->tid, FTPD_RTEMS_EVENT);
     2051          /* Initialize corresponding SessionInfo structure */
     2052          info->def_addr = addr;
     2053          if(0 > getsockname(ss, (struct sockaddr *)&addr, &addrLen))
     2054          {
     2055            syslog(LOG_ERR, "ftpd: getsockname(): %s", serr());
     2056            close_stream(info);
     2057            task_pool_release(info);
     2058          }
     2059          else
     2060          {
     2061            info->use_default = 1;
     2062            info->ctrl_addr  = addr;
     2063            info->pasv_socket = -1;
     2064            info->xfer_mode = TYPE_A;
     2065            info->cwd[0] = '\0';
     2066            info->data_addr.sin_port =
     2067              htons(ntohs(info->ctrl_addr.sin_port) - 1);
     2068            info->idle = ftpd_timeout;
     2069            /* Wakeup the session task.  The task will call task_pool_release
     2070               after it closes connection. */
     2071            rtems_event_send(info->tid, FTPD_RTEMS_EVENT);
     2072          }
    16142073        }
    16152074      }
    16162075    }
    16172076  }
    1618 }
    1619 
    1620 
    1621 /**************************************************************************
    1622  * Function: rtems_ftpd_start                                             *
    1623  **************************************************************************
    1624  * Description:                                                           *
    1625  *                                                                        *
    1626  *    Here, we start the FTPD task which waits for FTP requests and       *
    1627  *    services them.  This procedure returns to its caller once the       *
    1628  *    task is started.                                                    *
    1629  *                                                                        *
    1630  *                                                                        *
    1631  * Inputs:                                                                *
    1632  *                                                                        *
    1633  * Output:                                                                *
    1634  *                                                                        *
    1635  *    int - RTEMS_SUCCESSFUL on successful start of the daemon.           *
    1636  *                                                                        *
    1637  *************************************************************************/
     2077  rtems_task_delete(RTEMS_SELF);
     2078}
     2079
     2080
     2081/*PAGE
     2082 *
     2083 * rtems_ftpd_start
     2084 *
     2085 * Here, we start the FTPD task which waits for FTP requests and services
     2086 * them.  This procedure returns to its caller once the task is started.
     2087 *
     2088 *
     2089 * Input parameters:
     2090 *
     2091 * Output parameters:
     2092 *    returns RTEMS_SUCCESSFUL on successful start of the daemon.
     2093 */
    16382094int
    16392095rtems_initialize_ftpd()
     
    16542110  }
    16552111  priority = rtems_ftpd_configuration.priority;
     2112
     2113  ftpd_timeout = rtems_ftpd_configuration.idle;
     2114  if (ftpd_timeout < 0)
     2115    ftpd_timeout = 0;
     2116  rtems_ftpd_configuration.idle = ftpd_timeout;
     2117
     2118  ftpd_access = rtems_ftpd_configuration.access;
    16562119
    16572120  if (rtems_ftpd_configuration.tasks_count <= 0)
     
    16872150  }
    16882151
    1689   root[0] = '\0';
     2152  ftpd_root[0] = '\0';
    16902153  if (
    16912154    rtems_ftpd_configuration.root &&
     
    16932156    rtems_ftpd_configuration.root[0] == '/')
    16942157  {
    1695     strcpy(root, rtems_ftpd_configuration.root);
    1696     squeeze_path(root);
    1697     rtems_ftpd_configuration.root = root;
     2158    strcpy(ftpd_root, rtems_ftpd_configuration.root);
     2159    squeeze_path(ftpd_root, NULL);
     2160    rtems_ftpd_configuration.root = ftpd_root;
    16982161  }
    16992162
  • cpukit/ftpd/ftpd.h

    re8a7a46 r38371dbe  
    1010
    1111#define FTPD_CONTROL_PORT   21
     12
     13/* FTPD access control flags */
     14enum
     15{
     16  FTPD_NO_WRITE = 0x1,
     17  FTPD_NO_READ  = 0x2,
     18  FTPD_NO_RW    = FTPD_NO_WRITE | FTPD_NO_READ
     19};
    1220
    1321typedef int (*rtems_ftpd_hookfunction)(unsigned char *, unsigned long);
     
    2836   char const              *root;              /* Root for FTPD or 0 for / */
    2937   int                     tasks_count;        /* Max. connections    */
     38   int                     idle;               /* Idle timeout in seoconds
     39                                                  or 0 for no (inf) timeout */
     40   int                     access;             /* 0 - r/w, 1 - read-only,
     41                                                  2 - write-only,
     42                                                  3 - browse-only */
    3043};
    3144
Note: See TracChangeset for help on using the changeset viewer.