
commit c48811e759850815048713f91e30140f6df63aac Author: David Goulet <dgoulet@ev0ke.net> Date: Sun Jun 23 14:32:58 2013 -0400 Support the RESOLVE PTR Tor command in the socks5 API Signed-off-by: David Goulet <dgoulet@ev0ke.net> --- src/common/socks5.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/common/socks5.h | 10 ++++ 2 files changed, 138 insertions(+) diff --git a/src/common/socks5.c b/src/common/socks5.c index 8a16b9a..d007599 100644 --- a/src/common/socks5.c +++ b/src/common/socks5.c @@ -15,9 +15,11 @@ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include <arpa/inet.h> #include <assert.h> #include <errno.h> #include <inttypes.h> +#include <stdlib.h> #include <lib/torsocks.h> @@ -493,3 +495,129 @@ int socks5_recv_resolve_reply(struct connection *conn, uint32_t *ip_addr) error: return ret; } + +/* + * Send a SOCKS5 Tor resolve ptr request for a given ip address using an + * already connected connection. + * + * Return 0 on success or else a negative value. + */ +int socks5_send_resolve_ptr_request(const void *ip, struct connection *conn) +{ + int ret, ret_send; + char buffer[20]; /* Can't go higher than that (with IPv6). */ + char ip_str[INET6_ADDRSTRLEN]; + size_t msg_len, data_len; + struct socks5_request msg; + struct socks5_request_resolve_ptr req; + + assert(conn); + assert(conn->fd >= 0); + + DBG("[socks5] Resolve ptr request for ip %u", ip); + + memset(buffer, 0, sizeof(buffer)); + msg_len = sizeof(msg); + + msg.ver = SOCKS5_VERSION; + msg.cmd = SOCKS5_CMD_RESOLVE_PTR; + /* Always zeroed. */ + msg.rsv = 0; + + if (inet_ntop(AF_INET, ip, ip_str, sizeof(ip_str))) { + msg.atyp = SOCKS5_ATYP_IPV4; + memcpy(req.addr.ipv4, ip, 4); + } else if (inet_ntop(AF_INET6, ip, ip_str, sizeof(ip_str))) { + msg.atyp = SOCKS5_ATYP_IPV6; + memcpy(req.addr.ipv4, ip, 16); + } else { + ERR("Unknown address domain of %d", ip); + ret = -EINVAL; + goto error; + } + + /* 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 PTR for %u sent successfully", ip); + +error: + return ret; +} + +/* + * Receive a Tor resolve ptr reply on the given connection. The hostname value + * is populated with the returned name from Tor. On error, it's untouched. The + * memory is allocated so the caller needs to free the memory on success. + * + * Return 0 on success else a negative value. + */ +int socks5_recv_resolve_ptr_reply(struct connection *conn, char **_hostname) +{ + int ret; + ssize_t ret_recv; + char *hostname = NULL; + struct { + struct socks5_reply msg; + uint8_t len; + } buffer; + + assert(conn); + assert(conn >= 0); + assert(_hostname); + + 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_DOMAIN) { + /* Allocate hostname len plus an extra for the null byte. */ + hostname = zmalloc(buffer.len + 1); + if (!hostname) { + ret = -ENOMEM; + goto error; + } + ret_recv = recv_data(conn->fd, hostname, buffer.len); + if (ret_recv < 0) { + ret = ret_recv; + goto error; + } + hostname[buffer.len] = '\0'; + } else { + ERR("Bad SOCKS5 atyp reply %d", buffer.msg.atyp); + ret = -EINVAL; + goto error; + } + + *_hostname = hostname; + DBG("[socks5] Resolve reply received: %s", *_hostname); + return 0; + +error: + free(hostname); + return ret; +} diff --git a/src/common/socks5.h b/src/common/socks5.h index c11c097..9b47aa1 100644 --- a/src/common/socks5.h +++ b/src/common/socks5.h @@ -102,6 +102,14 @@ struct socks5_request_resolve { char name[UINT8_MAX]; }; +/* Use for the Tor resolve ptr command. */ +struct socks5_request_resolve_ptr { + union { + uint8_t ipv4[4]; + uint8_t ipv6[16]; + } addr; +}; + /* Non variable part of a reply. */ struct socks5_reply { uint8_t ver; @@ -123,5 +131,7 @@ 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); +int socks5_recv_resolve_ptr_reply(struct connection *conn, char **_hostname); +int socks5_send_resolve_ptr_request(const void *ip, struct connection *conn); #endif /* TORSOCKS_SOCKS_H */