Example #1
0
END_TEST

START_TEST (timer_remove_multi_test) {
  int res;
  module m;

  /* By providing a negative timerno, the return value should be the
   * dynamically generated timerno, which is greater than or equal to
   * 1024.
   */
  res = pr_timer_add(3, -1, &m, timers_test_cb, "test1");
  fail_unless(res >= 1024, "Failed to add timer (%d): %s", res,
    strerror(errno));

  res = pr_timer_add(3, -1, &m, timers_test_cb, "test2");
  fail_unless(res >= 1024, "Failed to add timer (%d): %s", res,
    strerror(errno));

  res = pr_timer_add(3, -1, &m, timers_test_cb, "test3");
  fail_unless(res >= 1024, "Failed to add timer (%d): %s", res,
    strerror(errno));

  res = pr_timer_remove(-1, &m);
  fail_unless(res == 3, "Failed to remove timers (%d): %s", res,
    strerror(errno));
}
Example #2
0
static void set_policy_timer(struct sftp_tap_policy *policy) {

  /* Start a timer which checks the last times we received and sent packets.
   * From there, we may want to inject a TAP message, depending on the
   * policy.
   */
  if (policy->check_interval > 0) {
    tap_timerno = pr_timer_add(policy->check_interval, -1,
      &sftp_module, check_packet_times_cb, "SFTP TAP check");
  }
}
Example #3
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 #4
0
END_TEST

START_TEST (timer_remove_test) {
  int res;

  res = pr_timer_remove(0, NULL);
  fail_unless(res == 0);

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

  res = pr_timer_remove(1, NULL);
  fail_unless(res == -1, "Failed to return -1 for non-matching timer ID");
  fail_unless(errno == ENOENT, "Failed to set errno to ENOENT");

  res = pr_timer_remove(0, NULL);
  fail_unless(res == 0, "Failed to remove timer (%d): %s", res,
    strerror(errno));

  fail_unless(timer_triggered_count == 0,
    "Expected trigger count of 0, got %u", timer_triggered_count);
}
static bool is_cache_exits(memcached_st *mmc,
                           const char *key) {
    int timer_id;
    memcached_return rc;
    char *cached_value;
    size_t value_len;
    uint32_t flag;

    /* todo */
    timer_id = pr_timer_add(3, -1, NULL, lmd_timeout_callback, "memcached_get");
    cached_value = memcached_get(mmc, key, strlen(key), &value_len, &flag, &rc);
    pr_timer_remove(timer_id, NULL);

    /* no cache */
    if(MEMCACHED_NOTFOUND == rc)
        return false;

    /* failed by other reason */
    if(MEMCACHED_SUCCESS  != rc &&
       MEMCACHED_NOTFOUND != rc) {

      pr_log_auth(PR_LOG_NOTICE,
        "%s: failed memcached_get() %s. but IGNORE",
         MODULE_NAME, memcached_strerror(mmc, rc));
      return false;
    }

    /* cache not fond */
    if(NULL == cached_value)
        return false;

    /* something wrong */
    if(0 == value_len)
        return false;

    free(cached_value);
    return true;
}
Example #6
0
int pr_timer_sleep(int seconds) {
  int timerno = 0;
  sigset_t oset;

  _sleep_sem = 0;

  if (alarms_blocked || _indispatch) {
    errno = EPERM;
    return -1;
  }

  timerno = pr_timer_add(seconds, -1, NULL, sleep_cb, "sleep");
  if (timerno == -1)
    return -1;

  sigemptyset(&oset);
  while (!_sleep_sem) {
    sigsuspend(&oset);
    handle_alarm();
  }

  return 0;
}
Example #7
0
static void dynmasq_postparse_ev(const void *event_data, void *user_data) {
    if (dynmasq_timer_interval != -1) {
        dynmasq_timer_id = pr_timer_add(dynmasq_timer_interval, -1,
                                        &dynmasq_module, dynmasq_refresh_cb, "dynmasq address refresh");
    }
}
Example #8
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 #10
0
File: data.c Project: flxflx/weasel
static int data_active_open(char *reason, off_t size) {
  conn_t *c;
  int rev;
  pr_netaddr_t *bind_addr;

  if (!reason && session.xfer.filename)
    reason = session.xfer.filename;

  if (pr_netaddr_get_family(session.c->local_addr) == pr_netaddr_get_family(session.c->remote_addr)) {
    bind_addr = session.c->local_addr;

  } else {
    /* In this scenario, the server has an IPv6 socket, but the remote client
     * is an IPv4 (or IPv4-mapped IPv6) peer.
     */
    bind_addr = pr_netaddr_v6tov4(session.xfer.p, session.c->local_addr);
  }

  session.d = pr_inet_create_conn(session.pool, -1, bind_addr,
    session.c->local_port-1, TRUE);

  /* Set the "stalled" timer, if any, to prevent the connection
   * open from taking too long
   */
  if (timeout_stalled) {
    pr_timer_add(timeout_stalled, PR_TIMER_STALLED, NULL, stalled_timeout_cb,
      "TimeoutStalled");
  }

  rev = pr_netaddr_set_reverse_dns(ServerUseReverseDNS);

  /* Protocol and socket options should be set before handshaking. */

  if (session.xfer.direction == PR_NETIO_IO_RD) {
    pr_inet_set_socket_opts(session.d->pool, session.d,
      (main_server->tcp_rcvbuf_override ? main_server->tcp_rcvbuf_len : 0), 0);
    
  } else {
    pr_inet_set_socket_opts(session.d->pool, session.d,
      0, (main_server->tcp_sndbuf_override ? main_server->tcp_sndbuf_len : 0));
  }

  /* Make sure that the necessary socket options are set on the socket prior
   * to the call to connect(2).
   */
  pr_inet_set_proto_opts(session.pool, session.d, main_server->tcp_mss_len, 0,
    IPTOS_THROUGHPUT, 1);
  pr_inet_generate_socket_event("core.data-connect", main_server,
    session.d->local_addr, session.d->listen_fd);

  if (pr_inet_connect(session.d->pool, session.d, &session.data_addr,
      session.data_port) == -1) {
    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;
  }

  c = pr_inet_openrw(session.pool, session.d, NULL, PR_NETIO_STRM_DATA,
    session.d->listen_fd, -1, -1, TRUE);

  pr_netaddr_set_reverse_dns(rev);

  if (c) {
    pr_log_debug(DEBUG4, "active data connection opened - local  : %s:%d",
      pr_netaddr_get_ipstr(session.d->local_addr), session.d->local_port);
    pr_log_debug(DEBUG4, "active data connection opened - remote : %s:%d",
      pr_netaddr_get_ipstr(session.d->remote_addr), session.d->remote_port);

    if (session.xfer.xfer_type != STOR_UNIQUE) {
      if (size)
        pr_response_send(R_150, _("Opening %s mode data connection for %s "
          "(%" PR_LU " bytes)"), MODE_STRING, reason, (pr_off_t) size);
      else
        pr_response_send(R_150, _("Opening %s mode data connection for %s"),
          MODE_STRING, reason);

    } else {

      /* Format of 150 responses for STOU is explicitly dictated by
       * RFC 1123:
       *
       *  4.1.2.9  STOU Command: RFC-959 Section 4.1.3
       *
       *    The STOU command stores into a uniquely named file.  When it
       *    receives an STOU command, a Server-FTP MUST return the
       *    actual file name in the "125 Transfer Starting" or the "150
       *    Opening Data Connection" message that precedes the transfer
       *    (the 250 reply code mentioned in RFC-959 is incorrect).  The
       *    exact format of these messages is hereby defined to be as
       *    follows:
       *
       *        125 FILE: pppp
       *        150 FILE: pppp
       *
       *    where pppp represents the unique pathname of the file that
       *    will be written.
       */
      pr_response_send(R_150, "FILE: %s", reason);
    }

    pr_inet_close(session.pool, session.d);
    pr_inet_set_nonblock(session.pool, session.d);
    session.d = c;
    return 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;
}
Example #11
0
File: data.c Project: flxflx/weasel
static int data_pasv_open(char *reason, off_t size) {
  conn_t *c;
  int rev;

  if (!reason && session.xfer.filename)
    reason = session.xfer.filename;

  /* Set the "stalled" timer, if any, to prevent the connection
   * open from taking too long
   */
  if (timeout_stalled) {
    pr_timer_add(timeout_stalled, PR_TIMER_STALLED, NULL, stalled_timeout_cb,
      "TimeoutStalled");
  }

  /* We save the state of our current disposition for doing reverse
   * lookups, and then set it to what the configuration wants it to
   * be.
   */
  rev = pr_netaddr_set_reverse_dns(ServerUseReverseDNS);

  /* Protocol and socket options should be set before handshaking. */

  if (session.xfer.direction == PR_NETIO_IO_RD) {
    pr_inet_set_socket_opts(session.d->pool, session.d,
      (main_server->tcp_rcvbuf_override ? main_server->tcp_rcvbuf_len : 0), 0);

  } else {
    pr_inet_set_socket_opts(session.d->pool, session.d,
      0, (main_server->tcp_sndbuf_override ? main_server->tcp_sndbuf_len : 0));
  }

  c = pr_inet_accept(session.pool, session.d, session.c, -1, -1, TRUE);
  pr_netaddr_set_reverse_dns(rev);

  if (c && c->mode != CM_ERROR) {
    pr_inet_close(session.pool, session.d);
    pr_inet_set_nonblock(session.pool, c);
    session.d = c;

    pr_log_debug(DEBUG4, "passive data connection opened - local  : %s:%d",
      pr_netaddr_get_ipstr(session.d->local_addr), session.d->local_port);
    pr_log_debug(DEBUG4, "passive data connection opened - remote : %s:%d",
      pr_netaddr_get_ipstr(session.d->remote_addr), session.d->remote_port);

    if (session.xfer.xfer_type != STOR_UNIQUE) {
      if (size) {
        pr_response_send(R_150, _("Opening %s mode data connection for %s "
          "(%" PR_LU " bytes)"), MODE_STRING, reason, (pr_off_t) size);

      } else {
        pr_response_send(R_150, _("Opening %s mode data connection for %s"),
          MODE_STRING, reason);
      }

    } else {

      /* Format of 150 responses for STOU is explicitly dictated by
       * RFC 1123:
       *
       *  4.1.2.9  STOU Command: RFC-959 Section 4.1.3
       *
       *    The STOU command stores into a uniquely named file.  When it
       *    receives an STOU command, a Server-FTP MUST return the
       *    actual file name in the "125 Transfer Starting" or the "150
       *    Opening Data Connection" message that precedes the transfer
       *    (the 250 reply code mentioned in RFC-959 is incorrect).  The
       *    exact format of these messages is hereby defined to be as
       *    follows:
       *
       *        125 FILE: pppp
       *        150 FILE: pppp
       *
       *    where pppp represents the unique pathname of the file that
       *    will be written.
       */
      pr_response_send(R_150, "FILE: %s", reason);
    }

    return 0;
  }

  /* Check for error conditions. */
  if (c && c->mode == CM_ERROR)
    pr_log_pri(PR_LOG_ERR, "Error: unable to accept an incoming data "
      "connection (%s)", strerror(c->xerrno));

  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;
}
Example #12
0
char *pr_ident_lookup(pool *p, conn_t *c) {
  char *ret = "UNKNOWN";
  pool *tmp_pool = NULL;
  conn_t *ident_conn = NULL, *ident_io = NULL;
  char buf[256] = {'\0'}, *tok = NULL, *tmp = NULL;
  int timerno, i = 0;
  int ident_port = pr_inet_getservport(p, "ident", "tcp");

  tmp_pool = make_sub_pool(p);
  ident_timeout = 0;
  nstrm = NULL;

  if (ident_port == -1) {
    destroy_pool(tmp_pool);
    return pstrdup(p, ret);
  }

  /* Set up our timer before going any further. */
  timerno = pr_timer_add(PR_TUNABLE_TIMEOUTIDENT, -1, NULL,
    (callback_t) ident_timeout_cb, "ident lookup");
  if (timerno <= 0) {
    destroy_pool(tmp_pool);
    return pstrdup(p, ret);
  }

  ident_conn = pr_inet_create_connection(tmp_pool, NULL, -1, c->local_addr,
    INPORT_ANY, FALSE);
  pr_inet_set_nonblock(tmp_pool, ident_conn);

  i = pr_inet_connect_nowait(tmp_pool, ident_conn, c->remote_addr, ident_port);
  if (i < 0) {
    int xerrno = errno;

    pr_timer_remove(timerno, ANY_MODULE);
    pr_inet_close(tmp_pool, ident_conn);
    pr_trace_msg(trace_channel, 5, "connection to %s, port %d failed: %s",
      pr_netaddr_get_ipstr(c->remote_addr), ident_port, strerror(xerrno));

    destroy_pool(tmp_pool);

    errno = xerrno;
    return pstrdup(p, ret);
  }

  if (!i) {
    /* Not yet connected. */
    nstrm = pr_netio_open(p, PR_NETIO_STRM_OTHR, ident_conn->listen_fd,
      PR_NETIO_IO_RD);
    pr_netio_set_poll_interval(nstrm, 1);

    switch (pr_netio_poll(nstrm)) {

      /* Aborted, timed out */
      case 1: {
        if (ident_timeout) {
          pr_timer_remove(timerno, ANY_MODULE);
          pr_netio_close(nstrm);
          pr_inet_close(tmp_pool, ident_conn);

          pr_trace_msg(trace_channel, 5, "lookup timed out, returning '%s'",
            ret);
          destroy_pool(tmp_pool);
          return pstrdup(p, ret);
        }
        break;
      }

      /* Error. */
      case -1: {
        int xerrno = errno;

        pr_timer_remove(timerno, ANY_MODULE);
        pr_netio_close(nstrm);
        pr_inet_close(tmp_pool, ident_conn);

        pr_trace_msg(trace_channel, 6, "lookup failed (%s), returning '%s'",
          strerror(xerrno), ret);
        destroy_pool(tmp_pool);

        errno = xerrno;
        return pstrdup(p, ret);
      }

      /* Connected. */
      default: {
        ident_conn->mode = CM_OPEN;

        if (pr_inet_get_conn_info(ident_conn, ident_conn->listen_fd) < 0) {
          int xerrno = errno;

          pr_timer_remove(timerno, ANY_MODULE);
          pr_netio_close(nstrm);
          pr_inet_close(tmp_pool, ident_conn);

          pr_trace_msg(trace_channel, 2,
            "lookup timed out (%s), returning '%s'", strerror(xerrno), ret);
          destroy_pool(tmp_pool);

          errno = xerrno;
          return pstrdup(p, ret);
        }
        break;
      }
    }
  }

  ident_io = pr_inet_openrw(tmp_pool, ident_conn, NULL, PR_NETIO_STRM_OTHR,
    -1, -1, -1, FALSE);
  if (ident_io == NULL) {
    int xerrno = errno;

    pr_timer_remove(timerno, ANY_MODULE);
    pr_inet_close(tmp_pool, ident_conn);

    pr_trace_msg(trace_channel, 3, "failed opening read/write connection: %s",
      strerror(xerrno));
    destroy_pool(tmp_pool);

    errno = xerrno;
    return pstrdup(p, ret);
  }

  nstrm = ident_io->instrm;

  pr_inet_set_nonblock(tmp_pool, ident_io);
  pr_netio_set_poll_interval(ident_io->instrm, 1);
  pr_netio_set_poll_interval(ident_io->outstrm, 1);

  pr_netio_printf(ident_io->outstrm, "%d, %d\r\n", c->remote_port,
    c->local_port);

  /* If the timer fires while in netio_gets(), netio_gets() will simply return
   * either a partial string, or NULL.  This works because ident_timeout_cb
   * aborts the stream from which we are reading.  netio_set_poll_interval() is
   * used to make sure significant delays don't occur on systems that
   * automatically restart syscalls after the SIGALRM signal.
   */

  pr_trace_msg(trace_channel, 4, "reading response from remote ident server");

  if (pr_netio_gets(buf, sizeof(buf), ident_io->instrm)) {
    strip_end(buf, "\r\n");

    pr_trace_msg(trace_channel, 6, "received '%s' from remote ident server",
      buf);

    tmp = buf;
    tok = get_token(&tmp, ":");
    if (tok && (tok = get_token(&tmp, ":"))) {
      while (*tok && isspace((int) *tok)) {
        pr_signals_handle();
        tok++;
      }
      strip_end(tok, " \t");

      if (strcasecmp(tok, "ERROR") == 0) {
        if (tmp) {
          while (*tmp && isspace((int) *tmp)) {
            pr_signals_handle();
            tmp++;
          }
	  strip_end(tmp, " \t");
          if (strcasecmp(tmp, "HIDDEN-USER") == 0)
            ret = "HIDDEN-USER";
        }

      } else if (strcasecmp(tok, "USERID") == 0) {
        if (tmp && (tok = get_token(&tmp, ":"))) {
          if (tmp) {
            while (*tmp && isspace((int) *tmp)) {
              pr_signals_handle();
              tmp++;
            }
            strip_end(tmp, " \t");
            ret = tmp;
          }
        }
      }
    }
  }

  pr_timer_remove(timerno, ANY_MODULE);
  pr_inet_close(tmp_pool, ident_io);
  pr_inet_close(tmp_pool, ident_conn);
  destroy_pool(tmp_pool);

  return pstrdup(p, ret);
}
Example #13
0
conn_t *proxy_conn_get_server_conn(pool *p, struct proxy_session *proxy_sess,
    pr_netaddr_t *remote_addr) {
  pr_netaddr_t *bind_addr = NULL, *local_addr = NULL;
  const char *remote_ipstr = NULL;
  unsigned int remote_port;
  conn_t *server_conn, *ctrl_conn;
  int res;

  if (proxy_sess->connect_timeout > 0) {
    const char *notes_key = "mod_proxy.proxy-connect-address";

    proxy_sess->connect_timerno = pr_timer_add(proxy_sess->connect_timeout,
      -1, &proxy_module, proxy_conn_connect_timeout_cb, "ProxyTimeoutConnect");

    (void) pr_table_remove(session.notes, notes_key, NULL);

    if (pr_table_add(session.notes, notes_key, remote_addr,
        sizeof(pr_netaddr_t)) < 0) {
      (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
        "error stashing proxy connect address note: %s", strerror(errno));
    }
  }

  remote_ipstr = pr_netaddr_get_ipstr(remote_addr);
  remote_port = ntohs(pr_netaddr_get_port(remote_addr));

  /* Check the family of the retrieved address vs what we'll be using
   * to connect.  If there's a mismatch, we need to get an addr with the
   * matching family.
   */

  if (pr_netaddr_get_family(session.c->local_addr) == pr_netaddr_get_family(remote_addr)) {
    local_addr = session.c->local_addr;

  } else {
    /* In this scenario, the proxy has an IPv6 socket, but the remote/backend
     * server has an IPv4 (or IPv4-mapped IPv6) address.  OR it's the proxy
     * which has an IPv4 socket, and the remote/backend server has an IPv6
     * address.
     */
    if (pr_netaddr_get_family(session.c->local_addr) == AF_INET) {
      char *ip_str;

      /* Convert the local address from an IPv4 to an IPv6 addr. */
      ip_str = pcalloc(p, INET6_ADDRSTRLEN + 1);
      snprintf(ip_str, INET6_ADDRSTRLEN, "::ffff:%s",
        pr_netaddr_get_ipstr(session.c->local_addr));
      local_addr = pr_netaddr_get_addr(p, ip_str, NULL);

    } else {
      local_addr = pr_netaddr_v6tov4(p, session.c->local_addr);
      if (local_addr == NULL) {
        pr_trace_msg(trace_channel, 4,
          "error converting IPv6 local address %s to IPv4 address: %s",
          pr_netaddr_get_ipstr(session.c->local_addr), strerror(errno));
      }
    }

    if (local_addr == NULL) {
      local_addr = session.c->local_addr;
    }
  }

  bind_addr = proxy_sess->src_addr;
  if (bind_addr == NULL) {
    bind_addr = local_addr;
  }

  /* Note: IF mod_proxy is running on localhost, and the connection to be
   * made is to a public IP address, then this connect(2) attempt would most
   * likely fail with ENETUNREACH, since localhost is a loopback network,
   * and of course not reachable from a public IP.  Thus we check for this
   * edge case (which happens often for development).
   */
  if (pr_netaddr_is_loopback(bind_addr) == TRUE) {
    const char *local_name;
    pr_netaddr_t *local_addr;

    local_name = pr_netaddr_get_localaddr_str(p);
    local_addr = pr_netaddr_get_addr(p, local_name, NULL);

    if (local_addr != NULL) {
      pr_trace_msg(trace_channel, 14,
        "%s is a loopback address, and unable to reach %s; using %s instead",
        pr_netaddr_get_ipstr(bind_addr), remote_ipstr,
        pr_netaddr_get_ipstr(local_addr));
      bind_addr = local_addr;
    }
  }

  server_conn = pr_inet_create_conn(p, -1, bind_addr, INPORT_ANY, FALSE);
  if (server_conn == NULL) {
    int xerrno = errno;

    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "error creating connection to %s: %s", pr_netaddr_get_ipstr(bind_addr),
      strerror(xerrno));

    pr_timer_remove(proxy_sess->connect_timerno, &proxy_module);
    errno = xerrno;
    return NULL;
  }

  pr_trace_msg(trace_channel, 11, "connecting to backend address %s#%u from %s",
    remote_ipstr, remote_port, pr_netaddr_get_ipstr(bind_addr));

  res = pr_inet_connect_nowait(p, server_conn, remote_addr,
    ntohs(pr_netaddr_get_port(remote_addr)));
  if (res < 0) {
    int xerrno = errno;

    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "error starting connect to %s#%u: %s", remote_ipstr, remote_port,
      strerror(xerrno));

    pr_timer_remove(proxy_sess->connect_timerno, &proxy_module);
    errno = xerrno;
    return NULL;
  }

  if (res == 0) {
    pr_netio_stream_t *nstrm;
    int nstrm_mode = PR_NETIO_IO_RD;

    if (proxy_opts & PROXY_OPT_USE_PROXY_PROTOCOL) {
      /* Rather than waiting for the stream to be readable (because the
       * other end sent us something), wait for the stream to be writable
       * so that we can send something to the other end).
       */
      nstrm_mode = PR_NETIO_IO_WR;
    }

    /* Not yet connected. */
    nstrm = proxy_netio_open(p, PR_NETIO_STRM_OTHR, server_conn->listen_fd,
      nstrm_mode);
    if (nstrm == NULL) {
      int xerrno = errno;

      (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
        "error opening stream to %s#%u: %s", remote_ipstr, remote_port,
        strerror(xerrno));

      pr_timer_remove(proxy_sess->connect_timerno, &proxy_module);
      pr_inet_close(p, server_conn);

      errno = xerrno;
      return NULL;
    }

    proxy_netio_set_poll_interval(nstrm, 1);

    switch (proxy_netio_poll(nstrm)) {
      case 1: {
        /* Aborted, timed out.  Note that we shouldn't reach here. */
        pr_timer_remove(proxy_sess->connect_timerno, &proxy_module);
        proxy_netio_close(nstrm);
        pr_inet_close(p, server_conn);

        errno = ETIMEDOUT;
        return NULL;
      }

      case -1: {
        /* Error */
        int xerrno = errno;

        (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
          "error connecting to %s#%u: %s", remote_ipstr, remote_port,
          strerror(xerrno));

        pr_timer_remove(proxy_sess->connect_timerno, &proxy_module);
        proxy_netio_close(nstrm);
        pr_inet_close(p, server_conn);

        errno = xerrno;
        return NULL;
      }

      default: {
        /* Connected */
        server_conn->mode = CM_OPEN;
        pr_timer_remove(proxy_sess->connect_timerno, &proxy_module);
        pr_table_remove(session.notes, "mod_proxy.proxy-connect-addr", NULL);

        res = pr_inet_get_conn_info(server_conn, server_conn->listen_fd);
        if (res < 0) {
          int xerrno = errno;

          (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
            "error obtaining local socket info on fd %d: %s",
            server_conn->listen_fd, strerror(xerrno));

          proxy_netio_close(nstrm);
          pr_inet_close(p, server_conn);

          errno = xerrno;
          return NULL;
        }

        break;
      }
    }
  }

  pr_trace_msg(trace_channel, 5,
    "successfully connected to %s#%u from %s#%d", remote_ipstr, remote_port,
    pr_netaddr_get_ipstr(server_conn->local_addr),
    ntohs(pr_netaddr_get_port(server_conn->local_addr)));

  ctrl_conn = proxy_inet_openrw(p, server_conn, NULL, PR_NETIO_STRM_CTRL, -1,
    -1, -1, FALSE);
  if (ctrl_conn == NULL) {
    int xerrno = errno;

    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "unable to open control connection to %s#%u: %s", remote_ipstr,
      remote_port, strerror(xerrno));

    pr_inet_close(p, server_conn);

    errno = xerrno;
    return NULL;
  }

  return ctrl_conn;
}
Example #14
0
static int data_active_open(char *reason, off_t size) {
  conn_t *c;
  int bind_port, rev;
  pr_netaddr_t *bind_addr;
  unsigned char *root_revoke = NULL;

  if (!reason && session.xfer.filename)
    reason = session.xfer.filename;

  if (pr_netaddr_get_family(session.c->local_addr) == pr_netaddr_get_family(session.c->remote_addr)) {
    bind_addr = session.c->local_addr;

  } else {
    /* In this scenario, the server has an IPv6 socket, but the remote client
     * is an IPv4 (or IPv4-mapped IPv6) peer.
     */
    bind_addr = pr_netaddr_v6tov4(session.xfer.p, session.c->local_addr);
  }

  /* Default source port to which to bind for the active transfer, as
   * per RFC959.
   */
  bind_port = session.c->local_port-1;

  /* A RootRevoke value of 0 indicates 'false', 1 indicates 'true', and
   * 2 indicates 'NonCompliantActiveTransfer'.  We change the source port for
   * a RootRevoke value of 2.
   */
  root_revoke = get_param_ptr(TOPLEVEL_CONF, "RootRevoke", FALSE);
  if (root_revoke != NULL &&
      *root_revoke == 2) {
    bind_port = INPORT_ANY;
  }

  session.d = pr_inet_create_conn(session.pool, -1, bind_addr, bind_port, TRUE);

  /* Default remote address to which to connect for an active transfer,
   * if the client has not specified a different address via PORT/EPRT,
   * as per RFC 959.
   */
  if (pr_netaddr_get_family(&session.data_addr) == AF_UNSPEC) {
    pr_log_debug(DEBUG6, "Client has not sent previous PORT/EPRT command, "
      "defaulting to %s#%u for active transfer",
      pr_netaddr_get_ipstr(session.c->remote_addr), session.c->remote_port);

    pr_netaddr_set_family(&session.data_addr, pr_netaddr_get_family(session.c->remote_addr));
    pr_netaddr_set_sockaddr(&session.data_addr, pr_netaddr_get_sockaddr(session.c->remote_addr));
  }

  /* Set the "stalled" timer, if any, to prevent the connection
   * open from taking too long
   */
  if (timeout_stalled) {
    pr_timer_add(timeout_stalled, PR_TIMER_STALLED, NULL, stalled_timeout_cb,
      "TimeoutStalled");
  }

  rev = pr_netaddr_set_reverse_dns(ServerUseReverseDNS);

  /* Protocol and socket options should be set before handshaking. */

  if (session.xfer.direction == PR_NETIO_IO_RD) {
    pr_inet_set_socket_opts(session.d->pool, session.d,
      (main_server->tcp_rcvbuf_override ? main_server->tcp_rcvbuf_len : 0), 0,
      main_server->tcp_keepalive);
    
  } else {
    pr_inet_set_socket_opts(session.d->pool, session.d,
      0, (main_server->tcp_sndbuf_override ? main_server->tcp_sndbuf_len : 0),
      main_server->tcp_keepalive);
  }

  /* Make sure that the necessary socket options are set on the socket prior
   * to the call to connect(2).
   */
  pr_inet_set_proto_opts(session.pool, session.d, main_server->tcp_mss_len, 0,
    IPTOS_THROUGHPUT, 1);
  pr_inet_generate_socket_event("core.data-connect", main_server,
    session.d->local_addr, session.d->listen_fd);

  if (pr_inet_connect(session.d->pool, session.d, &session.data_addr,
      session.data_port) == -1) {
    pr_log_debug(DEBUG6,
      "Error connecting to %s#%u for active data transfer: %s",
      pr_netaddr_get_ipstr(&session.data_addr), session.data_port,
      strerror(session.d->xerrno));
    pr_response_add_err(R_425, _("Unable to build data connection: %s"),
      strerror(session.d->xerrno));
    errno = session.d->xerrno;

    destroy_pool(session.d->pool);
    session.d = NULL;
    return -1;
  }

  c = pr_inet_openrw(session.pool, session.d, NULL, PR_NETIO_STRM_DATA,
    session.d->listen_fd, -1, -1, TRUE);

  pr_netaddr_set_reverse_dns(rev);

  if (c) {
    pr_log_debug(DEBUG4, "active data connection opened - local  : %s:%d",
      pr_netaddr_get_ipstr(session.d->local_addr), session.d->local_port);
    pr_log_debug(DEBUG4, "active data connection opened - remote : %s:%d",
      pr_netaddr_get_ipstr(session.d->remote_addr), session.d->remote_port);

    if (session.xfer.xfer_type != STOR_UNIQUE) {
      if (size) {
        pr_response_send(R_150, _("Opening %s mode data connection for %s "
          "(%" PR_LU " bytes)"), MODE_STRING, reason, (pr_off_t) size);

      } else {
        pr_response_send(R_150, _("Opening %s mode data connection for %s"),
          MODE_STRING, reason);
      }

    } else {

      /* Format of 150 responses for STOU is explicitly dictated by
       * RFC 1123:
       *
       *  4.1.2.9  STOU Command: RFC-959 Section 4.1.3
       *
       *    The STOU command stores into a uniquely named file.  When it
       *    receives an STOU command, a Server-FTP MUST return the
       *    actual file name in the "125 Transfer Starting" or the "150
       *    Opening Data Connection" message that precedes the transfer
       *    (the 250 reply code mentioned in RFC-959 is incorrect).  The
       *    exact format of these messages is hereby defined to be as
       *    follows:
       *
       *        125 FILE: pppp
       *        150 FILE: pppp
       *
       *    where pppp represents the unique pathname of the file that
       *    will be written.
       */
      pr_response_send(R_150, "FILE: %s", reason);
    }

    pr_inet_close(session.pool, session.d);
    pr_inet_set_nonblock(session.pool, session.d);
    session.d = c;
    return 0;
  }

  pr_response_add_err(R_425, _("Unable to build data connection: %s"),
    strerror(session.d->xerrno));
  errno = session.d->xerrno;

  destroy_pool(session.d->pool);
  session.d = NULL;
  return -1;
}