ftpd.c

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

C
2,063
字号
 * * Input parameters: *   p  - pointer to pointer to command * * Output parameters: *   p  - is changed to point to first non-option argument */static voidskip_options(char **p){  char* buf = *p;  char* last = NULL;  while(1) {    while(isspace(*buf))      ++buf;    if(*buf == '-') {      if(*++buf == '-') { /* `--' should terminate options */        if(isspace(*++buf)) {          last = buf;          do ++buf;          while(isspace(*buf));          break;        }      }      while(*buf && !isspace(*buf))        ++buf;      last = buf;    }    else      break;  }  if(last)    *last = '\0';  *p = buf;}/*PAGE * * split_command * * Split command into command itself, options, and arguments. Command itself * is converted to upper case. * * Input parameters: *   buf - initial command string * * Output parameter: *   buf  - is modified by inserting '\0' at ends of split entities *   cmd  - upper-cased command code *   opts - string containing all the options *   args - string containing all the arguments */voidsplit_command(char *buf, char **cmd, char **opts, char **args){  char* eoc;  char* p = buf;  while(isspace(*p))    ++p;  *cmd = p;  while(*p && !isspace(*p))  {    *p = toupper(*p);    ++p;  }  eoc = p;  if(*p)    *p++ = '\0';  while(isspace(*p))    ++p;  *opts = p;  skip_options(&p);  *args = p;  if(*opts == p)    *opts = eoc;  while(*p && *p != '\r' && *p != '\n')    ++p;  if(*p)    *p++ = '\0';}/*PAGE * * exec_command * * Parse and execute FTP command. * * FIXME: This section is somewhat of a hack.  We should have a better *        way to parse commands. * * Input parameters: *   info - corresponding SessionInfo structure *   cmd  - command to be executed (upper-case) *   args - arguments of the command * * Output parameters: *    NONE */static voidexec_command(FTPD_SessionInfo_t *info, char* cmd, char* args){  char fname[FTPD_BUFSIZE];  int wrong_command = 0;  fname[0] = '\0';  if (!strcmp("PORT", cmd))  {    command_port(info, args);  }  else if (!strcmp("PASV", cmd))  {    command_pasv(info);  }  else if (!strcmp("RETR", cmd))  {    sscanf(args, "%254s", fname);    command_retrieve(info, fname);  }  else if (!strcmp("STOR", cmd))  {    sscanf(args, "%254s", fname);    command_store(info, fname);  }  else if (!strcmp("LIST", cmd))  {    sscanf(args, "%254s", fname);    command_list(info, fname, 1);  }  else if (!strcmp("NLST", cmd))  {    sscanf(args, "%254s", fname);    command_list(info, fname, 0);  }  else if (!strcmp("MDTM", cmd))  {    sscanf(args, "%254s", fname);    command_mdtm(info, fname);  }  else if (!strcmp("SYST", cmd))  {    send_reply(info, 215, FTPD_SYSTYPE);  }  else if (!strcmp("TYPE", cmd))  {    if (args[0] == 'I')    {      info->xfer_mode = TYPE_I;      send_reply(info, 200, "Type set to I.");    }    else if (args[0] == 'A')    {      info->xfer_mode = TYPE_A;      send_reply(info, 200, "Type set to A.");    }    else    {      info->xfer_mode = TYPE_I;      send_reply(info, 504, "Type not implemented.  Set to I.");    }  }  else if (!strcmp("USER", cmd) || !strcmp("PASS", cmd))  {    send_reply(info, 230, "User logged in.");  }  else if (!strcmp("DELE", cmd))  {    if(!can_write())    {      send_reply(info, 550, "Access denied.");    }    else if (      1 == sscanf(args, "%254s", fname) &&      unlink(fname) == 0)    {      send_reply(info, 257, "DELE successful.");    }    else    {      send_reply(info, 550, "DELE failed.");    }  }  else if (!strcmp("SITE", cmd))  {    char* opts;    split_command(args, &cmd, &opts, &args);    if(!strcmp("CHMOD", cmd))    {      int mask;      if(!can_write())      {        send_reply(info, 550, "Access denied.");      }      else if(        2 == sscanf(args, "%o %254s", &mask, fname) &&        chmod(fname, (mode_t)mask) == 0)      {        send_reply(info, 257, "CHMOD successful.");      }      else      {        send_reply(info, 550, "CHMOD failed.");      }    }    else      wrong_command = 1;  }  else if (!strcmp("RMD", cmd))  {    if(!can_write())    {      send_reply(info, 550, "Access denied.");    }    else if (      1 == sscanf(args, "%254s", fname) &&      rmdir(fname) == 0)    {      send_reply(info, 257, "RMD successful.");    }    else    {      send_reply(info, 550, "RMD failed.");    }  }  else if (!strcmp("MKD", cmd))  {    if(!can_write())    {      send_reply(info, 550, "Access denied.");    }    else if (      1 == sscanf(args, "%254s", fname) &&      mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)    {      send_reply(info, 257, "MKD successful.");    }    else    {      send_reply(info, 550, "MKD failed.");    }  }  else if (!strcmp("CWD", cmd))  {    sscanf(args, "%254s", fname);    command_cwd(info, fname);  }  else if (!strcmp("CDUP", cmd))  {    command_cwd(info, "..");  }  else if (!strcmp("PWD", cmd))  {    command_pwd(info);  }  else    wrong_command = 1;  if(wrong_command)    send_reply(info, 500, "Command not understood.");}/*PAGE * * session * * This task handles single session.  It is waked up when the FTP daemon gets a * service request from a remote machine.  Here, we watch for commands that * will come through the control connection.  These commands are then parsed * and executed until the connection is closed, either unintentionally or * intentionally with the "QUIT" command. * * Input parameters: *   arg - pointer to corresponding SessionInfo. * * Output parameters: *   NONE */static voidsession(rtems_task_argument arg){  FTPD_SessionInfo_t  *const info = (FTPD_SessionInfo_t  *)arg;  int chroot_made = 0;  rtems_libio_set_private_env();  /* chroot() can fail here because the directory may not exist yet. */  chroot_made = chroot(ftpd_root) == 0;  while(1)  {    rtems_event_set set;    rtems_event_receive(FTPD_RTEMS_EVENT, RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT,      &set);    chroot_made = chroot_made || chroot(ftpd_root) == 0;    chdir("/");    errno = 0;    send_reply(info, 220, FTPD_SERVER_MESSAGE);    while (1)    {      char buf[FTPD_BUFSIZE];      char *cmd, *opts, *args;      if (fgets(buf, FTPD_BUFSIZE, info->ctrl_fp) == NULL)      {        syslog(LOG_INFO, "ftpd: Connection aborted.");        break;      }      split_command(buf, &cmd, &opts, &args);      if (!strcmp("QUIT", cmd))      {        send_reply(info, 221, "Goodbye.");        break;      }      else      {        exec_command(info, cmd, args);      }    }    /* Close connection and put ourselves back into the task pool. */    close_data_socket(info);    close_stream(info);    task_pool_release(info);  }}/*PAGE * * daemon * * This task runs forever.  It waits for service requests on the FTP port * (port 21 by default).  When a request is received, it opens a new session * to handle those requests until the connection is closed. * * Input parameters: *   NONE * * Output parameters: *   NONE */static voiddaemon(){  int                 s;  int                 addrLen;  struct sockaddr_in  addr;  FTPD_SessionInfo_t  *info = NULL;  s = socket(PF_INET, SOCK_STREAM, 0);  if (s < 0)    syslog(LOG_ERR, "ftpd: Error creating socket: %s", serr());  addr.sin_family      = AF_INET;  addr.sin_port        = htons(rtems_ftpd_configuration.port);  addr.sin_addr.s_addr = htonl(INADDR_ANY);  memset(addr.sin_zero, 0, sizeof(addr.sin_zero));  if (0 > bind(s, (struct sockaddr *)&addr, sizeof(addr)))    syslog(LOG_ERR, "ftpd: Error binding control socket: %s", serr());  else if (0 > listen(s, 1))    syslog(LOG_ERR, "ftpd: Error listening on control socket: %s", serr());  else while (1)  {    int ss;    addrLen = sizeof(addr);    ss = accept(s, (struct sockaddr *)&addr, &addrLen);    if (0 > ss)      syslog(LOG_ERR, "ftpd: Error accepting control connection: %s", serr());    else if(!set_socket_timeout(ss, ftpd_timeout))      close_socket(ss);    else    {      info = task_pool_obtain();      if (NULL == info)      {        close_socket(ss);      }      else      {        info->ctrl_socket = ss;        if ((info->ctrl_fp = fdopen(info->ctrl_socket, "r+")) == NULL)        {          syslog(LOG_ERR, "ftpd: fdopen() on socket failed: %s", serr());          close_stream(info);          task_pool_release(info);        }        else        {          /* Initialize corresponding SessionInfo structure */          info->def_addr = addr;          if(0 > getsockname(ss, (struct sockaddr *)&addr, &addrLen))          {            syslog(LOG_ERR, "ftpd: getsockname(): %s", serr());            close_stream(info);            task_pool_release(info);          }          else          {            info->use_default = 1;            info->ctrl_addr  = addr;            info->pasv_socket = -1;            info->data_socket = -1;            info->xfer_mode   = TYPE_A;            info->data_addr.sin_port =              htons(ntohs(info->ctrl_addr.sin_port) - 1);            info->idle = ftpd_timeout;            /* Wakeup the session task.  The task will call task_pool_release               after it closes connection. */            rtems_event_send(info->tid, FTPD_RTEMS_EVENT);          }        }      }    }  }  rtems_task_delete(RTEMS_SELF);}/*PAGE * * rtems_ftpd_start * * Here, we start the FTPD task which waits for FTP requests and services * them.  This procedure returns to its caller once the task is started. * * * Input parameters: * * Output parameters: *    returns RTEMS_SUCCESSFUL on successful start of the daemon. */intrtems_initialize_ftpd(){  rtems_status_code   sc;  rtems_id            tid;  rtems_task_priority priority;  int count;  if (rtems_ftpd_configuration.port == 0)  {    rtems_ftpd_configuration.port = FTPD_CONTROL_PORT;  }  if (rtems_ftpd_configuration.priority == 0)  {    rtems_ftpd_configuration.priority = 40;  }  priority = rtems_ftpd_configuration.priority;  ftpd_timeout = rtems_ftpd_configuration.idle;  if (ftpd_timeout < 0)    ftpd_timeout = 0;  rtems_ftpd_configuration.idle = ftpd_timeout;  ftpd_access = rtems_ftpd_configuration.access;  if (rtems_ftpd_configuration.tasks_count <= 0)    rtems_ftpd_configuration.tasks_count = 1;  count = rtems_ftpd_configuration.tasks_count;  if (!task_pool_init(count, priority))  {    syslog(LOG_ERR, "ftpd: Could not initialize task pool.");    return RTEMS_UNSATISFIED;  }  sc = rtems_task_create(rtems_build_name('F', 'T', 'P', 'D'),    priority, FTPD_STACKSIZE,    RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR |    RTEMS_INTERRUPT_LEVEL(0),    RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,    &tid);  if (sc == RTEMS_SUCCESSFUL)  {    sc = rtems_task_start(tid, daemon, 0);    if (sc != RTEMS_SUCCESSFUL)      rtems_task_delete(tid);  }  if (sc != RTEMS_SUCCESSFUL)  {    task_pool_done(count);    syslog(LOG_ERR, "ftpd: Could not create/start FTP daemon: %s",      rtems_status_text(sc));    return RTEMS_UNSATISFIED;  }  ftpd_root = "/";  if (    rtems_ftpd_configuration.root &&    rtems_ftpd_configuration.root[0] == '/'  )    ftpd_root = rtems_ftpd_configuration.root;  rtems_ftpd_configuration.root = ftpd_root;  syslog(LOG_INFO, "ftpd: FTP daemon started (%d session%s max)",    count, ((count > 1) ? "s" : ""));  return RTEMS_SUCCESSFUL;}

⌨️ 快捷键说明

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