[tor-commits] [torsocks/master] Implement the gethostbyname(3) torsocks call
dgoulet at torproject.org
dgoulet at torproject.org
Fri Apr 4 22:40:25 UTC 2014
commit c5f350d37f0a6c9ca81965bec71b5bb99e481bc2
Author: David Goulet <dgoulet at ev0ke.net>
Date: Sat Jun 22 15:28:50 2013 -0400
Implement the gethostbyname(3) torsocks call
Signed-off-by: David Goulet <dgoulet at ev0ke.net>
---
src/common/socks5.c | 112 ++++++++++++++++++++++++++++++++++++++++
src/common/socks5.h | 14 ++++-
src/lib/torsocks.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++--
src/lib/torsocks.h | 27 ++++++++++
4 files changed, 288 insertions(+), 6 deletions(-)
diff --git a/src/common/socks5.c b/src/common/socks5.c
index eb3baef..4e218e3 100644
--- a/src/common/socks5.c
+++ b/src/common/socks5.c
@@ -17,6 +17,7 @@
#include <assert.h>
#include <errno.h>
+#include <inttypes.h>
#include <lib/torsocks.h>
@@ -362,3 +363,114 @@ int socks5_recv_connect_reply(struct connection *conn)
error:
return ret;
}
+
+/*
+ * Send a SOCKS5 Tor resolve request for a given hostname using an already
+ * connected connection.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int socks5_send_resolve_request(const char *hostname, struct connection *conn)
+{
+ int ret, ret_send;
+ /* Buffer to send won't go over a full TCP size. */
+ char buffer[1500];
+ size_t name_len, msg_len, data_len;
+ struct socks5_request msg;
+ struct socks5_request_resolve req;
+
+ assert(hostname);
+ assert(conn);
+ assert(conn->fd >= 0);
+
+ memset(buffer, 0, sizeof(buffer));
+ msg_len = sizeof(msg);
+
+ msg.ver = SOCKS5_VERSION;
+ msg.cmd = SOCKS5_CMD_RESOLVE;
+ /* Always zeroed. */
+ msg.rsv = 0;
+ /* By default we use IPv4 address. */
+ msg.atyp = SOCKS5_ATYP_DOMAIN;
+
+ name_len = strlen(hostname);
+ if (name_len > sizeof(req.name)) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Setup resolve request. */
+ req.len = name_len;
+ memcpy(req.name, hostname, name_len);
+
+ /* Copy final buffer. */
+ memcpy(buffer, &msg, msg_len);
+ memcpy(buffer + msg_len, &req, sizeof(req));
+ data_len = msg_len + sizeof(req);
+
+ ret_send = send_data(conn->fd, &buffer, data_len);
+ if (ret_send < 0) {
+ ret = ret_send;
+ goto error;
+ }
+
+ /* Data was sent successfully. */
+ ret = 0;
+ DBG("[socks5] Resolve for %s sent successfully", hostname);
+
+error:
+ return ret;
+}
+
+/*
+ * Receive a Tor resolve reply on the given connection. The ip address pointer
+ * is populated with the replied value or else untouched on error.
+ *
+ * Return 0 on success else a negative value.
+ */
+int socks5_recv_resolve_reply(struct connection *conn, uint32_t *ip_addr)
+{
+ int ret;
+ ssize_t ret_recv;
+ struct {
+ struct socks5_reply msg;
+ uint32_t addr;
+ } buffer;
+
+ assert(conn);
+ assert(conn >= 0);
+ assert(ip_addr);
+
+ ret_recv = recv_data(conn->fd, &buffer, sizeof(buffer));
+ if (ret_recv < 0) {
+ ret = ret_recv;
+ goto error;
+ }
+
+ if (buffer.msg.ver != SOCKS5_VERSION) {
+ ERR("Bad SOCKS5 version reply");
+ ret = -ECONNABORTED;
+ goto error;
+ }
+
+ if (buffer.msg.rep != SOCKS5_REPLY_SUCCESS) {
+ ERR("Unable to resolve. Status reply: %d", buffer.msg.rep);
+ ret = -ECONNABORTED;
+ goto error;
+ }
+
+ if (buffer.msg.atyp == SOCKS5_ATYP_IPV4) {
+ *ip_addr = buffer.addr;
+ } else {
+ ERR("Bad SOCKS5 atyp reply %d", buffer.msg.atyp);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Everything went well and ip_addr has been populated. */
+ ret = 0;
+ DBG("[socks5] Resolve reply received: %" PRIu32, *ip_addr);
+
+error:
+ return ret;
+}
diff --git a/src/common/socks5.h b/src/common/socks5.h
index 34935f4..c11c097 100644
--- a/src/common/socks5.h
+++ b/src/common/socks5.h
@@ -34,8 +34,10 @@
#define SOCKS5_NO_AUTH_METHOD 0x00
#define SOCKS5_NO_ACCPT_METHOD 0xFF
-/* Request to connect. */
+/* Request command. */
#define SOCKS5_CMD_CONNECT 0x01
+#define SOCKS5_CMD_RESOLVE 0xF0
+#define SOCKS5_CMD_RESOLVE_PTR 0xF1
/* Address type. */
#define SOCKS5_ATYP_IPV4 0x01
@@ -94,6 +96,12 @@ struct socks5_request_domain {
uint16_t port;
};
+/* Use for the Tor resolve command. */
+struct socks5_request_resolve {
+ uint8_t len;
+ char name[UINT8_MAX];
+};
+
/* Non variable part of a reply. */
struct socks5_reply {
uint8_t ver;
@@ -112,4 +120,8 @@ int socks5_recv_method(struct connection *conn);
int socks5_send_connect_request(struct connection *conn);
int socks5_recv_connect_reply(struct connection *conn);
+/* Tor DNS resolve. */
+int socks5_send_resolve_request(const char *hostname, struct connection *conn);
+int socks5_recv_resolve_reply(struct connection *conn, uint32_t *ip_addr);
+
#endif /* TORSOCKS_SOCKS_H */
diff --git a/src/lib/torsocks.c b/src/lib/torsocks.c
index 0382d38..f6e3501 100644
--- a/src/lib/torsocks.c
+++ b/src/lib/torsocks.c
@@ -17,6 +17,7 @@
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <arpa/inet.h>
#include <assert.h>
#include <dlfcn.h>
#include <stdlib.h>
@@ -215,6 +216,38 @@ static void __attribute__((destructor)) tsocks_exit(void)
}
/*
+ * Setup a Tor connection meaning initiating the initial SOCKS5 handshake.
+ *
+ * Return 0 on success else a negative value.
+ */
+static int setup_tor_connection(struct connection *conn)
+{
+ int ret;
+
+ assert(conn);
+
+ DBG("Setting up a connection to the Tor network on fd %d", conn->fd);
+
+ ret = socks5_connect(conn);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ret = socks5_send_method(conn);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ret = socks5_recv_method(conn);
+ if (ret < 0) {
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
+/*
* Initiate a SOCK5 connection to the Tor network using the given connection.
* The socks5 API will use the torsocks configuration object to find the tor
* daemon.
@@ -230,31 +263,67 @@ static int connect_to_tor_network(struct connection *conn)
DBG("Connecting to the Tor network on fd %d", conn->fd);
- ret = socks5_connect(conn);
+ ret = setup_tor_connection(conn);
if (ret < 0) {
goto error;
}
- ret = socks5_send_method(conn);
+ ret = socks5_send_connect_request(conn);
if (ret < 0) {
goto error;
}
- ret = socks5_recv_method(conn);
+ ret = socks5_recv_connect_reply(conn);
if (ret < 0) {
goto error;
}
- ret = socks5_send_connect_request(conn);
+error:
+ return ret;
+}
+
+/*
+ * Resolve a hostname through Tor and set the ip address in the given pointer.
+ *
+ * Return 0 on success else a negative value and the result addr is untouched.
+ */
+static int tor_resolve(const char *hostname, uint32_t *ip_addr)
+{
+ int ret;
+ struct connection conn;
+
+ assert(hostname);
+ assert(ip_addr);
+
+ DBG("Resolving %s on the Tor network", hostname);
+
+ conn.fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (conn.fd < 0) {
+ PERROR("socket");
+ ret = -errno;
+ goto error;
+ }
+
+ ret = setup_tor_connection(&conn);
if (ret < 0) {
goto error;
}
- ret = socks5_recv_connect_reply(conn);
+ ret = socks5_send_resolve_request(hostname, &conn);
if (ret < 0) {
goto error;
}
+ ret = socks5_recv_resolve_reply(&conn, ip_addr);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ret = close(conn.fd);
+ if (ret < 0) {
+ PERROR("close");
+ }
+
error:
return ret;
}
@@ -347,3 +416,65 @@ LIBC_CONNECT_DECL
TSOCKS_SYM_EXIT_NOT_FOUND);
return tsocks_connect(LIBC_CONNECT_ARGS);
}
+
+/*
+ * Torsocks call for gethostbyname(3).
+ *
+ * NOTE: This call is OBSOLETE in the glibc.
+ */
+LIBC_GETHOSTBYNAME_RET_TYPE tsocks_gethostbyname(LIBC_GETHOSTBYNAME_SIG)
+{
+ int ret;
+ uint32_t ip;
+ const char *ret_str;
+
+ DBG("[gethostbyname] Requesting %s hostname", __name);
+
+ if (!__name) {
+ h_errno = HOST_NOT_FOUND;
+ goto error;
+ }
+
+ /* Resolve the given hostname through Tor. */
+ ret = tor_resolve(__name, &ip);
+ if (ret < 0) {
+ goto error;
+ }
+
+ /* Reset static host entry of tsocks. */
+ memset(&tsocks_he, 0, sizeof(tsocks_he));
+ memset(tsocks_he_addr_list, 0, sizeof(tsocks_he_addr_list));
+ memset(tsocks_he_addr, 0, sizeof(tsocks_he_addr));
+
+ ret_str = inet_ntop(AF_INET, &ip, tsocks_he_addr, sizeof(tsocks_he_addr));
+ if (!ret_str) {
+ PERROR("inet_ntop");
+ h_errno = NO_ADDRESS;
+ goto error;
+ }
+
+ tsocks_he_addr_list[0] = tsocks_he_addr;
+ tsocks_he_addr_list[1] = NULL;
+
+ tsocks_he.h_name = (char *) __name;
+ tsocks_he.h_aliases = NULL;
+ tsocks_he.h_length = sizeof(in_addr_t);
+ tsocks_he.h_addrtype = AF_INET;
+ tsocks_he.h_addr_list = tsocks_he_addr_list;
+
+ DBG("Hostname %s resolved to %s", __name, tsocks_he_addr);
+
+ errno = 0;
+ return &tsocks_he;
+
+error:
+ return NULL;
+}
+
+/*
+ * Libc hijacked symbol gethostbyname(3).
+ */
+LIBC_GETHOSTBYNAME_DECL
+{
+ return tsocks_gethostbyname(LIBC_GETHOSTBYNAME_ARGS);
+}
diff --git a/src/lib/torsocks.h b/src/lib/torsocks.h
index 9ba2955..7581764 100644
--- a/src/lib/torsocks.h
+++ b/src/lib/torsocks.h
@@ -33,6 +33,7 @@
#if (defined(__linux__) || defined(__FreeBSD__) || defined(__darwin__))
+/* connect(2) */
#include <sys/types.h>
#include <sys/socket.h>
@@ -44,6 +45,25 @@
#define LIBC_CONNECT_ARGS \
__sockfd, __addr, __addrlen
+/* gethostbyname(3) */
+#include <netdb.h>
+
+/*
+ * The man page specifies that this call can return a pointers to static data
+ * meaning that the caller needs to copy the returned data and not forced to
+ * use free(). So, we use static memory here to mimic the libc call and avoid
+ * memory leaks. This also void the need of hijacking freehostent(3).
+ */
+struct hostent tsocks_he;
+char *tsocks_he_addr_list[2];
+char tsocks_he_addr[INET_ADDRSTRLEN];
+
+#define LIBC_GETHOSTBYNAME_NAME gethostbyname
+#define LIBC_GETHOSTBYNAME_NAME_STR XSTR(LIBC_GETHOSTBYNAME_NAME)
+#define LIBC_GETHOSTBYNAME_RET_TYPE struct hostent *
+#define LIBC_GETHOSTBYNAME_SIG const char *__name
+#define LIBC_GETHOSTBYNAME_ARGS __name
+
#else
#error "OS not supported."
#endif /* __linux__ , __FreeBSD__, __darwin__ */
@@ -58,12 +78,19 @@ TSOCKS_LIBC_DECL(connect, LIBC_CONNECT_RET_TYPE, LIBC_CONNECT_SIG)
#define LIBC_CONNECT_DECL \
LIBC_CONNECT_RET_TYPE LIBC_CONNECT_NAME(LIBC_CONNECT_SIG)
+/* gethostbyname(3) */
+TSOCKS_LIBC_DECL(gethostbyname, LIBC_GETHOSTBYNAME_RET_TYPE,
+ LIBC_GETHOSTBYNAME_SIG)
+#define LIBC_GETHOSTBYNAME_DECL LIBC_GETHOSTBYNAME_RET_TYPE \
+ LIBC_GETHOSTBYNAME_NAME(LIBC_GETHOSTBYNAME_SIG)
+
/*
* Those are actions to do during the lookup process of libc symbols. For
* instance the connect(2) syscall is essential to Torsocks so the function
* call exits if not found.
*/
enum tsocks_sym_action {
+ TSOCKS_SYM_DO_NOTHING = 0,
TSOCKS_SYM_EXIT_NOT_FOUND = 1,
};
More information about the tor-commits
mailing list