ftpd.c

来自「RTEMS (Real-Time Executive for Multiproc」· C语言 代码 · 共 2,063 行 · 第 1/4 页

C
2,063
字号
/* FIXME: 1. Parse command is a hack.  We can do better. *        2. Some sort of access control? *        3. OSV: hooks support seems to be bad, as it requires storing of *           entire input file in memory.  Seem to be better to change it to *           something more reasonable, like having *           'hook_write(void const *buf, int count)' routine that will be *           called multiple times while file is being received. *        4. OSV: Remove hack with "/dev/null"? * *  FTP Server Daemon * *  Submitted by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu> * *  Changed by:   Sergei Organov <osv@javad.ru> (OSV) * *  Changes: * *    2001-01-31        Sergei Organov <osv@javad.ru> * *      * Hacks with current dir and root dir removed in favor of new libio *        support for task-local current and root directories. * *    2001-01-30        Sergei Organov <osv@javad.ru> * *      * Bug in `close_data_socket()' introduced by previous change fixed. *      * `command_pasv()' changed to set timeout on socket we are listening on *        and code fixed to don't close socket twice on error. *      * `serr()' changed to clear `errno'. *      * `data_socket()' changed to clear `errno' before `bind()'. *      * `session()' changed to clear `errno' before processing session. * *    2001-01-29        Sergei Organov <osv@javad.ru> * *      * `close_data_socket()' fixed to close both active and passive sockets *      * Initialize info->data_socket to -1 in `daemon()' *      * Initialize `fname' to empty string  in `exec_command()' * *    2001-01-22        Sergei Organov <osv@javad.ru> * *      * 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. * *    2001-01-17        Sergei Organov <osv@javad.ru> * *      * 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. * *    2001-01-16        Sergei Organov <osv@javad.ru> * *      * 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. * *    2001-01-08        Sergei Organov <osv@javad.ru> * *      * use pool of pre-created threads to handle sessions *      * LIST output now similar to what "/bin/ls -al" would output, thus *        FTP clients could parse it. *      * LIST NAME now works (both for files and directories) *      * keep track of CWD for every session separately *      * ability to specify root directory name in configuration table *      * options sent in commands are ignored, thus LIST -al FILE works *      * added support for NLST, CDUP and MDTM commands *      * buffers are allocated on stack instead of heap where possible *      * drop using of task notepad to pass parameters - use function *        arguments instead *      * various bug-fixes, e.g., use of PF_INET in socket() instead of *        AF_INET, use snprintf() instead of sprintf() everywhere for safety, *        etc. * *  $Id: ftpd.c,v 1.9 2002/09/07 23:07:58 joel Exp $ *//************************************************************************* *                                 ftpd.c ************************************************************************* * Description: * *    This file contains the daemon which services requests that appear *    on the FTP port.  This server is compatible with FTP, but it *    also provides 'hooks' to make it usable in situations where files *    are not used/necessary.  Once the server is started, it runs *    forever. * * *    Organization: * *       The FTP daemon is started upon boot along with a (configurable) *       number of tasks to handle sessions.  It runs all the time and *       waits for connections on the known FTP port (21).  When *       a connection is made, it wakes-up a 'session' task.  That *       session then interacts with the remote host.  When the session *       is complete, the session task goes to sleep.  The daemon still *       runs, however. * * * Supported commands are: * * RETR xxx     - Sends a file from the client. * STOR xxx     - Receives a file from the client.  xxx = filename. * LIST xxx     - Sends a file list to the client. * NLST xxx     - Sends a file list to the client. * USER         - Does nothing. * PASS         - Does nothing. * SYST         - Replies with the system type (`RTEMS'). * DELE xxx     - Delete file xxx. * MKD xxx      - Create directory xxx. * RMD xxx      - Remove directory xxx. * PWD          - Print working directory. * CWD xxx      - Change working directory. * CDUP         - Change to upper directory. * SITE CHMOD xxx yyy - Change permissions on file yyy to xxx. * PORT a,b,c,d,x,y   - Setup for a data port to IP address a.b.c.d *                      and port (x*256 + y). * MDTM xxx     - Send file modification date/time to the client. *                xxx = filename. * PASV         - Use passive mode data connection. * * * The public routines contained in this file are: * *    rtems_initialize_ftpd - Initializes and starts the server daemon, *                            then returns to its caller. * *------------------------------------------------------------------------ * Jake Janovetz * University of Illinois * 1406 West Green Street * Urbana IL  61801 ************************************************************************* * Change History: *  12/01/97   - Creation (JWJ) *  2001-01-08 - Changes by OSV *************************************************************************//************************************************************************* * Meanings of first and second digits of reply codes: * * Reply:  Description: *-------- -------------- *  1yz    Positive preliminary reply.  The action is being started but *         expect another reply before sending another command. *  2yz    Positive completion reply.  A new command can be sent. *  3yz    Positive intermediate reply.  The command has been accepted *         but another command must be sent. *  4yz    Transient negative completion reply.  The requested action did *         not take place, but the error condition is temporary so the *         command can be reissued later. *  5yz    Permanent negative completion reply.  The command was not *         accepted and should not be retried. *------------------------------------------------------------------------- *  x0z    Syntax errors. *  x1z    Information. *  x2z    Connections.  Replies referring to the control or data *         connections. *  x3z    Authentication and accounting.  Replies for the login or *         accounting commands. *  x4z    Unspecified. *  x5z    Filesystem status. *************************************************************************/#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <fcntl.h>#include <dirent.h>#include <errno.h>#include <ctype.h>#include <rtems.h>#include <rtems/rtems_bsdnet.h>#include <rtems/error.h>#include <rtems/libio.h>#include <rtems/userenv.h>#include <syslog.h>#include <sys/types.h>#include <sys/socket.h>#include <arpa/ftp.h>#include <netinet/in.h>#include "ftpd.h"#ifdef __GNUC__/* change to #if 1 to disable syslog entirely */#if 0#undef  syslog#define syslog(a, b, ...) while(0){}#endif#endif#define FTPD_SERVER_MESSAGE  "RTEMS FTP server (Version 1.1-JWJ) ready."#define FTPD_SYSTYPE "UNIX Type: L8"/* Seem to be unused */#if 0#define FTPD_WELCOME_MESSAGE \   "Welcome to the RTEMS FTP server.\n" \   "\n" \   "Login accepted.\n"#endif/* Various buffer sizes */enum{  FTPD_BUFSIZE  = 256,       /* Size for temporary buffers */  FTPD_DATASIZE = 1024,      /* Size for file transfer buffers */  FTPD_STACKSIZE = 8 * 1024, /* Tasks stack size */};/* Event to be used by session tasks for waiting */enum{  FTPD_RTEMS_EVENT = RTEMS_EVENT_1};/* Configuration table */extern struct rtems_ftpd_configuration rtems_ftpd_configuration;/* this is not prototyped in strict ansi mode */FILE *fdopen (int fildes, const char *mode);/*PAGE * SessionInfo structure. * * The following structure is allocated for each session. */typedef struct{  struct sockaddr_in  ctrl_addr;   /* Control connection self address */  struct sockaddr_in  data_addr;   /* Data address set by PORT command */  struct sockaddr_in  def_addr;    /* Default address for data */  int                 use_default; /* 1 - use default address for data */  FILE                *ctrl_fp;    /* File pointer for control connection */  int                 ctrl_socket; /* Socket for ctrl connection */  int                 pasv_socket; /* Socket for PASV connection */  int                 data_socket; /* Socket for data connection */  int                 idle;        /* Timeout in seconds */  int                 xfer_mode;   /* Transfer mode (ASCII/binary) */  rtems_id            tid;         /* Task id */} FTPD_SessionInfo_t;/* * TaskPool structure. */typedef struct{  FTPD_SessionInfo_t    *info;  FTPD_SessionInfo_t    **queue;  int                   count;  int                   head;  int                   tail;  rtems_id              mutex;  rtems_id              sem;} FTPD_TaskPool_t;/* * Task pool instance. */static FTPD_TaskPool_t task_pool;/* * Root directory */static char const* ftpd_root = "/";/* * Default idle timeout for sockets in seconds. */static int ftpd_timeout = 0;/* * Global access flags. */static int ftpd_access = 0;/*PAGE * * serr * * Return error string corresponding to current 'errno'. * */static char const*serr(void){  int err = errno;  errno = 0;  return strerror(err);}/*PAGE * * Utility routines for access control. * */static intcan_read(void){  return (ftpd_access & FTPD_NO_READ) == 0;}static intcan_write(void){  return (ftpd_access & FTPD_NO_WRITE) == 0;}/*PAGE * * Task pool management routines * *//*PAGE * * task_pool_done * * Cleanup task pool. * * Input parameters: *   count - number of entries in task pool to cleanup * * Output parameters: *   NONE * */static voidtask_pool_done(int count){  int i;  for(i = 0; i < count; ++i)    rtems_task_delete(task_pool.info[i].tid);  if(task_pool.info)    free(task_pool.info);  if(task_pool.queue)    free(task_pool.queue);  if(task_pool.mutex != (rtems_id)-1)    rtems_semaphore_delete(task_pool.mutex);  if(task_pool.sem != (rtems_id)-1)    rtems_semaphore_delete(task_pool.sem);  task_pool.info = 0;  task_pool.queue = 0;  task_pool.count = 0;  task_pool.sem = -1;  task_pool.mutex = -1;}/*PAGE * * task_pool_init * * Initialize task pool. * * Input parameters: *   count    - number of entries in task pool to create *   priority - priority tasks are started with * * Output parameters: *   returns 1 on success, 0 on failure. * */static void session(rtems_task_argument arg); /* Forward declare */static inttask_pool_init(int count, rtems_task_priority priority){  int i;  rtems_status_code sc;  char id = 'a';  task_pool.count = 0;  task_pool.head = task_pool.tail = 0;  task_pool.mutex = (rtems_id)-1;  task_pool.sem   = (rtems_id)-1;  sc = rtems_semaphore_create(    rtems_build_name('F', 'T', 'P', 'M'),    1,    RTEMS_DEFAULT_ATTRIBUTES    | RTEMS_BINARY_SEMAPHORE    | RTEMS_INHERIT_PRIORITY    | RTEMS_PRIORITY,    RTEMS_NO_PRIORITY,    &task_pool.mutex);  if(sc == RTEMS_SUCCESSFUL)    sc = rtems_semaphore_create(      rtems_build_name('F', 'T', 'P', 'S'),      count,      RTEMS_DEFAULT_ATTRIBUTES,      RTEMS_NO_PRIORITY,      &task_pool.sem);  if(sc != RTEMS_SUCCESSFUL) {    task_pool_done(0);    syslog(LOG_ERR, "ftpd: Can not create semaphores");    return 0;  }  task_pool.info = (FTPD_SessionInfo_t*)    malloc(sizeof(FTPD_SessionInfo_t) * count);  task_pool.queue = (FTPD_SessionInfo_t**)    malloc(sizeof(FTPD_SessionInfo_t*) * count);  if (NULL == task_pool.info || NULL == task_pool.queue)  {    task_pool_done(0);    syslog(LOG_ERR, "ftpd: Not enough memory");    return 0;  }  for(i = 0; i < count; ++i)  {    FTPD_SessionInfo_t *info = &task_pool.info[i];    sc = rtems_task_create(rtems_build_name('F', 'T', 'P', id),      priority, FTPD_STACKSIZE,      RTEMS_PREEMPT | RTEMS_NO_TIMESLICE |      RTEMS_NO_ASR | RTEMS_INTERRUPT_LEVEL(0),      RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,      &info->tid);    if (sc == RTEMS_SUCCESSFUL)    {      sc = rtems_task_start(        info->tid, session, (rtems_task_argument)info);      if (sc != RTEMS_SUCCESSFUL)        task_pool_done(i);    }    else      task_pool_done(i + 1);    if (sc != RTEMS_SUCCESSFUL)    {      syslog(LOG_ERR, "ftpd: Could not create/start FTPD session: %s",        rtems_status_text(sc));      return 0;    }    task_pool.queue[i] = task_pool.info + i;    if (++id > 'z')      id = 'a';  }  task_pool.count = count;  return 1;}/*PAGE * * task_pool_obtain * * Obtain free task from task pool. * * Input parameters: *   NONE * * Output parameters: *   returns pointer to the corresponding SessionInfo structure on success, *           NULL if there are no free tasks in the pool. * */static FTPD_SessionInfo_t*task_pool_obtain(){  FTPD_SessionInfo_t* info = 0;  rtems_status_code sc;  sc = rtems_semaphore_obtain(task_pool.sem, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT);  if (sc == RTEMS_SUCCESSFUL)  {    rtems_semaphore_obtain(task_pool.mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);    info = task_pool.queue[task_pool.head];    if(++task_pool.head >= task_pool.count)      task_pool.head = 0;    rtems_semaphore_release(task_pool.mutex);  }  return info;}/*PAGE * * task_pool_release * * Return task obtained by 'obtain()' back to the task pool. * * Input parameters: *   info  - pointer to corresponding SessionInfo structure. * * Output parameters: *   NONE * */static voidtask_pool_release(FTPD_SessionInfo_t* info){  rtems_semaphore_obtain(task_pool.mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?