commit ca5ba2956bcd4b5ee1e526ccf5914f52fe6e6d51
Author: Andrea Shepard <andrea(a)torproject.org>
Date: Sat Jan 24 17:31:12 2015 +0000
Support connection_exit_connect() to AF_UNIX sockets
---
src/or/connection.c | 194 +++++++++++++++++++++++++++++++---------------
src/or/connection.h | 7 ++
src/or/connection_edge.c | 36 +++++++--
3 files changed, 168 insertions(+), 69 deletions(-)
diff --git a/src/or/connection.c b/src/or/connection.c
index 97fdee7..f26ada0 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -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,6 +1640,74 @@ connection_connect(connection_t *conn, const char *address,
tor_socket_strerror(errno));
}
+ if (bindaddr && bind(s, bindaddr, bindaddr_len) < 0) {
+ *socket_error = tor_socket_errno(s);
+ 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);
+
+ 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 socket failed: %s",
+ tor_socket_strerror(e));
+ tor_close_socket(s);
+ return -1;
+ } else {
+ inprogress = 1;
+ }
+ }
+
+ /* it succeeded. we're connected. */
+ log_fn(inprogress ? LOG_DEBUG : LOG_INFO, LD_NET,
+ "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 */
+ *socket_error = SOCK_ERRNO(ENOBUFS);
+ 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 &&
@@ -1650,33 +1717,21 @@ connection_connect(connection_t *conn, const char *address,
!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));
+ 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 {
- if (bind(s, (struct sockaddr *) &ext_addr_sa, ext_addr_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),
- tor_socket_strerror(*socket_error));
- tor_close_socket(s);
- return -1;
- }
+ bind_addr = (struct sockaddr *)&bind_addr_ss;
}
}
}
- 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));
@@ -1685,36 +1740,51 @@ connection_connect(connection_t *conn, const char *address,
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) {
- 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));
- tor_close_socket(s);
- return -1;
- } else {
- inprogress = 1;
- }
- }
+ return connection_connect_sockaddr(conn, dest_addr, dest_addr_len,
+ bind_addr, bind_addr_len, socket_error);
+}
- /* 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);
- conn->s = s;
- if (connection_add_connecting(conn) < 0) {
- /* no space, forget it */
- *socket_error = SOCK_ERRNO(ENOBUFS);
+#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;
}
- return inprogress ? 0 : 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.
*/
static const char *
diff --git a/src/or/connection.h b/src/or/connection.h
index ce6ed28..50bea51 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -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
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index f541249..9690653 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -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,14 +2955,36 @@ connection_exit_connect(edge_connection_t *edge_conn)
return;
}
- addr = &conn->addr;
- port = conn->port;
+#ifdef HAVE_SYS_UN_H
+ if (conn->socket_family != AF_UNIX) {
+#else
+ {
+#endif /* defined(HAVE_SYS_UN_H) */
+ addr = &conn->addr;
+ port = conn->port;
+
+ if (tor_addr_family(addr) == AF_INET6)
+ conn->socket_family = AF_INET6;
+
+ log_debug(LD_EXIT, "about to try connecting");
+ 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);
- if (tor_addr_family(addr) == AF_INET6)
- conn->socket_family = AF_INET6;
+ log_debug(LD_EXIT, "about to try connecting");
+ result = connection_connect_unix(conn, conn->address, &socket_error);
+#endif /* defined(HAVE_SYS_UN_H) */
+ }
- log_debug(LD_EXIT,"about to try connecting");
- switch (connection_connect(conn, conn->address, addr, port, &socket_error)) {
+ switch (result) {
case -1: {
int reason = errno_to_stream_end_reason(socket_error);
connection_edge_end(edge_conn, reason);