ftpd.c

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

C
2,063
字号
      fd = creat(filename,        S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);    if (0 > fd)    {      send_reply(info, 550, "Error creating file.");      close_data_socket(info);      return;    }    if(info->xfer_mode == TYPE_I)    {      while ((n = recv(s, buf, FTPD_DATASIZE, 0)) > 0)      {        if (wrt(fd, buf, n) != n)        {          res = 0;          break;        }      }    }    else if(info->xfer_mode == TYPE_A)    {      int rest = 0;      int pended_cr = 0;      while (res && rest == 0 && (n = recv(s, buf, FTPD_DATASIZE, 0)) > 0)      {        char const* e = buf;        char const* b;        int i;        rest = n;        if(pended_cr && *e != '\n')        {          char const lf = '\r';          pended_cr = 0;          if(wrt(fd, &lf, 1) != 1)          {            res = 0;            break;          }        }        do        {          int count;          int sub = 0;          b = e;          for(i = 0; i < rest; ++i, ++e)          {            int pcr = pended_cr;            pended_cr = 0;            if(*e == '\r')            {              pended_cr = 1;            }            else if(*e == '\n')            {              if(pcr)              {                sub = 2;                ++i;                ++e;                break;              }              ++bare_lfs;            }          }          if(res == 0)            break;          count = i - sub - pended_cr;          if(count > 0 && wrt(fd, b, count) != count)          {            res = 0;            break;          }          if(sub == 2 && wrt(fd, e - 1, 1) != 1)            res = 0;        }        while((rest -= i) > 0);      }    }    if (0 > close(fd) || res == 0)    {      send_reply(info, 452, "Error writing file.");      close_data_socket(info);      return;    }  }  if (bare_lfs > 0)  {    snprintf(buf, FTPD_BUFSIZE,      "Transfer complete. WARNING! %d bare linefeeds received in ASCII mode.",      bare_lfs);    send_reply(info, 226, buf);  }  else    send_reply(info, 226, "Transfer complete.");  close_data_socket(info);}/*PAGE * * send_dirline * * Sends one line of LIST command reply corresponding to single file. * * Input parameters: *   s - socket descriptor to send data to *   wide - if 0, send only file name.  If not 0, send 'stat' info as well in *          "ls -l" format. *   curTime - current time *   path - path to be prepended to what is given by 'add' *   add  - path to be appended to what is given by 'path', the resulting path *          is then passed to 'stat()' routine *   name - file name to be reported in output *   buf  - buffer for temporary data * * Output parameters: *   returns 0 on failure, 1 on success * */static intsend_dirline(int s, int wide, time_t curTime, char const* path,  char const* add, char const* fname, char* buf){  if(wide)  {    struct stat stat_buf;    int plen = strlen(path);    int alen = strlen(add);    if(plen == 0)    {      buf[plen++] = '/';      buf[plen] = '\0';    }    else    {      strcpy(buf, path);      if(alen > 0 && buf[plen - 1] != '/')      {        buf[plen++] = '/';        if(plen >= FTPD_BUFSIZE)          return 0;        buf[plen] = '\0';      }    }    if(plen + alen >= FTPD_BUFSIZE)      return 0;    strcpy(buf + plen, add);    if (stat(buf, &stat_buf) == 0)    {      int len;      struct tm bt;      time_t tf = stat_buf.st_mtime;      enum { SIZE = 80 };      enum { SIX_MONTHS = 365*24*60*60/2 };      char timeBuf[SIZE];      gmtime_r(&tf, &bt);      if(curTime > tf + SIX_MONTHS || tf > curTime + SIX_MONTHS)        strftime (timeBuf, SIZE, "%b %d  %Y", &bt);      else        strftime (timeBuf, SIZE, "%b %d %H:%M", &bt);      len = snprintf(buf, FTPD_BUFSIZE,        "%c%c%c%c%c%c%c%c%c%c  1 %5d %5d %11u %s %s\r\n",        (S_ISLNK(stat_buf.st_mode)?('l'):          (S_ISDIR(stat_buf.st_mode)?('d'):('-'))),        (stat_buf.st_mode & S_IRUSR)?('r'):('-'),        (stat_buf.st_mode & S_IWUSR)?('w'):('-'),        (stat_buf.st_mode & S_IXUSR)?('x'):('-'),        (stat_buf.st_mode & S_IRGRP)?('r'):('-'),        (stat_buf.st_mode & S_IWGRP)?('w'):('-'),        (stat_buf.st_mode & S_IXGRP)?('x'):('-'),        (stat_buf.st_mode & S_IROTH)?('r'):('-'),        (stat_buf.st_mode & S_IWOTH)?('w'):('-'),        (stat_buf.st_mode & S_IXOTH)?('x'):('-'),        (int)stat_buf.st_uid,        (int)stat_buf.st_gid,        (int)stat_buf.st_size,        timeBuf,        fname      );      if(send(s, buf, len, 0) != len)        return 0;    }  }  else  {    int len = snprintf(buf, FTPD_BUFSIZE, "%s\r\n", fname);    if(send(s, buf, len, 0) != len)      return 0;  }  return 1;}/*PAGE * * command_list * * Send file list to client. * * Input parameters: *   info - corresponding SessionInfo structure *   char *fname  - File (or directory) to list. * * Output parameters: *   NONE */static voidcommand_list(FTPD_SessionInfo_t *info, char const *fname, int wide){  int                 s;  DIR                 *dirp = 0;  struct dirent       *dp = 0;  struct stat         stat_buf;  char                buf[FTPD_BUFSIZE];  time_t curTime;  int sc = 1;  send_reply(info, 150, "Opening ASCII mode data connection for LIST.");  s = data_socket(info);  if(0 > s)  {    syslog(LOG_ERR, "ftpd: Error connecting to data socket.");    return;  }  if(fname[0] == '\0')    fname = ".";  if (0 > stat(fname, &stat_buf))  {    snprintf(buf, FTPD_BUFSIZE,      "%s: No such file or directory.\r\n", fname);    send(s, buf, strlen(buf), 0);  }  else if (S_ISDIR(stat_buf.st_mode) && (NULL == (dirp = opendir(fname))))  {    snprintf(buf, FTPD_BUFSIZE,      "%s: Can not open directory.\r\n", fname);    send(s, buf, strlen(buf), 0);  }  else  {    time(&curTime);    if(!dirp && *fname)      sc = sc && send_dirline(s, wide, curTime, fname, "", fname, buf);    else {      /* FIXME: need "." and ".." only when '-a' option is given */      sc = sc && send_dirline(s, wide, curTime, fname, "", ".", buf);      sc = sc && send_dirline(s, wide, curTime, fname,        (strcmp(fname, ftpd_root) ? ".." : ""), "..", buf);      while (sc && (dp = readdir(dirp)) != NULL)        sc = sc &&          send_dirline(s, wide, curTime, fname, dp->d_name, dp->d_name, buf);    }  }  if(dirp)    closedir(dirp);  close_data_socket(info);  if(sc)    send_reply(info, 226, "Transfer complete.");  else    send_reply(info, 426, "Connection aborted.");}/*PAGE * * command_cwd * * Change current working directory. * * Input parameters: *   info - corresponding SessionInfo structure *   dir  - directory name passed in CWD command * * Output parameters: *   NONE * */static voidcommand_cwd(FTPD_SessionInfo_t  *info, char *dir){  if(chdir(dir) == 0)    send_reply(info, 250, "CWD command successful.");  else    send_reply(info, 550, "CWD command failed.");}/*PAGE * * command_pwd * * Send current working directory to client. * * Input parameters: *   info - corresponding SessionInfo structure * * Output parameters: *   NONE */static voidcommand_pwd(FTPD_SessionInfo_t  *info){  char buf[FTPD_BUFSIZE];  char const* cwd;  errno = 0;  buf[0] = '"';  cwd = getcwd(buf + 1, FTPD_BUFSIZE - 4);  if(cwd)  {    int len = strlen(cwd);    static char const txt[] = "\" is the current directory.";    int size = sizeof(txt);    if(len + size + 1 >= FTPD_BUFSIZE)      size = FTPD_BUFSIZE - len - 2;    memcpy(buf + len + 1, txt, size);    buf[len + size] = '\0';    send_reply(info, 250, buf);  }  else {    snprintf(buf, FTPD_BUFSIZE, "Error: %s.", serr());    send_reply(info, 452, buf);  }}/*PAGE * * command_mdtm * * Handle FTP MDTM command (send file modification time to client)/ * * Input parameters: *   info - corresponding SessionInfo structure *   fname - file name passed in MDTM command * * Output parameters: *   info->cwd is set to new CWD value. */static voidcommand_mdtm(FTPD_SessionInfo_t  *info, char const* fname){  struct stat stbuf;  char buf[FTPD_BUFSIZE];  if (0 > stat(fname, &stbuf))  {    snprintf(buf, FTPD_BUFSIZE, "%s: %s.", fname, serr());    send_reply(info, 550, buf);  }  else  {    struct tm *t = gmtime(&stbuf.st_mtime);    snprintf(buf, FTPD_BUFSIZE, "%04d%02d%02d%02d%02d%02d",      1900 + t->tm_year,      t->tm_mon+1, t->tm_mday,      t->tm_hour, t->tm_min, t->tm_sec);    send_reply(info, 213, buf);  }}/*PAGE * * command_port * * This procedure fills address for data connection given the IP address and * port of the remote machine. * * Input parameters: *   info - corresponding SessionInfo structure *   args - arguments to the "PORT" command. * * Output parameters: *   info->data_addr is set according to arguments of the PORT command. *   info->use_default is set to 0 on success, 1 on failure. */static voidcommand_port(FTPD_SessionInfo_t *info, char const *args){  enum { NUM_FIELDS = 6 };  unsigned int a[NUM_FIELDS];  int n;  close_data_socket(info);  n = sscanf(args, "%u,%u,%u,%u,%u,%u", a+0, a+1, a+2, a+3, a+4, a+5);  if(NUM_FIELDS == n)  {    int i;    unsigned8 b[NUM_FIELDS];    for(i = 0; i < NUM_FIELDS; ++i)    {      if(a[i] > 255)        break;      b[i] = (unsigned8)a[i];    }    if(i == NUM_FIELDS)    {      /* Note: while it contradicts with RFC959, we don't allow PORT command       * to specify IP address different than those of the originating client       * for the sake of safety. */      unsigned32 const *ip   = (unsigned32 *)b;      if(*ip == info->def_addr.sin_addr.s_addr)      {        info->data_addr.sin_addr.s_addr = *ip;        info->data_addr.sin_port        = *(unsigned16 *)(b + 4);        info->data_addr.sin_family      = AF_INET;        memset(info->data_addr.sin_zero, 0, sizeof(info->data_addr.sin_zero));        info->use_default = 0;        send_reply(info, 200, "PORT command successful.");        return; /* success */      }      else      {        send_reply(info, 425, "Address doesn't match peer's IP.");        return;      }    }  }  send_reply(info, 501, "Syntax error.");}/*PAGE * * command_pasv * * Handle FTP PASV command. * Open socket, listen for and accept connection on it. * * Input parameters: *   info - corresponding SessionInfo structure * * Output parameters: *   info->pasv_socket is set to the descriptor of the data socket */static voidcommand_pasv(FTPD_SessionInfo_t *info){  int s = -1;  int err = 1;  close_data_socket(info);  s = socket(PF_INET, SOCK_STREAM, 0);  if (s < 0)    syslog(LOG_ERR, "ftpd: Error creating PASV socket: %s", serr());  else  {    struct sockaddr_in addr;    int addrLen = sizeof(addr);    addr = info->ctrl_addr;    addr.sin_port = htons(0);    if (0 > bind(s, (struct sockaddr *)&addr, addrLen))      syslog(LOG_ERR, "ftpd: Error binding PASV socket: %s", serr());    else if (0 > listen(s, 1))      syslog(LOG_ERR, "ftpd: Error listening on PASV socket: %s", serr());    else if(set_socket_timeout(s, info->idle))    {      char buf[FTPD_BUFSIZE];      unsigned char const *ip, *p;      getsockname(s, (struct sockaddr *)&addr, &addrLen);      ip = (unsigned char const*)&(addr.sin_addr);      p  = (unsigned char const*)&(addr.sin_port);      snprintf(buf, FTPD_BUFSIZE, "Entering passive mode (%u,%u,%u,%u,%u,%u).",        ip[0], ip[1], ip[2], ip[3], p[0], p[1]);      send_reply(info, 227, buf);      info->pasv_socket = accept(s, (struct sockaddr *)&addr, &addrLen);      if (0 > info->pasv_socket)        syslog(LOG_ERR, "ftpd: Error accepting PASV connection: %s", serr());      else      {        close_socket(s);        s = -1;        err = 0;      }    }  }  if(err)  {    /* (OSV) The note is from FreeBSD FTPD.     * Note: a response of 425 is not mentioned as a possible response to     * the PASV command in RFC959.  However, it has been blessed as a     * legitimate response by Jon Postel in a telephone conversation     * with Rick Adams on 25 Jan 89. */    send_reply(info, 425, "Can't open passive connection.");    close_socket(s);  }}/*PAGE * * skip_options * * Utility routine to skip options (if any) from input command.

⌨️ 快捷键说明

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