commit 20e0b3820577e006858b17633290d7f04eab9166 Author: Luke Gallagher luke@hypergeometric.net Date: Thu Feb 13 10:43:43 2014 +1100
Test: add socks5 tests
Introduce function pointers for send_data and recv_data so they can be replaced for testing.
Closes #25
Signed-off-by: David Goulet dgoulet@ev0ke.net --- .gitignore | 1 + src/common/socks5.c | 41 +- src/common/socks5.h | 3 + tests/test_list | 1 + tests/unit/Makefile.am | 8 +- tests/unit/test_socks5.c | 1262 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1313 insertions(+), 3 deletions(-)
diff --git a/.gitignore b/.gitignore index eb70007..5394c5b 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ tests/unit/test_onion tests/unit/test_connection tests/unit/test_utils tests/unit/test_config-file +tests/unit/test_socks5 diff --git a/src/common/socks5.c b/src/common/socks5.c index e725c56..3c9544a 100644 --- a/src/common/socks5.c +++ b/src/common/socks5.c @@ -32,7 +32,7 @@ * * Return the number of bytes received or a negative errno error. */ -static ssize_t recv_data(int fd, void *buf, size_t len) +static ssize_t recv_data_impl(int fd, void *buf, size_t len) { ssize_t ret, read_len, read_left, index;
@@ -75,12 +75,17 @@ error: }
/* + * Initialize recv_data function pointer. + */ +static ssize_t (*recv_data)(int, void *, size_t) = recv_data_impl; + +/* * Send data to a given file descriptor using send(2). This handles partial * send and EINTR. * * Return the number of bytes sent or a negative errno error. */ -static ssize_t send_data(int fd, const void *buf, size_t len) +static ssize_t send_data_impl(int fd, const void *buf, size_t len) { ssize_t ret, sent_len, sent_left, index;
@@ -119,6 +124,11 @@ error: }
/* + * Initialize send_data function pointer. + */ +static ssize_t (*send_data)(int, const void *, size_t) = send_data_impl; + +/* * Connect to socks5 server address from the global configuration. * * Return 0 on success or else a negative value. @@ -812,3 +822,30 @@ error: free(hostname); return ret; } + +/* + * Initialize the function pointers send_data and recv_data. Passing in a NULL + * value will reset them to the original implementations. + * + * Note that where send_data and recv_data are defined they are initialized to + * the default implementations. + * + * The ability to set the send/recv data functions are mainly for the tests so + * error/validation can be inject in the SOCKS5 codeflow. + */ +ATTR_HIDDEN +void socks5_init(ssize_t (*new_send_data)(int, const void *, size_t), + ssize_t (*new_recv_data)(int, void *, size_t)) +{ + if (!new_send_data) { + send_data = send_data_impl; + } else { + send_data = new_send_data; + } + + if (!new_recv_data) { + recv_data = recv_data_impl; + } else { + recv_data = new_recv_data; + } +} diff --git a/src/common/socks5.h b/src/common/socks5.h index d589c14..35c7d2b 100644 --- a/src/common/socks5.h +++ b/src/common/socks5.h @@ -154,4 +154,7 @@ int socks5_recv_resolve_reply(struct connection *conn, void *addr, int socks5_recv_resolve_ptr_reply(struct connection *conn, char **_hostname); int socks5_send_resolve_ptr_request(struct connection *conn, const void *ip, int af);
+void socks5_init(ssize_t (*new_send_data)(int, const void *, size_t), + ssize_t (*new_recv_data)(int, void *, size_t)); + #endif /* TORSOCKS_SOCKS_H */ diff --git a/tests/test_list b/tests/test_list index e1fa93b..0c22c5e 100644 --- a/tests/test_list +++ b/tests/test_list @@ -3,3 +3,4 @@ ./unit/test_connection ./unit/test_utils ./unit/test_config-file +./unit/test_socks5 diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index f2b5efa..3fd9c19 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -2,13 +2,16 @@ AM_CFLAGS = -I$(top_srcdir)/include \ -I$(top_srcdir)/src \ -I$(top_srcdir)/tests/utils/ \ -I$(srcdir) \ + -I$(top_srcdir)/src/lib \ -DTORSOCKS_FIXTURE_PATH="`pwd`/fixtures/"
LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la
LIBCOMMON=$(top_builddir)/src/common/libcommon.la
-noinst_PROGRAMS = test_onion test_connection test_utils test_config-file +LIBTORSOCKS=$(top_builddir)/src/lib/libtorsocks.la + +noinst_PROGRAMS = test_onion test_connection test_utils test_config-file test_socks5
EXTRA_DIST = fixtures
@@ -23,3 +26,6 @@ test_utils_LDADD = $(LIBTAP) $(LIBCOMMON)
test_config_file_SOURCES = test_config-file.c test_config_file_LDADD = $(LIBTAP) $(LIBCOMMON) + +test_socks5_SOURCES = test_socks5.c +test_socks5_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBTORSOCKS) diff --git a/tests/unit/test_socks5.c b/tests/unit/test_socks5.c new file mode 100644 index 0000000..b3ed081 --- /dev/null +++ b/tests/unit/test_socks5.c @@ -0,0 +1,1262 @@ +/* + * Copyright (C) 2013 - David Goulet dgoulet@ev0ke.net + * Luke Gallagher luke@hypergeometric.net + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2 only, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <arpa/inet.h> + +#include <common/connection.h> +#include <common/defaults.h> +#include <common/socks5.h> + +#include <tap/tap.h> + +#define NUM_TESTS 41 + +static struct socks5_method_req method_req; +static struct socks5_request req; +static struct socks5_request_ipv4 req_ipv4; +static struct socks5_request_ipv6 req_ipv6; +static struct socks5_request_domain req_name; +static struct socks5_request_resolve req_resolve; +static struct socks5_request_resolve_ptr req_resolve_ptr; + +static struct connection *get_connection_stub(void) +{ + struct connection *conn = NULL; + struct connection_addr c_addr; + + connection_addr_set(CONNECTION_DOMAIN_INET, + DEFAULT_TOR_ADDRESS, + DEFAULT_TOR_PORT, + &c_addr); + conn = connection_create(1, (struct sockaddr *) &c_addr.u.sin); + + return conn; +} + +static struct connection *get_connection_ipv6_stub(void) +{ + struct connection *conn = NULL; + struct connection_addr c_addr; + + connection_addr_set(CONNECTION_DOMAIN_INET6, "::1", 9050, &c_addr); + conn = connection_create(1, (struct sockaddr *) &c_addr.u.sin6); + + return conn; +} + +static struct connection *get_connection_domain_stub(void) +{ + struct connection *conn = NULL; + char addr_str[] = "example.org"; + + conn = connection_create(1, NULL); + conn->dest_addr.domain = CONNECTION_DOMAIN_NAME; + conn->dest_addr.hostname.addr = strndup(addr_str, strlen(addr_str)); + conn->dest_addr.hostname.port = htons(9050); + + return conn; +} + +static void set_socks5_request(const void *buffer) +{ + req.ver = ((struct socks5_request *)buffer)->ver; + req.cmd = ((struct socks5_request *)buffer)->cmd; + req.rsv = ((struct socks5_request *)buffer)->rsv; + req.atyp = ((struct socks5_request *)buffer)->atyp; +} + +static ssize_t socks5_send_data_error_stub(int fd, const void *buf, size_t len) +{ + return -1; +} + +static ssize_t socks5_recv_data_error_stub(int fd, void *buf, size_t len) +{ + return -1; +} + +/* + * socks5_send_method test doubles. + */ + +static ssize_t socks5_send_method_valid_spy(int fd, const void *buf, size_t len) +{ + method_req.ver = ((struct socks5_method_req *)buf)->ver; + method_req.nmethods = ((struct socks5_method_req *)buf)->nmethods; + method_req.methods = ((struct socks5_method_req *)buf)->methods; + + return 1; +} + +/* + * socks5_recv_method test doubles. + */ + +static ssize_t socks5_recv_method_valid_stub(int fd, void *buf, size_t len) +{ + ((struct socks5_method_res *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_method_res *)buf)->method = SOCKS5_NO_AUTH_METHOD; + + return 1; +} + +static ssize_t socks5_recv_method_wrong_version_stub(int fd, void *buf, + size_t len) +{ + ((struct socks5_method_res *)buf)->ver = 0x04; + ((struct socks5_method_res *)buf)->method = SOCKS5_NO_AUTH_METHOD; + + return 1; +} + +static ssize_t socks5_recv_method_no_accept_stub(int fd, void *buf, size_t len) +{ + ((struct socks5_method_res *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_method_res *)buf)->method = SOCKS5_NO_ACCPT_METHOD; + + return 1; +} + +/* + * send_connect_request test doubles + */ + +static ssize_t socks5_send_connect_request_ipv4_spy(int fd, const void *buf, + size_t len) +{ + ssize_t buf_len = 0; + + set_socks5_request(buf); + buf_len += sizeof(struct socks5_request); + + req_ipv4 = (*(struct socks5_request_ipv4 *) (buf + buf_len)); + + return 1; +} + +static ssize_t socks5_send_connect_request_ipv6_spy(int fd, const void *buf, + size_t len) +{ + ssize_t buf_len = 0; + + set_socks5_request(buf); + buf_len += sizeof(struct socks5_request); + + req_ipv6 = (*(struct socks5_request_ipv6 *) (buf + buf_len)); + + return 1; +} + +static ssize_t socks5_send_connect_request_domain_spy(int fd, const void *buf, + size_t len) +{ + ssize_t buf_len = 0; + + set_socks5_request(buf); + buf_len += sizeof(struct socks5_request); + + /* + * Use memcpy since req_name.name is variable length. + */ + memcpy(&req_name.len, buf + buf_len, sizeof(req_name.len)); + buf_len += sizeof(req_name.len); + memcpy(&req_name.name, buf + buf_len, req_name.len); + buf_len += req_name.len; + memcpy(&req_name.port, buf + buf_len, sizeof(req_name.port)); + + return 1; +} + +/* + * socks5_receive_connect_reply test doubles. + */ + +static ssize_t socks5_recv_connect_reply_ipv4_success_stub(int fd, void *buf, + size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_SUCCESS; + ((struct socks5_reply *)buf)->rsv = 0; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + + return 1; +} + +static ssize_t socks5_recv_connect_reply_ipv4_fail_stub(int fd, void *buf, + size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_FAIL; + ((struct socks5_reply *)buf)->rsv = 0; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + + return 1; +} + +static ssize_t socks5_recv_connect_reply_ipv4_deny_rule_stub(int fd, void *buf, + size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_DENY_RULE; + ((struct socks5_reply *)buf)->rsv = 0; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + + return 1; +} + +static ssize_t socks5_recv_connect_reply_ipv4_no_net_stub(int fd, void *buf, + size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_NO_NET; + ((struct socks5_reply *)buf)->rsv = 0; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + + return 1; +} + +static ssize_t socks5_recv_connect_reply_ipv4_no_host_stub(int fd, void *buf, + size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_NO_HOST; + ((struct socks5_reply *)buf)->rsv = 0; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + + return 1; +} + +static ssize_t socks5_recv_connect_reply_ipv4_refused_stub(int fd, void *buf, + size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_REFUSED; + ((struct socks5_reply *)buf)->rsv = 0; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + + return 1; +} + +static ssize_t socks5_recv_connect_reply_ipv4_ttl_expired_stub(int fd, void *buf, + size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_TTL_EXP; + ((struct socks5_reply *)buf)->rsv = 0; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + + return 1; +} + +static ssize_t socks5_recv_connect_reply_ipv4_cmd_not_supported_stub(int fd, + void *buf, size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_CMD_NOTSUP; + ((struct socks5_reply *)buf)->rsv = 0; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + + return 1; +} + +static ssize_t socks5_recv_connect_reply_ipv4_addr_not_supported_stub(int fd, + void *buf, size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_ADR_NOTSUP; + ((struct socks5_reply *)buf)->rsv = 0; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + + return 1; +} + +static ssize_t socks5_recv_connect_reply_ipv4_unkown_stub(int fd, void *buf, + size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = 0x9; /* unassigned code */ + ((struct socks5_reply *)buf)->rsv = 0; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + + return 1; +} + +static ssize_t socks5_recv_connect_reply_ipv6_success_stub(int fd, void *buf, + size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_SUCCESS; + ((struct socks5_reply *)buf)->rsv = 0; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV6; + + return 1; +} + +/* + * socks5_send_resolve_request test doubles. + */ + +static ssize_t socks5_send_resolve_request_valid_spy(int fd, const void *buf, + size_t len) +{ + ssize_t buf_len = 0; + + set_socks5_request(buf); + buf_len += sizeof(struct socks5_request); + + memcpy(&req_resolve.len, buf + buf_len, sizeof(req_resolve.len)); + buf_len += sizeof(req_resolve.len); + memcpy(&req_resolve.name, buf + buf_len, req_resolve.len); + + return 1; +} + +/* + * socks5_recv_resolve_reply test doubles. + */ + +static ssize_t socks5_recv_resolve_reply_ipv4_stub(int fd, void *buf, + size_t len) +{ + static int count = 0; + uint8_t ipv4_stub[4]; + + if (0 == count) { + /* first call to recv_data */ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_SUCCESS; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + } else { + /* second call to recv data */ + inet_pton(AF_INET, "127.0.0.1", &ipv4_stub); + memcpy(buf, &ipv4_stub, len); + } + + count++; + + return 1; +} + +static ssize_t socks5_recv_resolve_reply_ipv6_stub(int fd, void *buf, + size_t len) +{ + static int count = 0; + uint8_t ipv6_stub[16]; + + if (0 == count) { + /* first call to recv_data */ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_SUCCESS; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV6; + } else { + /* second call to recv data */ + inet_pton(AF_INET6, "::1", &ipv6_stub); + memcpy(buf, &ipv6_stub, len); + } + + count++; + + return 1; +} + +static ssize_t socks5_recv_resolve_reply_incorrect_version_stub(int fd, + void *buf, size_t len) +{ + ((struct socks5_reply *)buf)->ver = 0x04; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_SUCCESS; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + + return 1; +} + +static ssize_t socks5_recv_resolve_reply_response_error_stub(int fd, void *buf, + size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_FAIL; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + + return 1; +} + +static ssize_t socks5_recv_resolve_reply_address_type_error_stub(int fd, + void *buf, size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_SUCCESS; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_DOMAIN; + + return 1; +} + +static ssize_t socks5_recv_resolve_reply_addrlen_error_stub(int fd, void *buf, + size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_SUCCESS; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + + return 10; +} + +/* + * socks5_send_resolve_ptr_request test doubles. + */ + +static ssize_t socks5_send_resolve_ptr_request_ipv4_spy(int fd, + const void *buf, size_t len) +{ + int buf_len = 0; + + set_socks5_request(buf); + buf_len += sizeof(struct socks5_request); + + req_resolve_ptr = (*(struct socks5_request_resolve_ptr *) (buf + buf_len)); + + return 1; +} + +static ssize_t socks5_send_resolve_ptr_request_ipv6_spy(int fd, + const void *buf, size_t len) +{ + int buf_len = 0; + + set_socks5_request(buf); + buf_len += sizeof(struct socks5_request); + + req_resolve_ptr = (*(struct socks5_request_resolve_ptr *) (buf + buf_len)); + + return 1; +} + +/* + * socks5_recv_resolve_ptr_reply test doubles. + */ + +static ssize_t socks5_recv_resolve_ptr_reply_stub(int fd, void *buf, + size_t len) +{ + static int count = 0; + int buf_len = 0; + char hostname[] = "example.org"; + + if (0 == count) { + /* first call to recv_data */ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_SUCCESS; + ((struct socks5_reply *)buf)->rsv = 0; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_DOMAIN; + + buf_len += sizeof(struct socks5_reply); + (*(uint8_t *)(buf + buf_len)) = strlen(hostname); + } else { + /* second call to recv data */ + memcpy(buf, &hostname, len); + } + + count++; + + return 1; +} + +static ssize_t socks5_recv_resolve_ptr_reply_atyp_error_stub(int fd, void *buf, + size_t len) +{ + ((struct socks5_reply *)buf)->ver = SOCKS5_VERSION; + ((struct socks5_reply *)buf)->rep = SOCKS5_REPLY_SUCCESS; + ((struct socks5_reply *)buf)->rsv = 0; + ((struct socks5_reply *)buf)->atyp = SOCKS5_ATYP_IPV4; + + return 1; +} + +/* + * socks5 tests + */ + +static void test_socks5_send_method_valid(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(socks5_send_method_valid_spy, NULL); + + ret = socks5_send_method(conn_stub, SOCKS5_NO_AUTH_METHOD); + + ok(ret == 0 && + method_req.ver == SOCKS5_VERSION && + method_req.nmethods == 0x01 && + method_req.methods == SOCKS5_NO_AUTH_METHOD, + "socks5 send method valid"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_send_method_failure(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(socks5_send_data_error_stub, NULL); + + ret = socks5_send_method(conn_stub, SOCKS5_NO_AUTH_METHOD); + + ok(ret == -1, "socks5 send method returns send error code"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_method_valid(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_method_valid_stub); + + ret = socks5_recv_method(conn_stub); + + ok(ret == 0, "socks5 recv method valid response"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_method_failure(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_data_error_stub); + + ret = socks5_recv_method(conn_stub); + + ok(ret == -1, "socks5 recv method returns recv error code"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_method_incorrect_version(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_method_wrong_version_stub); + + ret = socks5_recv_method(conn_stub); + + ok(ret == -ECONNABORTED, "socks5 recv method returns ECONNABORTED when " + "incorrect version"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_method_no_accept(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_method_no_accept_stub); + + ret = socks5_recv_method(conn_stub); + + ok(ret == -ECONNABORTED, "socks5 recv method returns ECONNABORTED when " + "no accept method"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_send_connect_request(void) +{ + int ret; + struct connection *conn_stub; + char ip[INET6_ADDRSTRLEN]; + + conn_stub = get_connection_stub(); + socks5_init(socks5_send_connect_request_ipv4_spy, NULL); + + ret = socks5_send_connect_request(conn_stub); + + inet_ntop(AF_INET, + (struct sockaddr_in *)&req_ipv4.addr, + ip, INET6_ADDRSTRLEN); + + ok(ret == 0 && + req.ver == SOCKS5_VERSION && + req.cmd == SOCKS5_CMD_CONNECT && + req.rsv == 0 && + req.atyp == SOCKS5_ATYP_IPV4 && + strncmp(ip, "127.0.0.1", INET6_ADDRSTRLEN) == 0 && + req_ipv4.port == htons(9050), + "socks5 send connect request IPv4"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); + + /* IPv6 */ + + conn_stub = get_connection_ipv6_stub(); + socks5_init(socks5_send_connect_request_ipv6_spy, NULL); + + ret = socks5_send_connect_request(conn_stub); + + inet_ntop(AF_INET6, + (struct sockaddr_in *)&req_ipv6.addr, + ip, INET6_ADDRSTRLEN); + + ok(ret == 0 && + req.ver == SOCKS5_VERSION && + req.cmd == SOCKS5_CMD_CONNECT && + req.rsv == 0 && + req.atyp == SOCKS5_ATYP_IPV6 && + strncmp(ip, "::1", INET6_ADDRSTRLEN) == 0 && + req_ipv6.port == htons(9050), + "socks5 send connect request IPv6"); + + /* Domain name */ + + conn_stub = get_connection_domain_stub(); + socks5_init(socks5_send_connect_request_domain_spy, NULL); + + ret = socks5_send_connect_request(conn_stub); + + ok(ret == 0 && + req.ver == SOCKS5_VERSION && + req.cmd == SOCKS5_CMD_CONNECT && + req.rsv == 0 && + req.atyp == SOCKS5_ATYP_DOMAIN && + strncmp((char *)req_name.name, + "example.org", + req_name.len) == 0 && + req_name.port == htons(9050), + "socks5 send connect request domain name"); + + /* Unkown connection domain */ + + conn_stub = get_connection_stub(); + conn_stub->dest_addr.domain = 0; + socks5_init(socks5_send_connect_request_domain_spy, NULL); + + ret = socks5_send_connect_request(conn_stub); + + ok(ret == -EINVAL, "socks5 send connect request returns error for " + "unkown connection domain"); +} + +static void test_socks5_send_connect_request_failure(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(socks5_send_data_error_stub, NULL); + + ret = socks5_send_connect_request(conn_stub); + + ok(ret == -1, "socks5 connect request returns error code from send"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_connect_reply_success(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_connect_reply_ipv4_success_stub); + + ret = socks5_recv_connect_reply(conn_stub); + + ok(ret == 0, "socks5 reply success"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_connect_reply_fail(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_connect_reply_ipv4_fail_stub); + + ret = socks5_recv_connect_reply(conn_stub); + + ok(ret == -ECONNREFUSED, "socks5 reply fail"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_connect_reply_deny_rule(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_connect_reply_ipv4_deny_rule_stub); + + ret = socks5_recv_connect_reply(conn_stub); + + ok(ret == -ECONNABORTED, "socks5 reply deny rule"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_connect_reply_no_net(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_connect_reply_ipv4_no_net_stub); + + ret = socks5_recv_connect_reply(conn_stub); + + ok(ret == -ENETUNREACH, "socks5 reply no net"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_connect_reply_no_host(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_connect_reply_ipv4_no_host_stub); + + ret = socks5_recv_connect_reply(conn_stub); + + ok(ret == -EHOSTUNREACH, "socks5 reply no host"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_connect_reply_refused(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_connect_reply_ipv4_refused_stub); + + ret = socks5_recv_connect_reply(conn_stub); + + ok(ret == -ECONNREFUSED, "socks5 reply refused"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_connect_reply_ttl_expired(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_connect_reply_ipv4_ttl_expired_stub); + + ret = socks5_recv_connect_reply(conn_stub); + + ok(ret == -ETIMEDOUT, "socks5 reply TTL expired"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_connect_reply_cmd_not_supported(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, + socks5_recv_connect_reply_ipv4_cmd_not_supported_stub); + + ret = socks5_recv_connect_reply(conn_stub); + + ok(ret == -ECONNREFUSED, "socks5 reply command not supported"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_connect_reply_addr_not_supported(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, + socks5_recv_connect_reply_ipv4_addr_not_supported_stub); + + ret = socks5_recv_connect_reply(conn_stub); + + ok(ret == -ECONNREFUSED, "socks5 reply address type not supported"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_connect_reply_unkown(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_connect_reply_ipv4_unkown_stub); + + ret = socks5_recv_connect_reply(conn_stub); + + ok(ret == -ECONNABORTED, "socks5 reply unkown code"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_connect_reply_ipv6_success(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_connect_reply_ipv6_success_stub); + + ret = socks5_recv_connect_reply(conn_stub); + + ok(ret == 0, "socks5 reply IPv6 success"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_send_resolve_request_valid(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(socks5_send_resolve_request_valid_spy, NULL); + + ret = socks5_send_resolve_request("foo", conn_stub); + + ok(ret == 0 && + req.ver == SOCKS5_VERSION && + req.cmd == SOCKS5_CMD_RESOLVE && + req.rsv == 0 && + req.atyp == SOCKS5_ATYP_DOMAIN && + strcmp((char *)req_resolve.name, "foo") == 0 && + req_resolve.len == strlen("foo"), + "socks5 resolve request valid"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_send_resolve_request_failure(void) +{ + int ret; + struct connection *conn_stub; + + conn_stub = get_connection_stub(); + socks5_init(socks5_send_data_error_stub, NULL); + + ret = socks5_send_resolve_request("foo", conn_stub); + + ok(ret == -1, "socks5 resolve request returns send error code"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); + + /* hostname greater than 255 in length */ + + conn_stub = get_connection_stub(); + /* no need to stub send_data it should not get called */ + + char long_hostname[257]; + memset(long_hostname, 0x41, sizeof(long_hostname)); + long_hostname[256] = '\0'; + + ret = socks5_send_resolve_request(long_hostname, conn_stub); + + ok(ret == -EINVAL, "socks5 resolve request hostname greater " + "than UINT8_MAX"); + + connection_destroy(conn_stub); +} + +static void test_socks5_recv_resolve_reply_valid(void) +{ + int ret; + struct connection *conn_stub; + uint32_t ipv4_addr; + uint8_t ipv6_addr[16]; + char ip_str[INET6_ADDRSTRLEN]; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_resolve_reply_ipv4_stub); + + ret = socks5_recv_resolve_reply(conn_stub, &ipv4_addr, sizeof(uint32_t)); + + inet_ntop(AF_INET, &ipv4_addr, ip_str, INET_ADDRSTRLEN); + + ok(ret == 0 && + strncmp(ip_str, "127.0.0.1", INET_ADDRSTRLEN) == 0, + "socks5 resolve reply valid IPv4 address"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); + + /* IPv6 */ + + conn_stub = get_connection_ipv6_stub(); + socks5_init(NULL, socks5_recv_resolve_reply_ipv6_stub); + + ret = socks5_recv_resolve_reply(conn_stub, &ipv6_addr, sizeof(ipv6_addr)); + + inet_ntop(AF_INET6, &ipv6_addr, ip_str, INET6_ADDRSTRLEN); + + ok(ret == 0 && + strncmp(ip_str, "::1", INET6_ADDRSTRLEN) == 0, + "socks5 resolve reply valid IPv6 address"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_resolve_reply_failure(void) +{ + int ret; + struct connection *conn_stub; + uint32_t dummy_ip_addr; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_data_error_stub); + + ret = socks5_recv_resolve_reply(conn_stub, &dummy_ip_addr, + sizeof(dummy_ip_addr)); + + ok(ret == -1, "socks5 resolve reply returns recv error code"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_resolve_reply_incorrect_version(void) +{ + int ret; + struct connection *conn_stub; + uint32_t dummy_ip_addr; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_resolve_reply_incorrect_version_stub); + + ret = socks5_recv_resolve_reply(conn_stub, &dummy_ip_addr, + sizeof(dummy_ip_addr)); + + ok(ret == -ECONNABORTED, "socks5 resolve reply incorrect version"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_resolve_reply_response_error(void) +{ + int ret; + struct connection *conn_stub; + uint32_t dummy_ip_addr; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_resolve_reply_response_error_stub); + + ret = socks5_recv_resolve_reply(conn_stub, &dummy_ip_addr, + sizeof(dummy_ip_addr)); + + ok(ret == -ECONNABORTED, "socks5 resolve reply response error"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_resolve_reply_address_type_error(void) +{ + int ret; + struct connection *conn_stub; + uint32_t dummy_ip_addr; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_resolve_reply_address_type_error_stub); + + ret = socks5_recv_resolve_reply(conn_stub, &dummy_ip_addr, + sizeof(dummy_ip_addr)); + + ok(ret == -EINVAL, "socks5 resolve reply address type error"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_recv_resolve_reply_addrlen_error(void) +{ + int ret; + struct connection *conn_stub; + uint32_t dummy_ip_addr; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_resolve_reply_addrlen_error_stub); + + ret = socks5_recv_resolve_reply(conn_stub, &dummy_ip_addr, 1); + + ok(ret == -EINVAL, "socks5 resolve reply address length error"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_send_resolve_ptr_request_valid(void) +{ + int ret; + struct connection *conn_stub; + uint32_t ipv4_addr_stub; + uint8_t ipv6_addr_stub[16]; + char ip_str[INET6_ADDRSTRLEN]; + + conn_stub = get_connection_stub(); + socks5_init(socks5_send_resolve_ptr_request_ipv4_spy, NULL); + inet_pton(AF_INET, "127.0.0.1", &ipv4_addr_stub); + + ret = socks5_send_resolve_ptr_request(conn_stub, &ipv4_addr_stub, AF_INET); + + inet_ntop(AF_INET, &req_resolve_ptr, ip_str, INET_ADDRSTRLEN); + + ok(ret == 0 && + req.ver == SOCKS5_VERSION && + req.cmd == SOCKS5_CMD_RESOLVE_PTR && + req.rsv == 0 && + req.atyp == SOCKS5_ATYP_IPV4 && + strncmp(ip_str, "127.0.0.1", INET_ADDRSTRLEN) == 0, + "socks5 send resolve ptr request valid IPv4"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); + + /* IPv6 */ + + conn_stub = get_connection_ipv6_stub(); + socks5_init(socks5_send_resolve_ptr_request_ipv6_spy, NULL); + inet_pton(AF_INET6, "::1", ipv6_addr_stub); + + ret = socks5_send_resolve_ptr_request(conn_stub, ipv6_addr_stub, AF_INET6); + + inet_ntop(AF_INET6, ipv6_addr_stub, ip_str, INET6_ADDRSTRLEN); + + ok(ret == 0 && + req.ver == SOCKS5_VERSION && + req.cmd == SOCKS5_CMD_RESOLVE_PTR && + req.rsv == 0 && + req.atyp == SOCKS5_ATYP_IPV6 && + strncmp(ip_str, "::1", INET6_ADDRSTRLEN) == 0, + "socks5 send resolve ptr request valid IPv6"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); +} + +static void test_socks5_send_resolve_ptr_request_failure(void) +{ + int ret; + struct connection *conn_stub; + uint32_t addr_stub; + + conn_stub = get_connection_stub(); + socks5_init(socks5_send_data_error_stub, NULL); + inet_pton(AF_INET, "127.0.0.1", &addr_stub); + + ret = socks5_send_resolve_ptr_request(conn_stub, &addr_stub, AF_INET); + + ok(ret == -1, "socks5 resolve ptr request returns send error code"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); + + /* Unkown domain */ + + conn_stub = get_connection_domain_stub(); + + ret = socks5_send_resolve_ptr_request(conn_stub, &addr_stub, 3); + + ok(ret == -EINVAL, "socks5 send resolve ptr request unkown domain"); + + connection_destroy(conn_stub); +} + +static void test_socks5_recv_resolve_ptr_reply_valid(void) +{ + int ret; + struct connection *conn_stub; + char *hostname = NULL; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_resolve_ptr_reply_stub); + + ret = socks5_recv_resolve_ptr_reply(conn_stub, &hostname); + + ok(ret == 0 && + strncmp(hostname, "example.org", strlen(hostname)) == 0, + "socks5 recv resolve ptr reply valid"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); + free(hostname); +} + +static void test_socks5_recv_resolve_ptr_reply_failure(void) +{ + int ret; + struct connection *conn_stub; + char *hostname = NULL; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_data_error_stub); + + ret = socks5_recv_resolve_ptr_reply(conn_stub, &hostname); + + ok(ret == -1, "socks5 recv resolve ptr reply returns recv error code"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); + free(hostname); +} + +static void test_socks5_recv_resolve_ptr_reply_incorrect_version(void) +{ + int ret; + struct connection *conn_stub; + char *hostname = NULL; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_resolve_reply_incorrect_version_stub); + + ret = socks5_recv_resolve_ptr_reply(conn_stub, &hostname); + + ok(ret == -ECONNABORTED, "socks5 recv resolve ptr reply incorrect version"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); + free(hostname); +} + +static void test_socks5_recv_resolve_ptr_reply_response_error(void) +{ + int ret; + struct connection *conn_stub; + char *hostname = NULL; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_resolve_reply_response_error_stub); + + ret = socks5_recv_resolve_ptr_reply(conn_stub, &hostname); + + ok(ret == -ECONNABORTED, "socks5 recv resolve ptr reply response error"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); + free(hostname); +} + +static void test_socks5_recv_resolve_ptr_reply_atyp_error(void) +{ + int ret; + struct connection *conn_stub; + char *hostname = NULL; + + conn_stub = get_connection_stub(); + socks5_init(NULL, socks5_recv_resolve_ptr_reply_atyp_error_stub); + + ret = socks5_recv_resolve_ptr_reply(conn_stub, &hostname); + + ok(ret == -EINVAL, "socks5 recv resolve ptr reply atyp error"); + + connection_destroy(conn_stub); + socks5_init(NULL, NULL); + free(hostname); +} + +int main(int argc, char **argv) +{ + /* Libtap call for the number of tests planned. */ + plan_tests(NUM_TESTS); + + diag("socks5 tests"); + + test_socks5_send_method_valid(); + test_socks5_send_method_failure(); + test_socks5_recv_method_valid(); + test_socks5_recv_method_failure(); + test_socks5_recv_method_incorrect_version(); + test_socks5_recv_method_no_accept(); + test_socks5_send_connect_request(); + test_socks5_send_connect_request_failure(); + test_socks5_recv_connect_reply_success(); + test_socks5_recv_connect_reply_fail(); + test_socks5_recv_connect_reply_deny_rule(); + test_socks5_recv_connect_reply_no_net(); + test_socks5_recv_connect_reply_no_host(); + test_socks5_recv_connect_reply_refused(); + test_socks5_recv_connect_reply_ttl_expired(); + test_socks5_recv_connect_reply_cmd_not_supported(); + test_socks5_recv_connect_reply_addr_not_supported(); + test_socks5_recv_connect_reply_unkown(); + test_socks5_recv_connect_reply_ipv6_success(); + test_socks5_send_resolve_request_valid(); + test_socks5_send_resolve_request_failure(); + test_socks5_recv_resolve_reply_valid(); + test_socks5_recv_resolve_reply_failure(); + test_socks5_recv_resolve_reply_incorrect_version(); + test_socks5_recv_resolve_reply_response_error(); + test_socks5_recv_resolve_reply_address_type_error(); + test_socks5_recv_resolve_reply_addrlen_error(); + test_socks5_send_resolve_ptr_request_valid(); + test_socks5_send_resolve_ptr_request_failure(); + test_socks5_recv_resolve_ptr_reply_valid(); + test_socks5_recv_resolve_ptr_reply_failure(); + test_socks5_recv_resolve_ptr_reply_incorrect_version(); + test_socks5_recv_resolve_ptr_reply_response_error(); + test_socks5_recv_resolve_ptr_reply_atyp_error(); + + return exit_status(); +}
tor-commits@lists.torproject.org