Example #1
0
/*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 void
command_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.");
}
Example #2
0
/*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 void
command_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;
    socklen_t 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);
  }
}
Example #3
0
/*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 void
session(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);
  }
}
Example #4
0
/*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 void
command_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;
    union {
      uint8_t b[NUM_FIELDS];
      struct {
        uint32_t ip;
        uint16_t port;
      } u ;
    } ip_info;

    for(i = 0; i < NUM_FIELDS; ++i)
    {
      if(a[i] > 255)
        break;
      ip_info.b[i] = (uint8_t)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. */
      if (ip_info.u.ip == info->def_addr.sin_addr.s_addr)
      {
        info->data_addr.sin_addr.s_addr = ip_info.u.ip;
        info->data_addr.sin_port        = ip_info.u.port;
        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.");
}
Example #5
0
int
dc_close2(int fd)
{
	int              res = 0;
	struct vsp_node *node;
	int32_t          size;


#ifdef DC_CALL_TRACE
	showTraceBack();
#endif

	/* nothing wrong ... yet */
	dc_errno = DEOK;

	node = delete_vsp_node(fd);
	if (node == NULL) {
		/* we have not such file descriptor, so lets give a try to system */
		return system_close(fd);
	}


	dc_real_fsync( node );


	if(node->unsafeWrite) {

		size = htonl(-1); /* send end of data */
		writen(node->dataFd, (char *) &size, sizeof(size), NULL);
		/* FIXME: error detection missing */

		if (get_fin(node) < 0) {
			dc_debug(DC_ERROR, "dc_close: mover did not FIN the data blocks.");
			res = -1;
		}
	}

	close_data_socket(node->dataFd);

	deleteQueue(node->queueID);

	m_unlock(&node->mux);

	node_destroy(node);

	return res;
}
Example #6
0
int
dc_close(int fd)
{
	int             res = 0;
	int             tmp;
	int32_t         size;
	int32_t         closemsg[6];
	int             msglen;
	struct vsp_node *node;


#ifdef DC_CALL_TRACE
	showTraceBack();
#endif


	/* nothing wrong ... yet */
	dc_errno = DEOK;

	node = delete_vsp_node(fd);
	if (node == NULL) {
		/* we have not such file descriptor, so lets give a try to system */
		dc_debug(DC_INFO, "Using system native close for [%d].", fd);
		return system_close(fd);
	}


	if ( node->lcb != NULL ) {
		dc_lcb_clean( node );
	}

	dc_real_fsync( node );

	if(node->unsafeWrite > 1) {

		size = htonl(-1); /* send end of data */
		writen(node->dataFd, (char *) &size, sizeof(size), NULL);
		/* FIXME: error detection missing */

		if (get_fin(node) < 0) {
			dc_debug(DC_ERROR, "dc_close: mover did not FIN the data blocks.");
			res = -1;
		}
	}

	if(node->reference == 0 ) {

		if( (node->sum != NULL) && ( node->sum->isOk == 1 ) ) {
			closemsg[0] = htonl(20);
			closemsg[2] = htonl(12);
			closemsg[3] = htonl(DCAP_DATA_SUM);
			closemsg[4] = htonl(node->sum->type);
			closemsg[5] = htonl(node->sum->sum);

			msglen = 6;
			dc_debug(DC_INFO, "File checksum is: %u", node->sum->sum);
		}else{
			closemsg[0] = htonl(4);
			msglen = 2;
		}

		closemsg[1] = htonl(IOCMD_CLOSE); /* actual command */

		dc_debug(DC_IO, "Sending CLOSE for fd:%d ID:%d.", node->dataFd, node->queueID);
		check_timeout_envar();
		dcap_set_alarm(closeTimeOut > 0 ? closeTimeOut : DCAP_IO_TIMEOUT/4);
		tmp = sendDataMessage(node, (char *) closemsg, msglen*sizeof(int32_t), ASCII_OK, NULL);
		/* FIXME: error detection missing */
		if( tmp < 0 ) {
			dc_debug(DC_ERROR, "sendDataMessage failed.");
			/* ignore close errors if file was open for read */
			if(node->flags & O_WRONLY) {
				res = -1;
			}

			if(isIOFailed) {
				isIOFailed = 0;
				/* command line dwon */
				if(!ping_pong(node)) {
					/* remove file descriptor from the list of control lines in use */
					lockMember();
					deleteMemberByValue(node->fd);
					unlockMember();
					pollDelete(node->fd);
					close_control_socket(node->fd, node->tunnel);
				}
			}
		}
		dcap_set_alarm(0);
		deleteQueue(node->queueID);

	}

	/*
	 * Even if there is still a reference to the dcap session,
	 * we have to close local socket descriptor.
	 */
	close_data_socket(node->dataFd);
	node_destroy(node);

	return res;
}
Example #7
0
/*PAGE
 *
 * command_store
 *
 * Performs the "STOR" command (receive data from client).
 *
 * Input parameters:
 *   info - corresponding SessionInfo structure
 *   char *filename   - Destination filename.
 *
 * Output parameters:
 *   NONE
 */
static void
command_store(FTPD_SessionInfo_t *info, char const *filename)
{
  int                    s;
  int                    n;
  unsigned long          size = 0;
  struct rtems_ftpd_hook *usehook = NULL;
  char                   buf[FTPD_DATASIZE];
  int                    res = 1;
  int                    bare_lfs = 0;
  int                    null = 0;
  typedef ssize_t (*WriteProc)(int, void const*, size_t);
  WriteProc              wrt = &write;

  if(!can_write())
  {
    send_reply(info, 550, "Access denied.");
    return;
  }

  send_mode_reply(info);

  s = data_socket(info);
  if(0 > s)
    return;

  null = !strcmp("/dev/null", filename);
  if (null)
  {
    /* File "/dev/null" just throws data away.
     *  FIXME: this is hack.  Using `/dev/null' filesystem entry would be
     *  better.
     */
    wrt = &discard;
  }

  if (!null && rtems_ftpd_configuration.hooks != NULL)
  {

    /* Search our list of hooks to see if we need to do something special. */
    struct rtems_ftpd_hook *hook;
    int i;

    i = 0;
    hook = &rtems_ftpd_configuration.hooks[i++];
    while (hook->filename != NULL)
    {
      if (!strcmp(hook->filename, filename))
      {
        usehook = hook;
        break;
      }
      hook = &rtems_ftpd_configuration.hooks[i++];
    }
  }

  if (usehook != NULL)
  {
    /*
     * OSV: FIXME: Small buffer could be used and hook routine
     * called multiple times instead.  Alternatively, the support could be
     * removed entirely in favor of configuring RTEMS pseudo-device with
     * given name.
     */

    char                *bigBufr;
    size_t filesize = rtems_ftpd_configuration.max_hook_filesize + 1;

    /*
     * Allocate space for our "file".
     */
    bigBufr = (char *)malloc(filesize);
    if (bigBufr == NULL)
    {
      send_reply(info, 451, "Local resource failure: malloc.");
      close_data_socket(info);
      return;
    }

    /*
     * Retrieve the file into our buffer space.
     */
    size = 0;
    while ((n = recv(s, bigBufr + size, filesize - size, 0)) > 0)
    {
      size += n;
    }
    if (size >= filesize)
    {
      send_reply(info, 451, "File too long: buffer size exceeded.");
      free(bigBufr);
      close_data_socket(info);
      return;
    }

    /*
     * Call our hook.
     */
    res = (usehook->hook_function)(bigBufr, size) == 0;
    free(bigBufr);
    if(!res)
    {
      send_reply(info, 451, "File processing failed.");
      close_data_socket(info);
      return;
    }
  }
  else
  {
    /* Data transfer to regular file or /dev/null. */
    int fd = 0;

    if(!null)
      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);

}
Example #8
0
/*PAGE
 *
 * command_retrieve
 *
 * Perform the "RETR" command (send file to client).
 *
 * Input parameters:
 *   info - corresponding SessionInfo structure
 *   char *filename  - source filename.
 *
 * Output parameters:
 *   NONE
 *
 */
static void
command_retrieve(FTPD_SessionInfo_t  *info, char const *filename)
{
  int                 s = -1;
  int                 fd = -1;
  char                buf[FTPD_DATASIZE];
  struct stat         stat_buf;
  int                 res = 0;

  if(!can_read())
  {
    send_reply(info, 550, "Access denied.");
    return;
  }

  if (0 > (fd = open(filename, O_RDONLY)))
  {
    send_reply(info, 550, "Error opening file.");
    return;
  }

  if (fstat(fd, &stat_buf) == 0 && S_ISDIR(stat_buf.st_mode))
  {
    if (-1 != fd)
      close(fd);
    send_reply(info, 550, "Is a directory.");
    return;
  }

  send_mode_reply(info);

  s = data_socket(info);

  if (0 <= s)
  {
    int n = -1;

    if(info->xfer_mode == TYPE_I)
    {
      while ((n = read(fd, buf, FTPD_DATASIZE)) > 0)
      {
        if(send(s, buf, n, 0) != n)
          break;
      }
    }
    else if (info->xfer_mode == TYPE_A)
    {
      int rest = 0;
      while (rest == 0 && (n = read(fd, buf, FTPD_DATASIZE)) > 0)
      {
        char const* e = buf;
        char const* b;
        int i;
        rest = n;
        do
        {
          char lf = '\0';

          b = e;
          for(i = 0; i < rest; ++i, ++e)
          {
            if(*e == '\n')
            {
              lf = '\n';
              break;
            }
          }
          if(send(s, b, i, 0) != i)
            break;
          if(lf == '\n')
          {
            if(send(s, "\r\n", 2, 0) != 2)
              break;
            ++e;
            ++i;
          }
        }
        while((rest -= i) > 0);
      }
    }

    if (0 == n)
    {
      if (0 == close(fd))
      {
        fd = -1;
        res = 1;
      }
    }
  }

  if (-1 != fd)
    close(fd);

  if (0 == res)
    send_reply(info, 451, "File read error.");
  else
    send_reply(info, 226, "Transfer complete.");

  close_data_socket(info);

  return;
}