diff --git a/CHANGELOG b/CHANGELOG
index 52573bfc5..b90c9796f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,10 @@
# Nmap Changelog ($Id$); -*-text-*-
+o [Nsock] Handle timers and timeouts via a priority queue (using a heap)
+ for improved performance. Nsock now only iterates over events which are
+ completed or expired instead of inspecting the entire event set at each
+ iteration. [Henri Doreau]
+
o [NSE] Update dns-cache-snoop script to use a new list of top 50
domains rather than a 2010 list. [Nicolle Neulist]
diff --git a/nsock/nsock.vcxproj b/nsock/nsock.vcxproj
index a357b3b07..03ec76b80 100644
--- a/nsock/nsock.vcxproj
+++ b/nsock/nsock.vcxproj
@@ -186,7 +186,7 @@
-
+
@@ -207,6 +207,7 @@
+
@@ -226,4 +227,4 @@
-
\ No newline at end of file
+
diff --git a/nsock/src/Makefile.in b/nsock/src/Makefile.in
index 5a5586da6..57050673e 100644
--- a/nsock/src/Makefile.in
+++ b/nsock/src/Makefile.in
@@ -28,20 +28,21 @@ NSOCKTESTDIR=@NSOCKTESTDIR@
TARGET = libnsock.a
-SRCS = error.c filespace.c gh_list.c nsock_connect.c nsock_core.c \
+SRCS = error.c filespace.c gh_heap.c nsock_connect.c nsock_core.c \
nsock_iod.c nsock_read.c nsock_timers.c nsock_write.c \
nsock_ssl.c nsock_event.c nsock_pool.c netutils.c nsock_pcap.c \
nsock_engines.c engine_select.c engine_epoll.c engine_kqueue.c \
engine_poll.c nsock_proxy.c nsock_log.c proxy_http.c proxy_socks4.c
-OBJS = error.o filespace.o gh_list.o nsock_connect.o nsock_core.o \
+OBJS = error.o filespace.o gh_heap.o nsock_connect.o nsock_core.o \
nsock_iod.o nsock_read.o nsock_timers.o nsock_write.o \
nsock_ssl.o nsock_event.o nsock_pool.o netutils.o nsock_pcap.o \
nsock_engines.o engine_select.o engine_epoll.o engine_kqueue.o \
engine_poll.o nsock_proxy.o nsock_log.o proxy_http.o proxy_socks4.o
DEPS = error.h filespace.h gh_list.h nsock_internal.h netutils.h nsock_pcap.h \
- nsock_log.h nsock_proxy.h ../include/nsock.h $(NBASEDIR)/libnbase.a
+ nsock_log.h nsock_proxy.h gh_heap.h ../include/nsock.h \
+ $(NBASEDIR)/libnbase.a
.c.o:
$(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@
diff --git a/nsock/src/engine_epoll.c b/nsock/src/engine_epoll.c
index 1a278486d..fb8f99f55 100644
--- a/nsock/src/engine_epoll.c
+++ b/nsock/src/engine_epoll.c
@@ -109,7 +109,8 @@ static void iterate_through_event_lists(mspool *nsp, int evcount);
/* defined in nsock_core.c */
void process_iod_events(mspool *nsp, msiod *nsi, int ev);
-void process_event(mspool *nsp, gh_list *evlist, msevent *nse, int ev);
+void process_event(mspool *nsp, gh_list_t *evlist, msevent *nse, int ev);
+void process_expired_events(mspool *nsp);
#if HAVE_PCAP
#ifndef PCAP_CAN_DO_SELECT
int pcap_read_on_nonselect(mspool *nsp);
@@ -247,6 +248,7 @@ int epoll_loop(mspool *nsp, int msec_timeout) {
int event_msecs; /* msecs before an event goes off */
int combined_msecs;
int sock_err = 0;
+ unsigned int iod_count;
struct epoll_engine_info *einfo = (struct epoll_engine_info *)nsp->engine_data;
assert(msec_timeout >= -1);
@@ -255,24 +257,28 @@ int epoll_loop(mspool *nsp, int msec_timeout) {
return 0; /* No need to wait on 0 events ... */
- if (GH_LIST_COUNT(&nsp->active_iods) > einfo->evlen) {
- einfo->evlen = GH_LIST_COUNT(&nsp->active_iods) * 2;
+ iod_count = gh_list_count(&nsp->active_iods);
+ if (iod_count > einfo->evlen) {
+ einfo->evlen = iod_count * 2;
einfo->events = (struct epoll_event *)safe_realloc(einfo->events, einfo->evlen * sizeof(struct epoll_event));
}
do {
+ msevent *nse;
+
nsock_log_debug_all(nsp, "wait for events");
- if (nsp->next_ev.tv_sec == 0)
+ nse = next_expirable_event(nsp);
+ if (!nse)
event_msecs = -1; /* None of the events specified a timeout */
else
- event_msecs = MAX(0, TIMEVAL_MSEC_SUBTRACT(nsp->next_ev, nsock_tod));
+ event_msecs = MAX(0, TIMEVAL_MSEC_SUBTRACT(nse->timeout, nsock_tod));
#if HAVE_PCAP
#ifndef PCAP_CAN_DO_SELECT
/* Force a low timeout when capturing packets on systems where
* the pcap descriptor is not select()able. */
- if (GH_LIST_COUNT(&nsp->pcap_read_events) > 0)
+ if (gh_list_count(&nsp->pcap_read_events) > 0)
if (event_msecs > PCAP_POLL_INTERVAL)
event_msecs = PCAP_POLL_INTERVAL;
#endif
@@ -334,80 +340,25 @@ static inline int get_evmask(struct epoll_engine_info *einfo, int n) {
* timer_events, etc) and take action for those that have completed (due to
* timeout, i/o, etc) */
void iterate_through_event_lists(mspool *nsp, int evcount) {
- int n, initial_iod_count;
struct epoll_engine_info *einfo = (struct epoll_engine_info *)nsp->engine_data;
- gh_list_elem *current, *next, *last, *timer_last, *last_active = NULL;
- msevent *nse;
- msiod *nsi;
-
- /* Clear it -- We will find the next event as we go through the list */
- nsp->next_ev.tv_sec = 0;
-
- last = GH_LIST_LAST_ELEM(&nsp->active_iods);
- timer_last = GH_LIST_LAST_ELEM(&nsp->timer_events);
-
- initial_iod_count = GH_LIST_COUNT(&nsp->active_iods);
+ int n;
for (n = 0; n < evcount; n++) {
- nsi = (msiod *)einfo->events[n].data.ptr;
- assert(nsi);
+ msiod *nsi = (msiod *)einfo->events[n].data.ptr;
- if (nsi->entry_in_nsp_active_iods == last)
- last = GH_LIST_ELEM_PREV(nsi->entry_in_nsp_active_iods);
+ assert(nsi);
/* process all the pending events for this IOD */
process_iod_events(nsp, nsi, get_evmask(einfo, n));
- if (nsi->state != NSIOD_STATE_DELETED) {
- gh_list_move_front(&nsp->active_iods, nsi->entry_in_nsp_active_iods);
- if (last_active == NULL)
- last_active = nsi->entry_in_nsp_active_iods;
- } else {
- gh_list_remove_elem(&nsp->active_iods, nsi->entry_in_nsp_active_iods);
- gh_list_prepend(&nsp->free_iods, nsi);
- }
- }
-
- if (evcount < initial_iod_count) {
- /* some IODs had no active events and need to be processed */
- if (!last_active)
- /* either no IOD had events or all IODs were deleted after event processing */
- current = GH_LIST_FIRST_ELEM(&nsp->active_iods);
- else
- /* IODs that had active events were pushed to the beginning of the list, start after them */
- current = GH_LIST_ELEM_NEXT(last_active);
- } else {
- /* all the IODs had events and were therefore processed */
- current = NULL;
- }
-
- /* cull timeouts amongst the non active IODs */
- while (current != NULL && GH_LIST_ELEM_PREV(current) != last) {
- nsi = (msiod *)GH_LIST_ELEM_DATA(current);
-
- if (nsi->state != NSIOD_STATE_DELETED && nsi->events_pending)
- process_iod_events(nsp, nsi, EV_NONE);
-
- next = GH_LIST_ELEM_NEXT(current);
if (nsi->state == NSIOD_STATE_DELETED) {
- gh_list_remove_elem(&nsp->active_iods, current);
- gh_list_prepend(&nsp->free_iods, nsi);
+ gh_list_remove(&nsp->active_iods, &nsi->nodeq);
+ gh_list_prepend(&nsp->free_iods, &nsi->nodeq);
}
- current = next;
}
- /* iterate through timers */
- for (current = GH_LIST_FIRST_ELEM(&nsp->timer_events);
- current != NULL && GH_LIST_ELEM_PREV(current) != timer_last; current = next) {
-
- nse = (msevent *)GH_LIST_ELEM_DATA(current);
-
- process_event(nsp, &nsp->timer_events, nse, EV_NONE);
-
- next = GH_LIST_ELEM_NEXT(current);
- if (nse->event_done)
- gh_list_remove_elem(&nsp->timer_events, current);
- }
+ /* iterate through timers and expired events */
+ process_expired_events(nsp);
}
#endif /* HAVE_EPOLL */
diff --git a/nsock/src/engine_kqueue.c b/nsock/src/engine_kqueue.c
index 390b9bb62..528a29fc0 100644
--- a/nsock/src/engine_kqueue.c
+++ b/nsock/src/engine_kqueue.c
@@ -102,7 +102,8 @@ static void iterate_through_event_lists(mspool *nsp, int evcount);
/* defined in nsock_core.c */
void process_iod_events(mspool *nsp, msiod *nsi, int ev);
-void process_event(mspool *nsp, gh_list *evlist, msevent *nse, int ev);
+void process_event(mspool *nsp, gh_list_t *evlist, msevent *nse, int ev);
+void process_expired_events(mspool *nsp);
#if HAVE_PCAP
#ifndef PCAP_CAN_DO_SELECT
int pcap_read_on_nonselect(mspool *nsp);
@@ -231,24 +232,27 @@ int kqueue_loop(mspool *nsp, int msec_timeout) {
return 0; /* No need to wait on 0 events ... */
- if (GH_LIST_COUNT(&nsp->active_iods) > kinfo->evlen) {
- kinfo->evlen = GH_LIST_COUNT(&nsp->active_iods) * 2;
+ if (gh_list_count(&nsp->active_iods) > kinfo->evlen) {
+ kinfo->evlen = gh_list_count(&nsp->active_iods) * 2;
kinfo->events = (struct kevent *)safe_realloc(kinfo->events, kinfo->evlen * sizeof(struct kevent));
}
do {
+ msevent *nse;
+
nsock_log_debug_all(nsp, "wait for events");
- if (nsp->next_ev.tv_sec == 0)
+ nse = next_expirable_event(nsp);
+ if (!nse)
event_msecs = -1; /* None of the events specified a timeout */
else
- event_msecs = MAX(0, TIMEVAL_MSEC_SUBTRACT(nsp->next_ev, nsock_tod));
+ event_msecs = MAX(0, TIMEVAL_MSEC_SUBTRACT(nse->timeout, nsock_tod));
#if HAVE_PCAP
#ifndef PCAP_CAN_DO_SELECT
/* Force a low timeout when capturing packets on systems where
* the pcap descriptor is not select()able. */
- if (GH_LIST_COUNT(&nsp->pcap_read_events) > 0)
+ if (gh_list_count(&nsp->pcap_read_events) > 0)
if (event_msecs > PCAP_POLL_INTERVAL)
event_msecs = PCAP_POLL_INTERVAL;
#endif
@@ -332,16 +336,8 @@ static inline int get_evmask(msiod *nsi, const struct kevent *kev) {
void iterate_through_event_lists(mspool *nsp, int evcount) {
int n;
struct kqueue_engine_info *kinfo = (struct kqueue_engine_info *)nsp->engine_data;
- gh_list_elem *current, *next, *last, *timer_last;
- msevent *nse;
msiod *nsi;
- /* Clear it -- We will find the next event as we go through the list */
- nsp->next_ev.tv_sec = 0;
-
- last = GH_LIST_LAST_ELEM(&nsp->active_iods);
- timer_last = GH_LIST_LAST_ELEM(&nsp->timer_events);
-
for (n = 0; n < evcount; n++) {
struct kevent *kev = &kinfo->events[n];
@@ -353,37 +349,22 @@ void iterate_through_event_lists(mspool *nsp, int evcount) {
IOD_PROPSET(nsi, IOD_PROCESSED);
}
- current = GH_LIST_FIRST_ELEM(&nsp->active_iods);
+ for (n = 0; n < evcount; n++) {
+ struct kevent *kev = &kinfo->events[n];
- /* cull timeouts amongst the non active IODs */
- while (current != NULL && GH_LIST_ELEM_PREV(current) != last) {
- msiod *nsi = (msiod *)GH_LIST_ELEM_DATA(current);
+ nsi = (msiod *)kev->udata;
- if (IOD_PROPGET(nsi, IOD_PROCESSED))
- IOD_PROPCLR(nsi, IOD_PROCESSED);
- else if (nsi->state != NSIOD_STATE_DELETED && nsi->events_pending)
- process_iod_events(nsp, nsi, EV_NONE);
-
- next = GH_LIST_ELEM_NEXT(current);
if (nsi->state == NSIOD_STATE_DELETED) {
- gh_list_remove_elem(&nsp->active_iods, current);
- gh_list_prepend(&nsp->free_iods, nsi);
+ if (IOD_PROPGET(nsi, IOD_PROCESSED)) {
+ IOD_PROPCLR(nsi, IOD_PROCESSED);
+ gh_list_remove(&nsp->active_iods, &nsi->nodeq);
+ gh_list_prepend(&nsp->free_iods, &nsi->nodeq);
+ }
}
- current = next;
}
- /* iterate through timers */
- for (current = GH_LIST_FIRST_ELEM(&nsp->timer_events);
- current != NULL && GH_LIST_ELEM_PREV(current) != timer_last; current = next) {
-
- nse = (msevent *)GH_LIST_ELEM_DATA(current);
-
- process_event(nsp, &nsp->timer_events, nse, EV_NONE);
-
- next = GH_LIST_ELEM_NEXT(current);
- if (nse->event_done)
- gh_list_remove_elem(&nsp->timer_events, current);
- }
+ /* iterate through timers and expired events */
+ process_expired_events(nsp);
}
#endif /* HAVE_KQUEUE */
diff --git a/nsock/src/engine_poll.c b/nsock/src/engine_poll.c
index 3dacc63a1..2661483a1 100644
--- a/nsock/src/engine_poll.c
+++ b/nsock/src/engine_poll.c
@@ -134,7 +134,8 @@ static void iterate_through_event_lists(mspool *nsp);
/* defined in nsock_core.c */
void process_iod_events(mspool *nsp, msiod *nsi, int ev);
-void process_event(mspool *nsp, gh_list *evlist, msevent *nse, int ev);
+void process_event(mspool *nsp, gh_list_t *evlist, msevent *nse, int ev);
+void process_expired_events(mspool *nsp);
#if HAVE_PCAP
#ifndef PCAP_CAN_DO_SELECT
int pcap_read_on_nonselect(mspool *nsp);
@@ -313,18 +314,21 @@ int poll_loop(mspool *nsp, int msec_timeout) {
return 0; /* No need to wait on 0 events ... */
do {
+ msevent *nse;
+
nsock_log_debug_all(nsp, "wait for events");
- if (nsp->next_ev.tv_sec == 0)
+ nse = next_expirable_event(nsp);
+ if (!nse)
event_msecs = -1; /* None of the events specified a timeout */
else
- event_msecs = MAX(0, TIMEVAL_MSEC_SUBTRACT(nsp->next_ev, nsock_tod));
+ event_msecs = MAX(0, TIMEVAL_MSEC_SUBTRACT(nse->timeout, nsock_tod));
#if HAVE_PCAP
#ifndef PCAP_CAN_DO_SELECT
/* Force a low timeout when capturing packets on systems where
* the pcap descriptor is not select()able. */
- if (GH_LIST_COUNT(&nsp->pcap_read_events) > 0)
+ if (gh_list_count(&nsp->pcap_read_events) > 0)
if (event_msecs > PCAP_POLL_INTERVAL)
event_msecs = PCAP_POLL_INTERVAL;
#endif
@@ -403,41 +407,26 @@ static inline int get_evmask(mspool *nsp, msiod *nsi) {
* timer_events, etc) and take action for those that have completed (due to
* timeout, i/o, etc) */
void iterate_through_event_lists(mspool *nsp) {
- gh_list_elem *current, *next, *last, *timer_last;
+ gh_lnode_t *current, *next, *last;
- /* Clear it -- We will find the next event as we go through the list */
- nsp->next_ev.tv_sec = 0;
+ last = gh_list_last_elem(&nsp->active_iods);
- last = GH_LIST_LAST_ELEM(&nsp->active_iods);
- timer_last = GH_LIST_LAST_ELEM(&nsp->timer_events);
-
- for (current = GH_LIST_FIRST_ELEM(&nsp->active_iods);
- current != NULL && GH_LIST_ELEM_PREV(current) != last; current = next) {
-
- msiod *nsi = (msiod *)GH_LIST_ELEM_DATA(current);
+ for (current = gh_list_first_elem(&nsp->active_iods);
+ current != NULL && gh_lnode_prev(current) != last;
+ current = next) {
+ msiod *nsi = container_of(current, msiod, nodeq);
process_iod_events(nsp, nsi, get_evmask(nsp, nsi));
- next = GH_LIST_ELEM_NEXT(current);
+ next = gh_lnode_next(current);
if (nsi->state == NSIOD_STATE_DELETED) {
- gh_list_remove_elem(&nsp->active_iods, current);
- gh_list_prepend(&nsp->free_iods, nsi);
+ gh_list_remove(&nsp->active_iods, current);
+ gh_list_prepend(&nsp->free_iods, current);
}
}
- /* iterate through timers */
- for (current = GH_LIST_FIRST_ELEM(&nsp->timer_events);
- current != NULL && GH_LIST_ELEM_PREV(current) != timer_last; current = next) {
-
- msevent *nse = (msevent *)GH_LIST_ELEM_DATA(current);
-
- process_event(nsp, &nsp->timer_events, nse, EV_NONE);
-
- next = GH_LIST_ELEM_NEXT(current);
- if (nse->event_done)
- gh_list_remove_elem(&nsp->timer_events, current);
- }
+ /* iterate through timers and expired events */
+ process_expired_events(nsp);
}
#endif /* HAVE_POLL */
-
diff --git a/nsock/src/engine_select.c b/nsock/src/engine_select.c
index e425513bb..3ba2765a9 100644
--- a/nsock/src/engine_select.c
+++ b/nsock/src/engine_select.c
@@ -94,8 +94,9 @@ struct io_engine engine_select = {
static void iterate_through_event_lists(mspool *nsp);
/* defined in nsock_core.c */
-void process_event(mspool *nsp, gh_list *evlist, msevent *nse, int ev);
+void process_event(mspool *nsp, gh_list_t *evlist, msevent *nse, int ev);
void process_iod_events(mspool *nsp, msiod *nsi, int ev);
+void process_expired_events(mspool *nsp);
#if HAVE_PCAP
#ifndef PCAP_CAN_DO_SELECT
@@ -252,18 +253,21 @@ int select_loop(mspool *nsp, int msec_timeout) {
return 0; /* No need to wait on 0 events ... */
do {
+ msevent *nse;
+
nsock_log_debug_all(nsp, "wait for events");
- if (nsp->next_ev.tv_sec == 0)
+ nse = next_expirable_event(nsp);
+ if (!nse)
event_msecs = -1; /* None of the events specified a timeout */
else
- event_msecs = MAX(0, TIMEVAL_MSEC_SUBTRACT(nsp->next_ev, nsock_tod));
+ event_msecs = MAX(0, TIMEVAL_MSEC_SUBTRACT(nse->timeout, nsock_tod));
#if HAVE_PCAP
#ifndef PCAP_CAN_DO_SELECT
/* Force a low timeout when capturing packets on systems where
* the pcap descriptor is not select()able. */
- if (GH_LIST_COUNT(&nsp->pcap_read_events))
+ if (gh_list_count(&nsp->pcap_read_events))
if (event_msecs > PCAP_POLL_INTERVAL)
event_msecs = PCAP_POLL_INTERVAL;
#endif
@@ -366,39 +370,25 @@ static inline int get_evmask(const mspool *nsp, const msiod *nsi) {
* timer_events, etc) and take action for those that have completed (due to
* timeout, i/o, etc) */
void iterate_through_event_lists(mspool *nsp) {
- gh_list_elem *current, *next, *last, *timer_last;
+ gh_lnode_t *current, *next, *last;
- /* Clear it -- We will find the next event as we go through the list */
- nsp->next_ev.tv_sec = 0;
+ last = gh_list_last_elem(&nsp->active_iods);
- last = GH_LIST_LAST_ELEM(&nsp->active_iods);
- timer_last = GH_LIST_LAST_ELEM(&nsp->timer_events);
+ for (current = gh_list_first_elem(&nsp->active_iods);
+ current != NULL && gh_lnode_prev(current) != last;
+ current = next) {
+ msiod *nsi = container_of(current, msiod, nodeq);
- for (current = GH_LIST_FIRST_ELEM(&nsp->active_iods);
- current != NULL && GH_LIST_ELEM_PREV(current) != last; current = next) {
- msiod *nsi = (msiod *)GH_LIST_ELEM_DATA(current);
-
if (nsi->state != NSIOD_STATE_DELETED && nsi->events_pending)
process_iod_events(nsp, nsi, get_evmask(nsp, nsi));
- next = GH_LIST_ELEM_NEXT(current);
+ next = gh_lnode_next(current);
if (nsi->state == NSIOD_STATE_DELETED) {
- gh_list_remove_elem(&nsp->active_iods, current);
- gh_list_prepend(&nsp->free_iods, nsi);
+ gh_list_remove(&nsp->active_iods, current);
+ gh_list_prepend(&nsp->free_iods, current);
}
}
- /* iterate through timers */
- for (current = GH_LIST_FIRST_ELEM(&nsp->timer_events);
- current != NULL && GH_LIST_ELEM_PREV(current) != timer_last; current = next) {
-
- msevent *nse = (msevent *)GH_LIST_ELEM_DATA(current);
-
- process_event(nsp, &nsp->timer_events, nse, EV_NONE);
-
- next = GH_LIST_ELEM_NEXT(current);
- if (nse->event_done)
- gh_list_remove_elem(&nsp->timer_events, current);
- }
+ /* iterate through timers and expired events */
+ process_expired_events(nsp);
}
-
diff --git a/nsock/src/gh_heap.c b/nsock/src/gh_heap.c
new file mode 100644
index 000000000..23413bc09
--- /dev/null
+++ b/nsock/src/gh_heap.c
@@ -0,0 +1,248 @@
+/***************************************************************************
+ * gh_heap.c -- heap based priority queue. *
+ * *
+ ***********************IMPORTANT NSOCK LICENSE TERMS***********************
+ * *
+ * The nsock parallel socket event library is (C) 1999-2013 Insecure.Com *
+ * LLC This library is free software; you may redistribute and/or *
+ * modify it under the terms of the GNU General Public License as *
+ * published by the Free Software Foundation; Version 2. This guarantees *
+ * your right to use, modify, and redistribute this software under certain *
+ * conditions. If this license is unacceptable to you, Insecure.Com LLC *
+ * may be willing to sell alternative licenses (contact *
+ * sales@insecure.com ). *
+ * *
+ * As a special exception to the GPL terms, Insecure.Com LLC grants *
+ * permission to link the code of this program with any version of the *
+ * OpenSSL library which is distributed under a license identical to that *
+ * listed in the included docs/licenses/OpenSSL.txt file, and distribute *
+ * linked combinations including the two. You must obey the GNU GPL in all *
+ * respects for all of the code used other than OpenSSL. If you modify *
+ * this file, you may extend this exception to your version of the file, *
+ * but you are not obligated to do so. *
+ * *
+ * If you received these files with a written license agreement stating *
+ * terms other than the (GPL) terms above, then that alternative license *
+ * agreement takes precedence over this comment. *
+ * *
+ * Source is provided to this software because we believe users have a *
+ * right to know exactly what a program is going to do before they run it. *
+ * This also allows you to audit the software for security holes (none *
+ * have been found so far). *
+ * *
+ * Source code also allows you to port Nmap to new platforms, fix bugs, *
+ * and add new features. You are highly encouraged to send your changes *
+ * to the dev@nmap.org mailing list for possible incorporation into the *
+ * main distribution. By sending these changes to Fyodor or one of the *
+ * Insecure.Org development mailing lists, or checking them into the Nmap *
+ * source code repository, it is understood (unless you specify otherwise) *
+ * that you are offering the Nmap Project (Insecure.Com LLC) the *
+ * unlimited, non-exclusive right to reuse, modify, and relicense the *
+ * code. Nmap will always be available Open Source, but this is important *
+ * because the inability to relicense code has caused devastating problems *
+ * for other Free Software projects (such as KDE and NASM). We also *
+ * occasionally relicense the code to third parties as discussed above. *
+ * If you wish to specify special license conditions of your *
+ * contributions, just say so when you send them. *
+ * *
+ * 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 v2.0 for more details *
+ * (http://www.gnu.org/licenses/gpl-2.0.html). *
+ * *
+ ***************************************************************************/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "nsock_config.h"
+#include "nbase_config.h"
+#endif
+
+#ifdef WIN32
+#include "nbase_winconfig.h"
+#endif
+
+#include
+#include "gh_heap.h"
+
+#define GH_SLOTS 128
+
+
+static gh_hnode_t **hnode_ptr(gh_heap_t *heap, unsigned int index) {
+ assert(index >= 0);
+ assert(index <= heap->count);
+ return &(heap->slots[index]);
+}
+
+gh_hnode_t *gh_heap_find(gh_heap_t *heap, unsigned int index) {
+ if (index >= heap->count)
+ return NULL;
+
+ return *hnode_ptr(heap, index);
+}
+
+static int hnode_up(gh_heap_t *heap, gh_hnode_t *hnode)
+{
+ unsigned int cur_idx = hnode->index;
+ gh_hnode_t **cur_ptr = hnode_ptr(heap, cur_idx);
+ unsigned int parent_idx;
+ gh_hnode_t **parent_ptr;
+ int action = 0;
+
+ assert(*cur_ptr == hnode);
+
+ while (cur_idx > 0) {
+ parent_idx = (cur_idx - 1) >> 1;
+
+ parent_ptr = hnode_ptr(heap, parent_idx);
+ assert((*parent_ptr)->index == parent_idx);
+
+ if (heap->cmp_op(*parent_ptr, hnode))
+ break;
+
+ (*parent_ptr)->index = cur_idx;
+ *cur_ptr = *parent_ptr;
+ cur_ptr = parent_ptr;
+ cur_idx = parent_idx;
+ action = 1;
+ }
+
+ hnode->index = cur_idx;
+ *cur_ptr = hnode;
+
+ return action;
+}
+
+static int hnode_down(gh_heap_t *heap, gh_hnode_t *hnode)
+{
+ unsigned int count = heap->count;
+ unsigned int ch1_idx, ch2_idx, cur_idx;
+ gh_hnode_t **ch1_ptr, **ch2_ptr, **cur_ptr;
+ gh_hnode_t *ch1, *ch2;
+ int action = 0;
+
+ cur_idx = hnode->index;
+ cur_ptr = hnode_ptr(heap, cur_idx);
+ assert(*cur_ptr == hnode);
+
+ while (cur_idx < count) {
+ ch1_idx = (cur_idx << 1) + 1;
+ if (ch1_idx >= count)
+ break;
+
+ ch1_ptr = hnode_ptr(heap, ch1_idx);
+ ch1 = *ch1_ptr;
+
+ ch2_idx = ch1_idx + 1;
+ if (ch2_idx < count) {
+ ch2_ptr = hnode_ptr(heap, ch2_idx);
+ ch2 = *ch2_ptr;
+
+ if (heap->cmp_op(ch2, ch1)) {
+ ch1_idx = ch2_idx;
+ ch1_ptr = ch2_ptr;
+ ch1 = ch2;
+ }
+ }
+
+ assert(ch1->index == ch1_idx);
+
+ if (heap->cmp_op(hnode, ch1))
+ break;
+
+ ch1->index = cur_idx;
+ *cur_ptr = ch1;
+ cur_ptr = ch1_ptr;
+ cur_idx = ch1_idx;
+ action = 1;
+ }
+
+ hnode->index = cur_idx;
+ *cur_ptr = hnode;
+
+ return action;
+}
+
+static int heap_grow(gh_heap_t *heap) {
+ /* Do we really need to grow? */
+ assert(heap->count == heap->highwm);
+
+ heap->slots = safe_realloc(heap->slots,
+ (heap->count + GH_SLOTS) * sizeof(gh_hnode_t *));
+ heap->highwm += GH_SLOTS;
+ return 0;
+}
+
+int gh_heap_init(gh_heap_t *heap, gh_heap_cmp_t cmp_op) {
+ int rc;
+
+ if (!cmp_op)
+ return -1;
+
+ heap->cmp_op = cmp_op;
+ heap->count = 0;
+ heap->highwm = 0;
+ heap->slots = NULL;
+
+ rc = heap_grow(heap);
+ if (rc)
+ gh_heap_free(heap);
+
+ return rc;
+}
+
+void gh_heap_free(gh_heap_t *heap) {
+ if (heap->highwm) {
+ assert(heap->slots);
+ free(heap->slots);
+ }
+ memset(heap, 0, sizeof(gh_heap_t));
+}
+
+int gh_heap_push(gh_heap_t *heap, gh_hnode_t *hnode) {
+ gh_hnode_t **new_ptr;
+ unsigned int new_index = heap->count;
+
+ assert(!gh_hnode_is_valid(hnode));
+
+ if (new_index == heap->highwm)
+ heap_grow(heap);
+
+ hnode->index = new_index;
+ new_ptr = hnode_ptr(heap, new_index);
+ heap->count++;
+ *new_ptr = hnode;
+
+ hnode_up(heap, hnode);
+ return 0;
+}
+
+int gh_heap_remove(gh_heap_t *heap, gh_hnode_t *hnode)
+{
+ unsigned int count = heap->count;
+ unsigned int cur_idx = hnode->index;
+ gh_hnode_t **cur_ptr;
+ gh_hnode_t *last;
+
+ assert(gh_hnode_is_valid(hnode));
+ assert(cur_idx < count);
+
+ cur_ptr = hnode_ptr(heap, cur_idx);
+ assert(*cur_ptr == hnode);
+
+ count--;
+ last = *hnode_ptr(heap, count);
+ heap->count = count;
+ if (last == hnode)
+ return 0;
+
+ last->index = cur_idx;
+ *cur_ptr = last;
+ if (!hnode_up(heap, *cur_ptr))
+ hnode_down(heap, *cur_ptr);
+
+ gh_hnode_invalidate(hnode);
+ return 0;
+}
diff --git a/nsock/src/gh_heap.h b/nsock/src/gh_heap.h
new file mode 100644
index 000000000..82407aabf
--- /dev/null
+++ b/nsock/src/gh_heap.h
@@ -0,0 +1,146 @@
+/***************************************************************************
+ * gh_heap.h -- heap based priority queues. *
+ * *
+ ***********************IMPORTANT NSOCK LICENSE TERMS***********************
+ * *
+ * The nsock parallel socket event library is (C) 1999-2013 Insecure.Com *
+ * LLC This library is free software; you may redistribute and/or *
+ * modify it under the terms of the GNU General Public License as *
+ * published by the Free Software Foundation; Version 2. This guarantees *
+ * your right to use, modify, and redistribute this software under certain *
+ * conditions. If this license is unacceptable to you, Insecure.Com LLC *
+ * may be willing to sell alternative licenses (contact *
+ * sales@insecure.com ). *
+ * *
+ * As a special exception to the GPL terms, Insecure.Com LLC grants *
+ * permission to link the code of this program with any version of the *
+ * OpenSSL library which is distributed under a license identical to that *
+ * listed in the included docs/licenses/OpenSSL.txt file, and distribute *
+ * linked combinations including the two. You must obey the GNU GPL in all *
+ * respects for all of the code used other than OpenSSL. If you modify *
+ * this file, you may extend this exception to your version of the file, *
+ * but you are not obligated to do so. *
+ * *
+ * If you received these files with a written license agreement stating *
+ * terms other than the (GPL) terms above, then that alternative license *
+ * agreement takes precedence over this comment. *
+ * *
+ * Source is provided to this software because we believe users have a *
+ * right to know exactly what a program is going to do before they run it. *
+ * This also allows you to audit the software for security holes (none *
+ * have been found so far). *
+ * *
+ * Source code also allows you to port Nmap to new platforms, fix bugs, *
+ * and add new features. You are highly encouraged to send your changes *
+ * to the dev@nmap.org mailing list for possible incorporation into the *
+ * main distribution. By sending these changes to Fyodor or one of the *
+ * Insecure.Org development mailing lists, or checking them into the Nmap *
+ * source code repository, it is understood (unless you specify otherwise) *
+ * that you are offering the Nmap Project (Insecure.Com LLC) the *
+ * unlimited, non-exclusive right to reuse, modify, and relicense the *
+ * code. Nmap will always be available Open Source, but this is important *
+ * because the inability to relicense code has caused devastating problems *
+ * for other Free Software projects (such as KDE and NASM). We also *
+ * occasionally relicense the code to third parties as discussed above. *
+ * If you wish to specify special license conditions of your *
+ * contributions, just say so when you send them. *
+ * *
+ * 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 v2.0 for more details *
+ * (http://www.gnu.org/licenses/gpl-2.0.html). *
+ * *
+ ***************************************************************************/
+
+/* $Id$ */
+
+#ifndef GH_HEAP_H
+#define GH_HEAP_H
+
+#ifdef HAVE_CONFIG_H
+#include "nsock_config.h"
+#include "nbase_config.h"
+#endif
+
+#ifdef WIN32
+#include "nbase_winconfig.h"
+#endif
+
+#include "error.h"
+#include
+
+
+#if !defined(container_of)
+#define container_of(ptr, type, member) \
+ ((type *)((char *)(ptr)-(char *)(&((type *)0)->member)))
+#endif
+
+
+typedef struct {
+ unsigned int index;
+} gh_hnode_t;
+
+/* POISON value, set heap node index to this value to indicate that the node is
+ * inactive (not part of a heap) */
+#define GH_HEAP_GUARD 0x19890721
+
+/* Node comparison function.
+ * Here lies all the intelligence of the tree.
+ * Return 1 if hnode1 < hnode2, 0 otherwise. */
+typedef int (*gh_heap_cmp_t)(gh_hnode_t *hnode1, gh_hnode_t *hnode2);
+
+
+typedef struct gh_heap {
+ gh_heap_cmp_t cmp_op;
+ unsigned int count;
+ unsigned int highwm;
+ gh_hnode_t **slots;
+} gh_heap_t;
+
+
+int gh_heap_init(gh_heap_t *heap, gh_heap_cmp_t cmp_op);
+
+void gh_heap_free(gh_heap_t *heap);
+
+int gh_heap_push(gh_heap_t *heap, gh_hnode_t *node);
+
+int gh_heap_remove(gh_heap_t *heap, gh_hnode_t *node);
+
+gh_hnode_t *gh_heap_find(gh_heap_t *heap, unsigned int index);
+
+
+static inline gh_hnode_t *gh_heap_min(gh_heap_t *heap) {
+ if (heap->count == 0)
+ return NULL;
+
+ return gh_heap_find(heap, 0);
+}
+
+static inline gh_hnode_t *gh_heap_pop(gh_heap_t *heap) {
+ gh_hnode_t *hnode;
+
+ hnode = gh_heap_find(heap, 0);
+ if (hnode != NULL)
+ gh_heap_remove(heap, hnode);
+
+ return hnode;
+}
+
+static inline size_t gh_heap_count(gh_heap_t *heap) {
+ return heap->count;
+}
+
+static inline int gh_heap_is_empty(gh_heap_t *heap) {
+ return heap->count == 0;
+}
+
+static inline void gh_hnode_invalidate(gh_hnode_t *node) {
+ node->index = GH_HEAP_GUARD;
+}
+
+static inline int gh_hnode_is_valid(const gh_hnode_t *node) {
+ return (node && node->index != GH_HEAP_GUARD);
+}
+
+#endif /* GH_HEAP_H */
diff --git a/nsock/src/gh_list.c b/nsock/src/gh_list.c
deleted file mode 100644
index 7d4ee3ae5..000000000
--- a/nsock/src/gh_list.c
+++ /dev/null
@@ -1,346 +0,0 @@
-/***************************************************************************
- * gh_list.c -- a simple doubly-linked list implementation with a very *
- * heavy focus on efficiency. *
- * *
- ***********************IMPORTANT NSOCK LICENSE TERMS***********************
- * *
- * The nsock parallel socket event library is (C) 1999-2013 Insecure.Com *
- * LLC This library is free software; you may redistribute and/or *
- * modify it under the terms of the GNU General Public License as *
- * published by the Free Software Foundation; Version 2. This guarantees *
- * your right to use, modify, and redistribute this software under certain *
- * conditions. If this license is unacceptable to you, Insecure.Com LLC *
- * may be willing to sell alternative licenses (contact *
- * sales@insecure.com ). *
- * *
- * As a special exception to the GPL terms, Insecure.Com LLC grants *
- * permission to link the code of this program with any version of the *
- * OpenSSL library which is distributed under a license identical to that *
- * listed in the included docs/licenses/OpenSSL.txt file, and distribute *
- * linked combinations including the two. You must obey the GNU GPL in all *
- * respects for all of the code used other than OpenSSL. If you modify *
- * this file, you may extend this exception to your version of the file, *
- * but you are not obligated to do so. *
- * *
- * If you received these files with a written license agreement stating *
- * terms other than the (GPL) terms above, then that alternative license *
- * agreement takes precedence over this comment. *
- * *
- * Source is provided to this software because we believe users have a *
- * right to know exactly what a program is going to do before they run it. *
- * This also allows you to audit the software for security holes (none *
- * have been found so far). *
- * *
- * Source code also allows you to port Nmap to new platforms, fix bugs, *
- * and add new features. You are highly encouraged to send your changes *
- * to the dev@nmap.org mailing list for possible incorporation into the *
- * main distribution. By sending these changes to Fyodor or one of the *
- * Insecure.Org development mailing lists, or checking them into the Nmap *
- * source code repository, it is understood (unless you specify otherwise) *
- * that you are offering the Nmap Project (Insecure.Com LLC) the *
- * unlimited, non-exclusive right to reuse, modify, and relicense the *
- * code. Nmap will always be available Open Source, but this is important *
- * because the inability to relicense code has caused devastating problems *
- * for other Free Software projects (such as KDE and NASM). We also *
- * occasionally relicense the code to third parties as discussed above. *
- * If you wish to specify special license conditions of your *
- * contributions, just say so when you send them. *
- * *
- * 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 v2.0 for more details *
- * (http://www.gnu.org/licenses/gpl-2.0.html). *
- * *
- ***************************************************************************/
-
-/* $Id$ */
-
-#include "nsock.h"
-
-#include "gh_list.h"
-
-#include
-
-#if HAVE_STRING_H
-#include
-#endif
-#if HAVE_STRINGS_H
-#include
-#endif
-
-
-#define SAFETY_CHECK_LIST(l) do { \
- assert(l); \
- assert((l)->magic == GH_LIST_MAGIC); \
- assert((l)->count == 0 || ((l)->first && (l)->last)); \
- assert((l)->count != 0 || ((l)->first == NULL && (l)->last == NULL)); \
- } while (0)
-
-#define SAFETY_CHECK_ELEM(e) do { \
- assert(e); \
- assert((e)->magic == GH_LIST_MAGIC); \
- } while (0)
-
-
-static inline struct gh_list_elem *get_free_buffer(struct gh_list *list) {
- struct gh_list_elem *newelem;
- int i;
-
- if (!list->free) {
- list->last_alloc *= 2;
- list->free = (struct gh_list_elem *)safe_malloc(list->last_alloc * sizeof(struct gh_list_elem));
- memset(list->free, 0, list->last_alloc * sizeof(struct gh_list_elem));
- list->free->allocated = 1;
- for (i=0; i < list->last_alloc - 1; i++) {
- (list->free + i)->next = list->free + i + 1;
- }
- }
- newelem = list->free;
- list->free = list->free->next;
-#ifndef NDEBUG
- newelem->magic = GH_LIST_MAGIC;
-#endif
- return newelem;
-}
-
-int gh_list_init(gh_list *newlist) {
- int i;
-
- if (!newlist)
- return -1;
-
- newlist->count = 0;
- newlist->first = newlist->last = NULL;
- newlist->last_alloc = 16;
- newlist->free = (struct gh_list_elem *)safe_malloc(newlist->last_alloc * sizeof(struct gh_list_elem));
-
- memset(newlist->free, 0, newlist->last_alloc * sizeof(struct gh_list_elem));
- newlist->free->allocated = 1;
-
- for (i = 0; i < newlist->last_alloc - 1; i++) {
- (newlist->free + i)->next = newlist->free + i + 1;
- }
- /* Not needed (newlist->free + newlist->last_alloc - 1)->next = NULL */
-#ifndef NDEBUG
- newlist->magic = GH_LIST_MAGIC;
-#endif
- return 0;
-}
-
-gh_list_elem *gh_list_append(gh_list *list, void *data) {
- gh_list_elem *newelem;
- gh_list_elem *oldlast;
-
- SAFETY_CHECK_LIST(list);
-
- newelem = get_free_buffer(list);
- oldlast = list->last;
-
- if (oldlast) {
- oldlast->next = newelem;
- newelem->prev = oldlast;
- } else {
- newelem->prev = NULL;
- }
-
- newelem->next = NULL;
- newelem->data = data;
-
-#ifndef NDEBUG
- newelem->magic = GH_LIST_MAGIC;
-#endif
-
- list->count++;
- list->last = newelem;
-
- if (list->count == 1)
- list->first = newelem;
-
- return newelem;
-}
-
-gh_list_elem *gh_list_prepend(gh_list *list, void *data) {
- gh_list_elem *newelem;
- gh_list_elem *oldfirst;
-
- SAFETY_CHECK_LIST(list);
-
- newelem = get_free_buffer(list);
- oldfirst = list->first;
- if (oldfirst) {
- oldfirst->prev = newelem;
- newelem->next = oldfirst;
- } else {
- newelem->next = NULL;
- }
-
- newelem->prev = NULL;
- newelem->data = data;
-
-#ifndef NDEBUG
- newelem->magic = GH_LIST_MAGIC;
-#endif
-
- list->count++;
- list->first = newelem;
-
- if (list->count == 1)
- list->last = newelem;
-
- return newelem;
-}
-
-gh_list_elem *gh_list_insert_before(gh_list *list, gh_list_elem *before, void *data) {
- gh_list_elem *newelem;
-
- SAFETY_CHECK_LIST(list);
- SAFETY_CHECK_ELEM(before);
-
- /* create or reuse a new cell */
- newelem = get_free_buffer(list);
-
- newelem->data = data;
- newelem->prev = before->prev;
- newelem->next = before;
-#ifndef NDEBUG
- newelem->magic = GH_LIST_MAGIC;
-#endif
-
- if (before->prev)
- before->prev->next = newelem;
- else
- list->first = newelem;
-
- before->prev = newelem;
-
- list->count++;
-
- return newelem;
-}
-
-void *gh_list_pop(gh_list *list) {
- struct gh_list_elem *oldelem;
-
- SAFETY_CHECK_LIST(list);
-
- oldelem = list->first;
- if (!oldelem)
- return NULL;
-
- list->first = list->first->next;
- if (list->first)
- list->first->prev = NULL;
-
- list->count--;
-
- if (list->count < 2)
- list->last = list->first;
-
- oldelem->next = list->free;
- list->free = oldelem;
-
- return oldelem->data;
-}
-
-int gh_list_free(gh_list *list) {
- struct gh_list_elem *current;
- char *free_list[32];
- int free_index = 0;
- int i = 0;
-
- SAFETY_CHECK_LIST(list);
-
-#ifndef NDEBUG
- list->magic++;
-#endif
-
- for (current = list->first; current; current = current->next) {
-#ifndef NDEBUG
- current->magic++;
-#endif
- if (current->allocated) {
- assert(free_index < 32);
- free_list[free_index++] = (char *)current;
- }
- }
-
- for (current = list->free; current; current = current->next)
- if (current->allocated) {
- assert(free_index < 32);
- free_list[free_index++] = (char *)current;
- }
-
- for (i = 0; i < free_index; i++)
- free(free_list[i]);
-
- return 0;
-}
-
-int gh_list_remove_elem(gh_list *list, gh_list_elem *elem) {
- SAFETY_CHECK_ELEM(elem);
- SAFETY_CHECK_LIST(list);
-
- if (elem->prev) {
- elem->prev->next = elem->next;
- } else {
- assert(list->first == elem);
- list->first = elem->next;
- }
-
- if (elem->next) {
- elem->next->prev = elem->prev;
- } else {
- assert(list->last == elem);
- list->last = elem->prev;
- }
-
-#ifndef NDEBUG
- elem->magic++;
-#endif
-
- elem->next = list->free;
- list->free = elem;
-
- list->count--;
- return 0;
-}
-
-int gh_list_move_front(gh_list *list, gh_list_elem *elem) {
- SAFETY_CHECK_LIST(list);
- SAFETY_CHECK_ELEM(elem);
-
- if (list->first == elem)
- return 0;
-
- /* remove element from its current position */
- elem->prev->next = elem->next;
-
- if (elem->next) {
- elem->next->prev = elem->prev;
- } else {
- assert(list->last == elem);
- list->last = elem->prev;
- }
-
- /* add element to the beginning list */
- list->first->prev = elem;
- elem->next = list->first;
- elem->prev = NULL;
- list->first = elem;
-
- return 0;
-}
-
-int gh_list_remove(gh_list *list, void *data) {
- struct gh_list_elem *current;
-
- SAFETY_CHECK_LIST(list);
-
- for (current = list->first; current; current = current->next) {
- if (current->data == data)
- return gh_list_remove_elem(list, current);
- }
- return -1;
-}
-
diff --git a/nsock/src/gh_list.h b/nsock/src/gh_list.h
index 77d94ae6a..9c4f04ef9 100644
--- a/nsock/src/gh_list.h
+++ b/nsock/src/gh_list.h
@@ -1,6 +1,5 @@
/***************************************************************************
- * gh_list.h -- a simple doubly-linked list implementation with a very *
- * heavy focus on efficiency. *
+ * gh_list.h -- a simple doubly-linked list implementation. *
* *
***********************IMPORTANT NSOCK LICENSE TERMS***********************
* *
@@ -71,78 +70,230 @@
#include "error.h"
#include
-#define GH_LIST_MAGIC 0xBADFACE
-
-/* Take a LIST ELEMENT (not just the data) and return the next one */
-#define GH_LIST_ELEM_NEXT(x) ((x)->next)
-
-/* Same as above but return the previous element */
-#define GH_LIST_ELEM_PREV(x) ((x)->prev)
-
-/* Take a LIST (not a list element) and return the first element */
-#define GH_LIST_FIRST_ELEM(x) ((x)->first)
-
-/* Same as above but return the last element */
-#define GH_LIST_LAST_ELEM(x) ((x)->last)
-
-/* Obtain the actual data stored in an element */
-#define GH_LIST_ELEM_DATA(x) ((x)->data)
-
-/* Obtain the number of elements in a list */
-#define GH_LIST_COUNT(x) ((x)->count)
+#define GH_LIST_MAGIC 0xBADFACE
+#define GH_LIST_PARANOID 0
-typedef struct gh_list_elem {
- void *data;
- struct gh_list_elem *next;
- struct gh_list_elem *prev;
-
- /* nonzero if this element was the first (or only) in a group that was
- * allocated. This means we can safely free() it as long as we are OK with
- * freeing others that were freed with it ... */
- int allocated;
-
-#ifndef NDEBUG
- unsigned long magic;
-#endif
-} gh_list_elem;
+typedef struct gh_list_node {
+ struct gh_list_node *next;
+ struct gh_list_node *prev;
+} gh_lnode_t;
typedef struct gh_list {
/* Number of elements in the list */
- int count;
- struct gh_list_elem *first;
- struct gh_list_elem *last;
+ unsigned int count;
+ gh_lnode_t *first;
+ gh_lnode_t *last;
+} gh_list_t;
- /* Instead of free()ing elements when something is removed from the list, we
- * stick them here for the next insert. */
- struct gh_list_elem *free;
- /* The number of list elements in the most recent malloc */
- int last_alloc;
+/* That one's an efficiency killer but it should reveal
+ * any inconsistency in nsock's lists management. To be
+ * called on every list we get and return. */
+static inline void paranoid_list_check(gh_list_t *list) {
+#if GH_LIST_PARANOID
+ switch (list->count) {
+ case 0:
+ assert(list->first == NULL);
+ assert(list->last == NULL);
+ break;
-#ifndef NDEBUG
- unsigned long magic;
+ case 1:
+ assert(list->first);
+ assert(list->last);
+ assert(list->first == list->last);
+ break;
+
+ default:
+ assert(list->first);
+ assert(list->last);
+ assert(list->first != list->last);
+ break;
+ }
#endif
-} gh_list;
+}
+static inline int gh_list_init(gh_list_t *newlist) {
+ newlist->count = 0;
+ newlist->first = NULL;
+ newlist->last = NULL;
+ return 0;
+}
-int gh_list_init(gh_list *newlist);
+static inline int gh_list_append(gh_list_t *list, gh_lnode_t *lnode) {
+ gh_lnode_t *oldlast;
-gh_list_elem *gh_list_append(gh_list *list, void *data);
+ paranoid_list_check(list);
-gh_list_elem *gh_list_prepend(gh_list *list, void *data);
+ oldlast = list->last;
+ if (oldlast)
+ oldlast->next = lnode;
-gh_list_elem *gh_list_insert_before(gh_list *list, gh_list_elem *before, void *data);
+ lnode->prev = oldlast;
+ lnode->next = NULL;
-void *gh_list_pop(gh_list *list);
+ list->count++;
+ list->last = lnode;
-int gh_list_remove(gh_list *list, void *data);
+ if (list->count == 1)
+ list->first = lnode;
-int gh_list_free(gh_list *list);
+ paranoid_list_check(list);
+ return 0;
+}
-int gh_list_move_front(gh_list *list, gh_list_elem *elem);
+static inline int gh_list_prepend(gh_list_t *list, gh_lnode_t *lnode) {
+ gh_lnode_t *oldfirst;
-int gh_list_remove_elem(gh_list *list, gh_list_elem *elem);
+ paranoid_list_check(list);
+
+ oldfirst = list->first;
+ if (oldfirst)
+ oldfirst->prev = lnode;
+
+ lnode->next = oldfirst;
+ lnode->prev = NULL;
+
+ list->count++;
+ list->first = lnode;
+
+ if (list->count == 1)
+ list->last = lnode;
+
+ paranoid_list_check(list);
+ return 0;
+}
+
+static inline int gh_list_insert_before(gh_list_t *list, gh_lnode_t *before,
+ gh_lnode_t *lnode) {
+ paranoid_list_check(list);
+
+ lnode->prev = before->prev;
+ lnode->next = before;
+
+ if (before->prev)
+ before->prev->next = lnode;
+ else
+ list->first = lnode;
+
+ before->prev = lnode;
+ list->count++;
+
+ paranoid_list_check(list);
+ return 0;
+}
+
+static inline gh_lnode_t *gh_list_pop(gh_list_t *list) {
+ gh_lnode_t *elem;
+
+ paranoid_list_check(list);
+
+ elem = list->first;
+ if (!elem) {
+ paranoid_list_check(list);
+ return NULL;
+ }
+
+ list->first = list->first->next;
+ if (list->first)
+ list->first->prev = NULL;
+
+ list->count--;
+
+ if (list->count < 2)
+ list->last = list->first;
+
+ elem->prev = NULL;
+ elem->next = NULL;
+
+ paranoid_list_check(list);
+ return elem;
+}
+
+static inline int gh_list_remove(gh_list_t *list, gh_lnode_t *lnode) {
+ paranoid_list_check(list);
+
+ if (lnode->prev) {
+ lnode->prev->next = lnode->next;
+ } else {
+ assert(list->first == lnode);
+ list->first = lnode->next;
+ }
+
+ if (lnode->next) {
+ lnode->next->prev = lnode->prev;
+ } else {
+ assert(list->last == lnode);
+ list->last = lnode->prev;
+ }
+
+ lnode->prev = NULL;
+ lnode->next = NULL;
+
+ list->count--;
+
+ paranoid_list_check(list);
+ return 0;
+}
+
+static inline int gh_list_free(gh_list_t *list) {
+ paranoid_list_check(list);
+
+ while (list->count > 0)
+ gh_list_pop(list);
+
+ paranoid_list_check(list);
+ memset(list, 0, sizeof(gh_list_t));
+ return 0;
+}
+
+static inline int gh_list_move_front(gh_list_t *list, gh_lnode_t *lnode) {
+ paranoid_list_check(list);
+ if (list->first == lnode)
+ return 0;
+
+ /* remove element from its current position */
+ lnode->prev->next = lnode->next;
+
+ if (lnode->next) {
+ lnode->next->prev = lnode->prev;
+ } else {
+ assert(list->last == lnode);
+ list->last = lnode->prev;
+ }
+
+ /* add element to the beginning of the list */
+ list->first->prev = lnode;
+ lnode->next = list->first;
+ lnode->prev = NULL;
+ list->first = lnode;
+
+ paranoid_list_check(list);
+ return 0;
+}
+
+/* Take a LIST ELEMENT (not just the data) and return the next one */
+static inline gh_lnode_t *gh_lnode_next(gh_lnode_t *elem) {
+ return elem->next;
+}
+
+/* Same as above but return the previous element */
+static inline gh_lnode_t *gh_lnode_prev(gh_lnode_t *elem) {
+ return elem->prev;
+}
+
+/* Take a LIST (not a list element) and return the first element */
+static inline gh_lnode_t *gh_list_first_elem(gh_list_t *list) {
+ return list->first;
+}
+
+/* Same as above but return the last element */
+static inline gh_lnode_t *gh_list_last_elem(gh_list_t *list) {
+ return list->last;
+}
+
+static inline unsigned int gh_list_count(gh_list_t *list) {
+ return list->count;
+}
#endif /* GH_LIST_H */
-
diff --git a/nsock/src/nsock_connect.c b/nsock/src/nsock_connect.c
index ec598d3c5..9fd396ae7 100644
--- a/nsock/src/nsock_connect.c
+++ b/nsock/src/nsock_connect.c
@@ -204,7 +204,7 @@ void nsock_connect_internal(mspool *ms, msevent *nse, int type, int proto, struc
nsock_log_debug_all(ms, "TCP connection request (EID %lu) redirected through proxy chain",
(long)nse->id);
- current = proxy_ctx_node_current(iod->px_ctx);
+ current = iod->px_ctx->px_current;
assert(current != NULL);
memcpy(&iod->px_ctx->target_ss, ss, sslen);
diff --git a/nsock/src/nsock_core.c b/nsock/src/nsock_core.c
index 85474e544..7267453ac 100644
--- a/nsock/src/nsock_core.c
+++ b/nsock/src/nsock_core.c
@@ -223,27 +223,33 @@ static void update_events(msiod * iod, mspool *ms, int ev_inc, int ev_dec) {
* loop just after its addition.
*/
static int iod_add_event(msiod *iod, msevent *nse) {
+ mspool *nsp = iod->nsp;
+
switch (nse->type) {
case NSE_TYPE_CONNECT:
case NSE_TYPE_CONNECT_SSL:
if (iod->first_connect)
- iod->first_connect = gh_list_insert_before(&iod->nsp->connect_events, iod->first_connect, nse);
+ gh_list_insert_before(&nsp->connect_events,
+ iod->first_connect, &nse->nodeq_io);
else
- iod->first_connect = gh_list_append(&iod->nsp->connect_events, nse);
+ gh_list_append(&nsp->connect_events, &nse->nodeq_io);
+ iod->first_connect = &nse->nodeq_io;
break;
case NSE_TYPE_READ:
if (iod->first_read)
- iod->first_read = gh_list_insert_before(&iod->nsp->read_events, iod->first_read, nse);
+ gh_list_insert_before(&nsp->read_events, iod->first_read, &nse->nodeq_io);
else
- iod->first_read = gh_list_append(&iod->nsp->read_events, nse);
+ gh_list_append(&nsp->read_events, &nse->nodeq_io);
+ iod->first_read = &nse->nodeq_io;
break;
case NSE_TYPE_WRITE:
if (iod->first_write)
- iod->first_write = gh_list_insert_before(&iod->nsp->write_events, iod->first_write, nse);
+ gh_list_insert_before(&nsp->write_events, iod->first_write, &nse->nodeq_io);
else
- iod->first_write = gh_list_append(&iod->nsp->write_events, nse);
+ gh_list_append(&nsp->write_events, &nse->nodeq_io);
+ iod->first_write = &nse->nodeq_io;
break;
#if HAVE_PCAP
@@ -262,15 +268,18 @@ static int iod_add_event(msiod *iod, msevent *nse) {
#endif
if (add_read) {
if (iod->first_read)
- iod->first_read = gh_list_insert_before(&iod->nsp->read_events, iod->first_read, nse);
+ gh_list_insert_before(&nsp->read_events, iod->first_read, &nse->nodeq_io);
else
- iod->first_read = gh_list_append(&iod->nsp->read_events, nse);
+ gh_list_append(&nsp->read_events, &nse->nodeq_io);
+ iod->first_read = &nse->nodeq_io;
}
if (add_pcap_read) {
if (iod->first_pcap_read)
- iod->first_pcap_read = gh_list_insert_before(&iod->nsp->pcap_read_events, iod->first_pcap_read, nse);
+ gh_list_insert_before(&nsp->pcap_read_events, iod->first_pcap_read,
+ &nse->nodeq_pcap);
else
- iod->first_pcap_read = gh_list_append(&iod->nsp->pcap_read_events, nse);
+ gh_list_append(&nsp->pcap_read_events, &nse->nodeq_pcap);
+ iod->first_pcap_read = &nse->nodeq_pcap;
}
break;
}
@@ -850,20 +859,21 @@ void handle_pcap_read_result(mspool *ms, msevent *nse, enum nse_status status) {
/* Returns whether something was read */
int pcap_read_on_nonselect(mspool *nsp) {
- gh_list_elem *current, *next;
+ gh_lnode_t *current, *next;
msevent *nse;
int ret = 0;
- for (current = GH_LIST_FIRST_ELEM(&nsp->pcap_read_events); current != NULL; current = next) {
- nse = (msevent *)GH_LIST_ELEM_DATA(current);
+ for (current = gh_list_first_elem(&nsp->pcap_read_events);
+ current != NULL;
+ current = next) {
+ nse = lnode_msevent2(current);
if (do_actual_pcap_read(nse) == 1) {
/* something received */
ret++;
break;
}
- next = GH_LIST_ELEM_NEXT(current);
+ next = gh_lnode_next(current);
}
-
return ret;
}
#endif /* HAVE_PCAP */
@@ -935,13 +945,16 @@ enum nsock_loopstatus nsock_loop(nsock_pool nsp, int msec_timeout) {
return quitstatus;
}
-void process_event(mspool *nsp, gh_list *evlist, msevent *nse, int ev) {
+void process_event(mspool *nsp, gh_list_t *evlist, msevent *nse, int ev) {
int match_r = 0, match_w = 0;
#if HAVE_OPENSSL
int desire_r = 0, desire_w = 0;
#endif
- nsock_log_debug_all(nsp, "Processing event %lu", nse->id);
+ nsock_log_debug_all(nsp, "Processing event %lu (timeout in %ldms, done=%d)",
+ nse->id,
+ (long)TIMEVAL_MSEC_SUBTRACT(nse->timeout, nsock_tod),
+ nse->event_done);
if (!nse->event_done) {
switch (nse->type) {
@@ -1015,18 +1028,24 @@ void process_event(mspool *nsp, gh_list *evlist, msevent *nse, int ev) {
* Of course we should destroy it only once.
* I assume we're now in read_event, so just unlink this event from
* pcap_read_event */
- if (((mspcap *)nse->iod->pcap)->pcap_desc >= 0 && nse->event_done && evlist == &nsp->read_events) {
+ if (((mspcap *)nse->iod->pcap)->pcap_desc >= 0
+ && nse->event_done
+ && evlist == &nsp->read_events) {
/* event is done, list is read_events and we're in BSD_HACK mode.
* So unlink event from pcap_read_events */
update_first_events(nse);
- gh_list_remove(&nsp->pcap_read_events, nse);
+ gh_list_remove(&nsp->pcap_read_events, &nse->nodeq_pcap);
- nsock_log_debug_all(nsp, "PCAP NSE #%lu: Removing event from PCAP_READ_EVENTS", nse->id);
+ nsock_log_debug_all(nsp, "PCAP NSE #%lu: Removing event from PCAP_READ_EVENTS",
+ nse->id);
}
- if (((mspcap *)nse->iod->pcap)->pcap_desc >= 0 && nse->event_done && evlist == &nsp->pcap_read_events) {
+ if (((mspcap *)nse->iod->pcap)->pcap_desc >= 0
+ && nse->event_done
+ && evlist == &nsp->pcap_read_events) {
update_first_events(nse);
- gh_list_remove(&nsp->read_events, nse);
- nsock_log_debug_all(nsp, "PCAP NSE #%lu: Removing event from READ_EVENTS", nse->id);
+ gh_list_remove(&nsp->read_events, &nse->nodeq_io);
+ nsock_log_debug_all(nsp, "PCAP NSE #%lu: Removing event from READ_EVENTS",
+ nse->id);
}
#endif
break;
@@ -1034,11 +1053,12 @@ void process_event(mspool *nsp, gh_list *evlist, msevent *nse, int ev) {
#endif
default:
fatal("Event has unknown type (%d)", nse->type);
- break; /* unreached */
- }
+ }
}
+
if (nse->event_done) {
- /* Security sanity check: don't return a functional SSL iod without setting an SSL data structure. */
+ /* Security sanity check: don't return a functional SSL iod without
+ * setting an SSL data structure. */
if (nse->type == NSE_TYPE_CONNECT_SSL && nse->status == NSE_STATUS_SUCCESS)
assert(nse->iod->ssl != NULL);
@@ -1046,14 +1066,6 @@ void process_event(mspool *nsp, gh_list *evlist, msevent *nse, int ev) {
/* WooHoo! The event is ready to be sent */
msevent_dispatch_and_delete(nsp, nse, 1);
- } else {
- /* Is this event the next-to-timeout? */
- if (nse->timeout.tv_sec != 0) {
- if (nsp->next_ev.tv_sec == 0)
- nsp->next_ev = nse->timeout;
- else if (TIMEVAL_AFTER(nsp->next_ev, nse->timeout))
- nsp->next_ev = nse->timeout;
- }
}
}
@@ -1061,7 +1073,7 @@ void process_iod_events(mspool *nsp, msiod *nsi, int ev) {
int i = 0;
/* store addresses of the pointers to the first elements of each kind instead
* of storing the values, as a connect can add a read for instance */
- gh_list_elem **start_elems[] = {
+ gh_lnode_t **start_elems[] = {
&nsi->first_connect,
&nsi->first_read,
&nsi->first_write,
@@ -1070,16 +1082,19 @@ void process_iod_events(mspool *nsp, msiod *nsi, int ev) {
#endif
NULL
};
- gh_list *evlists[] = {
- &nsi->nsp->connect_events,
- &nsi->nsp->read_events,
- &nsi->nsp->write_events,
+ gh_list_t *evlists[] = {
+ &nsp->connect_events,
+ &nsp->read_events,
+ &nsp->write_events,
#if HAVE_PCAP
- &nsi->nsp->pcap_read_events,
+ &nsp->pcap_read_events,
#endif
NULL
};
+ assert(nsp == nsi->nsp);
+ nsock_log_debug_all(nsp, "Processing events on IOD %lu (ev=%d)", nsi->id, ev);
+
/* We keep the events separate because we want to handle them in the
* order: connect => read => write => timer for several reasons:
*
@@ -1091,16 +1106,24 @@ void process_iod_events(mspool *nsp, msiod *nsi, int ev) {
* processed in the same cycle. In the same way, read() often
* leads to write().
*/
- for (i = 0; start_elems[i] != NULL; i++) {
- gh_list_elem *current, *next, *last;
+ for (i = 0; evlists[i] != NULL; i++) {
+ gh_lnode_t *current, *next, *last;
- /* for each list, get the last event and don't look past it as an event could
- * add another event in the same list and so on... */
- last = GH_LIST_LAST_ELEM(evlists[i]);
+ /* for each list, get the last event and don't look past it as an event
+ * could add another event in the same list and so on... */
+ last = gh_list_last_elem(evlists[i]);
- for (current = *start_elems[i]; current != NULL
- && GH_LIST_ELEM_PREV(current) != last; current = next) {
- msevent *nse = (msevent *)GH_LIST_ELEM_DATA(current);
+ for (current = *start_elems[i];
+ current != NULL && gh_lnode_prev(current) != last;
+ current = next) {
+ msevent *nse;
+
+#if HAVE_PCAP
+ if (evlists[i] == &nsi->nsp->pcap_read_events)
+ nse = lnode_msevent2(current);
+ else
+#endif
+ nse = lnode_msevent(current);
/* events are grouped by IOD. Break if we're done with the events for the
* current IOD */
@@ -1108,18 +1131,91 @@ void process_iod_events(mspool *nsp, msiod *nsi, int ev) {
break;
process_event(nsp, evlists[i], nse, ev);
- next = GH_LIST_ELEM_NEXT(current);
+ next = gh_lnode_next(current);
if (nse->event_done) {
/* event is done, remove it from the event list and update IOD pointers
* to the first events of each kind */
update_first_events(nse);
- gh_list_remove_elem(evlists[i], current);
+ gh_list_remove(evlists[i], current);
+ gh_list_append(&nsp->free_events, &nse->nodeq_io);
+
+ if (nse->timeout.tv_sec)
+ gh_heap_remove(&nsp->expirables, &nse->expire);
}
}
}
}
+static int msevent_unref(mspool *nsp, msevent *nse) {
+ switch (nse->type) {
+ case NSE_TYPE_CONNECT:
+ case NSE_TYPE_CONNECT_SSL:
+ gh_list_remove(&nsp->connect_events, &nse->nodeq_io);
+ break;
+
+ case NSE_TYPE_READ:
+ gh_list_remove(&nsp->read_events, &nse->nodeq_io);
+ break;
+
+ case NSE_TYPE_WRITE:
+ gh_list_remove(&nsp->write_events, &nse->nodeq_io);
+ break;
+
+#if HAVE_PCAP
+ case NSE_TYPE_PCAP_READ: {
+ char read = 0;
+ char pcap = 0;
+
+#if PCAP_BSD_SELECT_HACK
+ read = pcap = 1;
+#else
+ if (((mspcap *)nse->iod->pcap)->pcap_desc >= 0)
+ read = 1;
+ else
+ pcap = 1;
+#endif /* PCAP_BSD_SELECT_HACK */
+
+ if (read)
+ gh_list_remove(&nsp->read_events, &nse->nodeq_io);
+ if (pcap)
+ gh_list_remove(&nsp->pcap_read_events, &nse->nodeq_pcap);
+
+ break;
+ }
+#endif /* HAVE_PCAP */
+
+ case NSE_TYPE_TIMER:
+ /* Nothing to do */
+ break;
+
+ default:
+ fatal("Unknown event type %d", nse->type);
+ }
+ gh_list_append(&nsp->free_events, &nse->nodeq_io);
+ return 0;
+}
+
+void process_expired_events(mspool *nsp) {
+ for (;;) {
+ gh_hnode_t *hnode;
+ msevent *nse;
+
+ hnode = gh_heap_min(&nsp->expirables);
+ if (!hnode)
+ break;
+
+ nse = container_of(hnode, msevent, expire);
+ if (msevent_timedout(nse)) {
+ gh_heap_pop(&nsp->expirables);
+ process_event(nsp, NULL, nse, EV_NONE);
+ assert(nse->event_done);
+ update_first_events(nse);
+ msevent_unref(nsp, nse);
+ } else break;
+ }
+}
+
/* Calling this function will cause nsock_loop to quit on its next iteration
* with a return value of NSOCK_LOOP_QUIT. */
void nsock_loop_quit(nsock_pool nsp) {
@@ -1142,22 +1238,17 @@ const struct timeval *nsock_gettimeofday() {
* adjusting the descriptor select/poll lists, registering the timeout value,
* etc. */
void nsp_add_event(mspool *nsp, msevent *nse) {
- nsock_log_debug(nsp, "NSE #%lu: Adding event", nse->id);
-
- /* First lets do the event-type independent stuff, starting with timeouts */
- if (nse->event_done) {
- nsp->next_ev = nsock_tod;
- } else {
- if (nse->timeout.tv_sec != 0) {
- if (nsp->next_ev.tv_sec == 0)
- nsp->next_ev = nse->timeout;
- else if (TIMEVAL_AFTER(nsp->next_ev, nse->timeout))
- nsp->next_ev = nse->timeout;
- }
- }
+ nsock_log_debug(nsp, "NSE #%lu: Adding event (timeout in %ldms)",
+ nse->id,
+ (long)TIMEVAL_MSEC_SUBTRACT(nse->timeout, nsock_tod));
nsp->events_pending++;
+ if (!nse->event_done && nse->timeout.tv_sec) {
+ /* This event is expirable, add it to the queue */
+ gh_heap_push(&nsp->expirables, &nse->expire);
+ }
+
/* Now we do the event type specific actions */
switch (nse->type) {
case NSE_TYPE_CONNECT:
@@ -1198,7 +1289,7 @@ void nsp_add_event(mspool *nsp, msevent *nse) {
break;
case NSE_TYPE_TIMER:
- gh_list_append(&nsp->timer_events, nse);
+ /* nothing to do */
break;
#if HAVE_PCAP
diff --git a/nsock/src/nsock_engines.c b/nsock/src/nsock_engines.c
index b62ceed83..0a0b969f4 100644
--- a/nsock/src/nsock_engines.c
+++ b/nsock/src/nsock_engines.c
@@ -64,7 +64,6 @@
#include "nsock_internal.h"
-
#if HAVE_EPOLL
extern struct io_engine engine_epoll;
#define ENGINE_EPOLL &engine_epoll,
diff --git a/nsock/src/nsock_event.c b/nsock/src/nsock_event.c
index f54700e98..54365dc25 100644
--- a/nsock/src/nsock_event.c
+++ b/nsock/src/nsock_event.c
@@ -119,18 +119,23 @@ char *nse_readbuf(nsock_event nse, int *nbytes) {
return fs_str(&(me->iobuf));
}
-static void first_ev_next(msevent *nse, gh_list_elem **first) {
+static void first_ev_next(msevent *nse, gh_lnode_t **first, int nodeq2) {
if (!first || !*first)
return;
- if ((msevent *)GH_LIST_ELEM_DATA(*first) == nse) {
- gh_list_elem *next;
+ if (&nse->nodeq_io == *first || &nse->nodeq_pcap == *first) {
+ gh_lnode_t *next;
- next = GH_LIST_ELEM_NEXT(*first);
+ next = gh_lnode_next(*first);
if (next) {
- msevent *nse2 = (msevent *)GH_LIST_ELEM_DATA(next);
+ msevent *newevent;
- if (nse2->iod == nse->iod)
+ if (nodeq2)
+ newevent = lnode_msevent2(next);
+ else
+ newevent = lnode_msevent(next);
+
+ if (newevent->iod == nse->iod)
*first = next;
else
*first = NULL;
@@ -144,21 +149,21 @@ void update_first_events(msevent *nse) {
switch (get_event_id_type(nse->id)) {
case NSE_TYPE_CONNECT:
case NSE_TYPE_CONNECT_SSL:
- first_ev_next(nse, &nse->iod->first_connect);
+ first_ev_next(nse, &nse->iod->first_connect, 0);
break;
case NSE_TYPE_READ:
- first_ev_next(nse, &nse->iod->first_read);
+ first_ev_next(nse, &nse->iod->first_read, 0);
break;
case NSE_TYPE_WRITE:
- first_ev_next(nse, &nse->iod->first_write);
+ first_ev_next(nse, &nse->iod->first_write, 0);
break;
#if HAVE_PCAP
case NSE_TYPE_PCAP_READ:
- first_ev_next(nse, &nse->iod->first_read);
- first_ev_next(nse, &nse->iod->first_pcap_read);
+ first_ev_next(nse, &nse->iod->first_read, 0);
+ first_ev_next(nse, &nse->iod->first_pcap_read, 1);
break;
#endif
@@ -181,8 +186,9 @@ void update_first_events(msevent *nse) {
int nsock_event_cancel(nsock_pool ms_pool, nsock_event_id id, int notify) {
mspool *nsp = (mspool *)ms_pool;
enum nse_type type;
- gh_list *event_list = NULL, *event_list2 = NULL;
- gh_list_elem *current, *next;
+ unsigned int i;
+ gh_list_t *event_list = NULL, *event_list2 = NULL;
+ gh_lnode_t *current, *next;
msevent *nse = NULL;
assert(nsp);
@@ -205,8 +211,15 @@ int nsock_event_cancel(nsock_pool ms_pool, nsock_event_id id, int notify) {
break;
case NSE_TYPE_TIMER:
- event_list = &nsp->timer_events;
- break;
+ for (i = 0; i < gh_heap_count(&nsp->expirables); i++) {
+ gh_hnode_t *hnode;
+
+ hnode = gh_heap_find(&nsp->expirables, i);
+ nse = container_of(hnode, msevent, expire);
+ if (nse->id == id)
+ return msevent_cancel(nsp, nse, NULL, NULL, notify);
+ }
+ return 0;
#if HAVE_PCAP
case NSE_TYPE_PCAP_READ:
@@ -220,18 +233,18 @@ int nsock_event_cancel(nsock_pool ms_pool, nsock_event_id id, int notify) {
}
/* Now we try to find the event in the list */
- for (current = GH_LIST_FIRST_ELEM(event_list); current != NULL; current = next) {
- next = GH_LIST_ELEM_NEXT(current);
- nse = (msevent *)GH_LIST_ELEM_DATA(current);
+ for (current = gh_list_first_elem(event_list); current != NULL; current = next) {
+ next = gh_lnode_next(current);
+ nse = lnode_msevent(current);
if (nse->id == id)
break;
}
- if (current == NULL && event_list2){
+ if (current == NULL && event_list2) {
event_list = event_list2;
- for (current = GH_LIST_FIRST_ELEM(event_list); current != NULL; current = next) {
- next = GH_LIST_ELEM_NEXT(current);
- nse = (msevent *)GH_LIST_ELEM_DATA(current);
+ for (current = gh_list_first_elem(event_list); current != NULL; current = next) {
+ next = gh_lnode_next(current);
+ nse = lnode_msevent2(current);
if (nse->id == id)
break;
}
@@ -249,7 +262,8 @@ int nsock_event_cancel(nsock_pool ms_pool, nsock_event_id id, int notify) {
* element in event_list which holds the event. Pass a nonzero for notify if
* you want the program owning the event to be notified that it has been
* cancelled */
-int msevent_cancel(mspool *nsp, msevent *nse, gh_list *event_list, gh_list_elem *elem, int notify) {
+int msevent_cancel(mspool *nsp, msevent *nse, gh_list_t *event_list,
+ gh_lnode_t *elem, int notify) {
if (nse->event_done) {
/* This event has already been marked for death somewhere else -- it will be
* gone soon (and if we try to kill it now all hell will break loose due to
@@ -257,7 +271,8 @@ int msevent_cancel(mspool *nsp, msevent *nse, gh_list *event_list, gh_list_elem
return 0;
}
- nsock_log_info(nsp, "msevent_cancel on event #%li (type %s)", nse->id, nse_type2str(nse->type));
+ nsock_log_info(nsp, "msevent_cancel on event #%li (type %s)",
+ nse->id, nse_type2str(nse->type));
/* Now that we found the event... we go through the motions of cleanly
* cancelling it */
@@ -290,8 +305,16 @@ int msevent_cancel(mspool *nsp, msevent *nse, gh_list *event_list, gh_list_elem
}
assert(nse->event_done);
- update_first_events(nse);
- gh_list_remove_elem(event_list, elem);
+
+ if (nse->timeout.tv_sec)
+ gh_heap_remove(&nsp->expirables, &nse->expire);
+
+ if (event_list) {
+ update_first_events(nse);
+ gh_list_remove(event_list, elem);
+ }
+
+ gh_list_append(&nsp->free_events, &nse->nodeq_io);
nsock_log_debug_all(nsp, "NSE #%lu: Removing event from list", nse->id);
@@ -309,14 +332,14 @@ int msevent_cancel(mspool *nsp, msevent *nse, gh_list *event_list, gh_list_elem
if (((mspcap *)nse->iod->pcap)->pcap_desc >= 0 && event_list == &nsp->read_events) {
/* event is done, list is read_events and we're in BSD_HACK mode. So unlink
* event from pcap_read_events */
- gh_list_remove(&nsp->pcap_read_events, nse);
+ gh_list_remove(&nsp->pcap_read_events, &nse->nodeq_pcap);
nsock_log_debug_all(nsp, "PCAP NSE #%lu: Removing event from PCAP_READ_EVENTS", nse->id);
}
if (((mspcap *)nse->iod->pcap)->pcap_desc >= 0 && event_list == &nsp->pcap_read_events) {
/* event is done, list is read_events and we're in BSD_HACK mode.
* So unlink event from read_events */
- gh_list_remove(&nsp->read_events, nse);
+ gh_list_remove(&nsp->read_events, &nse->nodeq_io);
nsock_log_debug_all(nsp, "PCAP NSE #%lu: Removing event from READ_EVENTS", nse->id);
}
@@ -392,6 +415,7 @@ enum nse_type get_event_id_type(nsock_event_id event_id) {
msevent *msevent_new(mspool *nsp, enum nse_type type, msiod *msiod, int timeout_msecs,
nsock_ev_handler handler, void *userdata) {
msevent *nse;
+ gh_lnode_t *lnode;
/* Bring us up to date for the timeout calculation. */
gettimeofday(&nsock_tod, NULL);
@@ -402,14 +426,18 @@ msevent *msevent_new(mspool *nsp, enum nse_type type, msiod *msiod, int timeout_
}
/* First we check if one is available from the free list ... */
- nse = (msevent *)gh_list_pop(&nsp->free_events);
- if (!nse)
+ lnode = gh_list_pop(&nsp->free_events);
+ if (!lnode)
nse = (msevent *)safe_malloc(sizeof(msevent));
+ else
+ nse = lnode_msevent(lnode);
+
memset(nse, 0, sizeof(msevent));
nse->id = get_new_event_id(nsp, type);
nse->type = type;
nse->status = NSE_STATUS_NONE;
+ gh_hnode_invalidate(&nse->expire);
#if HAVE_OPENSSL
nse->sslinfo.ssl_desire = SSL_ERROR_NONE;
#endif
@@ -453,7 +481,6 @@ msevent *msevent_new(mspool *nsp, enum nse_type type, msiod *msiod, int timeout_
* has ALREADY been decremented (done during msevent_dispatch_and_delete) -- so
* remember to do this if you call msevent_delete() directly */
void msevent_delete(mspool *nsp, msevent *nse) {
-
if (nse->iod == NULL)
nsock_log_debug(nsp, "msevent_delete (IOD #NULL) (EID #%li)", nse->id);
else
@@ -471,7 +498,7 @@ void msevent_delete(mspool *nsp, msevent *nse) {
#endif
/* Now we add the event back into the free pool */
- gh_list_prepend(&nsp->free_events, nse);
+ nse->event_done = 1;
}
diff --git a/nsock/src/nsock_internal.h b/nsock/src/nsock_internal.h
index 0bc4364d7..a964d132b 100644
--- a/nsock/src/nsock_internal.h
+++ b/nsock/src/nsock_internal.h
@@ -73,6 +73,7 @@
#endif
#include "gh_list.h"
+#include "gh_heap.h"
#include "filespace.h"
#include "nsock.h" /* The public interface -- I need it for some enum defs */
#include "nsock_ssl.h"
@@ -165,28 +166,21 @@ typedef struct {
void *engine_data;
/* Active network events */
- gh_list connect_events;
- gh_list read_events;
- gh_list write_events;
- gh_list timer_events;
+ gh_list_t connect_events;
+ gh_list_t read_events;
+ gh_list_t write_events;
#if HAVE_PCAP
- gh_list pcap_read_events;
+ gh_list_t pcap_read_events;
#endif
+ gh_heap_t expirables;
/* Active iods and related lists of events */
- gh_list active_iods;
+ gh_list_t active_iods;
/* msiod structures that have been freed for reuse */
- gh_list free_iods;
+ gh_list_t free_iods;
/* When an event is deleted, we stick it here for later reuse */
- gh_list free_events;
-
- /* The soonest time that either a timer event goes
- * off or a read/write/connect expires. It is
- * updated each main loop round as we go through
- * the events. It is an absolute time. If there
- * are no events, tv_sec is 0 */
- struct timeval next_ev;
+ gh_list_t free_events;
/* Number of events pending (total) on all lists */
int events_pending;
@@ -236,11 +230,11 @@ typedef struct {
int events_pending;
/* Pending events */
- gh_list_elem *first_connect;
- gh_list_elem *first_read;
- gh_list_elem *first_write;
+ gh_lnode_t *first_connect;
+ gh_lnode_t *first_read;
+ gh_lnode_t *first_write;
#if HAVE_PCAP
- gh_list_elem *first_pcap_read;
+ gh_lnode_t *first_pcap_read;
#endif
int readsd_count;
@@ -273,7 +267,7 @@ typedef struct {
/* The mspool keeps track of msiods that have been allocated so that it can
* destroy them if the msp is deleted. This pointer makes it easy to remove
* this msiod from the allocated list when necessary */
- gh_list_elem *entry_in_nsp_active_iods;
+ gh_lnode_t nodeq;
#define IOD_REGISTERED 0x01
#define IOD_PROCESSED 0x02 /* internally used by engine_kqueue.c */
@@ -354,6 +348,18 @@ typedef struct {
/* The handler to call when event is complete */
nsock_ev_handler handler;
+ /* slot in the expirable binheap */
+ gh_hnode_t expire;
+
+ /* For some reasons (see nsock_pcap.c) we register pcap events as both read
+ * and pcap_read events when in PCAP_BSD_SELECT_HACK mode. We then need two
+ * gh_lnode_t handles. To make code simpler, we _always_ use _nodeq_pcap for
+ * pcap_read events and _nodeq_io for the other ones.
+ * When not in PCAP_BSD_SELECT_HACK mode we define both handles as members
+ * of an union to optimize memory footprint. */
+ gh_lnode_t nodeq_io;
+ gh_lnode_t nodeq_pcap;
+
/* Optional (NULL if unset) pointer to pass to the handler */
void *userdata;
@@ -439,7 +445,7 @@ msevent *msevent_new(mspool *nsp, enum nse_type type, msiod *msiod, int timeout_
* is the list element in event_list which holds the event. Pass a nonzero for
* notify if you want the program owning the event to be notified that it has
* been cancelled */
-int msevent_cancel(mspool *nsp, msevent *nse, gh_list *event_list, gh_list_elem *elem, int notify);
+int msevent_cancel(mspool *nsp, msevent *nse, gh_list_t *event_list, gh_lnode_t *elem, int notify);
/* Adjust various statistics, dispatches the event handler (if notify is
* nonzero) and then deletes the event. This function does NOT delete the event
@@ -486,5 +492,23 @@ void nsock_trace_handler_callback(mspool *ms, msevent *nse);
void nsi_set_ssl_session(msiod *iod, SSL_SESSION *sessid);
#endif
+static inline msevent *next_expirable_event(mspool *nsp) {
+ gh_hnode_t *hnode;
+
+ hnode = gh_heap_min(&nsp->expirables);
+ if (!hnode)
+ return NULL;
+
+ return container_of(hnode, msevent, expire);
+}
+
+static inline msevent *lnode_msevent(gh_lnode_t *lnode) {
+ return container_of(lnode, msevent, nodeq_io);
+}
+
+static inline msevent *lnode_msevent2(gh_lnode_t *lnode) {
+ return container_of(lnode, msevent, nodeq_pcap);
+}
+
#endif /* NSOCK_INTERNAL_H */
diff --git a/nsock/src/nsock_iod.c b/nsock/src/nsock_iod.c
index 32dacf4d3..416c8035b 100644
--- a/nsock/src/nsock_iod.c
+++ b/nsock/src/nsock_iod.c
@@ -87,12 +87,15 @@ nsock_iod nsi_new(nsock_pool nsockp, void *userdata) {
* will be destroyed when the nsi is destroyed. */
nsock_iod nsi_new2(nsock_pool nsockp, int sd, void *userdata) {
mspool *nsp = (mspool *)nsockp;
+ gh_lnode_t *lnode;
msiod *nsi;
- nsi = (msiod *)gh_list_pop(&nsp->free_iods);
- if (!nsi) {
+ lnode = gh_list_pop(&nsp->free_iods);
+ if (!lnode) {
nsi = (msiod *)safe_malloc(sizeof(msiod));
memset(nsi, 0, sizeof(*nsi));
+ } else {
+ nsi = container_of(lnode, msiod, nodeq);
}
if (sd == -1) {
@@ -149,7 +152,7 @@ nsock_iod nsi_new2(nsock_pool nsockp, int sd, void *userdata) {
nsi->id = nsp->next_iod_serial++;
/* The nsp keeps track of active msiods so it can delete them if it is deleted */
- nsi->entry_in_nsp_active_iods = gh_list_append(&nsp->active_iods, nsi);
+ gh_list_append(&nsp->active_iods, &nsi->nodeq);
nsock_log_info(nsp, "nsi_new (IOD #%lu)", nsi->id);
@@ -169,10 +172,10 @@ int socket_count_zero(msiod *iod, mspool *ms);
* quiit the program) */
void nsi_delete(nsock_iod nsockiod, int pending_response) {
msiod *nsi = (msiod *)nsockiod;
- gh_list_elem *evlist_ar[3];
- gh_list *corresp_list[3];
+ gh_lnode_t *evlist_ar[3];
+ gh_list_t *corresp_list[3];
int i;
- gh_list_elem *current, *next;
+ gh_lnode_t *current, *next;
assert(nsi);
@@ -207,8 +210,8 @@ void nsi_delete(nsock_iod nsockiod, int pending_response) {
for (current = evlist_ar[i]; current != NULL; current = next) {
msevent *nse;
- next = GH_LIST_ELEM_NEXT(current);
- nse = (msevent *)GH_LIST_ELEM_DATA(current);
+ next = gh_lnode_next(current);
+ nse = lnode_msevent(current);
/* we're done with this list of events for the current IOD */
if (nse->iod != nsi)
diff --git a/nsock/src/nsock_pcap.c b/nsock/src/nsock_pcap.c
index 484d85670..2a0ac5f89 100644
--- a/nsock/src/nsock_pcap.c
+++ b/nsock/src/nsock_pcap.c
@@ -411,12 +411,15 @@ int do_actual_pcap_read(msevent *nse) {
n = (nsock_pcap *)fs_str(&(nse->iobuf));
n->packet = (unsigned char *)fs_str(&(nse->iobuf)) + sizeof(npp);
- nsock_log_debug_all(nse->iod->nsp, "PCAP %s READ (IOD #%li) (EID #%li) size=%i",
+ nsock_log_debug_all(nse->iod->nsp,
+ "PCAP %s READ (IOD #%li) (EID #%li) size=%i",
__func__, nse->iod->id, nse->id, pkt_header->caplen);
- return 1;
+ rc = 1;
+ break;
case 0: /* timeout */
- return(0);
+ rc = 0;
+ break;
case -1: /* error */
fatal("pcap_next_ex() fatal error while reading from pcap: %s\n",
@@ -427,7 +430,8 @@ int do_actual_pcap_read(msevent *nse) {
default:
fatal("Unexpected return code from pcap_next_ex! (%d)\n", rc);
}
- return 0;
+
+ return rc;
}
void nse_readpcap(nsock_event nsev, const unsigned char **l2_data, size_t *l2_len,
diff --git a/nsock/src/nsock_pool.c b/nsock/src/nsock_pool.c
index d5f205faa..eb8a8d5be 100644
--- a/nsock/src/nsock_pool.c
+++ b/nsock/src/nsock_pool.c
@@ -132,6 +132,16 @@ void nsp_setdevice(nsock_pool nsp, const char *device) {
mt->device = device;
}
+static int expirable_cmp(gh_hnode_t *n1, gh_hnode_t *n2) {
+ msevent *nse1;
+ msevent *nse2;
+
+ nse1 = container_of(n1, msevent, expire);
+ nse2 = container_of(n2, msevent, expire);
+
+ return (TIMEVAL_BEFORE(nse1->timeout, nse2->timeout)) ? 1 : 0;
+}
+
/* And here is how you create an nsock_pool. This allocates, initializes, and
* returns an nsock_pool event aggregator. In the case of error, NULL will be
* returned. If you do not wish to immediately associate any userdata, pass in
@@ -167,8 +177,9 @@ nsock_pool nsp_new(void *userdata) {
#if HAVE_PCAP
gh_list_init(&nsp->pcap_read_events);
#endif
- /* initialize timer list */
- gh_list_init(&nsp->timer_events);
+
+ /* initialize timer heap */
+ gh_heap_init(&nsp->expirables, expirable_cmp);
/* initialize the list of IODs */
gh_list_init(&nsp->active_iods);
@@ -199,12 +210,11 @@ void nsp_delete(nsock_pool ms_pool) {
msevent *nse;
msiod *nsi;
int i;
- gh_list_elem *current, *next;
- gh_list *event_lists[] = {
+ gh_lnode_t *current, *next;
+ gh_list_t *event_lists[] = {
&nsp->connect_events,
&nsp->read_events,
&nsp->write_events,
- &nsp->timer_events,
#if HAVE_PCAP
&nsp->pcap_read_events,
#endif
@@ -215,13 +225,24 @@ void nsp_delete(nsock_pool ms_pool) {
/* First I go through all the events sending NSE_STATUS_KILL */
for (i = 0; event_lists[i] != NULL; i++) {
- while (GH_LIST_COUNT(event_lists[i]) > 0) {
- nse = (msevent *)gh_list_pop(event_lists[i]);
+ while (gh_list_count(event_lists[i]) > 0) {
+ gh_lnode_t *lnode = gh_list_pop(event_lists[i]);
+
+ assert(lnode);
+
+#if HAVE_PCAP
+ if (event_lists[i] == &nsp->pcap_read_events)
+ nse = lnode_msevent2(lnode);
+ else
+#endif
+ nse = lnode_msevent(lnode);
assert(nse);
+
nse->status = NSE_STATUS_KILL;
nsock_trace_handler_callback(nsp, nse);
nse->handler(nsp, nse, nse->userdata);
+
if (nse->iod) {
nse->iod->events_pending--;
assert(nse->iod->events_pending >= 0);
@@ -231,22 +252,45 @@ void nsp_delete(nsock_pool ms_pool) {
gh_list_free(event_lists[i]);
}
+ /* Kill timers too, they're not in event lists */
+ while (gh_heap_count(&nsp->expirables) > 0) {
+ gh_hnode_t *hnode;
+
+ hnode = gh_heap_pop(&nsp->expirables);
+ nse = container_of(hnode, msevent, expire);
+
+ if (nse->type == NSE_TYPE_TIMER) {
+ nse->status = NSE_STATUS_KILL;
+ nsock_trace_handler_callback(nsp, nse);
+ nse->handler(nsp, nse, nse->userdata);
+ msevent_delete(nsp, nse);
+ gh_list_append(&nsp->free_events, &nse->nodeq_io);
+ }
+ }
+
+ gh_heap_free(&nsp->expirables);
+
/* foreach msiod */
- for (current = GH_LIST_FIRST_ELEM(&nsp->active_iods); current != NULL; current = next) {
- next = GH_LIST_ELEM_NEXT(current);
- nsi = (msiod *)GH_LIST_ELEM_DATA(current);
+ for (current = gh_list_first_elem(&nsp->active_iods);
+ current != NULL;
+ current = next) {
+ next = gh_lnode_next(current);
+ nsi = container_of(current, msiod, nodeq);
+
nsi_delete(nsi, NSOCK_PENDING_ERROR);
- gh_list_remove_elem(&nsp->active_iods, current);
- gh_list_prepend(&nsp->free_iods, nsi);
+ gh_list_remove(&nsp->active_iods, current);
+ gh_list_prepend(&nsp->free_iods, &nsi->nodeq);
}
/* Now we free all the memory in the free iod list */
- while ((nsi = (msiod *)gh_list_pop(&nsp->free_iods))) {
+ while ((current = gh_list_pop(&nsp->free_iods))) {
+ nsi = container_of(current, msiod, nodeq);
free(nsi);
}
- while ((nse = (msevent *)gh_list_pop(&nsp->free_events))) {
+ while ((current = gh_list_pop(&nsp->free_events))) {
+ nse = lnode_msevent(current);
free(nse);
}
diff --git a/nsock/src/nsock_proxy.c b/nsock/src/nsock_proxy.c
index 8fddd838c..48508698f 100644
--- a/nsock/src/nsock_proxy.c
+++ b/nsock/src/nsock_proxy.c
@@ -103,7 +103,7 @@ int nsock_proxychain_new(const char *proxystr, nsock_proxychain *chain, nsock_po
parser = proxy_parser_new(proxystr);
while (!parser->done) {
- gh_list_append(&pxc->nodes, parser->value);
+ gh_list_append(&pxc->nodes, &parser->value->nodeq);
proxy_parser_next(parser);
}
proxy_parser_delete(parser);
@@ -124,9 +124,12 @@ void nsock_proxychain_delete(nsock_proxychain chain) {
struct proxy_chain *pchain = (struct proxy_chain *)chain;
if (pchain) {
- struct proxy_node *node;
+ gh_lnode_t *lnode;
- while ((node = (struct proxy_node *)gh_list_pop(&pchain->nodes)) != NULL) {
+ while ((lnode = gh_list_pop(&pchain->nodes)) != NULL) {
+ struct proxy_node *node;
+
+ node = container_of(lnode, struct proxy_node, nodeq);
node->spec->ops->node_delete(node);
}
@@ -147,7 +150,6 @@ int nsp_set_proxychain(nsock_pool nspool, nsock_proxychain chain) {
return 1;
}
-
struct proxy_chain_context *proxy_chain_context_new(nsock_pool nspool) {
mspool *nsp = (mspool *)nspool;
struct proxy_chain_context *ctx;
@@ -155,7 +157,9 @@ struct proxy_chain_context *proxy_chain_context_new(nsock_pool nspool) {
ctx = (struct proxy_chain_context *)safe_malloc(sizeof(struct proxy_chain_context));
ctx->px_chain = nsp->px_chain;
ctx->px_state = PROXY_STATE_INITIAL;
- ctx->px_current = GH_LIST_FIRST_ELEM(&nsp->px_chain->nodes);
+ ctx->px_current = container_of(gh_list_first_elem(&nsp->px_chain->nodes),
+ struct proxy_node,
+ nodeq);
return ctx;
}
@@ -437,7 +441,7 @@ void nsock_proxy_ev_dispatch(nsock_pool nspool, nsock_event nsevent, void *udata
if (nse->status == NSE_STATUS_SUCCESS) {
struct proxy_node *current;
- current = proxy_ctx_node_current(nse->iod->px_ctx);
+ current = nse->iod->px_ctx->px_current;
assert(current);
current->spec->ops->handler(nspool, nsevent, udata);
} else {
diff --git a/nsock/src/nsock_proxy.h b/nsock/src/nsock_proxy.h
index fdd865aea..33d291dc7 100644
--- a/nsock/src/nsock_proxy.h
+++ b/nsock/src/nsock_proxy.h
@@ -110,11 +110,12 @@ struct proxy_node {
size_t sslen;
unsigned short port;
char *nodestr; /* used for log messages */
+ gh_lnode_t nodeq;
};
/* Ordered list of proxy nodes, as specified in the proxy specification string. */
struct proxy_chain {
- gh_list nodes;
+ gh_list_t nodes;
};
/* IOD-specific context. For each IOD we establish a tunnel through the chain of
@@ -123,7 +124,7 @@ struct proxy_chain_context {
const struct proxy_chain *px_chain;
/* Nodes iterator in px_chain->nodes */
- gh_list_elem *px_current;
+ struct proxy_node *px_current;
/* Current node connection state. */
enum nsock_proxy_state px_state;
@@ -153,18 +154,17 @@ struct proxy_spec {
/* ------------------- UTIL FUNCTIONS ------------------- */
int proxy_resolve(const char *host, struct sockaddr *addr, size_t *addrlen);
-static inline struct proxy_node *proxy_ctx_node_current(struct proxy_chain_context *ctx) {
- return (struct proxy_node *)GH_LIST_ELEM_DATA(ctx->px_current);
-}
-
static inline struct proxy_node *proxy_ctx_node_next(struct proxy_chain_context *ctx) {
- gh_list_elem *next;
+ gh_lnode_t *next;
- next = GH_LIST_ELEM_NEXT(ctx->px_current);
- if (next)
- return (struct proxy_node *)GH_LIST_ELEM_DATA(next);
+ assert(ctx);
+ assert(ctx->px_current);
- return NULL;
+ next = gh_lnode_next(&ctx->px_current->nodeq);
+ if (!next)
+ return NULL;
+
+ return container_of(next, struct proxy_node, nodeq);
}
diff --git a/nsock/src/proxy_http.c b/nsock/src/proxy_http.c
index 17c61159a..1972bac71 100644
--- a/nsock/src/proxy_http.c
+++ b/nsock/src/proxy_http.c
@@ -153,19 +153,18 @@ static int handle_state_tcp_connected(mspool *nsp, msevent *nse, void *udata) {
/* TODO string check!! */
if (!((reslen >= 15) && strstr(res, "200 OK"))) {
- struct proxy_node *node;
+ struct proxy_node *node = px_ctx->px_current;
- node = proxy_ctx_node_current(px_ctx);
nsock_log_debug(nsp, "Connection refused from proxy %s", node->nodestr);
return -EINVAL;
}
px_ctx->px_state = PROXY_STATE_HTTP_TUNNEL_ESTABLISHED;
- if (px_ctx->px_current->next == NULL) {
+ if (proxy_ctx_node_next(px_ctx) == NULL) {
forward_event(nsp, nse, udata);
} else {
- px_ctx->px_current = px_ctx->px_current->next;
+ px_ctx->px_current = proxy_ctx_node_next(px_ctx);
px_ctx->px_state = PROXY_STATE_INITIAL;
nsock_proxy_ev_dispatch(nsp, nse, udata);
}
diff --git a/nsock/src/proxy_socks4.c b/nsock/src/proxy_socks4.c
index f9d9f4f4c..2cb4e79dc 100644
--- a/nsock/src/proxy_socks4.c
+++ b/nsock/src/proxy_socks4.c
@@ -184,9 +184,8 @@ static int handle_state_tcp_connected(mspool *nsp, msevent *nse, void *udata) {
res = nse_readbuf(nse, &reslen);
if (!(reslen == 8 && res[1] == 90)) {
- struct proxy_node *node;
+ struct proxy_node *node = px_ctx->px_current;
- node = proxy_ctx_node_current(px_ctx);
nsock_log_debug(nsp, "Ignoring invalid socks4 reply from proxy %s",
node->nodestr);
return -EINVAL;
@@ -194,10 +193,10 @@ static int handle_state_tcp_connected(mspool *nsp, msevent *nse, void *udata) {
px_ctx->px_state = PROXY_STATE_SOCKS4_TUNNEL_ESTABLISHED;
- if (px_ctx->px_current->next == NULL) {
+ if (proxy_ctx_node_next(px_ctx) == NULL) {
forward_event(nsp, nse, udata);
} else {
- px_ctx->px_current = px_ctx->px_current->next;
+ px_ctx->px_current = proxy_ctx_node_next(px_ctx);
px_ctx->px_state = PROXY_STATE_INITIAL;
nsock_proxy_ev_dispatch(nsp, nse, udata);
}
diff --git a/nsock/tests/Makefile b/nsock/tests/Makefile
index 8d0a29765..360c8ef1f 100644
--- a/nsock/tests/Makefile
+++ b/nsock/tests/Makefile
@@ -16,7 +16,8 @@ SRC = tests_main.c \
timer.c \
logs.c \
connect.c \
- ghlists.c
+ ghlists.c \
+ ghheaps.c
OBJ = $(SRC:.c=.o)
diff --git a/nsock/tests/ghheaps.c b/nsock/tests/ghheaps.c
new file mode 100644
index 000000000..a1478abde
--- /dev/null
+++ b/nsock/tests/ghheaps.c
@@ -0,0 +1,150 @@
+/*
+ * Nsock regression test suite
+ * Same license as nmap -- see http://nmap.org/book/man-legal.html
+ */
+
+#include "test-common.h"
+#include "../src/gh_heap.h"
+#include
+#include
+
+
+#define HEAP_COUNT 1
+
+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);
+ 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
index 5384d112f..1cacae224 100644
--- a/nsock/tests/ghlists.c
+++ b/nsock/tests/ghlists.c
@@ -9,138 +9,167 @@
#include
-#define INT2PTR(i) ((void *)(intptr_t)(i))
-#define PTR2INT(p) ((intptr_t)(void *)(p))
-
#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 lists[LIST_COUNT];
- gh_list_elem *current, *next;
+ 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++)
+ for (i = 0; i < LIST_COUNT; i++)
gh_list_init(&lists[i]);
- for (num=25000; num < 50000; num++) {
- for (i=0; i < LIST_COUNT; i++) {
- gh_list_append(&lists[i], INT2PTR(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=24999; num >= 0; num--) {
- for (i=0; i < LIST_COUNT; i++) {
- gh_list_prepend(&lists[i], INT2PTR(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 < 50000; num++) {
- for (i=0; i < LIST_COUNT; i++) {
- ret = PTR2INT(gh_list_pop(&lists[i]));
+ 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++) {
- ret = PTR2INT(gh_list_pop(&lists[i]));
- if (ret != 0) {
+ 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=24999; num >= 0; num--) {
- for (i=0; i < LIST_COUNT; i++) {
- gh_list_prepend(&lists[i], INT2PTR(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=25000; num < 50000; num++) {
- for (i=0; i < LIST_COUNT; i++) {
- gh_list_append(&lists[i], INT2PTR(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 < 50000; num++) {
+ for (num = 0; num < ELT_COUNT; num++) {
for (i=0; i < LIST_COUNT; i++) {
- ret = PTR2INT(gh_list_pop(&lists[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=25000; num < 50000; num++) {
- for (i=0; i < LIST_COUNT; i++) {
- gh_list_append(&lists[i], INT2PTR(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=24999; num >= 0; num--) {
- for (i=0; i < LIST_COUNT; i++) {
- gh_list_prepend(&lists[i], INT2PTR(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 < 50000; num++) {
- for (i=0; i < LIST_COUNT; i++) {
- ret = PTR2INT(gh_list_pop(&lists[i]));
+ 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=24999; num >= 0; num--) {
- for (i=0; i < LIST_COUNT; i++) {
- gh_list_prepend(&lists[i], INT2PTR(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=25000; num < 50000; num++) {
- for (i=0; i < LIST_COUNT; i++) {
- gh_list_append(&lists[i], INT2PTR(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) {
+ for (i = 0; i < LIST_COUNT; i++) {
+ num = 0;
+
+ for (current = gh_list_first_elem(&lists[i]); current; current = next) {
int k;
- next = GH_LIST_ELEM_NEXT(current);
- k = PTR2INT(GH_LIST_ELEM_DATA(current));
+ 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_elem(&lists[i], current);
+ gh_list_remove(&lists[i], current);
+ delnode(current);
num++;
}
- if (num != 50000) {
- fprintf(stderr, "Number is %d, even though %d was expected", num, 50000);
+ 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) {
+ 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]));
+ gh_list_count(&lists[i]));
return -EINVAL;
}
}
- for (i=0; i < LIST_COUNT; i++) {
+ for (i = 0; i < LIST_COUNT; i++) {
+ while ((current = gh_list_pop(&lists[i])) != NULL)
+ delnode(current);
+
gh_list_free(&lists[i]);
}
diff --git a/nsock/tests/test-common.h b/nsock/tests/test-common.h
index 832474493..93a6d3741 100644
--- a/nsock/tests/test-common.h
+++ b/nsock/tests/test-common.h
@@ -16,6 +16,12 @@
#include
+#if !defined(container_of)
+#define container_of(ptr, type, member) \
+ ((type *)((char *)(ptr)-(char *)(&((type *)0)->member)))
+#endif
+
+
#define PORT_UDP 55234
#define PORT_TCP 55235
#define PORT_TCPSSL 55236
diff --git a/nsock/tests/tests_main.c b/nsock/tests/tests_main.c
index 0de198317..dfdf74145 100644
--- a/nsock/tests/tests_main.c
+++ b/nsock/tests/tests_main.c
@@ -31,6 +31,8 @@ extern const struct test_case TestLogLevels;
extern const struct test_case TestErrLevels;
extern const struct test_case TestConnectTCP;
extern const struct test_case TestGHLists;
+extern const struct test_case TestGHHeaps;
+extern const struct test_case TestHeapOrdering;
static const struct test_case *TestCases[] = {
@@ -45,9 +47,13 @@ static const struct test_case *TestCases[] = {
&TestConnectTCP,
/* ---- ghlists.c */
&TestGHLists,
+ /* ---- ghheaps.c */
+ &TestGHHeaps,
+ &TestHeapOrdering,
NULL
};
+
static int test_case_run(const struct test_case *test) {
int rc;
void *tdata = NULL;