Commit ca5ba295 authored by Andrea Shepard's avatar Andrea Shepard Committed by Nick Mathewson
Browse files

Support connection_exit_connect() to AF_UNIX sockets

parent 65642916
Loading
Loading
Loading
Loading
+132 −62
Original line number Diff line number Diff line
@@ -56,6 +56,11 @@
#include <pwd.h>
#endif

#ifdef HAVE_SYS_UN_H
#include <sys/socket.h>
#include <sys/un.h>
#endif

static connection_t *connection_listener_new(
                               const struct sockaddr *listensockaddr,
                               socklen_t listensocklen, int type,
@@ -1585,37 +1590,31 @@ connection_init_accepted_conn(connection_t *conn,
  return 0;
}

/** Take conn, make a nonblocking socket; try to connect to
 * addr:port (they arrive in *host order*). If fail, return -1 and if
 * applicable put your best guess about errno into *<b>socket_error</b>.
 * Else assign s to conn-\>s: if connected return 1, if EAGAIN return 0.
 *
 * address is used to make the logs useful.
 *
 * On success, add conn to the list of polled connections.
 */
int
connection_connect(connection_t *conn, const char *address,
                   const tor_addr_t *addr, uint16_t port, int *socket_error)

static int
connection_connect_sockaddr(connection_t *conn,
                            const struct sockaddr *sa,
                            socklen_t sa_len,
                            const struct sockaddr *bindaddr,
                            socklen_t bindaddr_len,
                            int *socket_error)
{
  tor_socket_t s;
  int inprogress = 0;
  struct sockaddr_storage addrbuf;
  struct sockaddr *dest_addr;
  int dest_addr_len;
  const or_options_t *options = get_options();
  int protocol_family;

  tor_assert(conn);
  tor_assert(sa);
  tor_assert(socket_error);

  if (get_n_open_sockets() >= get_options()->ConnLimit_-1) {
    warn_too_many_conns();
    *socket_error = SOCK_ERRNO(ENOBUFS);
    return -1;
  }

  if (tor_addr_family(addr) == AF_INET6)
    protocol_family = PF_INET6;
  else
    protocol_family = PF_INET;
  protocol_family = sa->sa_family;

  if (get_options()->DisableNetwork) {
    /* We should never even try to connect anyplace if DisableNetwork is set.
@@ -1628,7 +1627,7 @@ connection_connect(connection_t *conn, const char *address,
    return -1;
  }

  s = tor_open_socket_nonblocking(protocol_family,SOCK_STREAM,IPPROTO_TCP);
  s = tor_open_socket_nonblocking(protocol_family, SOCK_STREAM, 0);
  if (! SOCKET_OK(s)) {
    *socket_error = tor_socket_errno(-1);
    log_warn(LD_NET,"Error creating network socket: %s",
@@ -1641,59 +1640,26 @@ connection_connect(connection_t *conn, const char *address,
             tor_socket_strerror(errno));
  }

  if (!tor_addr_is_loopback(addr)) {
    const tor_addr_t *ext_addr = NULL;
    if (protocol_family == AF_INET &&
        !tor_addr_is_null(&options->OutboundBindAddressIPv4_))
      ext_addr = &options->OutboundBindAddressIPv4_;
    else if (protocol_family == AF_INET6 &&
             !tor_addr_is_null(&options->OutboundBindAddressIPv6_))
      ext_addr = &options->OutboundBindAddressIPv6_;
    if (ext_addr) {
      struct sockaddr_storage ext_addr_sa;
      socklen_t ext_addr_len = 0;
      memset(&ext_addr_sa, 0, sizeof(ext_addr_sa));
      ext_addr_len = tor_addr_to_sockaddr(ext_addr, 0,
                                          (struct sockaddr *) &ext_addr_sa,
                                          sizeof(ext_addr_sa));
      if (ext_addr_len == 0) {
        log_warn(LD_NET,
                 "Error converting OutboundBindAddress %s into sockaddr. "
                 "Ignoring.", fmt_and_decorate_addr(ext_addr));
      } else {
        if (bind(s, (struct sockaddr *) &ext_addr_sa, ext_addr_len) < 0) {
  if (bindaddr && bind(s, bindaddr, bindaddr_len) < 0) {
    *socket_error = tor_socket_errno(s);
          log_warn(LD_NET,"Error binding network socket to %s: %s",
                   fmt_and_decorate_addr(ext_addr),
    log_warn(LD_NET,"Error binding network socket: %s",
             tor_socket_strerror(*socket_error));
    tor_close_socket(s);
    return -1;
  }
      }
    }
  }

  tor_assert(options);
  if (options->ConstrainedSockets)
    set_constrained_socket_buffers(s, (int)options->ConstrainedSockSize);

  memset(&addrbuf,0,sizeof(addrbuf));
  dest_addr = (struct sockaddr*) &addrbuf;
  dest_addr_len = tor_addr_to_sockaddr(addr, port, dest_addr, sizeof(addrbuf));
  tor_assert(dest_addr_len > 0);

  log_debug(LD_NET, "Connecting to %s:%u.",
            escaped_safe_str_client(address), port);

  if (connect(s, dest_addr, (socklen_t)dest_addr_len) < 0) {
  if (connect(s, sa, sa_len) < 0) {
    int e = tor_socket_errno(s);
    if (!ERRNO_IS_CONN_EINPROGRESS(e)) {
      /* yuck. kill it. */
      *socket_error = e;
      log_info(LD_NET,
               "connect() to %s:%u failed: %s",
               escaped_safe_str_client(address),
               port, tor_socket_strerror(e));
               "connect() to socket failed: %s",
               tor_socket_strerror(e));
      tor_close_socket(s);
      return -1;
    } else {
@@ -1703,9 +1669,8 @@ connection_connect(connection_t *conn, const char *address,

  /* it succeeded. we're connected. */
  log_fn(inprogress ? LOG_DEBUG : LOG_INFO, LD_NET,
         "Connection to %s:%u %s (sock "TOR_SOCKET_T_FORMAT").",
         escaped_safe_str_client(address),
         port, inprogress?"in progress":"established", s);
         "Connection to socket %s (sock "TOR_SOCKET_T_FORMAT").",
         inprogress ? "in progress" : "established", s);
  conn->s = s;
  if (connection_add_connecting(conn) < 0) {
    /* no space, forget it */
@@ -1713,7 +1678,112 @@ connection_connect(connection_t *conn, const char *address,
    return -1;
  }
  return inprogress ? 0 : 1;

}


/** Take conn, make a nonblocking socket; try to connect to
 * addr:port (they arrive in *host order*). If fail, return -1 and if
 * applicable put your best guess about errno into *<b>socket_error</b>.
 * Else assign s to conn-\>s: if connected return 1, if EAGAIN return 0.
 *
 * address is used to make the logs useful.
 *
 * On success, add conn to the list of polled connections.
 */
int
connection_connect(connection_t *conn, const char *address,
                   const tor_addr_t *addr, uint16_t port, int *socket_error)
{
  struct sockaddr_storage addrbuf;
  struct sockaddr_storage bind_addr_ss;
  struct sockaddr *bind_addr = NULL;
  struct sockaddr *dest_addr;
  int dest_addr_len, bind_addr_len = 0;
  const or_options_t *options = get_options();
  int protocol_family;

  if (tor_addr_family(addr) == AF_INET6)
    protocol_family = PF_INET6;
  else
    protocol_family = PF_INET;

  if (!tor_addr_is_loopback(addr)) {
    const tor_addr_t *ext_addr = NULL;
    if (protocol_family == AF_INET &&
        !tor_addr_is_null(&options->OutboundBindAddressIPv4_))
      ext_addr = &options->OutboundBindAddressIPv4_;
    else if (protocol_family == AF_INET6 &&
             !tor_addr_is_null(&options->OutboundBindAddressIPv6_))
      ext_addr = &options->OutboundBindAddressIPv6_;
    if (ext_addr) {
      socklen_t ext_addr_len = 0;
      memset(&bind_addr_ss, 0, sizeof(bind_addr_ss));
      bind_addr_len = tor_addr_to_sockaddr(ext_addr, 0,
                                           (struct sockaddr *) &bind_addr_ss,
                                           sizeof(bind_addr_ss));
      if (ext_addr_len == 0) {
        log_warn(LD_NET,
                 "Error converting OutboundBindAddress %s into sockaddr. "
                 "Ignoring.", fmt_and_decorate_addr(ext_addr));
      } else {
        bind_addr = (struct sockaddr *)&bind_addr_ss;
      }
    }
  }

  memset(&addrbuf,0,sizeof(addrbuf));
  dest_addr = (struct sockaddr*) &addrbuf;
  dest_addr_len = tor_addr_to_sockaddr(addr, port, dest_addr, sizeof(addrbuf));
  tor_assert(dest_addr_len > 0);

  log_debug(LD_NET, "Connecting to %s:%u.",
            escaped_safe_str_client(address), port);

  return connection_connect_sockaddr(conn, dest_addr, dest_addr_len,
                                     bind_addr, bind_addr_len, socket_error);
}

#ifdef HAVE_SYS_UN_H

/** Take conn, make a nonblocking socket; try to connect to
 * an AF_UNIX socket at socket_path. If fail, return -1 and if applicable
 * put your best guess about errno into *<b>socket_error</b>. Else assign s
 * to conn-\>s: if connected return 1, if EAGAIN return 0.
 *
 * On success, add conn to the list of polled connections.
 */
int
connection_connect_unix(connection_t *conn, const char *socket_path,
                        int *socket_error)
{
  struct sockaddr_un dest_addr;

  tor_assert(socket_path);

  /* Check that we'll be able to fit it into dest_addr later */
  if (strlen(socket_path) + 1 > sizeof(dest_addr.sun_path)) {
    log_warn(LD_NET,
             "Path %s is too long for an AF_UNIX socket\n",
             escaped_safe_str_client(socket_path));
    *socket_error = SOCK_ERRNO(ENAMETOOLONG);
    return -1;
  }

  memset(&dest_addr, 0, sizeof(dest_addr));
  dest_addr.sun_family = AF_UNIX;
  strlcpy(dest_addr.sun_path, socket_path, sizeof(dest_addr.sun_path));

  log_debug(LD_NET,
            "Connecting to AF_UNIX socket at %s.",
            escaped_safe_str_client(socket_path));

  return connection_connect_sockaddr(conn,
                       (struct sockaddr *)&dest_addr, sizeof(dest_addr),
                       NULL, 0, socket_error);
}

#endif /* defined(HAVE_SYS_UN_H) */

/** Convert state number to string representation for logging purposes.
 */
+7 −0
Original line number Diff line number Diff line
@@ -89,6 +89,13 @@ int connection_connect(connection_t *conn, const char *address,
                       const tor_addr_t *addr,
                       uint16_t port, int *socket_error);

#ifdef HAVE_SYS_UN_H

int connection_connect_unix(connection_t *conn, const char *socket_path,
                            int *socket_error);

#endif /* defined(HAVE_SYS_UN_H) */

/** Maximum size of information that we can fit into SOCKS5 username
    or password fields. */
#define MAX_SOCKS5_AUTH_FIELD_SIZE 255
+29 −7
Original line number Diff line number Diff line
@@ -2940,7 +2940,7 @@ connection_exit_connect(edge_connection_t *edge_conn)
  const tor_addr_t *addr;
  uint16_t port;
  connection_t *conn = TO_CONN(edge_conn);
  int socket_error = 0;
  int socket_error = 0, result;

  if ( (!connection_edge_is_rendezvous_stream(edge_conn) &&
        router_compare_to_my_exit_policy(&edge_conn->base_.addr,
@@ -2955,6 +2955,11 @@ connection_exit_connect(edge_connection_t *edge_conn)
    return;
  }

#ifdef HAVE_SYS_UN_H
  if (conn->socket_family != AF_UNIX) {
#else
  {
#endif /* defined(HAVE_SYS_UN_H) */
    addr = &conn->addr;
    port = conn->port;

@@ -2962,7 +2967,24 @@ connection_exit_connect(edge_connection_t *edge_conn)
      conn->socket_family = AF_INET6;

    log_debug(LD_EXIT, "about to try connecting");
  switch (connection_connect(conn, conn->address, addr, port, &socket_error)) {
    result = connection_connect(conn, conn->address,
                                addr, port, &socket_error);
#ifdef HAVE_SYS_UN_H
  } else {
    /*
     * In the AF_UNIX case, we expect to have already had conn->port = 1,
     * tor_addr_make_unspec(conn->addr) (cf. the way we mark in the incoming
     * case in connection_handle_listener_read()), and conn->address should
     * have the socket path to connect to.
     */
    tor_assert(conn->address && strlen(conn->address) > 0);

    log_debug(LD_EXIT, "about to try connecting");
    result = connection_connect_unix(conn, conn->address, &socket_error);
#endif /* defined(HAVE_SYS_UN_H) */
  }

  switch (result) {
    case -1: {
      int reason = errno_to_stream_end_reason(socket_error);
      connection_edge_end(edge_conn, reason);