diff options
Diffstat (limited to 'nsock/tests')
-rw-r--r-- | nsock/tests/Makefile.in | 43 | ||||
-rw-r--r-- | nsock/tests/README | 5 | ||||
-rw-r--r-- | nsock/tests/basic.c | 52 | ||||
-rw-r--r-- | nsock/tests/cancel.c | 134 | ||||
-rw-r--r-- | nsock/tests/connect.c | 113 | ||||
-rw-r--r-- | nsock/tests/ghheaps.c | 151 | ||||
-rw-r--r-- | nsock/tests/ghlists.c | 189 | ||||
-rw-r--r-- | nsock/tests/logs.c | 176 | ||||
-rw-r--r-- | nsock/tests/proxychain.c | 159 | ||||
-rwxr-xr-x | nsock/tests/run_tests.sh | 86 | ||||
-rw-r--r-- | nsock/tests/test-common.h | 89 | ||||
-rw-r--r-- | nsock/tests/tests_main.c | 133 | ||||
-rw-r--r-- | nsock/tests/timer.c | 129 |
13 files changed, 1459 insertions, 0 deletions
diff --git a/nsock/tests/Makefile.in b/nsock/tests/Makefile.in new file mode 100644 index 0000000..6fa8342 --- /dev/null +++ b/nsock/tests/Makefile.in @@ -0,0 +1,43 @@ +# +# nsock regression test suite +# Same license as nmap -- see https://nmap.org/book/man-legal.html +## + +NBASEDIR=@NBASEDIR@ +NSOCKLIB=../src/libnsock.a +NBASELIB=$(NBASEDIR)/libnbase.a + +CC = @CC@ +CPPFLAGS = @CPPFLAGS@ -I../include +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @OPENSSL_LIBS@ @LIBPCAP_LIBS@ @LIBS@ + +SRC = tests_main.c \ + basic.c \ + timer.c \ + logs.c \ + connect.c \ + ghlists.c \ + ghheaps.c \ + proxychain.c \ + cancel.c + +OBJ = $(SRC:.c=.o) + +EXE = tests_main + +all: $(SRC) $(EXE) + +$(EXE): $(OBJ) + $(CC) $(LDFLAGS) $(OBJ) -o $@ $(NSOCKLIB) $(NBASELIB) $(LIBS) + +.c.o: + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +clean: + $(RM) $(OBJ) $(EXE) + +rebuild: clean $(EXE) + +.PHONY: clean rebuild diff --git a/nsock/tests/README b/nsock/tests/README new file mode 100644 index 0000000..a95cc1c --- /dev/null +++ b/nsock/tests/README @@ -0,0 +1,5 @@ +Minimal regression test suite for nsock. + +Usage: + $ make + $ sh ./run_tests.sh diff --git a/nsock/tests/basic.c b/nsock/tests/basic.c new file mode 100644 index 0000000..9d8a12c --- /dev/null +++ b/nsock/tests/basic.c @@ -0,0 +1,52 @@ +/* + * Nsock regression test suite + * Same license as nmap -- see https://nmap.org/book/man-legal.html + */ + +#include "test-common.h" + + +struct basic_test_data { + nsock_pool nsp; +}; + +static int basic_setup(void **tdata) { + struct basic_test_data *btd; + + btd = calloc(1, sizeof(struct basic_test_data)); + if (btd == NULL) + return -ENOMEM; + + btd->nsp = nsock_pool_new(NULL); + + *tdata = btd; + return 0; +} + +static int basic_teardown(void *tdata) { + struct basic_test_data *btd = (struct basic_test_data *)tdata; + + if (tdata) { + nsock_pool_delete(btd->nsp); + free(tdata); + } + return 0; +} + +static int basic_udata(void *tdata) { + struct basic_test_data *btd = (struct basic_test_data *)tdata; + + AssertEqual(nsock_pool_get_udata(btd->nsp), NULL); + nsock_pool_set_udata(btd->nsp, btd); + AssertEqual(nsock_pool_get_udata(btd->nsp), btd); + return 0; +} + + + +const struct test_case TestPoolUserData = { + .t_name = "nsock pool user data", + .t_setup = basic_setup, + .t_run = basic_udata, + .t_teardown = basic_teardown +}; diff --git a/nsock/tests/cancel.c b/nsock/tests/cancel.c new file mode 100644 index 0000000..49f98bf --- /dev/null +++ b/nsock/tests/cancel.c @@ -0,0 +1,134 @@ +/* + * Nsock regression test suite + * Same license as nmap -- see https://nmap.org/book/man-legal.html + */ + +#include "test-common.h" + + +struct basic_test_data { + nsock_pool nsp; +}; + + +static void cancel_handler(nsock_pool nsp, nsock_event nse, void *udata) { + int *ev_done = (int *)udata; + + if (nse_status(nse) == NSE_STATUS_CANCELLED) + *ev_done = 1; +} + +static int cancel_setup(void **tdata) { + struct basic_test_data *btd; + + btd = calloc(1, sizeof(struct basic_test_data)); + if (btd == NULL) + return -ENOMEM; + + btd->nsp = nsock_pool_new(NULL); + + *tdata = btd; + return 0; +} + +static int cancel_teardown(void *tdata) { + struct basic_test_data *btd = (struct basic_test_data *)tdata; + + if (tdata) { + nsock_pool_delete(btd->nsp); + free(tdata); + } + return 0; +} + +static int cancel_tcp_run(void *tdata) { + struct basic_test_data *btd = (struct basic_test_data *)tdata; + struct sockaddr_in peer; + nsock_iod iod; + nsock_event_id id; + int done = 0; + + iod = nsock_iod_new(btd->nsp, NULL); + AssertNonNull(iod); + + memset(&peer, 0, sizeof(peer)); + peer.sin_family = AF_INET; + inet_aton("127.0.0.1", &peer.sin_addr); + + id = nsock_connect_tcp(btd->nsp, iod, cancel_handler, 4000, (void *)&done, + (struct sockaddr *)&peer, sizeof(peer), PORT_TCP); + nsock_event_cancel(btd->nsp, id, 1); + + nsock_iod_delete(iod, NSOCK_PENDING_SILENT); + + return (done == 1) ? 0 : -ENOEXEC; +} + +static int cancel_udp_run(void *tdata) { + struct basic_test_data *btd = (struct basic_test_data *)tdata; + struct sockaddr_in peer; + nsock_iod iod; + nsock_event_id id; + int done = 0; + + iod = nsock_iod_new(btd->nsp, NULL); + AssertNonNull(iod); + + memset(&peer, 0, sizeof(peer)); + peer.sin_family = AF_INET; + inet_aton("127.0.0.1", &peer.sin_addr); + + id = nsock_connect_udp(btd->nsp, iod, cancel_handler, (void *)&done, + (struct sockaddr *)&peer, sizeof(peer), PORT_UDP); + nsock_event_cancel(btd->nsp, id, 1); + + nsock_iod_delete(iod, NSOCK_PENDING_SILENT); + + return (done == 1) ? 0 : -ENOEXEC; +} + +static int cancel_ssl_run(void *tdata) { + struct basic_test_data *btd = (struct basic_test_data *)tdata; + struct sockaddr_in peer; + nsock_iod iod; + nsock_event_id id; + int done = 0; + + iod = nsock_iod_new(btd->nsp, NULL); + AssertNonNull(iod); + + memset(&peer, 0, sizeof(peer)); + peer.sin_family = AF_INET; + inet_aton("127.0.0.1", &peer.sin_addr); + + id = nsock_connect_ssl(btd->nsp, iod, cancel_handler, 4000, (void *)&done, + (struct sockaddr *)&peer, sizeof(peer), IPPROTO_TCP, + PORT_TCPSSL, NULL); + nsock_event_cancel(btd->nsp, id, 1); + + nsock_iod_delete(iod, NSOCK_PENDING_SILENT); + + return (done == 1) ? 0 : -ENOEXEC; +} + + +const struct test_case TestCancelTCP = { + .t_name = "schedule and cancel TCP connect", + .t_setup = cancel_setup, + .t_run = cancel_tcp_run, + .t_teardown = cancel_teardown +}; + +const struct test_case TestCancelUDP = { + .t_name = "schedule and cancel UDP pseudo-connect", + .t_setup = cancel_setup, + .t_run = cancel_udp_run, + .t_teardown = cancel_teardown +}; + +const struct test_case TestCancelSSL = { + .t_name = "schedule and cancel SSL connect", + .t_setup = cancel_setup, + .t_run = cancel_ssl_run, + .t_teardown = cancel_teardown +}; diff --git a/nsock/tests/connect.c b/nsock/tests/connect.c new file mode 100644 index 0000000..736b082 --- /dev/null +++ b/nsock/tests/connect.c @@ -0,0 +1,113 @@ +/* + * Nsock regression test suite + * Same license as nmap -- see https://nmap.org/book/man-legal.html + */ + +#include "test-common.h" + + +struct connect_test_data { + nsock_pool nsp; + nsock_iod nsi; + int connect_result; +}; + + +static void connect_handler(nsock_pool nsp, nsock_event nse, void *udata) { + struct connect_test_data *ctd; + + ctd = (struct connect_test_data *)nsock_pool_get_udata(nsp); + + switch(nse_status(nse)) { + case NSE_STATUS_SUCCESS: + ctd->connect_result = 0; + break; + + case NSE_STATUS_ERROR: + ctd->connect_result = -(nse_errorcode(nse)); + break; + + case NSE_STATUS_TIMEOUT: + ctd->connect_result = -ETIMEDOUT; + break; + + default: + ctd->connect_result = -EINVAL; + break; + } +} + +static int connect_setup(void **tdata) { + struct connect_test_data *ctd; + + ctd = calloc(1, sizeof(struct connect_test_data)); + if (ctd == NULL) + return -ENOMEM; + + ctd->nsp = nsock_pool_new(ctd); + AssertNonNull(ctd->nsp); + + ctd->nsi = nsock_iod_new(ctd->nsp, NULL); + AssertNonNull(ctd->nsi); + + *tdata = ctd; + return 0; +} + +static int connect_teardown(void *tdata) { + struct connect_test_data *ctd = (struct connect_test_data *)tdata; + + if (tdata) { + nsock_iod_delete(ctd->nsi, NSOCK_PENDING_SILENT); /* nsock_pool_delete would also handle it */ + nsock_pool_delete(ctd->nsp); + free(tdata); + } + return 0; +} + +static int connect_tcp(void *tdata) { + struct connect_test_data *ctd = (struct connect_test_data *)tdata; + struct sockaddr_in peer; + + memset(&peer, 0, sizeof(peer)); + peer.sin_family = AF_INET; + inet_aton("127.0.0.1", &peer.sin_addr); + + nsock_connect_tcp(ctd->nsp, ctd->nsi, connect_handler, 4000, NULL, + (struct sockaddr *)&peer, sizeof(peer), PORT_TCP); + + nsock_loop(ctd->nsp, 4000); + return ctd->connect_result; +} + +static int connect_tcp_failure(void *tdata) { + struct connect_test_data *ctd = (struct connect_test_data *)tdata; + struct sockaddr_in peer; + + memset(&peer, 0, sizeof(peer)); + peer.sin_family = AF_INET; + inet_aton("127.0.0.1", &peer.sin_addr); + + /* pass in addrlen == 0 to force connect(2) to fail */ + nsock_connect_tcp(ctd->nsp, ctd->nsi, connect_handler, 4000, NULL, + (struct sockaddr *)&peer, 0, PORT_TCP); + + nsock_loop(ctd->nsp, 4000); + AssertEqual(ctd->connect_result, -EINVAL); + return 0; +} + + +const struct test_case TestConnectTCP = { + .t_name = "simple tcp connection", + .t_setup = connect_setup, + .t_run = connect_tcp, + .t_teardown = connect_teardown +}; + +const struct test_case TestConnectFailure = { + .t_name = "tcp connection failure case", + .t_setup = connect_setup, + .t_run = connect_tcp_failure, + .t_teardown = connect_teardown +}; diff --git a/nsock/tests/ghheaps.c b/nsock/tests/ghheaps.c new file mode 100644 index 0000000..0d4d2cb --- /dev/null +++ b/nsock/tests/ghheaps.c @@ -0,0 +1,151 @@ +/* + * Nsock regression test suite + * Same license as nmap -- see https://nmap.org/book/man-legal.html + */ + +#include "test-common.h" +#include "../src/gh_heap.h" +#include <stdint.h> +#include <time.h> + + +#define HEAP_COUNT 3 + +struct testitem { + int val; + gh_hnode_t node; +}; + +static int hnode_int_cmp(gh_hnode_t *n1, gh_hnode_t *n2) { + struct testitem *a; + struct testitem *b; + + a = container_of(n1, struct testitem, node); + b = container_of(n2, struct testitem, node); + + return (a->val < b->val); +} + +static gh_hnode_t *mknode(int val) { + struct testitem *item; + + item = calloc(1, sizeof(struct testitem)); + assert(item != NULL); + item->val = val; + gh_hnode_invalidate(&item->node); + return &item->node; +} + +static int node2int(gh_hnode_t *hnode) { + struct testitem *item; + + item = container_of(hnode, struct testitem, node); + return item->val; +} + +static int ghheap_ordering(void *tdata) { + gh_heap_t heap; + int i, n, k; + + gh_heap_init(&heap, hnode_int_cmp); + + for (i = 25000; i < 50000; i++) + gh_heap_push(&heap, mknode(i)); + + for (i = 24999; i >= 0; i--) + gh_heap_push(&heap, mknode(i)); + + for (i = 25000; i < 50000; i++) + gh_heap_push(&heap, mknode(i)); + + n = -1; + do { + gh_hnode_t *current; + + current = gh_heap_pop(&heap); + assert(!gh_hnode_is_valid(current)); + k = node2int(current); + + if (k < n) + return -EINVAL; + + n = k; + free(container_of(current, struct testitem, node)); + } while (gh_heap_count(&heap) > 0); + + gh_heap_free(&heap); + return 0; +} + +static int ghheap_stress(void *tdata) { + gh_heap_t heaps[HEAP_COUNT]; + int i, num; + + for (i = 0; i < HEAP_COUNT; i++) + gh_heap_init(&heaps[i], hnode_int_cmp); + + for (num = 25000; num < 50000; num++) { + for (i = 0; i < HEAP_COUNT; i++) { + gh_heap_push(&heaps[i], mknode(num)); + } + } + + for (num = 24999; num >= 0; num--) { + for (i = 0; i < HEAP_COUNT; i++) { + gh_heap_push(&heaps[i], mknode(num)); + } + } + + for (num = 0; num < 50000; num++) { + for (i = 0; i < HEAP_COUNT; i++) { + int r_min, r_pop; + gh_hnode_t *hnode; + + r_min = node2int(gh_heap_min(&heaps[i])); + hnode = gh_heap_pop(&heaps[i]); + r_pop = node2int(hnode); + + if (r_min != r_pop) { + fprintf(stderr, "Bogus min/pop return values (%d != %d)\n", r_min, r_pop); + return -EINVAL; + } + + if (r_min != num) { + fprintf(stderr, "Bogus return value %d when expected %d\n", r_min, num); + return -EINVAL; + } + + free(container_of(hnode, struct testitem, node)); + } + } + + for (i = 0; i < HEAP_COUNT; i++) { + void *ret; + + ret = gh_heap_pop(&heaps[i]); + if (ret != NULL) { + fprintf(stderr, "Ret is bogus for heap %d\n", i); + return -EINVAL; + } + } + + for (i = 0; i < HEAP_COUNT; i++) + gh_heap_free(&heaps[i]); + + return 0; +} + + +const struct test_case TestGHHeaps = { + .t_name = "test nsock internal ghheaps", + .t_setup = NULL, + .t_run = ghheap_stress, + .t_teardown = NULL +}; + +const struct test_case TestHeapOrdering = { + .t_name = "test heaps conditions", + .t_setup = NULL, + .t_run = ghheap_ordering, + .t_teardown = NULL +}; diff --git a/nsock/tests/ghlists.c b/nsock/tests/ghlists.c new file mode 100644 index 0000000..f445022 --- /dev/null +++ b/nsock/tests/ghlists.c @@ -0,0 +1,189 @@ +/* + * Nsock regression test suite + * Same license as nmap -- see https://nmap.org/book/man-legal.html + */ + +#include "test-common.h" +/* Additional checks enabled */ +#define GH_LIST_PARANOID 1 +#include "../src/gh_list.h" +/* For container_of */ +#include "../src/gh_heap.h" +#include <stdint.h> +#include <time.h> + + +#define LIST_COUNT 16 +#define ELT_COUNT 2000 + + +struct testlist { + unsigned int val; + gh_lnode_t lnode; +}; + +static unsigned int nodeval(gh_lnode_t *lnode) { + struct testlist *tl; + + tl = container_of(lnode, struct testlist, lnode); + return tl->val; +} + +static gh_lnode_t *mknode(unsigned int val) { + struct testlist *tl; + + tl = calloc(1, sizeof(struct testlist)); + tl->val = val; + return &tl->lnode; +} + +static void delnode(gh_lnode_t *lnode) { + if (lnode) + free(container_of(lnode, struct testlist, lnode)); +} + +static int ghlist_stress(void *tdata) { + gh_list_t lists[LIST_COUNT]; + gh_lnode_t *current, *next; + int num = 0; + int ret; + int i; + + for (i = 0; i < LIST_COUNT; i++) + gh_list_init(&lists[i]); + + for (num = ELT_COUNT/2; num < ELT_COUNT; num++) { + for (i = 0; i < LIST_COUNT; i++) { + gh_list_append(&lists[i], mknode(num)); + } + } + + for (num = (ELT_COUNT/2 - 1); num >= 0; num--) { + for (i = 0; i < LIST_COUNT; i++) { + gh_list_prepend(&lists[i], mknode(num)); + } + } + + for (num = 0; num < ELT_COUNT; num++) { + for (i = 0; i < LIST_COUNT; i++) { + current = gh_list_pop(&lists[i]); + ret = nodeval(current); + if (ret != num) { + fprintf(stderr, "prepend_test: Bogus return value %d when expected %d\n", + ret, num); + return -EINVAL; + } + delnode(current); + } + } + for (i = 0; i < LIST_COUNT; i++) { + current = gh_list_pop(&lists[i]); + if (current) { + fprintf(stderr, "Ret is bogus for list %d", i); + return -EINVAL; + } + } + + for (num = (ELT_COUNT/2 - 1); num >= 0; num--) { + for (i = 0; i < LIST_COUNT; i++) { + gh_list_prepend(&lists[i], mknode(num)); + } + } + + for (num = ELT_COUNT/2; num < ELT_COUNT; num++) { + for (i = 0; i < LIST_COUNT; i++) { + gh_list_append(&lists[i], mknode(num)); + } + } + + for (num = 0; num < ELT_COUNT; num++) { + for (i=0; i < LIST_COUNT; i++) { + current = gh_list_pop(&lists[i]); + ret = nodeval(current); + if (ret != num) { + fprintf(stderr, "prepend_test: Bogus return value %d when expected %d\n", + ret, num); + return -EINVAL; + } + delnode(current); + } + } + + for (num = ELT_COUNT/2; num < ELT_COUNT; num++) { + for (i = 0; i < LIST_COUNT; i++) + gh_list_append(&lists[i], mknode(num)); + } + + for (num = ELT_COUNT/2 - 1; num >= 0; num--) { + for (i = 0; i < LIST_COUNT; i++) + gh_list_prepend(&lists[i], mknode(num)); + } + + for (num = 0; num < ELT_COUNT; num++) { + for (i = 0; i < LIST_COUNT; i++) { + current = gh_list_pop(&lists[i]); + ret = nodeval(current); + if (ret != num) { + fprintf(stderr, "prepend_test: Bogus return value %d when expected %d\n", + ret, num); + return -EINVAL; + } + delnode(current); + } + } + + for (num = ELT_COUNT/2 - 1; num >= 0; num--) { + for (i = 0; i < LIST_COUNT; i++) + gh_list_prepend(&lists[i], mknode(num)); + } + + for (num = ELT_COUNT/2; num < ELT_COUNT; num++) { + for (i=0; i < LIST_COUNT; i++) + gh_list_append(&lists[i], mknode(num)); + } + + for (i = 0; i < LIST_COUNT; i++) { + num = 0; + + for (current = gh_list_first_elem(&lists[i]); current; current = next) { + int k; + + next = gh_lnode_next(current); + k = nodeval(current); + if (k != num) { + fprintf(stderr, "Got %d when I expected %d\n", k, num); + return -EINVAL; + } + gh_list_remove(&lists[i], current); + delnode(current); + num++; + } + if (num != ELT_COUNT) { + fprintf(stderr, "Number is %d, even though %d was expected", num, ELT_COUNT); + return -EINVAL; + } + + if (gh_list_count(&lists[i]) != 0) { + fprintf(stderr, "List should be empty, but instead it has %d members!\n", + gh_list_count(&lists[i])); + return -EINVAL; + } + } + + for (i = 0; i < LIST_COUNT; i++) { + while ((current = gh_list_pop(&lists[i])) != NULL) + delnode(current); + + gh_list_free(&lists[i]); + } + + return 0; +} + +const struct test_case TestGHLists = { + .t_name = "test nsock internal ghlists", + .t_setup = NULL, + .t_run = ghlist_stress, + .t_teardown = NULL +}; + diff --git a/nsock/tests/logs.c b/nsock/tests/logs.c new file mode 100644 index 0000000..d316d34 --- /dev/null +++ b/nsock/tests/logs.c @@ -0,0 +1,176 @@ +/* + * Nsock regression test suite + * Same license as nmap -- see https://nmap.org/book/man-legal.html + */ + +#include "test-common.h" + + +struct log_test_data { + nsock_pool nsp; + nsock_loglevel_t current_level; + unsigned int got_dbgfull: 1; + unsigned int got_dbg: 1; + unsigned int got_info: 1; + unsigned int got_error: 1; + unsigned int total; + int errcode; +}; + +static struct log_test_data *GlobalLTD; + +static void log_handler(const struct nsock_log_rec *rec) { + GlobalLTD->total++; + switch(rec->level) { + case NSOCK_LOG_DBG_ALL: + GlobalLTD->got_dbgfull = 1; + break; + + case NSOCK_LOG_DBG: + GlobalLTD->got_dbg = 1; + break; + + case NSOCK_LOG_INFO: + GlobalLTD->got_info = 1; + break; + + case NSOCK_LOG_ERROR: + GlobalLTD->got_error = 1; + break; + + default: + fprintf(stderr, "UNEXPECTED LOG LEVEL (%d)!\n", (int)rec->level); + GlobalLTD->errcode = -EINVAL; + } +} + +static void nop_handler(nsock_pool nsp, nsock_event nse, void *udata) { +} + +static int check_loglevel(struct log_test_data *ltd, nsock_loglevel_t level) { + int rc = 0; + nsock_event_id id; + + nsock_set_loglevel(level); + + ltd->current_level = level; + + ltd->got_dbgfull = 0; + ltd->got_dbg = 0; + ltd->got_info = 0; + ltd->got_error = 0; + + ltd->total = 0; + ltd->errcode = 0; + + id = nsock_timer_create(ltd->nsp, nop_handler, 200, NULL); + nsock_event_cancel(ltd->nsp, id, 0); + + if (ltd->errcode) + return ltd->errcode; + + if (ltd->total < 1) + return -EINVAL; + + return rc; +} + +static int check_errlevel(struct log_test_data *ltd, nsock_loglevel_t level) { + nsock_event_id id; + + nsock_set_loglevel(level); + + ltd->current_level = level; + + ltd->got_dbgfull = 0; + ltd->got_dbg = 0; + ltd->got_info = 0; + ltd->got_error = 0; + + ltd->total = 0; + ltd->errcode = 0; + + id = nsock_timer_create(ltd->nsp, nop_handler, 200, NULL); + nsock_event_cancel(ltd->nsp, id, 0); + + if (ltd->errcode) + return ltd->errcode; + + if (ltd->total > 0) + return -EINVAL; + + return 0; +} + +static int log_setup(void **tdata) { + struct log_test_data *ltd; + + ltd = calloc(1, sizeof(struct log_test_data)); + if (ltd == NULL) + return -ENOMEM; + + ltd->nsp = nsock_pool_new(ltd); + AssertNonNull(ltd->nsp); + + *tdata = GlobalLTD = ltd; + return 0; +} + +static int log_teardown(void *tdata) { + struct log_test_data *ltd = (struct log_test_data *)tdata; + + if (tdata) { + nsock_pool_delete(ltd->nsp); + free(tdata); + } + nsock_set_log_function(NULL); + GlobalLTD = NULL; + return 0; +} + +static int log_check_std_levels(void *tdata) { + struct log_test_data *ltd = (struct log_test_data *)tdata; + nsock_loglevel_t lvl; + int rc = 0; + + nsock_set_log_function(log_handler); + + for (lvl = NSOCK_LOG_DBG_ALL; lvl < NSOCK_LOG_ERROR; lvl++) { + rc = check_loglevel(ltd, lvl); + if (rc) + return rc; + } + + return 0; +} + +static int log_check_err_levels(void *tdata) { + struct log_test_data *ltd = (struct log_test_data *)tdata; + nsock_loglevel_t lvl; + int rc = 0; + + nsock_set_log_function(log_handler); + + for (lvl = NSOCK_LOG_ERROR; lvl <= NSOCK_LOG_NONE; lvl++) { + rc = check_errlevel(ltd, NSOCK_LOG_ERROR); + if (rc) + return rc; + } + + return 0; +} + + +const struct test_case TestLogLevels = { + .t_name = "set standard log levels", + .t_setup = log_setup, + .t_run = log_check_std_levels, + .t_teardown = log_teardown +}; + +const struct test_case TestErrLevels = { + .t_name = "check error log levels", + .t_setup = log_setup, + .t_run = log_check_err_levels, + .t_teardown = log_teardown +}; diff --git a/nsock/tests/proxychain.c b/nsock/tests/proxychain.c new file mode 100644 index 0000000..2360a68 --- /dev/null +++ b/nsock/tests/proxychain.c @@ -0,0 +1,159 @@ +/* + * Nsock regression test suite + * Same license as nmap -- see https://nmap.org/book/man-legal.html + */ + +#include "test-common.h" +#include "../src/nsock_log.h" + +struct proxy_test_data { + int tn; + nsock_pool nsp; +}; +static struct proxy_test_data *GlobalTD; + +#define END_OF_TESTS -1 +#define GOOD 0 +#define BAD 1 +struct proxy_test { + int ttype; + const char *input; +}; + +static const struct proxy_test Tests[] = { + /* single proxy */ + /* http */ + {GOOD, "http://example.com"}, + {GOOD, "http://127.0.0.1/"}, + {GOOD, "http://1/some/crazy.path"}, + {GOOD, "http://127.1/some/path?q=@!weird&other=;"}, + {GOOD, "http://[::1]/"}, + {GOOD, "http://1:80/"}, + {GOOD, "http://1:8080"}, + {GOOD, "http://127.0.0.1:1234/"}, + {GOOD, "http://[::1]:8080/"}, + {GOOD, "http://[::1]:80"}, + /* https not supported! */ + {BAD, "https://example.com/"}, + /* No username/password in URI */ + {BAD, "https://scott:tiger@example.com/"}, + /* Port out of range */ + {BAD, "http://example.com:65536/"}, + /* Bad IPv6 syntax */ + {BAD, "http://::1/"}, + /* Missing host name */ + {BAD, "http://:8080/"}, + /* socks4 */ + {GOOD, "socks4://example.com"}, + {GOOD, "socks4://1"}, + /* socks4 does not support IPv6 */ + {BAD, "socks4://[::1]"}, + /* Does SOCKS4 really support a path like this? */ + {GOOD, "socks4://example.com/path?"}, + /* multiple proxies */ + {GOOD, "http://example.com:8080/,socks4://127.0.0.1/"}, + {GOOD, "http://[::1]/,socks4://example.com:5000/"}, + /* Should fail: socks4 cannot connect to IPv6 proxy */ + {GOOD, "socks4://127.0.0.1/,socks4://example.com/,http://[::1]:9090"}, + /* Dumb stuff */ + {BAD, ""}, + {BAD, ","}, + {BAD, ",,"}, + {BAD, "http://example.com/,"}, + {BAD, "http://example.com/,,"}, + {BAD, ",http://example.com/"}, + {BAD, ",,http://example.com/"}, + {BAD, "socks4://127.0.0.1/,http://example.com/,"}, + {BAD, ",socks4://127.0.0.1/,http://example.com/"}, + {BAD, "socks4://127.0.0.1/,,http://example.com/"}, + {BAD, "http://example.com:-1/"}, + {BAD, "http://example.com:0x80/"}, + {BAD, "http://example.com:0/"}, + {BAD, "http://example.com:2147483648"}, + {BAD, "http://example.com:21474836480"}, + {BAD, "http://:80"}, + {BAD, "http://example.com:80.com"}, + {BAD, "com"}, + {BAD, "example.com"}, + {BAD, "/example.com/"}, + {BAD, "//example.com/"}, + {BAD, "http/example.com/"}, + {BAD, "http//example.com/"}, + {BAD, "http:///example.com"}, + {BAD, "sptth://example.com/"}, + {BAD, " "}, + {BAD, ", "}, + {BAD, " ,"}, + {BAD, ", ,"}, + {BAD, " , , "}, + {BAD, "http://example.com/,asdf"}, + {END_OF_TESTS, NULL} +}; + +static int parser_test(void *testdata) { + int result = 0; + struct proxy_test_data *ptd = (struct proxy_test_data *)testdata; + const struct proxy_test *pt = &Tests[ptd->tn]; + while (pt->ttype != END_OF_TESTS) { + nsock_proxychain pxc = NULL; + if (pt->ttype == BAD) + nsock_log_info("Expected failure:"); + int ret = nsock_proxychain_new(pt->input, &pxc, NULL); + nsock_log_debug("Test %d result: %d", ptd->tn, ret); + if (ret > 0) { + if (pt->ttype == BAD) { + fprintf(stderr, "Proxy Test #%d: Failed to reject bad input: %s\n", ptd->tn, pt->input); + result = -1; + } + nsock_proxychain_delete(pxc); + } + else if (ret <= 0 && pt->ttype == GOOD) { + fprintf(stderr, "Proxy Test #%d: Failed to parse good input: %s\n", ptd->tn, pt->input); + result = -2; + } + ptd->tn++; + pt = &Tests[ptd->tn]; + } + return result; +} + +static void log_handler(const struct nsock_log_rec *rec) { + /* Only print log messages if we expect the test to succeed. */ + if (Tests[GlobalTD->tn].ttype == GOOD) { + fprintf(stderr, "Proxy Test #%d: %s(): %s\n", GlobalTD->tn, rec->func, rec->msg); + } +} + +static int proxy_setup(void **tdata) { + struct proxy_test_data *ptd = calloc(1, sizeof(struct proxy_test_data)); + if (ptd == NULL) + return -ENOMEM; + + ptd->nsp = nsock_pool_new(ptd); + AssertNonNull(ptd->nsp); + + nsock_set_log_function(log_handler); + + *tdata = GlobalTD = ptd; + return 0; +} + +static int proxy_teardown(void *tdata) { + struct proxy_test_data *ptd = (struct proxy_test_data *)tdata; + + if (tdata) { + nsock_pool_delete(ptd->nsp); + free(tdata); + } + nsock_set_log_function(NULL); + GlobalTD = NULL; + return 0; +} + + +const struct test_case TestProxyParse = { + .t_name = "test nsock proxychain parsing", + .t_setup = proxy_setup, + .t_run = parser_test, + .t_teardown = proxy_teardown +}; diff --git a/nsock/tests/run_tests.sh b/nsock/tests/run_tests.sh new file mode 100755 index 0000000..c0392f8 --- /dev/null +++ b/nsock/tests/run_tests.sh @@ -0,0 +1,86 @@ +#!/bin/sh + +# nsock regression test suite +# Same license as nmap -- see https://nmap.org/book/man-legal.html + +# hackish, I should consider using a configuration file. +PORT_UDP=$(grep "PORT_UDP " test-common.h | awk '{print $3}') +PORT_TCP=$(grep "PORT_TCP " test-common.h | awk '{print $3}') +PORT_TCPSSL=$(grep "PORT_TCPSSL " test-common.h | awk '{print $3}') + +EXEC_MAIN=./tests_main + +NCAT=${NCAT:-ncat} +if [ ! -x "$NCAT" -a -z "$(which $NCAT)" ]; then + echo "Can't find your ncat: $NCAT" + echo "Trying ../../ncat/ncat" + NCAT="../../ncat/ncat" + if [ ! -x "$NCAT" ]; then + echo "You haven't built Ncat." + echo "Skipping nsock tests." + exit 0 + fi +fi + + +if [ -n "$1" ] +then + case "$1" in + "gdb") + TRACER="gdb --args" + ;; + + "trace") + TRACER="strace" + ;; + + "leak") + TRACER="valgrind --leak-check=yes" + ;; + + "-h") + echo "Usage: `basename $0` [gdb|trace|leak]" + exit 0 + ;; + + *) + echo "Unknown mode $1" + exit 1 + ;; + esac +fi + + +setup_echo_udp() { + $NCAT -l --udp --sh-exec cat 127.0.0.1 $PORT_UDP & + pid_udp=$! + echo "started UDP listener on port $PORT_UDP (pid $pid_udp)" +} + +setup_echo_tcp() { + $NCAT -l --keep-open --sh-exec cat 127.0.0.1 $PORT_TCP & + pid_tcp=$! + echo "started TCP listener on port $PORT_TCP (pid $pid_tcp)" +} + +setup_echo_tcpssl() { + $NCAT -l --ssl --keep-open --sh-exec cat 127.0.0.1 $PORT_TCPSSL & + pid_tcpssl=$! + echo "started TCP SSL listener on port $PORT_TCPSSL (pid $pid_tcpssl)" +} + +cleanup_all() { + kill -s KILL $@ 2>&1 >> /dev/null +} + +main() { + setup_echo_udp $PORT_UDP + setup_echo_tcp $PORT_TCP + $EXEC_MAIN --ssl && setup_echo_tcpssl $PORT_TCPSSL + + $TRACER $EXEC_MAIN + + cleanup_all $pid_udp $pid_tcp $pid_tcpssl +} + +main diff --git a/nsock/tests/test-common.h b/nsock/tests/test-common.h new file mode 100644 index 0000000..d746460 --- /dev/null +++ b/nsock/tests/test-common.h @@ -0,0 +1,89 @@ +/* + * Nsock regression test suite + * Same license as nmap -- see https://nmap.org/book/man-legal.html + */ + + +#ifndef __TEST_COMMON_H +#define __TEST_COMMON_H + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <nsock.h> + + +#define PORT_UDP 55234 +#define PORT_TCP 55235 +#define PORT_TCPSSL 55236 + + +#define __ASSERT_BASE(stmt) do { \ + if (!(stmt)) { \ + fprintf(stderr, "(%s:%d) Assertion failed: " #stmt "\n", \ + __FILE__, __LINE__); \ + return -EINVAL; \ + } \ + } while (0) + + +#define AssertNonNull(a) __ASSERT_BASE((a) != NULL); +#define AssertEqual(a, b) __ASSERT_BASE((a) == (b)); +#define AssertStrEqual(a, b) __ASSERT_BASE(strcmp((a), (b)) == 0); + + +struct test_case { + const char *t_name; + int (*t_setup)(void **tdata); + int (*t_run)(void *tdata); + int (*t_teardown)(void *tdata); +}; + + +static inline const char *get_test_name(const struct test_case *test) { + return test->t_name; +} + +static inline int test_setup(const struct test_case *test, void **tdata) { + int rc; + + assert(test); + + if (test->t_setup) + rc = test->t_setup(tdata); + else + rc = 0; + + return rc; +} + +static inline int test_run(const struct test_case *test, void *tdata) { + int rc; + + assert(test); + + if (test->t_run) + rc = test->t_run(tdata); + else + rc = 0; + + return rc; +} + +static inline int test_teardown(const struct test_case *test, void *tdata) { + int rc; + + assert(test); + + if (test->t_teardown) + rc = test->t_teardown(tdata); + else + rc = 0; + + return rc; +} + +#endif /* ^__TEST_COMMON_H */ diff --git a/nsock/tests/tests_main.c b/nsock/tests/tests_main.c new file mode 100644 index 0000000..4ab6d07 --- /dev/null +++ b/nsock/tests/tests_main.c @@ -0,0 +1,133 @@ +/* + * Nsock regression test suite + * Same license as nmap -- see https://nmap.org/book/man-legal.html + */ + + +#include "test-common.h" +#include "string.h" + + +#ifndef WIN32 + #define RESET "\033[0m" + #define BOLDRED "\033[1m\033[31m" + #define BOLDGREEN "\033[1m\033[32m" + #define TEST_FAILED "[" BOLDRED "FAILED" RESET "]" + #define TEST_OK "[" BOLDGREEN "OK" RESET "]" +#else + /* WIN32 terminal has no ANSI driver */ + #define TEST_FAILED "[FAILED]" + #define TEST_OK "[OK]" +#endif + + + +/* socket_strerror() comes from nbase + * Declared here to work around a silly inclusion issue until I can fix it. */ +char *socket_strerror(int errnum); + +extern const struct test_case TestPoolUserData; +extern const struct test_case TestTimer; +extern const struct test_case TestLogLevels; +extern const struct test_case TestErrLevels; +extern const struct test_case TestConnectTCP; +extern const struct test_case TestConnectFailure; +extern const struct test_case TestGHLists; +extern const struct test_case TestGHHeaps; +extern const struct test_case TestHeapOrdering; +extern const struct test_case TestProxyParse; +extern const struct test_case TestCancelTCP; +extern const struct test_case TestCancelUDP; +#ifdef HAVE_OPENSSL +extern const struct test_case TestCancelSSL; +#endif + + +static const struct test_case *TestCases[] = { + /* ---- basic.c */ + &TestPoolUserData, + /* ---- timer.c */ + &TestTimer, + /* ---- logs.c */ + &TestLogLevels, + &TestErrLevels, + /* ---- connect.c */ + &TestConnectTCP, + &TestConnectFailure, + /* ---- ghlists.c */ + &TestGHLists, + /* ---- ghheaps.c */ + &TestGHHeaps, + &TestHeapOrdering, + /* ---- proxychain.c */ + &TestProxyParse, + /* ---- cancel.c */ + &TestCancelTCP, + &TestCancelUDP, +#ifdef HAVE_OPENSSL + &TestCancelSSL, +#endif + NULL +}; + + +static int test_case_run(const struct test_case *test) { + int rc; + void *tdata = NULL; + + rc = test_setup(test, &tdata); + if (rc) + return rc; + + rc = test_run(test, tdata); + if (rc) + return rc; + + return test_teardown(test, tdata); +} + +#ifdef WIN32 +static int win_init(void) { + WSADATA data; + int rc; + + rc = WSAStartup(MAKEWORD(2, 2), &data); + if (rc) + fatal("Failed to start winsock: %s\n", socket_strerror(rc)); + + return 0; +} +#endif + +int main(int ac, char **av) { + int rc, i; + + /* simple "do we have ssl" check for run_tests.sh */ + if (ac == 2 && !strncmp(av[1], "--ssl", 5)) { +#ifdef HAVE_SSL + return 0; +#else + return 1; +#endif + } + +#ifdef WIN32 + win_init(); +#endif + + for (i = 0; TestCases[i] != NULL; i++) { + const struct test_case *current = TestCases[i]; + const char *name = get_test_name(current); + + printf("%-48s", name); + fflush(stdout); + rc = test_case_run(current); + if (rc) { + printf(TEST_FAILED " (%s)\n", socket_strerror(-rc)); + break; + } + printf(TEST_OK "\n"); + } + return rc; +} + diff --git a/nsock/tests/timer.c b/nsock/tests/timer.c new file mode 100644 index 0000000..27250ce --- /dev/null +++ b/nsock/tests/timer.c @@ -0,0 +1,129 @@ +/* + * Nsock regression test suite + * Same license as nmap -- see https://nmap.org/book/man-legal.html + */ + +#include "test-common.h" +#include <time.h> + +#define TIMERS_BUFFLEN 1024 + + +struct timer_test_data { + nsock_pool nsp; + nsock_event_id timer_list[TIMERS_BUFFLEN]; + size_t timer_count; + int stop; /* set to non-zero to stop the test */ +}; + + +static void timer_handler(nsock_pool nsp, nsock_event nse, void *tdata); + + +static void add_timer(struct timer_test_data *ttd, int timeout) { + nsock_event_id id; + + id = nsock_timer_create(ttd->nsp, timer_handler, timeout, ttd); + ttd->timer_list[ttd->timer_count++] = id; +} + +static void timer_handler(nsock_pool nsp, nsock_event nse, void *tdata) { + struct timer_test_data *ttd = (struct timer_test_data *)tdata; + int rnd, rnd2; + + if (nse_status(nse) != NSE_STATUS_SUCCESS) { + ttd->stop = -nsock_pool_get_error(nsp); + return; + } + + if (ttd->timer_count > TIMERS_BUFFLEN - 3) + return; + + rnd = rand() % ttd->timer_count; + rnd2 = rand() % 3; + + switch (rnd2) { + case 0: + /* Do nothing */ + /* Actually I think I'll create two timers :) */ + add_timer(ttd, rand() % 3000); + add_timer(ttd, rand() % 3000); + break; + + case 1: + /* Try to kill another id (which may or may not be active */ + nsock_event_cancel(nsp, ttd->timer_list[rnd], rand() % 2); + break; + + case 2: + /* Create a new timer */ + add_timer(ttd, rand() % 3000); + break; + + default: + assert(0); + } +} + +static int timer_setup(void **tdata) { + struct timer_test_data *ttd; + + srand(time(NULL)); + + ttd = calloc(1, sizeof(struct timer_test_data)); + if (ttd == NULL) + return -ENOMEM; + + ttd->nsp = nsock_pool_new(NULL); + AssertNonNull(ttd->nsp); + + *tdata = ttd; + return 0; +} + +static int timer_teardown(void *tdata) { + struct timer_test_data *ttd = (struct timer_test_data *)tdata; + + if (tdata) { + nsock_pool_delete(ttd->nsp); + free(tdata); + } + return 0; +} + +static int timer_totalmess(void *tdata) { + struct timer_test_data *ttd = (struct timer_test_data *)tdata; + enum nsock_loopstatus loopret; + int num_loops = 0; + + add_timer(ttd, 1800); + add_timer(ttd, 800); + add_timer(ttd, 1300); + add_timer(ttd, 0); + add_timer(ttd, 100); + + /* Now lets get this party started right! */ + while (num_loops++ < 5 && !ttd->stop) { + loopret = nsock_loop(ttd->nsp, 1500); + switch (loopret) { + case NSOCK_LOOP_TIMEOUT: + /* nothing to do */ + break; + + case NSOCK_LOOP_NOEVENTS: + return 0; + + default: + return -(nsock_pool_get_error(ttd->nsp)); + } + } + return ttd->stop; +} + +const struct test_case TestTimer = { + .t_name = "test timer operations", + .t_setup = timer_setup, + .t_run = timer_totalmess, + .t_teardown = timer_teardown +}; + |