diff --git a/Makefile.in b/Makefile.in index 757e6bd6..d36dd611 100644 --- a/Makefile.in +++ b/Makefile.in @@ -18,7 +18,7 @@ VPATH = @srcdir@ default: cd src && $(MAKE) $@ -all : default +all : default install: all cd src && $(MAKE) $@ @@ -28,7 +28,7 @@ uninstall: cd src && $(MAKE) $@ cd doc && $(MAKE) $@ -doc: +doc: cd doc && $(MAKE) $@ example: @@ -37,6 +37,9 @@ example: test: cd src && $(MAKE) $@ +example: + cd src && $(MAKE) $@ + clean: cd src && $(MAKE) $@ cd doc && $(MAKE) $@ @@ -53,7 +56,7 @@ $(distdir).tar.gz: $(distdir) tar chof - $(distdir) | gzip -9 -c > $@ rm -rf $(distdir) -$(distdir): +$(distdir): mkdir -p $(distdir)/src mkdir -p $(distdir)/src/getdns mkdir -p $(distdir)/src/test @@ -68,6 +71,7 @@ $(distdir): cp $(srcdir)/ltmain.sh $(distdir) cp $(srcdir)/src/*.in $(distdir)/src cp $(srcdir)/src/*.[ch] $(distdir)/src + cp $(srcdir)/src/extension/*.[ch] $(distdir)/src/extension cp $(srcdir)/src/getdns/*.in $(distdir)/src/getdns cp $(srcdir)/src/getdns/*.h $(distdir)/src/getdns cp $(srcdir)/src/test/Makefile.in $(distdir)/src/test @@ -98,7 +102,7 @@ distcheck: $(distdir).tar.gz Makefile: Makefile.in config.status ./config.status $@ - + configure.status: configure ./config.status --recheck diff --git a/configure b/configure index 47c1a3f1..bc2b996f 100755 --- a/configure +++ b/configure @@ -2,7 +2,7 @@ # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for getdns 0.1.0. # -# Report bugs to . +# Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -275,7 +275,7 @@ fi $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and -$0: melinda.shore@nomountain.net about your system, +$0: stub-resolver@verisignlabs.com about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." @@ -592,7 +592,7 @@ PACKAGE_NAME='getdns' PACKAGE_TARNAME='getdns' PACKAGE_VERSION='0.1.0' PACKAGE_STRING='getdns 0.1.0' -PACKAGE_BUGREPORT='melinda.shore@nomountain.net' +PACKAGE_BUGREPORT='stub-resolver@verisignlabs.com' PACKAGE_URL='' ac_unique_file="src/getdns/getdns.h" @@ -634,6 +634,7 @@ ac_includes_default="\ ac_subst_vars='LTLIBOBJS LIBOBJS +have_libevent DOXYGEN INSTALL_DATA INSTALL_SCRIPT @@ -1386,7 +1387,7 @@ Some influential environment variables: Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. -Report bugs to . +Report bugs to . _ACEOF ac_status=$? fi @@ -10853,127 +10854,8 @@ fi # Checks for libraries. found_all_libs=1 -{ $as_echo "$as_me:${as_lineno-$LINENO}: Checking for dependencies libevent, ldns" >&5 -$as_echo "$as_me: Checking for dependencies libevent, ldns" >&6;} -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing event_loop" >&5 -$as_echo_n "checking for library containing event_loop... " >&6; } -if ${ac_cv_search_event_loop+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_func_search_save_LIBS=$LIBS -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char event_loop (); -int -main () -{ -return event_loop (); - ; - return 0; -} -_ACEOF -for ac_lib in '' event_core event; do - if test -z "$ac_lib"; then - ac_res="none required" - else - ac_res=-l$ac_lib - LIBS="-l$ac_lib $ac_func_search_save_LIBS" - fi - if ac_fn_c_try_link "$LINENO"; then : - ac_cv_search_event_loop=$ac_res -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext - if ${ac_cv_search_event_loop+:} false; then : - break -fi -done -if ${ac_cv_search_event_loop+:} false; then : - -else - ac_cv_search_event_loop=no -fi -rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_event_loop" >&5 -$as_echo "$ac_cv_search_event_loop" >&6; } -ac_res=$ac_cv_search_event_loop -if test "$ac_res" != no; then : - test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" - -else - -ac_cv_search_event_loop='' -LDFLAGS="$LDFLAGS -L/usr/local/lib/event" -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing event_loop" >&5 -$as_echo_n "checking for library containing event_loop... " >&6; } -if ${ac_cv_search_event_loop+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_func_search_save_LIBS=$LIBS -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char event_loop (); -int -main () -{ -return event_loop (); - ; - return 0; -} -_ACEOF -for ac_lib in '' event_core event; do - if test -z "$ac_lib"; then - ac_res="none required" - else - ac_res=-l$ac_lib - LIBS="-l$ac_lib $ac_func_search_save_LIBS" - fi - if ac_fn_c_try_link "$LINENO"; then : - ac_cv_search_event_loop=$ac_res -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext - if ${ac_cv_search_event_loop+:} false; then : - break -fi -done -if ${ac_cv_search_event_loop+:} false; then : - -else - ac_cv_search_event_loop=no -fi -rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_event_loop" >&5 -$as_echo "$ac_cv_search_event_loop" >&6; } -ac_res=$ac_cv_search_event_loop -if test "$ac_res" != no; then : - test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" - -else - found_all_libs=0 -fi - - -fi - +{ $as_echo "$as_me:${as_lineno-$LINENO}: Checking for dependencies ldns" >&5 +$as_echo "$as_me: Checking for dependencies ldns" >&6;} { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ldns_dname_new_frm_str in -lldns" >&5 $as_echo_n "checking for ldns_dname_new_frm_str in -lldns... " >&6; } if ${ac_cv_lib_ldns_ldns_dname_new_frm_str+:} false; then : @@ -11079,9 +10961,9 @@ fi found_libunbound=1 { $as_echo "$as_me:${as_lineno-$LINENO}: Checking for dependency libunbound" >&5 $as_echo "$as_me: Checking for dependency libunbound" >&6;} -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ub_ctx_set_event in -lunbound" >&5 -$as_echo_n "checking for ub_ctx_set_event in -lunbound... " >&6; } -if ${ac_cv_lib_unbound_ub_ctx_set_event+:} false; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ub_fd in -lunbound" >&5 +$as_echo_n "checking for ub_fd in -lunbound... " >&6; } +if ${ac_cv_lib_unbound_ub_fd+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -11095,27 +10977,27 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext #ifdef __cplusplus extern "C" #endif -char ub_ctx_set_event (); +char ub_fd (); int main () { -return ub_ctx_set_event (); +return ub_fd (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_unbound_ub_ctx_set_event=yes + ac_cv_lib_unbound_ub_fd=yes else - ac_cv_lib_unbound_ub_ctx_set_event=no + ac_cv_lib_unbound_ub_fd=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_unbound_ub_ctx_set_event" >&5 -$as_echo "$ac_cv_lib_unbound_ub_ctx_set_event" >&6; } -if test "x$ac_cv_lib_unbound_ub_ctx_set_event" = xyes; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_unbound_ub_fd" >&5 +$as_echo "$ac_cv_lib_unbound_ub_fd" >&6; } +if test "x$ac_cv_lib_unbound_ub_fd" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBUNBOUND 1 _ACEOF @@ -11128,7 +11010,7 @@ fi if test $found_libunbound == 0 then - as_fn_error $? "libunbound is missing or does not incude event code, you may need to patch your libunbound sources and rebuild. See the getdns build instructions" "$LINENO" 5 + as_fn_error $? "libunbound is missing." "$LINENO" 5 fi for ac_prog in doxygen @@ -11261,7 +11143,64 @@ _ACEOF esac -for ac_func in event_base_new event_base_free +have_libevent=1 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing event_loop" >&5 +$as_echo_n "checking for library containing event_loop... " >&6; } +if ${ac_cv_search_event_loop+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char event_loop (); +int +main () +{ +return event_loop (); + ; + return 0; +} +_ACEOF +for ac_lib in '' event_core event; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_event_loop=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_event_loop+:} false; then : + break +fi +done +if ${ac_cv_search_event_loop+:} false; then : + +else + ac_cv_search_event_loop=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_event_loop" >&5 +$as_echo "$ac_cv_search_event_loop" >&6; } +ac_res=$ac_cv_search_event_loop +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +$as_echo "#define HAVE_LIBEVENT 1" >>confdefs.h + + for ac_func in event_base_new event_base_free do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -11274,6 +11213,11 @@ fi done +else + have_libevent=0 +fi + + ac_config_files="$ac_config_files Makefile src/Makefile src/getdns/Makefile src/example/Makefile src/test/Makefile doc/Makefile" @@ -11849,7 +11793,7 @@ $config_headers Configuration commands: $config_commands -Report bugs to ." +Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 diff --git a/configure.ac b/configure.ac index eeb70675..9ff143de 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,7 @@ # AC_PREREQ([2.56]) -AC_INIT([getdns], [0.1.0], [melinda.shore@nomountain.net]) +AC_INIT([getdns], [0.1.0], [stub-resolver@verisignlabs.com]) AC_CONFIG_SRCDIR([src/getdns/getdns.h]) # AM_INIT_AUTOMAKE # LT_INIT @@ -128,12 +128,7 @@ fi # Checks for libraries. found_all_libs=1 -AC_MSG_NOTICE([Checking for dependencies libevent, ldns]) -AC_SEARCH_LIBS([event_loop], [event_core event], [], [ -ac_cv_search_event_loop='' -LDFLAGS="$LDFLAGS -L/usr/local/lib/event" -AC_SEARCH_LIBS([event_loop], [event_core event], [], [found_all_libs=0]) -]) +AC_MSG_NOTICE([Checking for dependencies ldns]) AC_CHECK_LIB([ldns], [ldns_dname_new_frm_str], [], [found_all_libs=0]) AC_CHECK_LIB([idn], [idna_to_ascii_8z], [], [found_all_libs=0]) @@ -142,14 +137,14 @@ then AC_MSG_ERROR([One more dependencies is missing]) fi -# break out libunbound from other libraries since we are currently using a +# break out libunbound from other libraries since we are currently using a # patch to the sources found_libunbound=1 AC_MSG_NOTICE([Checking for dependency libunbound]) -AC_CHECK_LIB([unbound], [ub_ctx_set_event], [], [found_libunbound=0]) +AC_CHECK_LIB([unbound], [ub_fd], [], [found_libunbound=0]) if test $found_libunbound == 0 then - AC_MSG_ERROR([libunbound is missing or does not incude event code, you may need to patch your libunbound sources and rebuild. See the getdns build instructions]) + AC_MSG_ERROR([libunbound is missing.]) fi AC_CHECK_PROGS([DOXYGEN], [doxygen]) @@ -167,16 +162,22 @@ AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_TYPE_UINT8_T -AC_CHECK_FUNCS([event_base_new event_base_free]) -AH_BOTTOM([ -#ifndef HAVE_EVENT_BASE_FREE -#define event_base_free(x) /* nop */ -#endif -#ifndef HAVE_EVENT_BASE_NEW -#define event_base_new event_init -#endif -]) +have_libevent=1 +AC_SEARCH_LIBS([event_loop], + [event_core event], + [AC_DEFINE([HAVE_LIBEVENT], [1], [Define to 1 to enable the libevent extension])] + [AC_CHECK_FUNCS([event_base_new event_base_free])] + [AH_BOTTOM([ + #ifndef HAVE_EVENT_BASE_FREE + #define event_base_free(x) /* nop */ + #endif + #ifndef HAVE_EVENT_BASE_NEW + #define event_base_new event_init + #endif + ])], + [have_libevent=0]) +AC_SUBST(have_libevent) AC_CONFIG_FILES([Makefile src/Makefile src/getdns/Makefile src/example/Makefile src/test/Makefile doc/Makefile]) if [ test -n "$DOXYGEN" ] then AC_CONFIG_FILES([src/Doxyfile]) diff --git a/src/Makefile.in b/src/Makefile.in index 0b9e55ab..bdea7157 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -20,6 +20,7 @@ exec_prefix = @exec_prefix@ bindir = @bindir@ libdir = @libdir@ includedir = @includedir@ +have_libevent = @have_libevent@ # datarootdir is here to please some checkers datarootdir=@datarootdir@ INSTALL = @INSTALL@ @@ -35,6 +36,10 @@ GETDNS_OBJ=sync.lo context.lo list.lo dict.lo convert.lo general.lo \ hostname.lo service.lo request-internal.lo validate_dnssec.lo \ util-internal.lo getdns_error.lo rr-dict.lo validation-chain.lo +ifeq ($(have_libevent),1) + GETDNS_OBJ += extension/libevent.lo +endif + .SUFFIXES: .c .o .a .lo .h .c.o: @@ -54,24 +59,31 @@ install: libgetdns.la $(INSTALL) -m 755 -d $(DESTDIR)$(includedir) $(INSTALL) -m 755 -d $(DESTDIR)$(includedir)/getdns $(INSTALL) -m 644 $(srcdir)/getdns/getdns.h $(DESTDIR)$(includedir)/getdns/getdns.h + ifeq ($(have_libevent),1) + $(INSTALL) -m 644 $(srcdir)/getdns/getdns_ext_libevent.h $(DESTDIR)$(includedir)/getdns/ + endif $(INSTALL) -m 755 -d $(DESTDIR)$(libdir) $(LIBTOOL) --mode=install cp libgetdns.la $(DESTDIR)$(libdir) $(LIBTOOL) --mode=finish $(DESTDIR)$(libdir) uninstall: - rm -f $(DESTDIR)$(includedir)/getdns/getdns.h + rm -rf $(DESTDIR)$(includedir)/getdns $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/libgetdns.la libgetdns.la: $(GETDNS_OBJ) $(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) -o $@ $(GETDNS_OBJ) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -test: libgetdns.la +test: cd test && $(MAKE) $@ +example: + cd example && $(MAKE) $@ + + clean: cd test && $(MAKE) $@ cd example && $(MAKE) $@ - rm -f *.o *.lo $(PROGRAMS) libgetdns.la + rm -f *.o *.lo extension/*.lo $(PROGRAMS) libgetdns.la rm -rf .libs distclean : clean diff --git a/src/config.h.in b/src/config.h.in index 94641fb9..8c55f7ef 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -15,6 +15,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H +/* Define to 1 to enable the libevent extension */ +#undef HAVE_LIBEVENT + /* Define to 1 if you have the `idn' library (-lidn). */ #undef HAVE_LIBIDN @@ -111,10 +114,10 @@ #undef uint8_t -#ifndef HAVE_EVENT_BASE_FREE -#define event_base_free(x) /* nop */ -#endif -#ifndef HAVE_EVENT_BASE_NEW -#define event_base_new event_init -#endif - + #ifndef HAVE_EVENT_BASE_FREE + #define event_base_free(x) /* nop */ + #endif + #ifndef HAVE_EVENT_BASE_NEW + #define event_base_new event_init + #endif + diff --git a/src/context.c b/src/context.c index 71b4d047..561a3447 100644 --- a/src/context.c +++ b/src/context.c @@ -35,17 +35,12 @@ */ #include "config.h" -#ifdef HAVE_EVENT2_EVENT_H -# include -#else -# include -#endif #include #include #include #include #include -#include +#include #include #include "context.h" @@ -64,11 +59,13 @@ static struct getdns_list *create_from_ldns_list(struct getdns_context *, ldns_rdf **, size_t); static getdns_return_t set_os_defaults(struct getdns_context *); static int transaction_id_cmp(const void *, const void *); +static int timeout_cmp(const void *, const void *); static void set_ub_string_opt(struct getdns_context *, char *, char *); static void set_ub_number_opt(struct getdns_context *, char *, uint16_t); static inline void clear_resolution_type_set_flag(struct getdns_context *, uint16_t); static void dispatch_updated(struct getdns_context *, uint16_t); static void cancel_dns_req(getdns_dns_req *); +static void cancel_outstanding_requests(struct getdns_context*, int); /* Stuff to make it compile pedantically */ #define UNUSED_PARAM(x) ((void)(x)) @@ -218,6 +215,9 @@ set_os_defaults(struct getdns_context *context) return GETDNS_RETURN_GOOD; } /* set_os_defaults */ +/* compare of transaction ids in DESCENDING order + so that 0 comes last +*/ static int transaction_id_cmp(const void *id1, const void *id2) { @@ -234,7 +234,7 @@ transaction_id_cmp(const void *id1, const void *id2) *((const getdns_transaction_t *) id2); if (t1 == t2) { return 0; - } else if (t1 < t2) { + } else if (t1 > t2) { return -1; } else { return 1; @@ -242,6 +242,33 @@ transaction_id_cmp(const void *id1, const void *id2) } } +static int timeout_cmp(const void *to1, const void *to2) { + if (to1 == NULL && to2 == NULL) { + return 0; + } else if (to1 == NULL && to2 != NULL) { + return 1; + } else if (to1 != NULL && to2 == NULL) { + return -1; + } else { + const getdns_timeout_data_t* t1 = (const getdns_timeout_data_t*) to1; + const getdns_timeout_data_t* t2 = (const getdns_timeout_data_t*) to2; + if (t1->timeout_time.tv_sec < t2->timeout_time.tv_sec) { + return -1; + } else if (t1->timeout_time.tv_sec > t2->timeout_time.tv_sec) { + return 1; + } else { + /* compare usec.. */ + if (t1->timeout_time.tv_usec < t2->timeout_time.tv_usec) { + return -1; + } else if (t1->timeout_time.tv_usec > t2->timeout_time.tv_usec) { + return 1; + } else { + return transaction_id_cmp(&t1->transaction_id, &t2->transaction_id); + } + } + } +} + /* * getdns_context_create * @@ -283,11 +310,7 @@ getdns_context_create_with_extended_memory_functions( result->mf.mf.ext.realloc = realloc; result->mf.mf.ext.free = free; - result->event_base_sync = event_base_new(); - result->unbound_sync = ub_ctx_create_event(result->event_base_sync); - /* create the async one also so options are kept up to date */ - result->unbound_async = ub_ctx_create_event(result->event_base_sync); - result->event_base_async = NULL; + result->unbound_ctx = ub_ctx_create(); result->resolution_type_set = 0; @@ -309,6 +332,11 @@ getdns_context_create_with_extended_memory_functions( result->edns_version = 0; result->edns_do_bit = 0; + result->extension = NULL; + result->extension_data = NULL; + result->timeouts_by_time = ldns_rbtree_create(timeout_cmp); + result->timeouts_by_id = ldns_rbtree_create(transaction_id_cmp); + if (set_from_os) { if (GETDNS_RETURN_GOOD != set_os_defaults(result)) { getdns_context_destroy(result); @@ -377,18 +405,19 @@ getdns_context_destroy(struct getdns_context *context) if (context->namespaces) GETDNS_FREE(context->my_mf, context->namespaces); + cancel_outstanding_requests(context, 0); + getdns_list_destroy(context->dns_root_servers); getdns_list_destroy(context->suffix); getdns_list_destroy(context->dnssec_trust_anchors); getdns_list_destroy(context->upstream_list); /* destroy the ub context */ - ub_ctx_delete(context->unbound_async); - ub_ctx_delete(context->unbound_sync); - - event_base_free(context->event_base_sync); + ub_ctx_delete(context->unbound_ctx); ldns_rbtree_free(context->outbound_requests); + ldns_rbtree_free(context->timeouts_by_id); + ldns_rbtree_free(context->timeouts_by_time); GETDNS_FREE(context->my_mf, context); return; @@ -415,8 +444,7 @@ getdns_context_set_context_update_callback(struct getdns_context *context, static void set_ub_string_opt(struct getdns_context *ctx, char *opt, char *value) { - ub_ctx_set_option(ctx->unbound_sync, opt, value); - ub_ctx_set_option(ctx->unbound_async, opt, value); + ub_ctx_set_option(ctx->unbound_ctx, opt, value); } static void @@ -891,27 +919,6 @@ getdns_context_set_memory_functions(struct getdns_context *context, context, MF_PLAIN, mf.ext.malloc, mf.ext.realloc, mf.ext.free); } /* getdns_context_set_memory_functions*/ - -/* - * getdns_extension_set_libevent_base - * - */ -getdns_return_t -getdns_extension_set_libevent_base(struct getdns_context *context, - struct event_base * this_event_base) -{ - RETURN_IF_NULL(context, GETDNS_RETURN_BAD_CONTEXT); - if (this_event_base) { - ub_ctx_set_event(context->unbound_async, this_event_base); - context->event_base_async = this_event_base; - } else { - ub_ctx_set_event(context->unbound_async, - context->event_base_sync); - context->event_base_async = NULL; - } - return GETDNS_RETURN_GOOD; -} /* getdns_extension_set_libevent_base */ - /* cancel the request */ static void cancel_dns_req(getdns_dns_req * req) @@ -921,7 +928,7 @@ cancel_dns_req(getdns_dns_req * req) if (netreq->state == NET_REQ_IN_FLIGHT) { /* for ev based ub, this should always prevent * the callback from firing */ - ub_cancel(req->unbound, netreq->unbound_id); + ub_cancel(req->context->unbound_ctx, netreq->unbound_id); netreq->state = NET_REQ_CANCELED; } else if (netreq->state == NET_REQ_NOT_SENT) { netreq->state = NET_REQ_CANCELED; @@ -1071,19 +1078,15 @@ getdns_context_prepare_for_resolution(struct getdns_context *context) return GETDNS_RETURN_BAD_CONTEXT; } /* set upstreams */ - ub_setup_stub(context->unbound_async, context->upstream_list, - upstream_len); - ub_setup_stub(context->unbound_sync, context->upstream_list, + ub_setup_stub(context->unbound_ctx, context->upstream_list, upstream_len); /* use /etc/hosts */ - ub_ctx_hosts(context->unbound_sync, NULL); - ub_ctx_hosts(context->unbound_async, NULL); + ub_ctx_hosts(context->unbound_ctx, NULL); } else if (context->resolution_type == GETDNS_CONTEXT_RECURSING) { /* set recursive */ /* TODO: use the root servers via root hints file */ - ub_ctx_set_fwd(context->unbound_async, NULL); - ub_ctx_set_fwd(context->unbound_sync, NULL); + ub_ctx_set_fwd(context->unbound_ctx, NULL); } else { /* bogus? */ @@ -1129,48 +1132,271 @@ getdns_context_clear_outbound_request(getdns_dns_req * req) return GETDNS_RETURN_GOOD; } + + char * getdns_strdup(const struct mem_funcs *mfs, const char *s) { - size_t sz = strlen(s) + 1; - char *r = GETDNS_XMALLOC(*mfs, char, sz); - if (r) - return memcpy(r, s, sz); - else - return NULL; + size_t sz = strlen(s) + 1; + char *r = GETDNS_XMALLOC(*mfs, char, sz); + if (r) + return memcpy(r, s, sz); + else + return NULL; } struct getdns_bindata * getdns_bindata_copy(struct mem_funcs *mfs, const struct getdns_bindata *src) { - struct getdns_bindata *dst; + struct getdns_bindata *dst; - if (!src) - return NULL; + if (!src) + return NULL; - dst = GETDNS_MALLOC(*mfs, struct getdns_bindata); - if (!dst) - return NULL; + dst = GETDNS_MALLOC(*mfs, struct getdns_bindata); + if (!dst) + return NULL; - dst->size = src->size; - dst->data = GETDNS_XMALLOC(*mfs, uint8_t, src->size); - if (!dst->data) { - GETDNS_FREE(*mfs, dst); - return NULL; - } - (void) memcpy(dst->data, src->data, src->size); - return dst; + dst->size = src->size; + dst->data = GETDNS_XMALLOC(*mfs, uint8_t, src->size); + if (!dst->data) { + GETDNS_FREE(*mfs, dst); + return NULL; + } + (void) memcpy(dst->data, src->data, src->size); + return dst; } void getdns_bindata_destroy(struct mem_funcs *mfs, struct getdns_bindata *bindata) { - if (!bindata) - return; - GETDNS_FREE(*mfs, bindata->data); - GETDNS_FREE(*mfs, bindata); + if (!bindata) + return; + GETDNS_FREE(*mfs, bindata->data); + GETDNS_FREE(*mfs, bindata); } +/* get the fd */ +int getdns_context_fd(struct getdns_context* context) { + RETURN_IF_NULL(context, -1); + return ub_fd(context->unbound_ctx); +} + +int +getdns_context_get_num_pending_requests(struct getdns_context* context, + struct timeval* next_timeout) { + RETURN_IF_NULL(context, GETDNS_RETURN_BAD_CONTEXT); + int r = context->outbound_requests->count; + if (r > 0) { + if (!context->extension && next_timeout) { + /* get the first timeout */ + ldns_rbnode_t* first = ldns_rbtree_first(context->timeouts_by_time); + if (first) { + getdns_timeout_data_t* timeout_data = (getdns_timeout_data_t*) first->data; + *next_timeout = (timeout_data->timeout_time); + } + } + } + return r; +} + +/* process async reqs */ +getdns_return_t getdns_context_process_async(struct getdns_context* context) { + RETURN_IF_NULL(context, GETDNS_RETURN_BAD_CONTEXT); + if (ub_poll(context->unbound_ctx)) { + if (ub_process(context->unbound_ctx) != 0) { + /* need an async return code? */ + return GETDNS_RETURN_GENERIC_ERROR; + } + } + if (context->extension != NULL) { + /* no need to process timeouts since it is delegated + * to the extension */ + return GETDNS_RETURN_GOOD; + } + getdns_timeout_data_t key; + /* set to 0 so it is the last timeout if we have + * two with the same time */ + key.transaction_id = 0; + if (gettimeofday(&key.timeout_time, NULL) != 0) { + return GETDNS_RETURN_GENERIC_ERROR; + } + ldns_rbnode_t* next_timeout = ldns_rbtree_first(context->timeouts_by_time); + while (next_timeout) { + getdns_timeout_data_t* timeout_data = (getdns_timeout_data_t*) next_timeout->data; + if (timeout_cmp(timeout_data, &key) > 0) { + /* no more timeouts need to be fired. */ + break; + } + /* get the next_timeout */ + next_timeout = ldns_rbtree_next(next_timeout); + /* delete the node */ + /* timeout data is freed in the clear_timeout */ + ldns_rbnode_t* to_del = ldns_rbtree_delete(context->timeouts_by_time, timeout_data); + if (to_del) { + /* should always exist .. */ + GETDNS_FREE(context->my_mf, to_del); + } + + /* fire the timeout */ + timeout_data->callback(timeout_data->userarg); + } + + return GETDNS_RETURN_GOOD; +} + +typedef struct timeout_accumulator { + getdns_transaction_t* ids; + int idx; +} timeout_accumulator; + +static void +accumulate_outstanding_transactions(ldns_rbnode_t* node, void* arg) { + timeout_accumulator* acc = (timeout_accumulator*) arg; + acc->ids[acc->idx] = *((getdns_transaction_t*) node->key); + acc->idx++; +} + +static void +cancel_outstanding_requests(struct getdns_context* context, int fire_callback) { + if (context->outbound_requests->count > 0) { + timeout_accumulator acc; + int i; + acc.idx = 0; + acc.ids = GETDNS_XMALLOC(context->my_mf, getdns_transaction_t, context->outbound_requests->count); + ldns_traverse_postorder(context->outbound_requests, accumulate_outstanding_transactions, &acc); + for (i = 0; i < acc.idx; ++i) { + getdns_context_cancel_request(context, acc.ids[i], fire_callback); + } + } +} + +getdns_return_t +getdns_extension_detach_eventloop(struct getdns_context* context) +{ + RETURN_IF_NULL(context, GETDNS_RETURN_BAD_CONTEXT); + getdns_return_t r = GETDNS_RETURN_GOOD; + if (context->extension) { + /* cancel all outstanding requests */ + cancel_outstanding_requests(context, 1); + r = context->extension->cleanup_data(context, context->extension_data); + if (r != GETDNS_RETURN_GOOD) { + return r; + } + context->extension = NULL; + context->extension_data = NULL; + } + return r; +} + +getdns_return_t +getdns_extension_set_eventloop(struct getdns_context* context, + getdns_eventloop_extension* extension, void* extension_data) +{ + RETURN_IF_NULL(context, GETDNS_RETURN_BAD_CONTEXT); + RETURN_IF_NULL(extension, GETDNS_RETURN_INVALID_PARAMETER); + getdns_return_t r = getdns_extension_detach_eventloop(context); + if (r != GETDNS_RETURN_GOOD) { + return r; + } + context->extension = extension; + context->extension_data = extension_data; + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_context_schedule_timeout(struct getdns_context* context, + getdns_transaction_t id, uint16_t timeout, getdns_timeout_callback callback, + void* userarg) { + RETURN_IF_NULL(context, GETDNS_RETURN_BAD_CONTEXT); + RETURN_IF_NULL(callback, GETDNS_RETURN_INVALID_PARAMETER); + getdns_return_t result; + /* create a timeout */ + getdns_timeout_data_t* timeout_data = GETDNS_MALLOC(context->my_mf, getdns_timeout_data_t); + if (!timeout_data) { + return GETDNS_RETURN_GENERIC_ERROR; + } + timeout_data->context = context; + timeout_data->transaction_id = id; + timeout_data->callback = callback; + timeout_data->userarg = userarg; + timeout_data->extension_timer = NULL; + + /* insert into transaction tree */ + ldns_rbnode_t *node = GETDNS_MALLOC(context->my_mf, ldns_rbnode_t); + if (!node) { + GETDNS_FREE(context->my_mf, timeout_data); + return GETDNS_RETURN_GENERIC_ERROR; + } + node->key = &(timeout_data->transaction_id); + node->data = timeout_data; + node->left = NULL; + node->right = NULL; + if (!ldns_rbtree_insert(context->timeouts_by_id, node)) { + /* free the node */ + GETDNS_FREE(context->my_mf, timeout_data); + GETDNS_FREE(context->my_mf, node); + return GETDNS_RETURN_GENERIC_ERROR; + } + + if (context->extension) { + result = context->extension->schedule_timeout(context, context->extension_data, + timeout, timeout_data, &(timeout_data->extension_timer)); + } else { + result = GETDNS_RETURN_GENERIC_ERROR; + if (gettimeofday(&timeout_data->timeout_time, NULL) == 0) { + /* increment */ + uint16_t num_secs = timeout / 1000; + /* timeout is in millis */ + timeout_data->timeout_time.tv_sec += num_secs; + + ldns_rbnode_t* id_node = GETDNS_MALLOC(context->my_mf, ldns_rbnode_t); + if (id_node) { + id_node->key = timeout_data; + id_node->data = timeout_data; + id_node->left = NULL; + id_node->right = NULL; + if (!ldns_rbtree_insert(context->timeouts_by_time, id_node)) { + GETDNS_FREE(context->my_mf, id_node); + } else { + result = GETDNS_RETURN_GOOD; + } + } + } + } + if (result != GETDNS_RETURN_GOOD) { + GETDNS_FREE(context->my_mf, timeout_data); + GETDNS_FREE(context->my_mf, node); + } + return result; +} + +getdns_return_t +getdns_context_clear_timeout(struct getdns_context* context, + getdns_transaction_t id) { + RETURN_IF_NULL(context, GETDNS_RETURN_BAD_CONTEXT); + /* find the timeout_data by id */ + ldns_rbnode_t* node = ldns_rbtree_delete(context->timeouts_by_id, &id); + if (!node) { + return GETDNS_RETURN_UNKNOWN_TRANSACTION; + } + getdns_timeout_data_t* timeout_data = (getdns_timeout_data_t*) node->data; + GETDNS_FREE(context->my_mf, node); + if (context->extension) { + context->extension->clear_timeout(context, context->extension, + timeout_data->extension_timer); + } else { + /* make sure it is removed from the timeout node */ + ldns_rbnode_t* to_del = ldns_rbtree_delete(context->timeouts_by_time, timeout_data); + if (to_del) { + GETDNS_FREE(context->my_mf, to_del); + } + } + GETDNS_FREE(context->my_mf, timeout_data); + return GETDNS_RETURN_GOOD; +} + + /* context.c */ diff --git a/src/context.h b/src/context.h index 82d6f71f..e34d555f 100644 --- a/src/context.h +++ b/src/context.h @@ -40,7 +40,6 @@ #include #include "types-internal.h" -struct event_base; struct getdns_dns_req; struct ldns_rbtree_t; struct ub_ctx; @@ -70,15 +69,9 @@ struct getdns_context { struct mem_funcs mf; struct mem_funcs my_mf; - /* Event loop for sync requests */ - struct event_base *event_base_sync; - /* Event loop for async requests */ - struct event_base *event_base_async; - /* The underlying unbound contexts that do * the real work */ - struct ub_ctx *unbound_sync; - struct ub_ctx *unbound_async; + struct ub_ctx *unbound_ctx; /* which resolution type the contexts are configured for * 0 means nothing set @@ -89,6 +82,26 @@ struct getdns_context { * outbound requests -> transaction to getdns_dns_req */ struct ldns_rbtree_t *outbound_requests; + + /* + * Event loop extension functions + * These structs are static and should never be freed + * since they are just a collection of function pointers + */ + getdns_eventloop_extension* extension; + /* + * Extension data that will be freed by the functions + * in the extension struct + */ + void* extension_data; + + /* + * Timeout info one tree to manage timeout data + * keyed by transaction id. Second to manage by + * timeout time (ascending) + */ + struct ldns_rbtree_t *timeouts_by_id; + struct ldns_rbtree_t *timeouts_by_time; }; /** internal functions **/ @@ -118,4 +131,20 @@ void getdns_bindata_destroy( struct mem_funcs *mfs, struct getdns_bindata *bindata); +/* extension stuff */ +getdns_return_t getdns_extension_set_eventloop(struct getdns_context* context, + getdns_eventloop_extension* extension, void* extension_data); + +getdns_return_t +getdns_extension_detach_eventloop(struct getdns_context* context); + + +/* timeout scheduling */ +getdns_return_t getdns_context_schedule_timeout(struct getdns_context* context, + getdns_transaction_t id, uint16_t timeout, getdns_timeout_callback callback, + void* userarg); + +getdns_return_t getdns_context_clear_timeout(struct getdns_context* context, + getdns_transaction_t id); + #endif /* _GETDNS_CONTEXT_H_ */ diff --git a/src/example/Makefile.in b/src/example/Makefile.in index 009c39c2..e47e81b9 100644 --- a/src/example/Makefile.in +++ b/src/example/Makefile.in @@ -17,7 +17,7 @@ VPATH = @srcdir@ CC=gcc CFLAGS=@CFLAGS@ -Wall -I$(srcdir)/ -I$(srcdir)/../ -I/usr/local/include -std=c99 -LDFLAGS=@LDFLAGS@ -L. -L.. -L/usr/local/lib +LDFLAGS=@LDFLAGS@ -L. -L.. -L$(srcdir)/../ -L/usr/local/lib LDLIBS=-lgetdns @LIBS@ PROGRAMS=example-all-functions example-simple-answers example-tree example-synchronous example-reverse diff --git a/src/example/getdns_libevent.h b/src/example/getdns_libevent.h index 0d3ec94f..179dc3a4 100644 --- a/src/example/getdns_libevent.h +++ b/src/example/getdns_libevent.h @@ -1,5 +1,5 @@ #include -#include "config.h" +#include #ifdef HAVE_EVENT2_EVENT_H #include #else diff --git a/src/extension/libevent.c b/src/extension/libevent.c new file mode 100644 index 00000000..02b27262 --- /dev/null +++ b/src/extension/libevent.c @@ -0,0 +1,162 @@ +/** + * \file + * \brief Public interfaces to getdns, include in your application to use getdns API. + * + * This source was taken from the original pseudo-implementation by + * Paul Hoffman. + */ + +/* + * Copyright (c) 2013, NLNet Labs, Versign, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "config.h" +#include "context.h" +#include + +#ifdef HAVE_EVENT2_EVENT_H +# include +#else +# include +# define evutil_socket_t int +# define event_free free +# define evtimer_new(b, cb, arg) event_new((b), -1, 0, (cb), (arg)) +#endif +#define RETURN_IF_NULL(ptr, code) if(ptr == NULL) return code; + +#ifndef HAVE_EVENT2_EVENT_H +static struct event * +event_new(struct event_base *b, evutil_socket_t fd, short ev, void* cb, void *arg) +{ + struct event* e = (struct event*)calloc(1, sizeof(struct event)); + if(!e) return NULL; + event_set(e, fd, ev, cb, arg); + event_base_set(b, e); + return e; +} +#endif /* no event2 */ + +/* extension info */ +struct event_data { + struct event* event; + struct event_base* event_base; +}; + +/* lib event callbacks */ +static void +getdns_libevent_cb(evutil_socket_t fd, short what, void *userarg) { + struct getdns_context* context = (struct getdns_context*) userarg; + getdns_context_process_async(context); +} + +static void +getdns_libevent_timeout_cb(evutil_socket_t fd, short what, void* userarg) { + getdns_timeout_data_t* timeout_data = (getdns_timeout_data_t*) userarg; + timeout_data->callback(timeout_data->userarg); +} + +/* getdns extension functions */ +static getdns_return_t +getdns_libevent_cleanup(struct getdns_context* context, void* data) { + struct event_data *edata = (struct event_data*) data; + event_del(edata->event); + event_free(edata->event); + free(edata); + return GETDNS_RETURN_GOOD; +} + +static getdns_return_t +getdns_libevent_schedule_timeout(struct getdns_context* context, + void* eventloop_data, uint16_t timeout, + getdns_timeout_data_t* timeout_data, + void** eventloop_timer) { + + struct timeval tv; + struct event* ev = NULL; + struct event_data* ev_data = (struct event_data*) eventloop_data; + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + ev = evtimer_new(ev_data->event_base, getdns_libevent_timeout_cb, timeout_data); + evtimer_add(ev, &tv); + + *eventloop_timer = ev; + return GETDNS_RETURN_GOOD; +} + +static getdns_return_t +getdns_libevent_clear_timeout(struct getdns_context* context, + void* eventloop_data, void** eventloop_timer) { + struct event* ev = (struct event*) eventloop_timer; + event_del(ev); + event_free(ev); + return GETDNS_RETURN_GOOD; +} + + +static getdns_eventloop_extension LIBEVENT_EXT = { + getdns_libevent_cleanup, + getdns_libevent_schedule_timeout, + getdns_libevent_clear_timeout +}; + +/* + * getdns_extension_set_libevent_base + * + */ +getdns_return_t +getdns_extension_set_libevent_base(struct getdns_context *context, + struct event_base * this_event_base) +{ + RETURN_IF_NULL(context, GETDNS_RETURN_BAD_CONTEXT); + RETURN_IF_NULL(this_event_base, GETDNS_RETURN_INVALID_PARAMETER); + /* TODO: cleanup current extension base */ + getdns_return_t r = getdns_extension_detach_eventloop(context); + if (r != GETDNS_RETURN_GOOD) { + return r; + } + int fd = getdns_context_fd(context); + struct event* getdns_event = event_new(this_event_base, fd, EV_READ | EV_PERSIST, getdns_libevent_cb, context); + if (!getdns_event) { + return GETDNS_RETURN_GENERIC_ERROR; + } + event_add(getdns_event, NULL); + + /* TODO: use context functs? */ + struct event_data* ev_data = (struct event_data*) malloc(sizeof(struct event_data)); + if (!ev_data) { + /* cleanup */ + event_del(getdns_event); + event_free(getdns_event); + return GETDNS_RETURN_GENERIC_ERROR; + } + ev_data->event = getdns_event; + ev_data->event_base = this_event_base; + + return getdns_extension_set_eventloop(context, &LIBEVENT_EXT, ev_data); +} /* getdns_extension_set_libevent_base */ diff --git a/src/general.c b/src/general.c index 64aa3493..8c21f7f7 100644 --- a/src/general.c +++ b/src/general.c @@ -35,17 +35,8 @@ */ #include "config.h" -#ifdef HAVE_EVENT2_EVENT_H -# include -#else -# include -# define evutil_socket_t int -# define event_free free -# define evtimer_new(b, cb, arg) event_new((b), -1, 0, (cb), (arg)) -#endif #include #include -#include #include #include "context.h" #include "types-internal.h" @@ -57,11 +48,9 @@ #define UNUSED_PARAM(x) ((void)(x)) /* declarations */ -static void ub_resolve_callback(void *arg, int err, void* result, - int packet_len, int sec, char *bogus); -static void ub_resolve_timeout(evutil_socket_t fd, short what, void *arg); -static void ub_local_resolve_timeout(evutil_socket_t fd, short what, - void *arg); +static void ub_resolve_callback(void* mydata, int err, struct ub_result* result); +static void ub_resolve_timeout(void *arg); +static void ub_local_resolve_timeout(void *arg); static void handle_network_request_error(getdns_network_req * netreq, int err); static void handle_dns_request_complete(getdns_dns_req * dns_req); @@ -71,27 +60,12 @@ typedef struct netreq_cb_data { getdns_network_req *netreq; int err; - void *result; - int packet_len; - int sec; - char *bogus; + struct ub_result* ub_res; } netreq_cb_data; -#ifndef HAVE_EVENT2_EVENT_H -static struct event * -event_new(struct event_base *b, evutil_socket_t fd, short ev, void* cb, void *arg) -{ - struct event* e = (struct event*)calloc(1, sizeof(struct event)); - if(!e) return NULL; - event_set(e, fd, ev, cb, arg); - event_base_set(b, e); - return e; -} -#endif /* no event2 */ - /* cancel, cleanup and send timeout to callback */ static void -ub_resolve_timeout(evutil_socket_t fd, short what, void *arg) +ub_resolve_timeout(void *arg) { getdns_dns_req *dns_req = (getdns_dns_req *) arg; struct getdns_context *context = dns_req->context; @@ -109,7 +83,7 @@ ub_resolve_timeout(evutil_socket_t fd, short what, void *arg) } static void -ub_local_resolve_timeout(evutil_socket_t fd, short what, void *arg) +ub_local_resolve_timeout(void *arg) { netreq_cb_data *cb_data = (netreq_cb_data *) arg; @@ -117,18 +91,15 @@ ub_local_resolve_timeout(evutil_socket_t fd, short what, void *arg) * invalid after calling ub_resolve_callback */ getdns_dns_req *dnsreq = cb_data->netreq->owner; - event_free(dnsreq->local_cb_timer); - dnsreq->local_cb_timer = NULL; + /* clear the timeout */ + + getdns_context_clear_timeout(dnsreq->context, dnsreq->local_timeout_id); + dnsreq->local_timeout_id = 0; /* just call ub_resolve_callback */ - ub_resolve_callback(cb_data->netreq, cb_data->err, cb_data->result, - cb_data->packet_len, cb_data->sec, cb_data->bogus); + ub_resolve_callback(cb_data->netreq, cb_data->err, cb_data->ub_res); /* cleanup the state */ - free(cb_data->result); - if (cb_data->bogus) { - free(cb_data->bogus); - } free(cb_data); } @@ -176,7 +147,7 @@ static int submit_network_request(getdns_network_req * netreq) { getdns_dns_req *dns_req = netreq->owner; - int r = ub_resolve_event(dns_req->unbound, + int r = ub_resolve_async(dns_req->context->unbound_ctx, dns_req->name, netreq->request_type, netreq->request_class, @@ -188,10 +159,15 @@ submit_network_request(getdns_network_req * netreq) } static void -ub_resolve_callback(void *arg, int err, void *result, int packet_len, - int sec, char *bogus) +ub_resolve_callback(void* arg, int err, struct ub_result* ub_res) +// ub_resolve_callback(void *arg, int err, ldns_buffer * result, int sec, +// char *bogus) { - getdns_network_req *netreq = (getdns_network_req *) arg; + getdns_network_req *netreq = (getdns_network_req *) arg; + if (err != 0) { + handle_network_request_error(netreq, err); + return; + } /* if netreq->state == NET_REQ_NOT_SENT here, that implies * that ub called us back immediately - probably from a local file. * This most likely means that getdns_general has not returned @@ -201,72 +177,49 @@ ub_resolve_callback(void *arg, int err, void *result, int packet_len, * we can make this less hacky, but it gets interesting when multiple * netreqs need to be issued and some resolve immediately vs. not. */ - struct timeval tv; - getdns_dns_req *dnsreq = netreq->owner; - netreq_cb_data *cb_data = - (netreq_cb_data *) malloc(sizeof(netreq_cb_data)); + getdns_dns_req *dnsreq = netreq->owner; + netreq_cb_data *cb_data = + (netreq_cb_data *) malloc(sizeof(netreq_cb_data)); + cb_data->netreq = netreq; + cb_data->err = err; + cb_data->ub_res = ub_res; - cb_data->netreq = netreq; - cb_data->err = err; - cb_data->sec = sec; - cb_data->result = NULL; - cb_data->bogus = NULL; /* unused but here in case we need it */ - if (result) { - cb_data->result = (uint8_t *) malloc(packet_len); - if (!cb_data->result) { - cb_data->err = GETDNS_RETURN_GENERIC_ERROR; - } else { - /* copy */ - (void) memcpy( - cb_data->result, result, packet_len); - } - } - /* schedule the timeout */ - dnsreq->local_cb_timer = - evtimer_new(dnsreq->ev_base, ub_local_resolve_timeout, - cb_data); - tv.tv_sec = 0; - /* half ms */ - tv.tv_usec = 500; - evtimer_add(dnsreq->local_cb_timer, &tv); + dnsreq->local_timeout_id = ldns_get_random(); + + getdns_context_schedule_timeout(dnsreq->context, + dnsreq->local_timeout_id, 1, ub_local_resolve_timeout, cb_data); return; } netreq->state = NET_REQ_FINISHED; - if (err) { - handle_network_request_error(netreq, err); - } else { - /* parse */ - ldns_status r = - ldns_wire2pkt(&(netreq->result), result, packet_len); - if (r != LDNS_STATUS_OK) { - handle_network_request_error(netreq, r); + /* parse */ + /* TODO: optimize */ + getdns_return_t r = getdns_apply_network_result(netreq, ub_res); + ub_resolve_free(ub_res); + if (r != GETDNS_RETURN_GOOD) { + handle_network_request_error(netreq, err); + } else { + /* is this the last request */ + if (!netreq->next) { + /* finished */ + handle_dns_request_complete(netreq->owner); } else { - /* is this the last request */ - if (!netreq->next) { - /* finished */ - handle_dns_request_complete(netreq->owner); - } else { - /* not finished - update to next request and ship it */ - getdns_dns_req *dns_req = netreq->owner; - dns_req->current_req = netreq->next; - submit_network_request(netreq->next); - } + /* not finished - update to next request and ship it */ + getdns_dns_req *dns_req = netreq->owner; + dns_req->current_req = netreq->next; + submit_network_request(netreq->next); } } } getdns_return_t -getdns_general_ub(struct ub_ctx *unbound, - struct event_base *ev_base, - struct getdns_context *context, +getdns_general_ub(struct getdns_context *context, const char *name, uint16_t request_type, struct getdns_dict *extensions, void *userarg, - getdns_transaction_t * transaction_id, getdns_callback_t callbackfn) + getdns_transaction_t * transaction_id, + getdns_callback_t callbackfn) { - /* timeout */ - struct timeval tv; getdns_return_t gr; int r; @@ -281,7 +234,6 @@ getdns_general_ub(struct ub_ctx *unbound, /* request state */ getdns_dns_req *req = dns_req_new(context, - unbound, name, request_type, extensions); @@ -299,11 +251,11 @@ getdns_general_ub(struct ub_ctx *unbound, getdns_context_track_outbound_request(req); /* assign a timeout */ - req->ev_base = ev_base; - req->timeout = evtimer_new(ev_base, ub_resolve_timeout, req); - tv.tv_sec = context->timeout / 1000; - tv.tv_usec = (context->timeout % 1000) * 1000; - evtimer_add(req->timeout, &tv); + // req->ev_base = ev_base; + // req->timeout = evtimer_new(ev_base, ub_resolve_timeout, req); + /* schedule the timeout */ + getdns_context_schedule_timeout(context, req->trans_id, + context->timeout, ub_resolve_timeout, req); /* issue the first network req */ @@ -331,7 +283,7 @@ getdns_general(struct getdns_context *context, { int extcheck = GETDNS_RETURN_GOOD; - if (!context || !context->event_base_async) { + if (!context) { /* Can't do async without an event loop * or callback */ @@ -352,9 +304,7 @@ getdns_general(struct getdns_context *context, if (extcheck != GETDNS_RETURN_GOOD) return extcheck; - return getdns_general_ub(context->unbound_async, - context->event_base_async, - context, + return getdns_general_ub(context, name, request_type, extensions, userarg, transaction_id, callback); } /* getdns_general */ diff --git a/src/general.h b/src/general.h index 5e2dc674..4a699524 100644 --- a/src/general.h +++ b/src/general.h @@ -9,7 +9,7 @@ /* * Copyright (c) 2013, Versign, Inc. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright @@ -40,13 +40,8 @@ /* private inner helper used by sync and async */ -struct ub_ctx; -struct event_base; - getdns_return_t -getdns_general_ub(struct ub_ctx *unbound, - struct event_base *ev_base, - struct getdns_context *context, +getdns_general_ub(struct getdns_context *context, const char *name, uint16_t request_type, struct getdns_dict *extensions, diff --git a/src/getdns/getdns.h b/src/getdns/getdns.h index 4c461d7c..122fab9f 100644 --- a/src/getdns/getdns.h +++ b/src/getdns/getdns.h @@ -43,8 +43,6 @@ extern "C" { #endif -struct event_base; - #define GETDNS_COMPILATION_COMMENT The API implementation should fill in something here, such as a compilation version string and date, and change it each time the API is compiled. /** @@ -797,19 +795,12 @@ char *getdns_pretty_print_dict(const struct getdns_dict *some_dict); char *getdns_display_ip_address(const struct getdns_bindata *bindata_of_ipv4_or_ipv6_address); -/* -getdns_return_t getdns_context_set_context_update_callback( - struct getdns_context *context, - void (*value) (struct getdns_context *context, uint16_t changed_item) - ); -*/ getdns_return_t getdns_context_set_context_update_callback( struct getdns_context * context, void (*value)(struct getdns_context *context, uint16_t changed_item) ); - getdns_return_t getdns_context_set_resolution_type(struct getdns_context *context, uint16_t value); @@ -881,11 +872,14 @@ getdns_context_set_extended_memory_functions(struct getdns_context *context, void (*free) (void *userarg, void *ptr) ); -/* Extension - refactor to abstract async evt loop */ -/* For libevent, which we are using for these examples */ -getdns_return_t -getdns_extension_set_libevent_base(struct getdns_context *context, - struct event_base *this_event_base); +/* Async support */ +struct timeval; +int getdns_context_get_num_pending_requests(struct getdns_context* context, struct timeval* next_timeout); + +/* get the fd */ +int getdns_context_fd(struct getdns_context* context); +/* process async reqs */ +getdns_return_t getdns_context_process_async(struct getdns_context* context); #ifdef __cplusplus } diff --git a/src/getdns/getdns_ext_libevent.h b/src/getdns/getdns_ext_libevent.h new file mode 100644 index 00000000..0073bbf2 --- /dev/null +++ b/src/getdns/getdns_ext_libevent.h @@ -0,0 +1,55 @@ +/** + * \file + * \brief Public interfaces to getdns, include in your application to use getdns API. + * + * This source was taken from the original pseudo-implementation by + * Paul Hoffman. + */ + +/* + * Copyright (c) 2013, NLNet Labs, Versign, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GETDNS_EXT_LIBEVENT_H +#define GETDNS_EXT_LIBEVENT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct event_base; + +/* For libevent, which we are using for these examples */ +getdns_return_t +getdns_extension_set_libevent_base(struct getdns_context *context, + struct event_base *this_event_base); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/request-internal.c b/src/request-internal.c index 7a11554a..3ad976f3 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -9,7 +9,7 @@ /* * Copyright (c) 2013, Versign, Inc. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright @@ -34,15 +34,8 @@ */ #include "config.h" -#ifdef HAVE_EVENT2_EVENT_H -# include -#else -# include -# define event_free free -#endif #include "types-internal.h" #include "util-internal.h" -#include void network_req_free(getdns_network_req * net_req) @@ -103,16 +96,11 @@ dns_req_free(getdns_dns_req * req) net_req = next; } - /* cleanup timeout */ - if (req->timeout) { - event_del(req->timeout); - event_free(req->timeout); - } + if (req->local_timeout_id != 0) { + getdns_context_clear_timeout(context, req->local_timeout_id); + } - if (req->local_cb_timer) { - event_del(req->local_cb_timer); - event_free(req->local_cb_timer); - } + getdns_context_clear_timeout(context, req->trans_id); /* free strduped name */ free(req->name); @@ -123,7 +111,6 @@ dns_req_free(getdns_dns_req * req) /* create a new dns req to be submitted */ getdns_dns_req * dns_req_new(struct getdns_context *context, - struct ub_ctx *unbound, const char *name, uint16_t request_type, struct getdns_dict *extensions) { @@ -139,20 +126,17 @@ dns_req_new(struct getdns_context *context, result->name = strdup(name); result->context = context; - result->unbound = unbound; result->canceled = 0; result->current_req = NULL; result->first_req = NULL; result->trans_id = ldns_get_random(); - result->timeout = NULL; - result->local_cb_timer = NULL; - result->ev_base = NULL; getdns_dict_copy(extensions, &result->extensions); /* will be set by caller */ result->user_pointer = NULL; result->user_callback = NULL; + result->local_timeout_id = 0; /* create the requests */ req = network_req_new(result, diff --git a/src/sync.c b/src/sync.c index 48b0ad84..7cb4878d 100644 --- a/src/sync.c +++ b/src/sync.c @@ -34,13 +34,8 @@ */ #include "config.h" -#ifdef HAVE_EVENT2_EVENT_H -# include -#else -# include -#endif #include -#include +#include #include "context.h" #include "general.h" #include "types-internal.h" @@ -51,14 +46,28 @@ #define UNUSED_PARAM(x) ((void)(x)) #define RETURN_IF_NULL(ptr, code) if(ptr == NULL) return code; -static void -sync_callback_func(struct getdns_context *context, - uint16_t callback_type, - struct getdns_dict *response, - void *userarg, getdns_transaction_t transaction_id) -{ - - *((struct getdns_dict **) userarg) = response; +getdns_return_t submit_request_sync(getdns_dns_req* req) { + struct ub_result* ub_res = NULL; + getdns_return_t gr = GETDNS_RETURN_GOOD; + getdns_network_req *netreq = req->first_req; + while (netreq) { + int r = ub_resolve(req->context->unbound_ctx, + req->name, + netreq->request_type, + netreq->request_class, + &ub_res); + if (r != 0) { + return GETDNS_RETURN_GENERIC_ERROR; + } + gr = getdns_apply_network_result(netreq, ub_res); + ub_resolve_free(ub_res); + ub_res = NULL; + if (gr != GETDNS_RETURN_GOOD) { + return gr; + } + netreq = netreq->next; + } + return gr; } getdns_return_t @@ -78,12 +87,22 @@ getdns_general_sync(struct getdns_context *context, } response_status = validate_extensions(extensions); if (response_status == GETDNS_RETURN_GOOD) { - response_status = getdns_general_ub(context->unbound_sync, - context->event_base_sync, - context, name, request_type, - extensions, (void *) response, NULL, sync_callback_func); - - event_base_dispatch(context->event_base_sync); + /* for each netreq we call ub_ctx_resolve */ + /* request state */ + getdns_dns_req *req = dns_req_new(context, + name, + request_type, + extensions); + if (!req) { + return GETDNS_RETURN_GENERIC_ERROR; + } + response_status = submit_request_sync(req); + if (response_status != GETDNS_RETURN_GOOD) { + dns_req_free(req); + return response_status; + } + *response = create_getdns_response(req); + dns_req_free(req); } return response_status; } diff --git a/src/test/Makefile.in b/src/test/Makefile.in index 79425016..dd6e0769 100644 --- a/src/test/Makefile.in +++ b/src/test/Makefile.in @@ -17,7 +17,7 @@ VPATH = @srcdir@ CC=gcc CFLAGS=@CFLAGS@ -Wall -I$(srcdir)/ -I$(srcdir)/../ -I/usr/local/include -std=c99 $(cflags) -LDFLAGS=@LDFLAGS@ -L. -L.. -L/usr/local/lib +LDFLAGS=@LDFLAGS@ -L. -L.. -L$(srcdir)/../ -L/usr/local/lib LDLIBS=-lgetdns @LIBS@ -lcheck PROGRAMS=tests_dict tests_list tests_stub_async tests_stub_sync check_getdns tests_dnssec @@ -45,7 +45,7 @@ tests_stub_sync: tests_stub_sync.o check_getdns_common: check_getdns_common.o $(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ check_getdns_common.o -check_getdns: check_getdns.o check_getdns_common.o +check_getdns: check_getdns.o check_getdns_common.o $(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ check_getdns.o check_getdns_common.o tests_dnssec: tests_dnssec.o testmessages.o diff --git a/src/test/check_getdns.c b/src/test/check_getdns.c index fec45abb..62d980e7 100644 --- a/src/test/check_getdns.c +++ b/src/test/check_getdns.c @@ -42,9 +42,12 @@ int -main (void) +main (int argc, char** argv) { int number_failed; + if (argc > 1) { + event_loop_type = atoi(argv[1]); + } SRunner *sr ; Suite *getdns_general_suite(void); diff --git a/src/test/check_getdns_common.c b/src/test/check_getdns_common.c index 4b5b9897..7d7249a6 100644 --- a/src/test/check_getdns_common.c +++ b/src/test/check_getdns_common.c @@ -5,12 +5,16 @@ #include #include #include +#include #include "check_getdns_common.h" +#include +#include int callback_called = 0; int callback_completed = 0; int callback_canceled = 0; uint16_t expected_changed_item = 0; +int event_loop_type = 0; /* * extract_response extracts all of the various information @@ -20,25 +24,25 @@ void extract_response(struct getdns_dict *response, struct extracted_response *e { ck_assert_msg(response != NULL, "Response should not be NULL"); - ASSERT_RC(getdns_dict_get_int(response, "answer_type", &ex_response->top_answer_type), + ASSERT_RC(getdns_dict_get_int(response, "answer_type", &ex_response->top_answer_type), GETDNS_RETURN_GOOD, "Failed to extract \"top answer_type\""); - ASSERT_RC(getdns_dict_get_bindata(response, "canonical_name", &ex_response->top_canonical_name), + ASSERT_RC(getdns_dict_get_bindata(response, "canonical_name", &ex_response->top_canonical_name), GETDNS_RETURN_GOOD, "Failed to extract \"top canonical_name\""); - ASSERT_RC(getdns_dict_get_list(response, "just_address_answers", &ex_response->just_address_answers), + ASSERT_RC(getdns_dict_get_list(response, "just_address_answers", &ex_response->just_address_answers), GETDNS_RETURN_GOOD, "Failed to extract \"just_address_answers\""); ck_assert_msg(ex_response->just_address_answers != NULL, "just_address_answers should not be NULL"); - ASSERT_RC(getdns_dict_get_list(response, "replies_full", &ex_response->replies_full), + ASSERT_RC(getdns_dict_get_list(response, "replies_full", &ex_response->replies_full), GETDNS_RETURN_GOOD, "Failed to extract \"replies_full\""); ck_assert_msg(ex_response->replies_full != NULL, "replies_full should not be NULL"); - ASSERT_RC(getdns_dict_get_list(response, "replies_tree", &ex_response->replies_tree), + ASSERT_RC(getdns_dict_get_list(response, "replies_tree", &ex_response->replies_tree), GETDNS_RETURN_GOOD, "Failed to extract \"replies_tree\""); ck_assert_msg(ex_response->replies_tree != NULL, "replies_tree should not be NULL"); - ASSERT_RC(getdns_list_get_dict(ex_response->replies_tree, 0, &ex_response->replies_tree_sub_dict), + ASSERT_RC(getdns_list_get_dict(ex_response->replies_tree, 0, &ex_response->replies_tree_sub_dict), GETDNS_RETURN_GOOD, "Failed to extract \"replies_tree[0]\""); ck_assert_msg(ex_response->replies_tree_sub_dict != NULL, "replies_tree[0] dict should not be NULL"); @@ -46,29 +50,29 @@ void extract_response(struct getdns_dict *response, struct extracted_response *e GETDNS_RETURN_GOOD, "Failed to extract \"additional\""); ck_assert_msg(ex_response->additional != NULL, "additional should not be NULL"); - ASSERT_RC(getdns_dict_get_list(ex_response->replies_tree_sub_dict, "answer", &ex_response->answer), + ASSERT_RC(getdns_dict_get_list(ex_response->replies_tree_sub_dict, "answer", &ex_response->answer), GETDNS_RETURN_GOOD, "Failed to extract \"answer\""); ck_assert_msg(ex_response->answer != NULL, "answer should not be NULL"); - ASSERT_RC(getdns_dict_get_int(ex_response->replies_tree_sub_dict, "answer_type", &ex_response->answer_type), + ASSERT_RC(getdns_dict_get_int(ex_response->replies_tree_sub_dict, "answer_type", &ex_response->answer_type), GETDNS_RETURN_GOOD, "Failed to extract \"answer_type\""); - ASSERT_RC(getdns_dict_get_list(ex_response->replies_tree_sub_dict, "authority", &ex_response->authority), + ASSERT_RC(getdns_dict_get_list(ex_response->replies_tree_sub_dict, "authority", &ex_response->authority), GETDNS_RETURN_GOOD, "Failed to extract \"authority\""); ck_assert_msg(ex_response->authority != NULL, "authority should not be NULL"); - ASSERT_RC(getdns_dict_get_bindata(ex_response->replies_tree_sub_dict, "canonical_name", &ex_response->canonical_name), + ASSERT_RC(getdns_dict_get_bindata(ex_response->replies_tree_sub_dict, "canonical_name", &ex_response->canonical_name), GETDNS_RETURN_GOOD, "Failed to extract \"canonical_name\""); - ASSERT_RC(getdns_dict_get_dict(ex_response->replies_tree_sub_dict, "header", &ex_response->header), + ASSERT_RC(getdns_dict_get_dict(ex_response->replies_tree_sub_dict, "header", &ex_response->header), GETDNS_RETURN_GOOD, "Failed to extract \"header\""); ck_assert_msg(ex_response->header != NULL, "header should not be NULL"); - ASSERT_RC(getdns_dict_get_dict(ex_response->replies_tree_sub_dict, "question", &ex_response->question), + ASSERT_RC(getdns_dict_get_dict(ex_response->replies_tree_sub_dict, "question", &ex_response->question), GETDNS_RETURN_GOOD, "Failed to extract \"question\""); ck_assert_msg(ex_response->question != NULL, "question should not be NULL"); - ASSERT_RC(getdns_dict_get_int(response, "status", &ex_response->status), + ASSERT_RC(getdns_dict_get_int(response, "status", &ex_response->status), GETDNS_RETURN_GOOD, "Failed to extract \"status\""); } @@ -87,17 +91,17 @@ void assert_noerror(struct extracted_response *ex_response) /* * assert_nodata asserts that ancount in the header and the * of the answer section (list) are both zero. - */ + */ void assert_nodata(struct extracted_response *ex_response) { uint32_t ancount; size_t length; - ASSERT_RC(getdns_dict_get_int(ex_response->header, "ancount", &ancount), + ASSERT_RC(getdns_dict_get_int(ex_response->header, "ancount", &ancount), GETDNS_RETURN_GOOD, "Failed to extract \"ancount\""); ck_assert_msg(ancount == 0, "Expected ancount == 0, got %d", ancount); - ASSERT_RC(getdns_list_get_length(ex_response->answer, &length), + ASSERT_RC(getdns_list_get_length(ex_response->answer, &length), GETDNS_RETURN_GOOD, "Failed to extract \"answer\" length"); ck_assert_msg(length == 0, "Expected \"answer\" length == 0, got %d", length); } @@ -110,7 +114,7 @@ void assert_nodata(struct extracted_response *ex_response) */ void assert_address_in_answer(struct extracted_response *ex_response, int a, int aaaa) { - uint32_t ancount; + uint32_t ancount; size_t length; struct getdns_dict *rr_dict; uint32_t type; @@ -127,9 +131,9 @@ void assert_address_in_answer(struct extracted_response *ex_response, int a, int for(i = 0; i < length; i++) { - ASSERT_RC(getdns_list_get_dict(ex_response->answer, i, &rr_dict), + ASSERT_RC(getdns_list_get_dict(ex_response->answer, i, &rr_dict), GETDNS_RETURN_GOOD, "Failed to extract \"answer\" record"); - ASSERT_RC(getdns_dict_get_int(rr_dict, "type", &type), + ASSERT_RC(getdns_dict_get_int(rr_dict, "type", &type), GETDNS_RETURN_GOOD, "Failed to extract \"type\" from answer record"); switch (type) { @@ -244,8 +248,8 @@ void callbackfn(struct getdns_context *context, /* * If userarg is NULL, either a negative test case - * erroneously reached the query state, or the value - * in userarg (verification function) was somehow + * erroneously reached the query state, or the value + * in userarg (verification function) was somehow * lost in transit. */ ck_assert_msg(userarg != NULL, "Callback called with NULL userarg"); @@ -282,8 +286,38 @@ void callbackfn(struct getdns_context *context, void update_callbackfn(struct getdns_context *context, uint16_t changed_item) { - - ck_assert_msg(changed_item == expected_changed_item, + + ck_assert_msg(changed_item == expected_changed_item, "Expected changed_item == %d, got %d", changed_item, expected_changed_item); } + +void run_event_loop(struct getdns_context* context, struct event_base* base) { + if (event_loop_type == 0) { + while (getdns_context_get_num_pending_requests(context, NULL) > 0) { + event_base_loop(base, EVLOOP_ONCE); + } + } else if (event_loop_type == 1) { + struct timeval tv; + while (getdns_context_get_num_pending_requests(context, &tv) > 0) { + int fd = getdns_context_fd(context); + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + select(fd + 1, &read_fds, NULL, NULL, &tv); + getdns_context_process_async(context); + } + } +} + +struct event_base* create_event_base(struct getdns_context* context) { + if (event_loop_type == 0) { + struct event_base* result = event_base_new(); + ck_assert_msg(result != NULL, "Event base creation failed"); + ASSERT_RC(getdns_extension_set_libevent_base(context, result), + GETDNS_RETURN_GOOD, + "Return code from getdns_extension_set_libevent_base()"); + return result; + } + return NULL; +} diff --git a/src/test/check_getdns_common.h b/src/test/check_getdns_common.h index 31f1e536..6cb58f40 100644 --- a/src/test/check_getdns_common.h +++ b/src/test/check_getdns_common.h @@ -1,6 +1,8 @@ #ifndef _check_getdns_common_h_ #define _check_getdns_common_h_ +#include "check_getdns_libevent.h" + #define TRUE 1 #define FALSE 0 #define MAXLEN 200 @@ -9,7 +11,8 @@ extern int callback_completed; extern int callback_canceled; extern uint16_t expected_changed_item; - + extern int event_loop_type; + struct extracted_response { uint32_t top_answer_type; struct getdns_bindata *top_canonical_name; @@ -26,7 +29,7 @@ struct getdns_dict *question; uint32_t status; }; - + /* * The ASSERT_RC macro is used to assert * whether the return code from the last @@ -42,12 +45,12 @@ "%s: expecting %s: %d, but received: %d: %s", \ prefix, #expected_rc, expected_rc, evaluated_rc, error_string); \ } - + /* - * The CONTEXT_CREATE macro is used to + * The CONTEXT_CREATE macro is used to * create a context and assert the proper - * return code is returned. - */ + * return code is returned. + */ #define CONTEXT_CREATE(set_from_os) \ ASSERT_RC(getdns_context_create(&context, set_from_os), \ GETDNS_RETURN_GOOD, \ @@ -60,24 +63,19 @@ #define CONTEXT_DESTROY getdns_context_destroy(context); /* - * The EVENT_BASE_CREATE macro is used to - * create an event base and put it in the - * context. - */ - #define EVENT_BASE_CREATE \ - event_base = event_base_new(); \ - ck_assert_msg(event_base != NULL, "Event base creation failed"); \ - ASSERT_RC(getdns_extension_set_libevent_base(context, event_base), \ - GETDNS_RETURN_GOOD, \ - "Return code from getdns_extension_set_libevent_base()"); - + * The EVENT_BASE_CREATE macro is used to + * create an event base and put it in the + * context. + */ + #define EVENT_BASE_CREATE event_base = create_event_base(context); + /* * The RUN_EVENT_LOOP macro calls the event loop. */ - #define RUN_EVENT_LOOP event_base_dispatch(event_base); + #define RUN_EVENT_LOOP run_event_loop(context, event_base); /* - * The LIST_CREATE macro simply creates a + * The LIST_CREATE macro simply creates a * list and verifies the returned pointer * is not NULL. */ @@ -92,7 +90,7 @@ #define LIST_DESTROY(list) getdns_list_destroy(list); /* - * The DICT_CREATE macro simply creates a + * The DICT_CREATE macro simply creates a * dict and verifies the returned pointer * is not NULL. */ @@ -105,8 +103,8 @@ * The DICT_DESTROY macro destroys a dict. */ #define DICT_DESTROY(dict) getdns_dict_destroy(dict); - - /* + + /* * The process_response macro declares the * variables needed to house the response and * calls the function that extracts it. @@ -116,14 +114,14 @@ extract_response(response, &ex_response); // - // FUNCTION DECLARATIONS - // + // FUNCTION DECLARATIONS + // /* * extract_response extracts all of the various information * a test may want to look at from the response. */ - void extract_response(struct getdns_dict *response, struct extracted_response *ex_response); + void extract_response(struct getdns_dict *response, struct extracted_response *ex_response); /* * assert_noerror asserts that the rcode is 0. @@ -137,10 +135,10 @@ void assert_nodata(struct extracted_response *ex_response); /* - * assert_address_records_in_answer asserts that ancount in - * the header * is >= 1, ancount is equal to the length - * of "answer", and that all of * the records in the - * answer section are A and/or AAAA resource records based + * assert_address_records_in_answer asserts that ancount in + * the header * is >= 1, ancount is equal to the length + * of "answer", and that all of * the records in the + * answer section are A and/or AAAA resource records based * on the value of the a/aaaa arguments. */ void assert_address_in_answer(struct extracted_response *ex_response, int a, int aaaa); @@ -169,17 +167,22 @@ * be called for positive tests and will verify the * response that is returned. */ - void callbackfn(struct getdns_context *context, + void callbackfn(struct getdns_context *context, uint16_t callback_type, - struct getdns_dict *response, + struct getdns_dict *response, void *userarg, getdns_transaction_t transaction_id); /* - * update_callbackfn is the callback function given to - * getdns_context_set_context_update_callback tests. + * update_callbackfn is the callback function given to + * getdns_context_set_context_update_callback tests. */ - void update_callbackfn(struct getdns_context *context, + void update_callbackfn(struct getdns_context *context, uint16_t changed_item); + /* run the event loop */ + void run_event_loop(struct getdns_context *context, struct event_base* event_base); + + struct event_base* create_event_base(struct getdns_context* context); + #endif diff --git a/src/test/tests_dnssec.c b/src/test/tests_dnssec.c index a9f4f3e9..20464321 100644 --- a/src/test/tests_dnssec.c +++ b/src/test/tests_dnssec.c @@ -44,6 +44,7 @@ #include #include "testmessages.h" #include +#include /* Set up the callback function, which will also do the processing of the results */ void diff --git a/src/test/tests_stub_async.c b/src/test/tests_stub_async.c index 62b4831c..d43d3c4e 100644 --- a/src/test/tests_stub_async.c +++ b/src/test/tests_stub_async.c @@ -43,6 +43,8 @@ #include #include "testmessages.h" #include +#include +#include /* Set up the callback function, which will also do the processing of the results */ void @@ -90,8 +92,12 @@ main(int argc, char** argv) getdns_context_destroy(this_context); return (GETDNS_RETURN_GENERIC_ERROR); } - (void) getdns_extension_set_libevent_base(this_context, - this_event_base); + if (getdns_extension_set_libevent_base(this_context, + this_event_base) != GETDNS_RETURN_GOOD) { + fprintf(stderr, "Setting event base failed."); + getdns_context_destroy(this_context); + return (GETDNS_RETURN_GENERIC_ERROR); + } /* Set up the getdns call */ const char *this_name = argc > 1 ? argv[1] : "www.google.com"; char *this_userarg = "somestring"; // Could add things here to help identify this call @@ -117,7 +123,10 @@ main(int argc, char** argv) // } else { /* Call the event loop */ - event_base_dispatch(this_event_base); + event_base_loop(this_event_base, EVLOOP_ONCE); + while (getdns_context_get_num_pending_requests(this_context, NULL) > 0) { + event_base_loop(this_event_base, EVLOOP_ONCE); + } // TODO: check the return value above } /* Clean up */ diff --git a/src/types-internal.h b/src/types-internal.h index 4c5170d9..36d90e9e 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -9,7 +9,7 @@ /* * Copyright (c) 2013, Versign, Inc. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright @@ -100,9 +100,6 @@ struct getdns_context; /* declarations */ struct getdns_dns_req; struct getdns_network_req; -struct ub_ctx; -struct event; -struct event_base; typedef enum network_req_state_enum { @@ -165,21 +162,9 @@ typedef struct getdns_dns_req /* first request in list */ struct getdns_network_req *first_req; - /* request timeout event */ - struct event *timeout; - - /* local callback timer */ - struct event *local_cb_timer; - - /* event base this req is scheduled on */ - struct event_base *ev_base; - /* context that owns the request */ struct getdns_context *context; - /* ub_ctx issuing the request */ - struct ub_ctx *unbound; - /* request extensions */ struct getdns_dict *extensions; @@ -190,6 +175,9 @@ typedef struct getdns_dns_req /* the transaction id */ getdns_transaction_t trans_id; + /* local timeout id */ + getdns_transaction_t local_timeout_id; + } getdns_dns_req; #define MF_PLAIN ((void *)&plain_mem_funcs_user_arg) @@ -247,11 +235,40 @@ getdns_network_req *network_req_new(getdns_dns_req * owner, /* dns request utils */ getdns_dns_req *dns_req_new(struct getdns_context *context, - struct ub_ctx *unbound, const char *name, uint16_t request_type, struct getdns_dict *extensions); void dns_req_free(getdns_dns_req * req); + +/* extensions */ +typedef void (*getdns_timeout_callback) (void* userarg); + +/* context timeout data */ +typedef struct getdns_timeout_data { + getdns_transaction_t transaction_id; + struct timeval timeout_time; + getdns_timeout_callback callback; + void* userarg; + void* extension_timer; + struct getdns_context* context; +} getdns_timeout_data_t; + +typedef getdns_return_t (*getdns_eventloop_cleanup_t)(struct getdns_context* context, void* eventloop_data); +typedef getdns_return_t (*getdns_eventloop_schedule_timeout_t)(struct getdns_context* context, + void* eventloop_data, uint16_t timeout, + getdns_timeout_data_t* timeout_data, + void** eventloop_timer); +typedef getdns_return_t (*getdns_eventloop_clear_timeout_t)(struct getdns_context* context, + void* eventloop_data, void** eventloop_timer); + + +typedef struct getdns_eventloop_extension { + getdns_eventloop_cleanup_t cleanup_data; + getdns_eventloop_schedule_timeout_t schedule_timeout; + getdns_eventloop_clear_timeout_t clear_timeout; +} getdns_eventloop_extension; + + #endif /* types-internal.h */ diff --git a/src/util-internal.c b/src/util-internal.c index 05893753..df01afdb 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -41,6 +41,7 @@ #include "list.h" #include "util-internal.h" #include "types-internal.h" +#include #include "rr-dict.h" /** @@ -571,6 +572,22 @@ validate_extensions(struct getdns_dict * extensions) return GETDNS_RETURN_GOOD; } /* validate_extensions */ +getdns_return_t +getdns_apply_network_result(getdns_network_req* netreq, + struct ub_result* ub_res) { + ldns_buffer *result = ldns_buffer_new(ub_res->answer_len); + if (!result) { + return GETDNS_RETURN_GENERIC_ERROR; + } + ldns_status r = + ldns_wire2pkt(&(netreq->result), ub_res->answer_packet, ub_res->answer_len); + if (r != LDNS_STATUS_OK) { + return GETDNS_RETURN_GENERIC_ERROR; + } + return GETDNS_RETURN_GOOD; +} + + getdns_return_t validate_dname(const char* dname) { int len; diff --git a/src/util-internal.h b/src/util-internal.h index 485041da..5ef57506 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -41,6 +41,10 @@ #include #include "context.h" +struct ub_result; +struct getdns_network_req; +getdns_return_t getdns_apply_network_result(getdns_network_req* netreq, struct ub_result* result); + #define GETDNS_MAX_DNAME_LEN 255 #define GETDNS_MAX_LABEL_LEN 63 diff --git a/src/validation-chain.c b/src/validation-chain.c index 84b506f2..96f36f87 100644 --- a/src/validation-chain.c +++ b/src/validation-chain.c @@ -110,24 +110,23 @@ static void callback_on_complete_chain(struct validation_chain *chain) static void -ub_chain_response_callback(void *arg, int err, void *result, int packet_len, - int sec, char *bogus) +ub_chain_response_callback(void *arg, int err, struct ub_result* ub_res) { struct chain_response *response = (struct chain_response *) arg; - ldns_status r; - ldns_pkt *p; - ldns_rr_list *answer; - ldns_rr_list *keys; - size_t i; + ldns_status r; + ldns_pkt *p; + ldns_rr_list *answer; + ldns_rr_list *keys; + size_t i; - response->err = err; - response->sec = sec; - response->bogus = bogus; + response->err = err; + response->sec = ub_res ? ub_res->secure : 0; + response->bogus = ub_res ? ub_res->why_bogus : NULL; - if (result == NULL) - goto done; + if (ub_res == NULL) + goto done; - r = ldns_wire2pkt(&p, (uint8_t *)result, (size_t)packet_len); + r = ldns_wire2pkt(&p, ub_res->answer_packet, ub_res->answer_len); if (r != LDNS_STATUS_OK) { if (err == 0) response->err = r; @@ -165,6 +164,7 @@ ub_chain_response_callback(void *arg, int err, void *result, int packet_len, ldns_rr_list_free(keys); ldns_pkt_free(p); + ub_resolve_free(ub_res); done: if (response->err == 0 && response->result == NULL) response->err = -1; @@ -202,14 +202,14 @@ static void launch_chain_link_lookup(struct validation_chain *chain, char *name) ldns_rbtree_insert(&(chain->root), (ldns_rbnode_t *)link); chain->lock++; - r = ub_resolve_event(chain->dns_req->unbound, + r = ub_resolve_async(chain->dns_req->context->unbound_ctx, name, LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN, &link->DNSKEY, ub_chain_response_callback, &link->DNSKEY.unbound_id); if (r != 0) link->DNSKEY.err = r; if (name[0] != '.' || name[1] != '\0') { - r = ub_resolve_event(chain->dns_req->unbound, + r = ub_resolve_async(chain->dns_req->context->unbound_ctx, name, LDNS_RR_TYPE_DS, LDNS_RR_CLASS_IN, &link->DS, ub_chain_response_callback, &link->DS.unbound_id); if (r != 0)