Example #1
0
File: data.c Project: flxflx/weasel
/* pr_data_sendfile() actually transfers the data on the data connection.
 * ASCII translation is not performed.
 * return 0 if reading and data connection closes, or -1 if error
 */
pr_sendfile_t pr_data_sendfile(int retr_fd, off_t *offset, off_t count) {
  int flags, error;
  pr_sendfile_t len = 0, total = 0;
#if defined(HAVE_AIX_SENDFILE)
  struct sf_parms parms;
  int rc;
#endif /* HAVE_AIX_SENDFILE */

  if (session.xfer.direction == PR_NETIO_IO_RD)
    return -1;

  flags = fcntl(PR_NETIO_FD(session.d->outstrm), F_GETFL);
  if (flags == -1)
    return -1;

  /* Set fd to blocking-mode for sendfile() */
  if (flags & O_NONBLOCK) {
    if (fcntl(PR_NETIO_FD(session.d->outstrm), F_SETFL, flags^O_NONBLOCK) == -1)
      return -1;
  }

  for (;;) {
#if defined(HAVE_LINUX_SENDFILE) || defined(HAVE_SOLARIS_SENDFILE)
    off_t orig_offset = *offset;

    /* Linux semantics are fairly straightforward in a glibc 2.x world:
     *
     *   #include <sys/sendfile.h>
     *
     *   ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
     *
     * Unfortunately, this API does not allow for an off_t number of bytes
     * to be sent, only a size_t.  This means we need to make sure that
     * the given count does not exceed the maximum value for a size_t.  Even
     * worse, the return value is a ssize_t, not a size_t, which means
     * the maximum value used can only be of the maximum _signed_ value,
     * not the maximum unsigned value.  This means calling sendfile() more
     * times.  How annoying.
     */

#if defined(HAVE_LINUX_SENDFILE)
    if (count > INT_MAX)
      count = INT_MAX;

#elif defined(HAVE_SOLARIS_SENDFILE)
# if SIZEOF_SIZE_T == SIZEOF_INT
    if (count > INT_MAX)
      count = INT_MAX;
# elif SIZEOF_SIZE_T == SIZEOF_LONG
    if (count > LONG_MAX)
      count = LONG_MAX;
# elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
    if (count > LLONG_MAX)
      count = LLONG_MAX;
# endif
#endif /* !HAVE_SOLARIS_SENDFILE */

    len = sendfile(PR_NETIO_FD(session.d->outstrm), retr_fd, offset, count);

    if (len != -1 &&
        len < count) {
      /* Under Linux semantics, this occurs when a signal has interrupted
       * sendfile().
       */
      if (XFER_ABORTED) {
        errno = EINTR;

        session.xfer.total_bytes += len;
        session.total_bytes += len;
        session.total_bytes_out += len;
        session.total_raw_out += len;

        return -1;
      }

      count -= len;

      /* Only reset the timers if data have actually been written out. */
      if (len > 0) {
        if (timeout_stalled) {
          pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE);
        }

        if (timeout_idle) {
          pr_timer_reset(PR_TIMER_IDLE, ANY_MODULE);
        }
      }

      session.xfer.total_bytes += len;
      session.total_bytes += len;
      session.total_bytes_out += len;
      session.total_raw_out += len;
      total += len;

      pr_signals_handle();
      continue;

    } else if (len == -1) {
      /* Linux updates offset on error, not len like BSD, fix up so
       * BSD-based code works.
       */
      len = *offset - orig_offset;
      *offset = orig_offset;

#elif defined(HAVE_BSD_SENDFILE)
    /* BSD semantics for sendfile are flexible...it'd be nice if we could
     * standardize on something like it.  The semantics are:
     *
     *   #include <sys/types.h>
     *   #include <sys/socket.h>
     *   #include <sys/uio.h>
     *
     *   int sendfile(int in_fd, int out_fd, off_t offset, size_t count,
     *                struct sf_hdtr *hdtr, off_t *len, int flags)
     *
     *  The comments above, about size_t versus off_t, apply here as
     *  well.  Except that BSD's sendfile() uses an off_t * for returning
     *  the number of bytes sent, so we can use the maximum unsigned
     *  value.
     */

#if SIZEOF_SIZE_T == SIZEOF_INT
    if (count > UINT_MAX)
      count = UINT_MAX;
#elif SIZEOF_SIZE_T == SIZEOF_LONG
    if (count > ULONG_MAX)
      count = ULONG_MAX;
#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
    if (count > ULLONG_MAX)
      count = ULLONG_MAX;
#endif

    if (sendfile(retr_fd, PR_NETIO_FD(session.d->outstrm), *offset, count,
        NULL, &len, 0) == -1) {

#elif defined(HAVE_MACOSX_SENDFILE)
    off_t orig_len = count;
    int res;

    /* Since Mac OSX uses the fourth argument as a value-return parameter,
     * success or failure, we need to put the result into len after the
     * call.
     */

    res = sendfile(retr_fd, PR_NETIO_FD(session.d->outstrm), *offset, &orig_len,
        NULL, 0);
    len = orig_len;

    if (res == -1) {
#elif defined(HAVE_AIX_SENDFILE)

    memset(&parms, 0, sizeof(parms));

    parms.file_descriptor = retr_fd;
    parms.file_offset = (uint64_t) *offset;
    parms.file_bytes = (int64_t) count;

    rc  = send_file(&(PR_NETIO_FD(session.d->outstrm)), &(parms), (uint_t)0);
    len = (int) parms.bytes_sent;

    if (rc == -1 || rc == 1) {

#endif /* HAVE_AIX_SENDFILE */

      /* IMO, BSD's semantics are warped.  Apparently, since we have our
       * alarms tagged SA_INTERRUPT (allowing system calls to be
       * interrupted - primarily for select), BSD will interrupt a
       * sendfile operation as well, so we have to catch and handle this
       * case specially.  It should also be noted that the sendfile(2) man
       * page doesn't state any of this.
       *
       * HP/UX has the same semantics, however, EINTR is well documented
       * as a side effect in the sendfile(2) man page.  HP/UX, however,
       * is implemented horribly wrong.  If a signal would result in
       * -1 being returned and EINTR being set, what ACTUALLY happens is
       * that errno is cleared and the number of bytes written is returned.
       *
       * For obvious reasons, HP/UX sendfile is not supported yet.
       */
      if (errno == EINTR) {
        if (XFER_ABORTED) {
          session.xfer.total_bytes += len;
          session.total_bytes += len;
          session.total_bytes_out += len;
          session.total_raw_out += len;

          return -1;
        }

        pr_signals_handle();

        /* If we got everything in this transaction, we're done. */
        if (len >= count) {
          break;

        } else {
          count -= len;
        }

        *offset += len;

        if (timeout_stalled) {
          pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE);
        }

        if (timeout_idle) {
          pr_timer_reset(PR_TIMER_IDLE, ANY_MODULE);
        }

        session.xfer.total_bytes += len;
        session.total_bytes += len;
        session.total_bytes_out += len;
        session.total_raw_out += len;
        total += len;

        continue;
      }

      error = errno;
      fcntl(PR_NETIO_FD(session.d->outstrm), F_SETFL, flags);
      errno = error;

      return -1;
    }

    break;
  }

  if (flags & O_NONBLOCK)
    fcntl(PR_NETIO_FD(session.d->outstrm), F_SETFL, flags);

  if (timeout_stalled) {
    pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE);
  }

  if (timeout_idle) {
    pr_timer_reset(PR_TIMER_IDLE, ANY_MODULE);
  }

  session.xfer.total_bytes += len;
  session.total_bytes += len;
  session.total_bytes_out += len;
  session.total_raw_out += len;
  total += len;

  return total;
}
Example #2
0
int proxy_ftp_ctrl_handle_async(pool *p, conn_t *backend_conn,
                                conn_t *frontend_conn) {
    if (!(proxy_sess_state & PROXY_SESS_STATE_CONNECTED)) {
        /* Nothing to do if we're not yet connected to the backend server. */
        return 0;
    }

    while (TRUE) {
        fd_set rfds;
        struct timeval tv;
        int ctrlfd, res, xerrno = 0;

        /* By using a timeout of zero, we effect a poll on the fd. */
        tv.tv_sec = 0;
        tv.tv_usec = 0;

        pr_signals_handle();

        FD_ZERO(&rfds);

        ctrlfd = PR_NETIO_FD(backend_conn->instrm);
        FD_SET(ctrlfd, &rfds);

        res = select(ctrlfd + 1, &rfds, NULL, NULL, &tv);
        if (res < 0) {
            xerrno = errno;

            if (xerrno == EINTR) {
                pr_signals_handle();
                continue;
            }

            (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
                                    "error calling select(2) on backend control connection (fd %d): %s",
                                    ctrlfd, strerror(xerrno));
            return 0;
        }

        if (res == 0) {
            /* Nothing there. */
            break;
        }

        pr_trace_msg(trace_channel, 19,
                     "select(2) reported %d for backend %s (fd %d)", res,
                     backend_conn->remote_name, ctrlfd);

        if (FD_ISSET(ctrlfd, &rfds)) {
            unsigned int resp_nlines = 0;
            pr_response_t *resp;

            pr_timer_reset(PR_TIMER_IDLE, ANY_MODULE);

            pr_trace_msg(trace_channel, 9, "reading async response from backend %s",
                         backend_conn->remote_name);

            resp = proxy_ftp_ctrl_recv_resp(p, backend_conn, &resp_nlines);
            if (resp == NULL) {
                xerrno = errno;

                (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
                                        "error receiving response from backend control connection: %s",
                                        strerror(xerrno));

                errno = xerrno;
                return -1;
            }

            res = proxy_ftp_ctrl_send_resp(p, frontend_conn, resp, resp_nlines);
            if (res < 0) {
                xerrno = errno;

                (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
                                        "error sending response to frontend control connection: %s",
                                        strerror(xerrno));

                errno = xerrno;
                return -1;
            }
        }

        break;
    }

    return 0;
}