[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