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;