summaryrefslogtreecommitdiffstats
path: root/nsock/tests
diff options
context:
space:
mode:
Diffstat (limited to 'nsock/tests')
-rw-r--r--nsock/tests/Makefile.in43
-rw-r--r--nsock/tests/README5
-rw-r--r--nsock/tests/basic.c52
-rw-r--r--nsock/tests/cancel.c134
-rw-r--r--nsock/tests/connect.c113
-rw-r--r--nsock/tests/ghheaps.c151
-rw-r--r--nsock/tests/ghlists.c189
-rw-r--r--nsock/tests/logs.c176
-rw-r--r--nsock/tests/proxychain.c159
-rwxr-xr-xnsock/tests/run_tests.sh86
-rw-r--r--nsock/tests/test-common.h89
-rw-r--r--nsock/tests/tests_main.c133
-rw-r--r--nsock/tests/timer.c129
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
+};
+