diff --git a/README.md b/README.md index dbd72731..ec28e45c 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ The goals of this implementation of the getdns API are: * Provide an open source implementation, in C, of the formally described getdns API by getdns API team at * Initial support for FreeBSD, OSX, Linux (CentOS/RHEL, Ubuntu) via functional "configure" script +* Initial support for Windows 8.1 * Initial support to include the Android platform * Include examples and tests as part of the build * Document code using doxygen @@ -84,6 +85,7 @@ Building/External Dependencies ============================== External dependencies are linked outside the getdns API build tree (we rely on configure to find them). We would like to keep the dependency tree short. +Please refer to section for building on Windows for separate dependency and build instructions. * [libunbound from NLnet Labs](https://unbound.net/) version 1.4.16 or later. * [libidn from the FSF](https://www.gnu.org/software/libidn/) version 1. @@ -112,6 +114,8 @@ The implementation works with a variety of event loops, each built as a separate * [libuv](https://github.com/joyent/libuv) * [libev](http://software.schmorp.de/pkg/libev.html) +NOTE: The current Windows implementation does not support the above. + ## Regression Tests A suite of regression tests are included with the library, if you make changes or just @@ -119,6 +123,8 @@ want to sanity check things on your system take a look at src/test. You will ne to install [libcheck](http://check.sourceforge.net/) and [libldns from NLnet Labs](https://nlnetlabs.nl/projects/ldns/) version 1.6.17 or later. Both libraries are also available from many of the package repositories for the more popular operating systems. +NOTE: The current Windows implementation does not support the above. + ## DNSSEC For the library to be DNSSEC capable, it needs to know the root trust anchor. @@ -167,8 +173,9 @@ The primary platforms targeted are Linux and FreeBSD, other platform are support * RHEL/CentOS 6.4 * OSX 10.8 * Ubuntu 14.04 +* Microsoft Windows 8.1 (initial support for DNSSEC but no TLS provided for version 0.5.1) -We intend to add MS-Windows, Android and other platforms to the releases as we have time to port it. +We intend to add Android and other platforms to the releases as we have time to port it. ##Platform Specific Build Reports @@ -233,6 +240,22 @@ Note that in order to compile the examples, the `--with-libevent` switch is requ As of the 0.2.0 release, when installing via Homebrew, the trust anchor is expected to be located at `$(brew --prefix)/etc/getdns-root.key`. Additionally, the OpenSSL library installed by Homebrew is linked against. Note that the Homebrew OpenSSL installation clones the Keychain certificates to the default OpenSSL location so TLS certificate authentication should work out of the box. + +### Microsoft Windows 8.1 + +The windows version of getdns currently only is supported in stub only mode. + +To configure: + ./configure --enable-use-winsock --enable-stub-only --with-trust-anchor="c:\\\MinGW\\\msys\\\1.0\\\etc\\\unbound\\\getdns-root.key" + + The trust anchor is also installed by unbound on c:\program Files (X86)\unbound\root.key and can be referenced from there + or anywhere else that the user chooses to configure it. + + After configuring, do a 'make' and 'make install' to build getdns for Windows. + + There is a sample, getdns_query.exe which can be built in src/samplewin using 'make' + + Contributors ============ * Theogene Bucuti diff --git a/configure.ac b/configure.ac index 7422f67c..a59974f8 100644 --- a/configure.ac +++ b/configure.ac @@ -429,9 +429,30 @@ case "$enable_stub_only" in ;; esac + + # search to set include and library paths right # find libidn my_with_libidn=1 + +AC_MSG_NOTICE([Checking if building on Windows]) +AC_ARG_ENABLE(use-winsock, AC_HELP_STRING([--enable-use-winsock], [Enable building on Microsoft Windows using mingw])) +case "$enable_use_winsock" in + yes) + AC_MSG_NOTICE([ Building on Windows ... YES! ]) + AC_DEFINE_UNQUOTED([USE_WINSOCK], [1], [Define this to enable Windows build.]) + AC_DEFINE_UNQUOTED([GETDNS_ON_WINDOWS], [1], [Define this to enable Windows build.]) + AC_DEFINE_UNQUOTED([HAVE_WINSOCK2_H], [1], [Define this to enable Windows build.]) + AC_DEFINE_UNQUOTED([HAVE_WS2TCPIP_H], [1], [Define this to enable Windows build.]) + AC_DEFINE_UNQUOTED([STUB_NATIVE_DNSSEC], [1], [Define this to enable Windows build.]) + my_with_libunbound=0 + my_with_libidn=0 + ;; + no|*) + AC_MSG_NOTICE([ Building on Windows ... YES! ]) + ;; +esac + AC_ARG_WITH(libidn, AS_HELP_STRING([--with-libidn=pathname], [path to libidn (default: search /usr/local ..)]), [], [withval="yes"]) @@ -985,6 +1006,35 @@ esac AC_SUBST(C99COMPATFLAGS) AH_BOTTOM([ + +/* the version of the windows API enabled */ +#undef WINVER +#undef _WIN32_WINNT +#define WINVER 0x0600 // 0x0502 +#define _WIN32_WINNT 0x0600 // 0x0502 +#ifdef HAVE_WINSOCK2_H +#include +#include +#endif + +#ifdef HAVE_WS2TCPIP_H +#include +#endif + +#ifndef USE_WINSOCK +#define ARG_LL "%ll" +#else +#define ARG_LL "%I64" +#endif + +/* detect if we need to cast to unsigned int for FD_SET to avoid warnings */ +#ifdef HAVE_WINSOCK2_H +#define FD_SET_T (u_int) +#else +#define FD_SET_T +#endif + + #include #include #include @@ -1094,13 +1144,6 @@ unsigned char *SHA512(void* data, unsigned int data_len, unsigned char *digest); # define ATTR_UNUSED(x) x #endif /* !HAVE_ATTR_UNUSED */ -/* detect if we need to cast to unsigned int for FD_SET to avoid warnings */ -#ifdef HAVE_WINSOCK2_H -#define FD_SET_T (u_int) -#else -#define FD_SET_T -#endif - #ifdef TIME_WITH_SYS_TIME # include # include diff --git a/src/Makefile.in b/src/Makefile.in index d6d90054..1a9d29d6 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -74,7 +74,7 @@ LIBOBJDIR= LIBOBJS=@LIBOBJS@ COMPAT_OBJ=$(LIBOBJS:.o=.lo) -UTIL_OBJ=mini_event.lo rbtree.lo val_secalgo.lo +UTIL_OBJ=mini_event.lo winsock_event.lo rbtree.lo val_secalgo.lo EXTENSION_OBJ=libmini_event.lo libevent.lo libev.lo @@ -229,63 +229,63 @@ const-info.lo const-info.o: $(srcdir)/const-info.c getdns/getdns.h getdns/getdns context.lo context.o: $(srcdir)/context.c config.h $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h \ $(srcdir)/gldns/wire2str.h $(srcdir)/context.h getdns/getdns.h getdns/getdns_extra.h \ getdns/getdns.h $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h $(srcdir)/extension/libmini_event.h \ - config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h \ + config.h $(srcdir)/util/mini_event.h $(srcdir)/util/winsock_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h \ $(srcdir)/util-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h \ $(srcdir)/dnssec.h $(srcdir)/stub.h $(srcdir)/list.h convert.lo convert.o: $(srcdir)/convert.c config.h getdns/getdns.h getdns/getdns_extra.h \ getdns/getdns.h $(srcdir)/util-internal.h $(srcdir)/context.h $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h \ - $(srcdir)/extension/libmini_event.h config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \ + $(srcdir)/extension/libmini_event.h config.h $(srcdir)/util/mini_event.h $(srcdir)/util/winsock_event.h $(srcdir)/util/rbtree.h \ $(srcdir)/types-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h \ $(srcdir)/gldns/wire2str.h $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h dict.lo dict.o: $(srcdir)/dict.c $(srcdir)/types-internal.h getdns/getdns.h getdns/getdns_extra.h \ getdns/getdns.h $(srcdir)/util/rbtree.h $(srcdir)/util-internal.h config.h $(srcdir)/context.h \ - $(srcdir)/extension/libmini_event.h config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \ + $(srcdir)/extension/libmini_event.h config.h $(srcdir)/util/mini_event.h $(srcdir)/util/winsock_event.h $(srcdir)/util/rbtree.h \ $(srcdir)/types-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h \ $(srcdir)/dict.h $(srcdir)/list.h $(srcdir)/const-info.h $(srcdir)/gldns/wire2str.h dnssec.lo dnssec.o: $(srcdir)/dnssec.c getdns/getdns.h config.h $(srcdir)/context.h \ getdns/getdns_extra.h getdns/getdns.h $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h \ - $(srcdir)/extension/libmini_event.h config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \ + $(srcdir)/extension/libmini_event.h config.h $(srcdir)/util/mini_event.h $(srcdir)/util/winsock_event.h $(srcdir)/util/rbtree.h \ $(srcdir)/types-internal.h $(srcdir)/util-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ $(srcdir)/gldns/pkthdr.h $(srcdir)/dnssec.h $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/wire2str.h \ $(srcdir)/gldns/keyraw.h $(srcdir)/gldns/parseutil.h $(srcdir)/general.h $(srcdir)/dict.h $(srcdir)/list.h \ $(srcdir)/util/val_secalgo.h general.lo general.o: $(srcdir)/general.c config.h $(srcdir)/gldns/wire2str.h $(srcdir)/context.h getdns/getdns.h \ getdns/getdns_extra.h getdns/getdns.h $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h \ - $(srcdir)/extension/libmini_event.h config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \ + $(srcdir)/extension/libmini_event.h config.h $(srcdir)/util/mini_event.h $(srcdir)/util/winsock_event.h $(srcdir)/util/rbtree.h \ $(srcdir)/types-internal.h $(srcdir)/util-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ $(srcdir)/gldns/pkthdr.h $(srcdir)/dnssec.h $(srcdir)/stub.h list.lo list.o: $(srcdir)/list.c $(srcdir)/types-internal.h getdns/getdns.h getdns/getdns_extra.h \ getdns/getdns.h $(srcdir)/util/rbtree.h $(srcdir)/util-internal.h config.h $(srcdir)/context.h \ - $(srcdir)/extension/libmini_event.h config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \ + $(srcdir)/extension/libmini_event.h config.h $(srcdir)/util/mini_event.h $(srcdir)/util/winsock_event.h $(srcdir)/util/rbtree.h \ $(srcdir)/types-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h \ $(srcdir)/list.h $(srcdir)/dict.h request-internal.lo request-internal.o: $(srcdir)/request-internal.c config.h $(srcdir)/types-internal.h \ getdns/getdns.h getdns/getdns_extra.h getdns/getdns.h $(srcdir)/util/rbtree.h \ $(srcdir)/util-internal.h $(srcdir)/context.h $(srcdir)/extension/libmini_event.h config.h \ - $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ + $(srcdir)/util/mini_event.h $(srcdir)/util/winsock_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/str2wire.h \ $(srcdir)/gldns/rrdef.h $(srcdir)/dict.h rr-dict.lo rr-dict.o: $(srcdir)/rr-dict.c $(srcdir)/rr-dict.h config.h getdns/getdns.h $(srcdir)/gldns/gbuffer.h \ $(srcdir)/util-internal.h $(srcdir)/context.h getdns/getdns_extra.h getdns/getdns.h \ $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h $(srcdir)/extension/libmini_event.h config.h \ - $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h $(srcdir)/rr-iter.h \ + $(srcdir)/util/winsock_event.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h $(srcdir)/rr-iter.h \ $(srcdir)/gldns/pkthdr.h $(srcdir)/dict.h rr-iter.lo rr-iter.o: $(srcdir)/rr-iter.c $(srcdir)/rr-iter.h getdns/getdns.h $(srcdir)/rr-dict.h config.h \ $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/gldns/rrdef.h stub.lo stub.o: $(srcdir)/stub.c config.h $(srcdir)/stub.h getdns/getdns.h $(srcdir)/types-internal.h \ getdns/getdns_extra.h getdns/getdns.h $(srcdir)/util/rbtree.h $(srcdir)/gldns/gbuffer.h \ $(srcdir)/gldns/pkthdr.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h $(srcdir)/rr-iter.h \ - $(srcdir)/rr-dict.h $(srcdir)/context.h $(srcdir)/extension/libmini_event.h config.h $(srcdir)/util/mini_event.h \ + $(srcdir)/rr-dict.h $(srcdir)/context.h $(srcdir)/extension/libmini_event.h config.h $(srcdir)/util/mini_event.h $(srcdir)/util/winsock_event.h \ $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h $(srcdir)/util-internal.h $(srcdir)/general.h sync.lo sync.o: $(srcdir)/sync.c getdns/getdns.h config.h $(srcdir)/context.h getdns/getdns_extra.h \ getdns/getdns.h $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h $(srcdir)/extension/libmini_event.h \ - config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h $(srcdir)/general.h \ + config.h $(srcdir)/util/mini_event.h $(srcdir)/util/winsock_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h $(srcdir)/general.h \ $(srcdir)/util-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h \ $(srcdir)/dnssec.h $(srcdir)/stub.h $(srcdir)/gldns/wire2str.h util-internal.lo util-internal.o: $(srcdir)/util-internal.c getdns/getdns.h $(srcdir)/dict.h $(srcdir)/util/rbtree.h \ $(srcdir)/types-internal.h getdns/getdns_extra.h getdns/getdns.h $(srcdir)/list.h \ $(srcdir)/util-internal.h config.h $(srcdir)/context.h $(srcdir)/extension/libmini_event.h config.h \ - $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ + $(srcdir)/util/winsock_event.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h version.lo version.o: version.c gbuffer.lo gbuffer.o: $(srcdir)/gldns/gbuffer.c config.h $(srcdir)/gldns/gbuffer.h @@ -309,17 +309,19 @@ getentropy_solaris.lo getentropy_solaris.o: $(srcdir)/compat/getentropy_solaris. getentropy_win.lo getentropy_win.o: $(srcdir)/compat/getentropy_win.c sha512.lo sha512.o: $(srcdir)/compat/sha512.c config.h strlcpy.lo strlcpy.o: $(srcdir)/compat/strlcpy.c config.h -mini_event.lo mini_event.o: $(srcdir)/util/mini_event.c config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \ +winsock_event.lo winsock_event.o: $(srcdir)/util/winsock_event.c config.h $(srcdir)/util/winsock_event.h $(srcdir)/util/rbtree.h \ + $(srcdir)/util/fptr_wlist.h + mini_event.lo mini_event.o: $(srcdir)/util/mini_event.c config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \ $(srcdir)/util/fptr_wlist.h rbtree.lo rbtree.o: $(srcdir)/util/rbtree.c config.h $(srcdir)/util/log.h $(srcdir)/util-internal.h config.h \ $(srcdir)/context.h getdns/getdns.h getdns/getdns_extra.h getdns/getdns.h \ $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h $(srcdir)/extension/libmini_event.h \ - $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ + $(srcdir)/util/winsock_event.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/util/fptr_wlist.h val_secalgo.lo val_secalgo.o: $(srcdir)/util/val_secalgo.c config.h $(srcdir)/util/val_secalgo.h $(srcdir)/util/log.h \ $(srcdir)/util-internal.h config.h $(srcdir)/context.h getdns/getdns.h getdns/getdns_extra.h \ getdns/getdns.h $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h $(srcdir)/extension/libmini_event.h \ - $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ + $(srcdir)/util/winsock_event.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/keyraw.h \ $(srcdir)/gldns/gbuffer.h libev.lo libev.o: $(srcdir)/extension/libev.c $(srcdir)/getdns/getdns_ext_libev.h getdns/getdns.h \ @@ -329,7 +331,7 @@ libevent.lo libevent.o: $(srcdir)/extension/libevent.c $(srcdir)/getdns/getdns_e getdns/getdns.h getdns/getdns_extra.h $(srcdir)/types-internal.h getdns/getdns.h \ getdns/getdns_extra.h $(srcdir)/util/rbtree.h config.h libmini_event.lo libmini_event.o: $(srcdir)/extension/libmini_event.c $(srcdir)/extension/libmini_event.h \ - config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h \ + config.h $(srcdir)/util/winsock_event.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h \ getdns/getdns.h getdns/getdns_extra.h getdns/getdns.h $(srcdir)/util/rbtree.h \ $(srcdir)/context.h config.h $(srcdir)/types-internal.h $(srcdir)/extension/libmini_event.h \ $(srcdir)/util-internal.h $(srcdir)/context.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ @@ -337,5 +339,5 @@ libmini_event.lo libmini_event.o: $(srcdir)/extension/libmini_event.c $(srcdir)/ libuv.lo libuv.o: $(srcdir)/extension/libuv.c config.h $(srcdir)/getdns/getdns_ext_libuv.h \ getdns/getdns.h getdns/getdns_extra.h $(srcdir)/util-internal.h config.h $(srcdir)/context.h \ getdns/getdns.h getdns/getdns_extra.h $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h \ - $(srcdir)/extension/libmini_event.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \ + $(srcdir)/extension/libmini_event.h $(srcdir)/util/winsock_event.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \ $(srcdir)/types-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h diff --git a/src/compat/arc4random.c b/src/compat/arc4random.c index 27a626b7..890699cd 100644 --- a/src/compat/arc4random.c +++ b/src/compat/arc4random.c @@ -33,8 +33,42 @@ #include #include #include -#ifndef UB_ON_WINDOWS +#ifndef GETDNS_ON_WINDOWS #include +#else +#include +#include + +int getentropy(void *buf, size_t len); + +/* +* On Windows, CryptGenRandom is supposed to be a well-seeded +* cryptographically strong random number generator. +*/ +int +getentropy(void *buf, size_t len) +{ + HCRYPTPROV provider; + + if (len > 256) { + errno = EIO; + return -1; + } + + if (CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT) == 0) + goto fail; + if (CryptGenRandom(provider, len, buf) == 0) { + CryptReleaseContext(provider, 0); + goto fail; + } + CryptReleaseContext(provider, 0); + return (0); + +fail: + errno = EIO; + return (-1); +} #endif #define KEYSTREAM_ONLY @@ -73,7 +107,7 @@ _rs_init(u_char *buf, size_t n) return; if (rs == NULL) { -#ifndef UB_ON_WINDOWS +#ifndef GETDNS_ON_WINDOWS if ((rs = mmap(NULL, sizeof(*rs), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) abort(); @@ -88,7 +122,7 @@ _rs_init(u_char *buf, size_t n) #endif } if (rsx == NULL) { -#ifndef UB_ON_WINDOWS +#ifndef GETDNS_ON_WINDOWS if ((rsx = mmap(NULL, sizeof(*rsx), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) abort(); diff --git a/src/compat/getentropy_linux.c b/src/compat/getentropy_linux.c index 76f0f9df..6c642a96 100644 --- a/src/compat/getentropy_linux.c +++ b/src/compat/getentropy_linux.c @@ -16,7 +16,10 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + #include "config.h" +#ifndef GETDNS_ON_WINDOWS + /* #define _POSIX_C_SOURCE 199309L @@ -538,3 +541,4 @@ getentropy_fallback(void *buf, size_t len) errno = EIO; return -1; } +#endif \ No newline at end of file diff --git a/src/context.c b/src/context.c index a623cec5..5076a14a 100644 --- a/src/context.c +++ b/src/context.c @@ -34,17 +34,27 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" + +#ifndef USE_WINSOCK #include +#include +#include +#else +#include +#include +typedef unsigned short in_port_t; +#endif + +#include #include #include #include -#include -#include + #include -#include #include -#include "config.h" + #include "gldns/str2wire.h" #include "gldns/wire2str.h" #include "context.h" @@ -356,7 +366,11 @@ create_local_hosts(getdns_context *context) int start_of_line = 1; getdns_dict *address = NULL; +#ifdef USE_WINSOCK + in = fopen("c:\\WINDOWS\\system32\\drivers\\etc\\hosts", "r"); +#else in = fopen("/etc/hosts", "r"); +#endif while (fgets(pos, (int)(sizeof(buf) - (pos - buf)), in)) { pos = buf; /* Break out of for to read more */ @@ -751,6 +765,95 @@ set_os_defaults(struct getdns_context *context) return GETDNS_RETURN_GOOD; } /* set_os_defaults */ +#ifdef USE_WINSOCK +static getdns_return_t +set_os_defaults_windows(struct getdns_context *context) +{ + FILE *in; + char line[1024], domain[1024]; + char *parse, *token, prev_ch; + size_t upstreams_limit = 10, length; + struct getdns_bindata bindata; + struct addrinfo hints; + struct addrinfo *result; + getdns_upstream *upstream; + int s; + + if (context->fchg_resolvconf == NULL) { + context->fchg_resolvconf = + GETDNS_MALLOC(context->my_mf, struct filechg); + if (context->fchg_resolvconf == NULL) + return GETDNS_RETURN_MEMORY_ERROR; + context->fchg_resolvconf->fn = "InvalidOnWindows"; + context->fchg_resolvconf->prevstat = NULL; + context->fchg_resolvconf->changes = GETDNS_FCHG_NOCHANGES; + context->fchg_resolvconf->errors = GETDNS_FCHG_NOERROR; + } + _getdns_filechg_check(context, context->fchg_resolvconf); + + context->suffix = getdns_list_create_with_context(context); + context->upstreams = upstreams_create(context, upstreams_limit); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = 0; /* Datagram socket */ + hints.ai_flags = AI_NUMERICHOST; /* No reverse name lookups */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + //g + FIXED_INFO *info; + ULONG buflen = sizeof(*info); + IP_ADDR_STRING *ptr; + + info = (FIXED_INFO *)malloc(sizeof(FIXED_INFO)); + if (info == NULL) + return GETDNS_RETURN_GENERIC_ERROR; + + if (GetNetworkParams(info, &buflen) == ERROR_BUFFER_OVERFLOW) { + free(info); + info = (FIXED_INFO *)malloc(buflen); + if (info == NULL) + return GETDNS_RETURN_GENERIC_ERROR; + } + + if (GetNetworkParams(info, &buflen) == NO_ERROR) { + int retval = 0; + ptr = &(info->DnsServerList); + *domain = 0; + while (ptr) { + for (size_t i = 0; i < GETDNS_UPSTREAM_TRANSPORTS; i++) { + char *port_str = getdns_port_str_array[i]; + if ((s = getaddrinfo(ptr->IpAddress.String, port_str, &hints, &result))) + continue; + if (!result) + continue; + + upstream = &context->upstreams-> + upstreams[context->upstreams->count++]; + upstream_init(upstream, context->upstreams, result); + upstream->transport = getdns_upstream_transports[i]; + freeaddrinfo(result); + } + ptr = ptr->Next; + + } + free(info); + } + + + (void)getdns_list_get_length(context->suffix, &length); + if (length == 0 && *domain != 0) { + bindata.data = (uint8_t *)domain; + bindata.size = strlen(domain) + 1; + (void)getdns_list_set_bindata(context->suffix, 0, &bindata); + } + return GETDNS_RETURN_GOOD; +} /* set_os_defaults_windows */ +#endif + /* compare of transaction ids in DESCENDING order so that 0 comes last */ @@ -891,8 +994,14 @@ getdns_context_create_with_extended_memory_functions( result->fchg_resolvconf = NULL; result->fchg_hosts = NULL; + //g resolv.conf does not exist on Windows, handle differently +#ifndef USE_WINSOCK if (set_from_os && (r = set_os_defaults(result))) goto error; +#else + if (set_from_os && (r = set_os_defaults_windows(result))) + goto error; +#endif result->dnssec_allowed_skew = 0; result->edns_maximum_udp_payload_size = -1; @@ -1686,7 +1795,7 @@ getdns_context_set_dnssec_allowed_skew(struct getdns_context *context, */ getdns_return_t getdns_context_set_upstream_recursive_servers(struct getdns_context *context, - struct getdns_list *upstream_list) +struct getdns_list *upstream_list) { getdns_return_t r; size_t count = 0; @@ -1703,16 +1812,16 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; } memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - hints.ai_socktype = 0; /* Datagram socket */ - hints.ai_flags = AI_NUMERICHOST; /* No reverse name lookups */ - hints.ai_protocol = 0; /* Any protocol */ + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = 0; /* Datagram socket */ + hints.ai_flags = AI_NUMERICHOST; /* No reverse name lookups */ + hints.ai_protocol = 0; /* Any protocol */ hints.ai_canonname = NULL; - hints.ai_addr = NULL; - hints.ai_next = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; upstreams = upstreams_create( - context, count * GETDNS_UPSTREAM_TRANSPORTS); + context, count * GETDNS_UPSTREAM_TRANSPORTS); for (i = 0; i < count; i++) { getdns_dict *dict; getdns_bindata *address_type; @@ -1727,7 +1836,7 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, goto error; if ((r = getdns_dict_get_bindata( - dict, "address_type",&address_type))) + dict, "address_type", &address_type))) goto error; if (address_type->size < 4) goto invalid_parameter; @@ -1738,24 +1847,24 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, else goto invalid_parameter; if ((r = getdns_dict_get_bindata( - dict, "address_data", &address_data))) + dict, "address_data", &address_data))) goto error; if ((addr.ss_family == AF_INET && - address_data->size != 4) || - (addr.ss_family == AF_INET6 && - address_data->size != 16)) + address_data->size != 4) || + (addr.ss_family == AF_INET6 && + address_data->size != 16)) goto invalid_parameter; if (inet_ntop(addr.ss_family, address_data->data, - addrstr, 1024) == NULL) + addrstr, 1024) == NULL) goto invalid_parameter; if (getdns_dict_get_bindata(dict, "scope_id", &scope_id) == - GETDNS_RETURN_GOOD) { + GETDNS_RETURN_GOOD) { if (strlen(addrstr) + scope_id->size > 1022) goto invalid_parameter; eos = &addrstr[strlen(addrstr)]; *eos++ = '%'; - (void) memcpy(eos, scope_id->data, scope_id->size); + (void)memcpy(eos, scope_id->data, scope_id->size); eos[scope_id->size] = 0; } @@ -1768,10 +1877,10 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, continue; if (getdns_upstream_transports[j] != GETDNS_TRANSPORT_TLS) - (void) getdns_dict_get_int(dict, "port", &port); + (void)getdns_dict_get_int(dict, "port", &port); else - (void) getdns_dict_get_int(dict, "tls_port", &port); - (void) snprintf(portstr, 1024, "%d", (int)port); + (void)getdns_dict_get_int(dict, "tls_port", &port); + (void)snprintf(portstr, 1024, "%d", (int)port); if (getaddrinfo(addrstr, portstr, &hints, &ai)) goto invalid_parameter; @@ -1787,12 +1896,12 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, upstream_init(upstream, upstreams, ai); upstream->transport = getdns_upstream_transports[j]; if (getdns_upstream_transports[j] == GETDNS_TRANSPORT_TLS || - getdns_upstream_transports[j] == GETDNS_TRANSPORT_STARTTLS) { + getdns_upstream_transports[j] == GETDNS_TRANSPORT_STARTTLS) { if ((r = getdns_dict_get_bindata( dict, "tls_auth_name", &tls_auth_name)) == GETDNS_RETURN_GOOD) { /*TODO: VALIDATE THIS STRING!*/ memcpy(upstream->tls_auth_name, - (char *)tls_auth_name->data, + (char *)tls_auth_name->data, tls_auth_name->size); upstream->tls_auth_name[tls_auth_name->size] = '\0'; } @@ -1813,8 +1922,7 @@ invalid_parameter: error: _getdns_upstreams_dereference(upstreams); return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; -} /* getdns_context_set_upstream_recursive_servers */ - +} static void set_ub_edns_maximum_udp_payload_size(struct getdns_context* context, @@ -2249,8 +2357,9 @@ _getdns_context_prepare_for_resolution(struct getdns_context *context, #ifdef HAVE_TLS_v1_2 /* Create client context, use TLS v1.2 only for now */ context->tls_ctx = SSL_CTX_new(TLSv1_2_client_method()); - if(context->tls_ctx == NULL) - return GETDNS_RETURN_BAD_CONTEXT; + if (context->tls_ctx == NULL) + printf("ERROR! Bad TLS context!"); + //g return GETDNS_RETURN_BAD_CONTEXT; /* Be strict and only use the cipher suites recommended in RFC7525 Unless we later fallback to opportunistic. */ const char* const PREFERRED_CIPHERS = "EECDH+aRSA+AESGCM:EECDH+aECDSA+AESGCM:EDH+aRSA+AESGCM"; diff --git a/src/convert.c b/src/convert.c index 082fe7db..0958a55c 100644 --- a/src/convert.c +++ b/src/convert.c @@ -34,9 +34,11 @@ #include #include -#include #include #include "config.h" +#ifndef USE_WINSOCK +#include +#endif #ifdef HAVE_LIBIDN #include #include diff --git a/src/dict.c b/src/dict.c index 822d5deb..b544ccb7 100644 --- a/src/dict.c +++ b/src/dict.c @@ -35,10 +35,13 @@ */ #include +#include "config.h" +#ifndef USE_WINSOCK #include #include #include #include +#endif #include "types-internal.h" #include "util-internal.h" diff --git a/src/extension/libmini_event.c b/src/extension/libmini_event.c index 266f73ef..d62fc89e 100644 --- a/src/extension/libmini_event.c +++ b/src/extension/libmini_event.c @@ -205,6 +205,17 @@ _getdns_mini_event_init(getdns_context *context, _getdns_mini_event *ext) if (!ext) return GETDNS_RETURN_INVALID_PARAMETER; +#ifdef USE_WINSOCK + int r; + WSADATA wsa_data; + + if ((r = WSAStartup(MAKEWORD(2, 2), &wsa_data)) != 0) { + printf("could not init winsock. WSAStartup: %s", + wsa_strerror(r)); + return GETDNS_RETURN_GENERIC_ERROR; + } +#endif + ext->n_events = 0; ext->loop.vmt = &_getdns_mini_event_vmt; ext->base = _getdns_event_init(&ext->time_secs, &ext->time_tv); diff --git a/src/extension/libmini_event.h b/src/extension/libmini_event.h index 70b2cc53..c3f5b19a 100644 --- a/src/extension/libmini_event.h +++ b/src/extension/libmini_event.h @@ -35,7 +35,11 @@ #define _GETDNS_LIBMINI_EVENT_H_ #include "config.h" +#ifndef USE_WINSOCK #include "util/mini_event.h" +#else +#include "util/winsock_event.h" +#endif #include "types-internal.h" typedef struct _getdns_mini_event { diff --git a/src/samplewin/Makefile.in b/src/samplewin/Makefile.in new file mode 100644 index 00000000..90a2d327 --- /dev/null +++ b/src/samplewin/Makefile.in @@ -0,0 +1,150 @@ +# +# @configure_input@ +# +# Copyright (c) 2013, Verisign, Inc., NLNet Labs +# 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 names of the copyright holders 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. + +package = @PACKAGE_NAME@ +version = @PACKAGE_VERSION@ +tarname = @PACKAGE_TARNAME@ +distdir = $(tarname)-$(version) + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +INSTALL = @INSTALL@ +LIBTOOL = ../../libtool + +srcdir = @srcdir@ + +have_libevent = @have_libevent@ +have_libuv = @have_libuv@ +have_libev = @have_libev@ +NOLIBCHECK = @NOLIBCHECK@ +NOLIBLDNS = @NOLIBLDNS@ + +EXTENSION_LIBEVENT_EXT_LIBS=@EXTENSION_LIBEVENT_EXT_LIBS@ +EXTENSION_LIBEVENT_LDFLAGS=@EXTENSION_LIBEVENT_LDFLAGS@ +EXTENSION_LIBUV_EXT_LIBS=@EXTENSION_LIBUV_EXT_LIBS@ +EXTENSION_LIBUV_LDFLAGS=@EXTENSION_LIBUV_LDFLAGS@ +EXTENSION_LIBEV_EXT_LIBS=@EXTENSION_LIBEV_EXT_LIBS@ +EXTENSION_LIBEV_LDFLAGS=@EXTENSION_LIBEV_LDFLAGS@ + +CHECK_GETDNS=@CHECK_GETDNS@ +CHECK_UV_PROG=@CHECK_UV_PROG@ +CHECK_EVENT_PROG=@CHECK_EVENT_PROG@ +CHECK_EV_PROG=@CHECK_EV_PROG@ + +CC=@CC@ +CFLAGS=-I$(srcdir)/.. -I$(srcdir) -I.. $(cflags) @CFLAGS@ -I/usr/local/ssl/include +LDFLAGS=-L.. @LDFLAGS@ -L/usr/local/ssl/lib -L/usr/local/lib/ +LDLIBS=../libgetdns.la @LIBS@ -lldns -lssl -lcrypto -lgdi32 -lws2_32 -liphlpapi +CHECK_LIBS=@CHECK_LIBS@ +CHECK_CFLAGS=@CHECK_CFLAGS@ +LDNS_LIBS=@LDNS_LIBS@ +LDNS_CFLAGS=@LDNS_CFLAGS@ +LDNS_LDFLAGS=@LDNS_LDFLAGS@ + +CHECK_OBJS=check_getdns_common.lo check_getdns_context_set_timeout.lo \ + check_getdns.lo check_getdns_transport.lo + +ALL_OBJS=$(CHECK_OBJS) getdns_query.lo + +PROGRAMS=getdns_query + +.SUFFIXES: .c .o .a .lo .h + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + +.c.lo: + $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $< -o $@ + +default: all + +all: $(PROGRAMS) + +$(ALL_OBJS): + $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) $(LDNS_CFLAGS) -c $(srcdir)/$(@:.lo=.c) -o $@ + +getdns_query: getdns_query.lo + $(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) -o $@ getdns_query.lo $(LDFLAGS) $(LDLIBS) + + + +clean: + rm -f *.o *.lo $(PROGRAMS) scratchpad + rm -rf .libs + rm -f check_getdns.log + + +$(distdir): FORCE + mkdir -p $(distdir)/src + cp configure.ac $(distdir) + cp configure $(distdir) + cp Makefile.in $(distdir) + cp src/Makefile.in $(distdir)/src + +distcheck: $(distdir).tar.gz + gzip -cd $(distdir).tar.gz | tar xvf - + cd $(distdir) && ./configure + cd $(distdir) && $(MAKE) all + cd $(distdir) && $(MAKE) check + cd $(distdir) && $(MAKE) DESTDIR=$${PWD}/_inst install + cd $(distdir) && $(MAKE) DESTDIR=$${PWD}/_inst uninstall + @remaining="`find $${PWD}/$(distdir)/_inst -type f | wc -l`"; \ + if test "$${remaining}" -ne 0; then + echo "@@@ $${remaining} file(s) remaining in stage directory!"; \ + exit 1; \ + fi + cd $(distdir) && $(MAKE) clean + rm -rf $(distdir) + @echo "*** Package $(distdir).tar.gz is ready for distribution" + +Makefile: $(srcdir)/Makefile.in ../../config.status + cd ../.. && ./config.status src/test/Makefile + +configure.status: configure + cd ../.. && ./config.status --recheck + +depend: + (cd $(srcdir) ; awk 'BEGIN{P=1}{if(P)print}/^# Dependencies/{P=0}' Makefile.in > Makefile.in.new ) + (cd $(srcdir) ; gcc -MM -I. -I.. *.c | \ + sed -e 's? \([a-z_-]*\)\.\([ch]\)? $$(srcdir)/\1.\2?g' \ + -e 's? \$$(srcdir)/config\.h? ../config.h?g' \ + -e 's? $$(srcdir)/\.\./getdns/getdns_extra\.h? ../getdns/getdns_extra.h?g' \ + -e 's? \.\./getdns/getdns_ext_libevent\.h? $$(srcdir)/../getdns/getdns_ext_libevent.h?g' \ + -e 's? \.\./getdns/getdns_ext_libev\.h? $$(srcdir)/../getdns/getdns_ext_libev.h?g' \ + -e 's? \.\./getdns/getdns_ext_libuv\.h? $$(srcdir)/../getdns/getdns_ext_libuv.h?g' \ + -e 's!\(.*\)\.o[ :]*!\1.lo \1.o: !g' >> Makefile.in.new ) + (cd $(srcdir) ; diff Makefile.in.new Makefile.in && rm Makefile.in.new \ + || mv Makefile.in.new Makefile.in ) + + + +.PHONY: clean + +getdns_query.lo getdns_query.o: $(srcdir)/getdns_query.c ../config.h ../getdns/getdns.h \ + ../getdns/getdns_extra.h \ No newline at end of file diff --git a/src/samplewin/getdns_query.c b/src/samplewin/getdns_query.c new file mode 100644 index 00000000..b2319ad8 --- /dev/null +++ b/src/samplewin/getdns_query.c @@ -0,0 +1,1524 @@ +/* + * Copyright (c) 2013, NLNet Labs, Verisign, 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 names of the copyright holders 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 "config.h" +#include +#include +#include +#include +#include +#include +#include "util-internal.h" + +#define MAX_TIMEOUTS FD_SETSIZE + +/* Eventloop based on select */ +typedef struct my_eventloop { + getdns_eventloop base; + getdns_eventloop_event *fd_events[FD_SETSIZE]; + uint64_t fd_timeout_times[FD_SETSIZE]; + getdns_eventloop_event *timeout_events[MAX_TIMEOUTS]; + uint64_t timeout_times[MAX_TIMEOUTS]; +} my_eventloop; + +static uint64_t get_now_plus(uint64_t amount) +{ + struct timeval tv; + uint64_t now; + + if (gettimeofday(&tv, NULL)) { + perror("gettimeofday() failed"); + exit(EXIT_FAILURE); + } + now = tv.tv_sec * 1000000 + tv.tv_usec; + + return (now + amount * 1000) >= now ? now + amount * 1000 : -1; +} + +getdns_return_t +my_eventloop_schedule(getdns_eventloop *loop, + int fd, uint64_t timeout, getdns_eventloop_event *event) +{ + my_eventloop *my_loop = (my_eventloop *)loop; + size_t i; + + assert(loop); + assert(event); + assert(fd < FD_SETSIZE); + + DEBUG_SCHED( "%s(loop: %p, fd: %d, timeout: %"PRIu64", event: %p)\n" + , __FUNCTION__, loop, fd, timeout, event); + if (fd >= 0 && (event->read_cb || event->write_cb)) { + assert(my_loop->fd_events[fd] == NULL); + + my_loop->fd_events[fd] = event; + my_loop->fd_timeout_times[fd] = get_now_plus(timeout); + event->ev = (void *) (intptr_t) fd + 1; + + DEBUG_SCHED( "scheduled read/write at %d\n", fd); + return GETDNS_RETURN_GOOD; + } + + assert(event->timeout_cb && !event->read_cb && !event->write_cb); + + for (i = 0; i < MAX_TIMEOUTS; i++) { + if (my_loop->timeout_events[i] == NULL) { + my_loop->timeout_events[i] = event; + my_loop->timeout_times[i] = get_now_plus(timeout); + event->ev = (void *) (intptr_t) i + 1; + + DEBUG_SCHED( "scheduled timeout at %d\n", (int)i); + return GETDNS_RETURN_GOOD; + } + } + return GETDNS_RETURN_GENERIC_ERROR; +} + +getdns_return_t +my_eventloop_clear(getdns_eventloop *loop, getdns_eventloop_event *event) +{ + my_eventloop *my_loop = (my_eventloop *)loop; + size_t i; + + assert(loop); + assert(event); + + DEBUG_SCHED( "%s(loop: %p, event: %p)\n", __FUNCTION__, loop, event); + + i = (intptr_t)event->ev - 1; + assert(i >= 0 && i < FD_SETSIZE); + + if (event->timeout_cb && !event->read_cb && !event->write_cb) { + assert(my_loop->timeout_events[i] == event); + my_loop->timeout_events[i] = NULL; + } else { + assert(my_loop->fd_events[i] == event); + my_loop->fd_events[i] = NULL; + } + event->ev = NULL; + return GETDNS_RETURN_GOOD; +} + +void my_eventloop_cleanup(getdns_eventloop *loop) +{ +} + +void my_read_cb(int fd, getdns_eventloop_event *event) +{ + DEBUG_SCHED( "%s(fd: %d, event: %p)\n", __FUNCTION__, fd, event); + event->read_cb(event->userarg); +} + +void my_write_cb(int fd, getdns_eventloop_event *event) +{ + DEBUG_SCHED( "%s(fd: %d, event: %p)\n", __FUNCTION__, fd, event); + event->write_cb(event->userarg); +} + +void my_timeout_cb(int fd, getdns_eventloop_event *event) +{ + DEBUG_SCHED( "%s(fd: %d, event: %p)\n", __FUNCTION__, fd, event); + event->timeout_cb(event->userarg); +} + +void my_eventloop_run_once(getdns_eventloop *loop, int blocking) +{ + my_eventloop *my_loop = (my_eventloop *)loop; + + fd_set readfds, writefds; + int fd, max_fd = -1; + uint64_t now, timeout = (uint64_t)-1; + size_t i; + struct timeval tv; + + assert(loop); + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + now = get_now_plus(0); + + for (i = 0; i < MAX_TIMEOUTS; i++) { + if (!my_loop->timeout_events[i]) + continue; + if (now > my_loop->timeout_times[i]) + my_timeout_cb(-1, my_loop->timeout_events[i]); + else if (my_loop->timeout_times[i] < timeout) + timeout = my_loop->timeout_times[i]; + } + for (fd = 0; fd < FD_SETSIZE; fd++) { + if (!my_loop->fd_events[fd]) + continue; + if (my_loop->fd_events[fd]->read_cb) + FD_SET(fd, &readfds); + if (my_loop->fd_events[fd]->write_cb) + FD_SET(fd, &writefds); + if (fd > max_fd) + max_fd = fd; + if (my_loop->fd_timeout_times[fd] < timeout) + timeout = my_loop->fd_timeout_times[fd]; + } + if (max_fd == -1 && timeout == (uint64_t)-1) + return; + + if (! blocking || now > timeout) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } else { + tv.tv_sec = (timeout - now) / 1000000; + tv.tv_usec = (timeout - now) % 1000000; + } + if (select(max_fd + 1, &readfds, &writefds, NULL, &tv) < 0) { + perror("select() failed"); + exit(EXIT_FAILURE); + } + now = get_now_plus(0); + for (fd = 0; fd < FD_SETSIZE; fd++) { + if (my_loop->fd_events[fd] && + my_loop->fd_events[fd]->read_cb && + FD_ISSET(fd, &readfds)) + my_read_cb(fd, my_loop->fd_events[fd]); + + if (my_loop->fd_events[fd] && + my_loop->fd_events[fd]->write_cb && + FD_ISSET(fd, &writefds)) + my_write_cb(fd, my_loop->fd_events[fd]); + + if (my_loop->fd_events[fd] && + my_loop->fd_events[fd]->timeout_cb && + now > my_loop->fd_timeout_times[fd]) + my_timeout_cb(fd, my_loop->fd_events[fd]); + + i = fd; + if (my_loop->timeout_events[i] && + my_loop->timeout_events[i]->timeout_cb && + now > my_loop->timeout_times[i]) + my_timeout_cb(-1, my_loop->timeout_events[i]); + } +} + +void my_eventloop_run(getdns_eventloop *loop) +{ + my_eventloop *my_loop = (my_eventloop *)loop; + size_t i; + + assert(loop); + + i = 0; + while (i < MAX_TIMEOUTS) { + if (my_loop->fd_events[i] || my_loop->timeout_events[i]) { + my_eventloop_run_once(loop, 1); + i = 0; + } else { + i++; + } + } +} + +void my_eventloop_init(my_eventloop *loop) +{ + static getdns_eventloop_vmt my_eventloop_vmt = { + my_eventloop_cleanup, + my_eventloop_schedule, + my_eventloop_clear, + my_eventloop_run, + my_eventloop_run_once + }; + + (void) memset(loop, 0, sizeof(my_eventloop)); + loop->base.vmt = &my_eventloop_vmt; +} + +static int quiet = 0; +static int batch_mode = 0; +static char *query_file = NULL; +static int json = 0; +static char *the_root = "."; +static char *name; +static getdns_context *context; +static getdns_dict *extensions; +static uint16_t request_type = GETDNS_RRTYPE_NS; +static int timeout, edns0_size, padding_blocksize; +static int async = 0, interactive = 0; +static enum { GENERAL, ADDRESS, HOSTNAME, SERVICE } calltype = GENERAL; + +int get_rrtype(const char *t); + +getdns_dict * +ipaddr_dict(getdns_context *context, char *ipstr) +{ + getdns_dict *r = getdns_dict_create_with_context(context); + char *s = strchr(ipstr, '%'), *scope_id_str = ""; + char *p = strchr(ipstr, '@'), *portstr = ""; + char *t = strchr(ipstr, '#'), *tls_portstr = ""; + char *n = strchr(ipstr, '~'), *tls_namestr = ""; + uint8_t buf[sizeof(struct in6_addr)]; + getdns_bindata addr; + + addr.data = buf; + + if (!r) return NULL; + if (s) { + *s = 0; + scope_id_str = s + 1; + } + if (p) { + *p = 0; + portstr = p + 1; + } + if (t) { + *t = 0; + tls_portstr = t + 1; + } + if (n) { + *n = 0; + tls_namestr = n + 1; + } + if (strchr(ipstr, ':')) { + getdns_dict_util_set_string(r, "address_type", "IPv6"); + addr.size = 16; + if (inet_pton(AF_INET6, ipstr, buf) <= 0) { + getdns_dict_destroy(r); + return NULL; + } + } else { + getdns_dict_util_set_string(r, "address_type", "IPv4"); + addr.size = 4; + if (inet_pton(AF_INET, ipstr, buf) <= 0) { + getdns_dict_destroy(r); + return NULL; + } + } + getdns_dict_set_bindata(r, "address_data", &addr); + if (*portstr) + getdns_dict_set_int(r, "port", (int32_t)atoi(portstr)); + if (*tls_portstr) + getdns_dict_set_int(r, "tls_port", (int32_t)atoi(tls_portstr)); + if (*tls_namestr) { + getdns_dict_util_set_string(r, "tls_auth_name", tls_namestr); + } + if (*scope_id_str) + getdns_dict_util_set_string(r, "scope_id", scope_id_str); + + return r; +} + +static getdns_return_t +fill_transport_list(getdns_context *context, char *transport_list_str, + getdns_transport_list_t *transports, size_t *transport_count) +{ + size_t max_transports = *transport_count; + *transport_count = 0; + for ( size_t i = 0 + ; i < max_transports && i < strlen(transport_list_str) + ; i++, (*transport_count)++) { + switch(*(transport_list_str + i)) { + case 'U': + transports[i] = GETDNS_TRANSPORT_UDP; + break; + case 'T': + transports[i] = GETDNS_TRANSPORT_TCP; + break; + case 'L': + transports[i] = GETDNS_TRANSPORT_TLS; + break; + case 'S': + transports[i] = GETDNS_TRANSPORT_STARTTLS; + break; + default: + fprintf(stderr, "Unrecognised transport '%c' in string %s\n", + *(transport_list_str + i), transport_list_str); + return GETDNS_RETURN_GENERIC_ERROR; + } + } + return GETDNS_RETURN_GOOD; +} + +void +print_usage(FILE *out, const char *progname) +{ + fprintf(out, "usage: %s [@] [+extension] [] []\n", + progname); + fprintf(out, "options:\n"); + fprintf(out, "\t-a\tPerform asynchronous resolution " + "(default = synchronous)\n"); + fprintf(out, "\t-A\taddress lookup ( is ignored)\n"); + fprintf(out, "\t-B\tBatch mode. Schedule all messages before processing responses.\n"); + fprintf(out, "\t-b \tSet edns0 max_udp_payload size\n"); + fprintf(out, "\t-c\tSend Client Subnet privacy request\n"); + fprintf(out, "\t-D\tSet edns0 do bit\n"); + fprintf(out, "\t-d\tclear edns0 do bit\n"); + fprintf(out, "\t-e \tSet idle timeout in miliseconds\n"); + fprintf(out, "\t-F \tread the queries from the specified file\n"); + fprintf(out, "\t-G\tgeneral lookup\n"); + fprintf(out, "\t-H\thostname lookup. ( must be an IP address; is ignored)\n"); + fprintf(out, "\t-h\tPrint this help\n"); + fprintf(out, "\t-i\tPrint api information\n"); + fprintf(out, "\t-I\tInteractive mode (> 1 queries on same context)\n"); + fprintf(out, "\t-j\tOutput json response dict\n"); + fprintf(out, "\t-J\tPretty print json response dict\n"); + fprintf(out, "\t-k\tPrint root trust anchors\n"); + fprintf(out, "\t-n\tSet TLS authentication mode to NONE (default)\n"); + fprintf(out, "\t-m\tSet TLS authentication mode to HOSTNAME\n"); + fprintf(out, "\t-p\tPretty print response dict\n"); + fprintf(out, "\t-P \tPad TLS queries to a multiple of blocksize\n"); + fprintf(out, "\t-r\tSet recursing resolution type\n"); + fprintf(out, "\t-q\tQuiet mode - don't print response\n"); + fprintf(out, "\t-s\tSet stub resolution type (default = recursing)\n"); + fprintf(out, "\t-S\tservice lookup ( is ignored)\n"); + fprintf(out, "\t-t \tSet timeout in miliseconds\n"); + fprintf(out, "\t-T\tSet transport to TCP only\n"); + fprintf(out, "\t-O\tSet transport to TCP only keep connections open\n"); + fprintf(out, "\t-L\tSet transport to TLS only keep connections open\n"); + fprintf(out, "\t-E\tSet transport to TLS with TCP fallback only keep connections open\n"); + fprintf(out, "\t-R\tSet transport to STARTTLS with TCP fallback only keep connections open\n"); + fprintf(out, "\t-u\tSet transport to UDP with TCP fallback\n"); + fprintf(out, "\t-U\tSet transport to UDP only\n"); + fprintf(out, "\t-l \tSet transport list. List can contain 1 of each of the characters\n"); + fprintf(out, "\t\t\t U T L S for UDP, TCP, TLS or STARTTLS e.g 'UT' or 'LST' \n"); + +} + +static getdns_return_t validate_chain(getdns_dict *response) +{ + getdns_return_t r; + getdns_list *validation_chain; + getdns_list *replies_tree; + getdns_dict *reply; + getdns_list *to_validate; + getdns_list *trust_anchor; + size_t i; + int s; + + if (!(to_validate = getdns_list_create())) + return GETDNS_RETURN_MEMORY_ERROR; + + trust_anchor = getdns_root_trust_anchor(NULL); + + if ((r = getdns_dict_get_list( + response, "validation_chain", &validation_chain))) + goto error; + + if ((r = getdns_dict_get_list( + response, "replies_tree", &replies_tree))) + goto error; + + fprintf(stdout, "replies_tree dnssec_status: "); + switch ((s = getdns_validate_dnssec( + replies_tree, validation_chain, trust_anchor))) { + + case GETDNS_DNSSEC_SECURE: + fprintf(stdout, "GETDNS_DNSSEC_SECURE\n"); + break; + case GETDNS_DNSSEC_BOGUS: + fprintf(stdout, "GETDNS_DNSSEC_BOGUS\n"); + break; + case GETDNS_DNSSEC_INDETERMINATE: + fprintf(stdout, "GETDNS_DNSSEC_INDETERMINATE\n"); + break; + case GETDNS_DNSSEC_INSECURE: + fprintf(stdout, "GETDNS_DNSSEC_INSECURE\n"); + break; + case GETDNS_DNSSEC_NOT_PERFORMED: + fprintf(stdout, "GETDNS_DNSSEC_NOT_PERFORMED\n"); + break; + default: + fprintf(stdout, "%d\n", (int)s); + } + + i = 0; + while (!(r = getdns_list_get_dict(replies_tree, i++, &reply))) { + + if ((r = getdns_list_set_dict(to_validate, 0, reply))) + goto error; + + fprintf( stdout + , "reply %zu, dnssec_status: ", i); + switch ((s = getdns_validate_dnssec( + to_validate, validation_chain, trust_anchor))) { + + case GETDNS_DNSSEC_SECURE: + fprintf(stdout, "GETDNS_DNSSEC_SECURE\n"); + break; + case GETDNS_DNSSEC_BOGUS: + fprintf(stdout, "GETDNS_DNSSEC_BOGUS\n"); + break; + case GETDNS_DNSSEC_INDETERMINATE: + fprintf(stdout, "GETDNS_DNSSEC_INDETERMINATE\n"); + break; + case GETDNS_DNSSEC_INSECURE: + fprintf(stdout, "GETDNS_DNSSEC_INSECURE\n"); + break; + case GETDNS_DNSSEC_NOT_PERFORMED: + fprintf(stdout, "GETDNS_DNSSEC_NOT_PERFORMED\n"); + break; + default: + fprintf(stdout, "%d\n", (int)s); + } + } + if (r == GETDNS_RETURN_NO_SUCH_LIST_ITEM) + r = GETDNS_RETURN_GOOD; +error: + getdns_list_destroy(trust_anchor); + getdns_list_destroy(to_validate); + + return GETDNS_RETURN_GOOD; +} + +void callback(getdns_context *context, getdns_callback_type_t callback_type, + getdns_dict *response, void *userarg, getdns_transaction_t trans_id) +{ + char *response_str; + + /* This is a callback with data */; + if (response && !quiet && (response_str = json ? + getdns_print_json_dict(response, json == 1) + : getdns_pretty_print_dict(response))) { + + fprintf(stdout, "ASYNC response:\n%s\n", response_str); + validate_chain(response); + free(response_str); + } + + if (callback_type == GETDNS_CALLBACK_COMPLETE) { + fprintf(stdout, + "Response code was: GOOD. Status was: Callback with ID %llu was successfull.\n", + (unsigned long long)trans_id); + + } else if (callback_type == GETDNS_CALLBACK_CANCEL) + fprintf(stderr, + "An error occurred: The callback with ID %llu was cancelled. Exiting.\n", + (unsigned long long)trans_id); + else { + fprintf(stderr, + "An error occurred: The callback got a callback_type of %d. Exiting.\n", + callback_type); + fprintf(stderr, + "Error : '%s'\n", + getdns_get_errorstr_by_id(callback_type)); + } + getdns_dict_destroy(response); + response = NULL; +} + +#define CONTINUE ((getdns_return_t)-2) +#define CONTINUE_ERROR ((getdns_return_t)-3) + +static getdns_return_t set_cookie(getdns_dict *exts, char *cookie) +{ + uint8_t data[40]; + size_t i; + getdns_return_t r = GETDNS_RETURN_GENERIC_ERROR; + getdns_bindata bindata; + + getdns_dict *opt_parameters = getdns_dict_create(); + getdns_list *options = getdns_list_create(); + getdns_dict *option = getdns_dict_create(); + + if (*cookie == '=') + cookie++; + + for (i = 0; i < 40 && *cookie; i++) { + if (*cookie >= '0' && *cookie <= '9') + data[i] = (uint8_t)(*cookie - '0') << 4; + else if (*cookie >= 'a' && *cookie <= 'f') + data[i] = (uint8_t)(*cookie - 'a' + 10) << 4; + else if (*cookie >= 'A' && *cookie <= 'F') + data[i] = (uint8_t)(*cookie - 'A' + 10) << 4; + else + goto done; + cookie++; + if (*cookie >= '0' && *cookie <= '9') + data[i] |= (uint8_t)(*cookie - '0'); + else if (*cookie >= 'a' && *cookie <= 'f') + data[i] |= (uint8_t)(*cookie - 'a' + 10); + else if (*cookie >= 'A' && *cookie <= 'F') + data[i] |= (uint8_t)(*cookie - 'A' + 10); + else + goto done; + cookie++;; + } + bindata.data = data; + bindata.size = i; + if ((r = getdns_dict_set_int(option, "option_code", 10))) + goto done; + if ((r = getdns_dict_set_bindata(option, "option_data", &bindata))) + goto done; + if ((r = getdns_list_set_dict(options, 0, option))) + goto done; + if ((r = getdns_dict_set_list(opt_parameters, "options", options))) + goto done; + r = getdns_dict_set_dict(exts, "add_opt_parameters", opt_parameters); +done: + getdns_dict_destroy(option); + getdns_list_destroy(options); + getdns_dict_destroy(opt_parameters); + return r; +} + +getdns_return_t parse_args(int argc, char **argv) +{ + getdns_return_t r = GETDNS_RETURN_GOOD; + size_t i; + char *arg, *c, *endptr; + int t, print_api_info = 0, print_trust_anchors = 0; + getdns_list *upstream_list = NULL; + getdns_list *tas = NULL; + size_t upstream_count = 0; + + for (i = 1; i < argc; i++) { + arg = argv[i]; + if ((t = get_rrtype(arg)) >= 0) { + request_type = t; + continue; + + } else if (arg[0] == '+') { + if (arg[1] == 's' && arg[2] == 'i' && arg[3] == 't' && + (arg[4] == '=' || arg[4] == '\0')) { + if ((r = set_cookie(extensions, arg+4))) { + fprintf(stderr, "Could not set cookie:" + " %d", r); + break; + } + } else if (arg[1] == '0') { + /* Unset all existing extensions*/ + getdns_dict_destroy(extensions); + extensions = getdns_dict_create(); + break; + } else if ((r = getdns_dict_set_int(extensions, arg+1, + GETDNS_EXTENSION_TRUE))) { + fprintf(stderr, "Could not set extension " + "\"%s\": %d\n", argv[i], r); + break; + } + continue; + + } else if (arg[0] == '@') { + getdns_dict *upstream = ipaddr_dict(context, arg + 1); + if (upstream) { + if (!upstream_list && + !(upstream_list = + getdns_list_create_with_context(context))){ + fprintf(stderr, "Could not create upstream list\n"); + return GETDNS_RETURN_MEMORY_ERROR; + } + getdns_list_set_dict(upstream_list, + upstream_count++, upstream); + } + continue; + } else if (arg[0] != '-') { + name = arg; + continue; + } + for (c = arg+1; *c; c++) { + switch (*c) { + case 'a': + async = 1; + break; + case 'A': + calltype = ADDRESS; + break; + case 'b': + if (c[1] != 0 || ++i >= argc || !*argv[i]) { + fprintf(stderr, "max_udp_payload_size " + "expected after -b\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + edns0_size = strtol(argv[i], &endptr, 10); + if (*endptr || edns0_size < 0) { + fprintf(stderr, "positive " + "numeric max_udp_payload_size " + "expected after -b\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + getdns_context_set_edns_maximum_udp_payload_size( + context, (uint16_t) edns0_size); + goto next; + case 'c': + if (getdns_context_set_edns_client_subnet_private(context, 1)) + return GETDNS_RETURN_GENERIC_ERROR; + break; + case 'D': + (void) getdns_context_set_edns_do_bit(context, 1); + break; + case 'd': + (void) getdns_context_set_edns_do_bit(context, 0); + break; + case 'F': + if (c[1] != 0 || ++i >= argc || !*argv[i]) { + fprintf(stderr, "file name expected " + "after -F\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + query_file = argv[i]; + interactive = 1; + break; + case 'G': + calltype = GENERAL; + break; + case 'H': + calltype = HOSTNAME; + break; + case 'h': + print_usage(stdout, argv[0]); + return CONTINUE; + case 'i': + print_api_info = 1; + break; + case 'I': + interactive = 1; + break; + case 'j': + json = 2; + break; + case 'J': + json = 1; + break; + case 'k': + print_trust_anchors = 1; + break; + case 'n': + getdns_context_set_tls_authentication(context, + GETDNS_AUTHENTICATION_NONE); + break; + case 'm': + getdns_context_set_tls_authentication(context, + GETDNS_AUTHENTICATION_HOSTNAME); + break; + case 'P': + if (c[1] != 0 || ++i >= argc || !*argv[i]) { + fprintf(stderr, "tls_query_padding_blocksize " + "expected after -P\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + padding_blocksize = strtol(argv[i], &endptr, 10); + if (*endptr || padding_blocksize < 0) { + fprintf(stderr, "non-negative " + "numeric padding blocksize expected " + "after -P\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + if (getdns_context_set_tls_query_padding_blocksize( + context, padding_blocksize)) + return GETDNS_RETURN_GENERIC_ERROR; + goto next; + case 'p': + json = 0; + case 'q': + quiet = 1; + break; + case 'r': + getdns_context_set_resolution_type( + context, + GETDNS_RESOLUTION_RECURSING); + break; + case 's': + getdns_context_set_resolution_type( + context, GETDNS_RESOLUTION_STUB); + break; + case 'S': + calltype = SERVICE; + break; + case 't': + if (c[1] != 0 || ++i >= argc || !*argv[i]) { + fprintf(stderr, "timeout expected " + "after -t\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + timeout = strtol(argv[i], &endptr, 10); + if (*endptr || timeout < 0) { + fprintf(stderr, "positive " + "numeric timeout expected " + "after -t\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + getdns_context_set_timeout( + context, timeout); + goto next; + case 'e': + if (c[1] != 0 || ++i >= argc || !*argv[i]) { + fprintf(stderr, "idle timeout expected " + "after -t\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + timeout = strtol(argv[i], &endptr, 10); + if (*endptr || timeout < 0) { + fprintf(stderr, "positive " + "numeric idle timeout expected " + "after -t\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + getdns_context_set_idle_timeout( + context, timeout); + goto next; + case 'T': + getdns_context_set_dns_transport(context, + GETDNS_TRANSPORT_TCP_ONLY); + break; + case 'O': + getdns_context_set_dns_transport(context, + GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN); + break; + case 'L': + getdns_context_set_dns_transport(context, + GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN); + break; + case 'E': + getdns_context_set_dns_transport(context, + GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN); + break; + case 'R': + getdns_context_set_dns_transport(context, + GETDNS_TRANSPORT_STARTTLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN); + break; + case 'u': + getdns_context_set_dns_transport(context, + GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP); + break; + case 'U': + getdns_context_set_dns_transport(context, + GETDNS_TRANSPORT_UDP_ONLY); + break; + case 'l': + if (c[1] != 0 || ++i >= argc || !*argv[i]) { + fprintf(stderr, "transport list expected " + "after -l\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + getdns_transport_list_t transports[10]; + size_t transport_count = sizeof(transports); + if ((r = fill_transport_list(context, argv[i], transports, &transport_count)) || + (r = getdns_context_set_dns_transport_list(context, + transport_count, transports))){ + fprintf(stderr, "Could not set transports\n"); + return r; + } + break; + case 'B': + batch_mode = 1; + break; + + + default: + fprintf(stderr, "Unknown option " + "\"%c\"\n", *c); + for (i = 0; i < argc; i++) + fprintf(stderr, "%d: \"%s\"\n", (int)i, argv[i]); + return GETDNS_RETURN_GENERIC_ERROR; + } + } +next: ; + } + if (r) + return r; + if (upstream_count && + (r = getdns_context_set_upstream_recursive_servers( + context, upstream_list))) { + fprintf(stderr, "Error setting upstream recursive servers\n"); + } + if (print_api_info) { + fprintf(stdout, "%s\n", getdns_pretty_print_dict( + getdns_context_get_api_information(context))); + return CONTINUE; + } + if (print_trust_anchors) { + if ((tas = getdns_root_trust_anchor(NULL))) { + fprintf(stdout, "%s\n", getdns_pretty_print_list(tas)); + return CONTINUE; + } else + return CONTINUE_ERROR; + } + return r; +} + +getdns_return_t do_the_call(void) +{ + getdns_return_t r; + getdns_dict *address = NULL; + getdns_dict *response = NULL; + char *response_str; + uint32_t status; + + if (calltype == HOSTNAME && + !(address = ipaddr_dict(context, name))) { + fprintf(stderr, "Could not convert \"%s\" " + "to an IP address", name); + return GETDNS_RETURN_GOOD; + } + if (async) { + switch (calltype) { + case GENERAL: + r = getdns_general(context, name, request_type, + extensions, &response, NULL, callback); + break; + case ADDRESS: + r = getdns_address(context, name, + extensions, &response, NULL, callback); + break; + case HOSTNAME: + r = getdns_hostname(context, address, + extensions, &response, NULL, callback); + break; + case SERVICE: + r = getdns_service(context, name, + extensions, &response, NULL, callback); + break; + default: + r = GETDNS_RETURN_GENERIC_ERROR; + break; + } + if (r == GETDNS_RETURN_GOOD && !batch_mode) + getdns_context_run(context); + if (r != GETDNS_RETURN_GOOD) + fprintf(stderr, "An error occurred: %d '%s'\n", r, + getdns_get_errorstr_by_id(r)); + } else { + switch (calltype) { + case GENERAL: + r = getdns_general_sync(context, name, + request_type, extensions, &response); + break; + case ADDRESS: + r = getdns_address_sync(context, name, + extensions, &response); + break; + case HOSTNAME: + r = getdns_hostname_sync(context, address, + extensions, &response); + break; + case SERVICE: + r = getdns_service_sync(context, name, + extensions, &response); + break; + default: + r = GETDNS_RETURN_GENERIC_ERROR; + break; + } + if (r != GETDNS_RETURN_GOOD) { + fprintf(stderr, "An error occurred: %d '%s'\n", r, + getdns_get_errorstr_by_id(r)); + return r; + } + if (response && !quiet) { + if ((response_str = json ? + getdns_print_json_dict(response, json == 1) + : getdns_pretty_print_dict(response))) { + + fprintf( stdout, "SYNC response:\n%s\n" + , response_str); + validate_chain(response); + free(response_str); + } else { + r = GETDNS_RETURN_MEMORY_ERROR; + fprintf( stderr + , "Could not print response\n"); + } + } + getdns_dict_get_int(response, "status", &status); + fprintf(stdout, "Response code was: GOOD. Status was: %s\n", + getdns_get_errorstr_by_id(status)); + if (response) + getdns_dict_destroy(response); + } + return r; +} + +my_eventloop my_loop; +FILE *fp; + +void read_line_cb(void *userarg) +{ + getdns_eventloop_event *read_line_ev = userarg; + getdns_return_t r; + + char line[1024], *token, *linev[256]; + int linec; + + if (!fgets(line, 1024, fp) || !*line) { + if (query_file) + fprintf(stdout,"End of file."); + my_eventloop_clear(&my_loop.base, read_line_ev); + return; + } + if (query_file) + fprintf(stdout,"Found query: %s", line); + + linev[0] = __FILE__; + linec = 1; + if (!(token = strtok(line, " \t\f\n\r"))) { + if (! query_file) { + printf("> "); + fflush(stdout); + } + return; + } + if (*token == '#') { + fprintf(stdout,"Result: Skipping comment\n"); + if (! query_file) { + printf("> "); + fflush(stdout); + } + return; + } + do linev[linec++] = token; + while (linec < 256 && (token = strtok(NULL, " \t\f\n\r"))); + + if (((r = parse_args(linec, linev)) || (r = do_the_call())) && + (r != CONTINUE && r != CONTINUE_ERROR)) + my_eventloop_clear(&my_loop.base, read_line_ev); + + else if (! query_file) { + printf("> "); + fflush(stdout); + } +} + +int +main(int argc, char **argv) +{ + getdns_return_t r; + + name = the_root; + if ((r = getdns_context_create(&context, 1))) { + fprintf(stderr, "Create context failed: %d\n", r); + return r; + } + my_eventloop_init(&my_loop); + if ((r = getdns_context_set_eventloop(context, &my_loop.base))) + goto done_destroy_context; + if ((r = getdns_context_set_use_threads(context, 1))) + goto done_destroy_context; + extensions = getdns_dict_create(); + if (! extensions) { + fprintf(stderr, "Could not create extensions dict\n"); + r = GETDNS_RETURN_MEMORY_ERROR; + goto done_destroy_context; + } + if ((r = parse_args(argc, argv))) + goto done_destroy_context; + + if (query_file) { + fp = fopen(query_file, "rt"); + if (fp == NULL) { + fprintf(stderr, "Could not open query file: %s\n", query_file); + goto done_destroy_context; + } + } else + fp = stdin; + + /* Make the call */ + if (interactive) { + getdns_eventloop_event read_line_ev = { + &read_line_ev, read_line_cb, NULL, NULL, NULL }; + (void) my_eventloop_schedule( + &my_loop.base, fileno(fp), -1, &read_line_ev); + if (!query_file) { + printf("> "); + fflush(stdout); + } + my_eventloop_run(&my_loop.base); + } + else + r = do_the_call(); + + if ((r == GETDNS_RETURN_GOOD && batch_mode)) + getdns_context_run(context); + + /* Clean up */ + getdns_dict_destroy(extensions); +done_destroy_context: + getdns_context_destroy(context); + + if (fp) + fclose(fp); + + if (r == CONTINUE) + return 0; + else if (r == CONTINUE_ERROR) + return 1; + fprintf(stdout, "\nAll done.\n"); + return r; +} + +int get_rrtype(const char *t) { + char *endptr; + int r; + + switch (t[0]) { + case 'A': + case 'a': switch (t[1]) { + case '\0': return GETDNS_RRTYPE_A; + case '6': if (t[2] == '\0') return GETDNS_RRTYPE_A6; + return -1; + case 'A': + case 'a': /* before "AA", final "AA" (GETDNS_RRTYPE_AAAA) */ + if ((t[2]|0x20) == 'a' && (t[3]|0x20) == 'a' && t[4] == '\0') + return GETDNS_RRTYPE_AAAA; + return -1; + case 'F': + case 'f': /* before "AF", final "SDB" (GETDNS_RRTYPE_AFSDB) */ + if ((t[2]|0x20) == 's' && (t[3]|0x20) == 'd' && (t[4]|0x20) == 'b' && t[5] == '\0') + return GETDNS_RRTYPE_AFSDB; + return -1; + case 'P': + case 'p': /* before "AP", final "L" (GETDNS_RRTYPE_APL) */ + if ((t[2]|0x20) == 'l' && t[3] == '\0') + return GETDNS_RRTYPE_APL; + return -1; + case 'T': + case 't': /* before "AT", final "MA" (GETDNS_RRTYPE_ATMA) */ + if ((t[2]|0x20) == 'm' && (t[3]|0x20) == 'a' && t[4] == '\0') + return GETDNS_RRTYPE_ATMA; + return -1; + case 'X': + case 'x': /* before "AX", final "FR" (GETDNS_RRTYPE_AXFR) */ + if ((t[2]|0x20) == 'f' && (t[3]|0x20) == 'r' && t[4] == '\0') + return GETDNS_RRTYPE_AXFR; + return -1; + default : return -1; + }; + case 'C': + case 'c': switch (t[1]) { + case 'A': + case 'a': /* before "CA", final "A" (GETDNS_RRTYPE_CAA) */ + if ((t[2]|0x20) == 'a' && t[3] == '\0') + return GETDNS_RRTYPE_CAA; + return -1; + case 'D': + case 'd': switch (t[2]) { + case 'N': + case 'n': /* before "CDN", final "SKEY" (GETDNS_RRTYPE_CDNSKEY) */ + if ((t[3]|0x20) == 's' && (t[4]|0x20) == 'k' && (t[5]|0x20) == 'e' && (t[6]|0x20) == 'y' && t[7] == '\0') + return GETDNS_RRTYPE_CDNSKEY; + return -1; + case 'S': + case 's': if (t[3] == '\0') return GETDNS_RRTYPE_CDS; + return -1; + default : return -1; + }; + case 'E': + case 'e': /* before "CE", final "RT" (GETDNS_RRTYPE_CERT) */ + if ((t[2]|0x20) == 'r' && (t[3]|0x20) == 't' && t[4] == '\0') + return GETDNS_RRTYPE_CERT; + return -1; + case 'N': + case 'n': /* before "CN", final "AME" (GETDNS_RRTYPE_CNAME) */ + if ((t[2]|0x20) == 'a' && (t[3]|0x20) == 'm' && (t[4]|0x20) == 'e' && t[5] == '\0') + return GETDNS_RRTYPE_CNAME; + return -1; + case 'S': + case 's': /* before "CS", final "YNC" (GETDNS_RRTYPE_CSYNC) */ + if ((t[2]|0x20) == 'y' && (t[3]|0x20) == 'n' && (t[4]|0x20) == 'c' && t[5] == '\0') + return GETDNS_RRTYPE_CSYNC; + return -1; + + default : return -1; + }; + case 'D': + case 'd': switch (t[1]) { + case 'H': + case 'h': /* before "DH", final "CID" (GETDNS_RRTYPE_DHCID) */ + if ((t[2]|0x20) == 'c' && (t[3]|0x20) == 'i' && (t[4]|0x20) == 'd' && t[5] == '\0') + return GETDNS_RRTYPE_DHCID; + return -1; + case 'L': + case 'l': /* before "DL", final "V" (GETDNS_RRTYPE_DLV) */ + if ((t[2]|0x20) == 'v' && t[3] == '\0') + return GETDNS_RRTYPE_DLV; + return -1; + case 'N': + case 'n': switch (t[2]) { + case 'A': + case 'a': /* before "DNA", final "ME" (GETDNS_RRTYPE_DNAME) */ + if ((t[3]|0x20) == 'm' && (t[4]|0x20) == 'e' && t[5] == '\0') + return GETDNS_RRTYPE_DNAME; + return -1; + case 'S': + case 's': /* before "DNS", final "KEY" (GETDNS_RRTYPE_DNSKEY) */ + if ((t[3]|0x20) == 'k' && (t[4]|0x20) == 'e' && (t[5]|0x20) == 'y' && t[6] == '\0') + return GETDNS_RRTYPE_DNSKEY; + return -1; + default : return -1; + }; + case 'S': + case 's': if (t[2] == '\0') return GETDNS_RRTYPE_DS; + return -1; + default : return -1; + }; + case 'E': + case 'e': switch (t[1]) { + case 'I': + case 'i': /* before "EI", final "D" (GETDNS_RRTYPE_EID) */ + if ((t[2]|0x20) == 'd' && t[3] == '\0') + return GETDNS_RRTYPE_EID; + return -1; + case 'U': + case 'u': /* before "EU", next "I" */ + if ((t[2]|0x20) != 'i') + return -1; + switch (t[3]) { + case '4': /* before "EUI4", final "8" (GETDNS_RRTYPE_EUI48) */ + if (t[4] == '8' && t[5] == '\0') + return GETDNS_RRTYPE_EUI48; + return -1; + case '6': /* before "EUI6", final "4" (GETDNS_RRTYPE_EUI64) */ + if (t[4] == '4' && t[5] == '\0') + return GETDNS_RRTYPE_EUI64; + return -1; + default : return -1; + }; + default : return -1; + }; + case 'G': + case 'g': switch (t[1]) { + case 'I': + case 'i': /* before "GI", final "D" (GETDNS_RRTYPE_GID) */ + if ((t[2]|0x20) == 'd' && t[3] == '\0') + return GETDNS_RRTYPE_GID; + return -1; + case 'P': + case 'p': /* before "GP", final "OS" (GETDNS_RRTYPE_GPOS) */ + if ((t[2]|0x20) == 'o' && (t[3]|0x20) == 's' && t[4] == '\0') + return GETDNS_RRTYPE_GPOS; + return -1; + default : return -1; + }; + case 'H': + case 'h': /* before "H", next "I" */ + if ((t[1]|0x20) != 'i') + return -1; + switch (t[2]) { + case 'N': + case 'n': /* before "HIN", final "FO" (GETDNS_RRTYPE_HINFO) */ + if ((t[3]|0x20) == 'f' && (t[4]|0x20) == 'o' && t[5] == '\0') + return GETDNS_RRTYPE_HINFO; + return -1; + case 'P': + case 'p': if (t[3] == '\0') return GETDNS_RRTYPE_HIP; + return -1; + default : return -1; + }; + case 'I': + case 'i': switch (t[1]) { + case 'P': + case 'p': /* before "IP", final "SECKEY" (GETDNS_RRTYPE_IPSECKEY) */ + if ((t[2]|0x20) == 's' && (t[3]|0x20) == 'e' && (t[4]|0x20) == 'c' && (t[5]|0x20) == 'k' && (t[6]|0x20) == 'e' && (t[7]|0x20) == 'y' && t[8] == '\0') + return GETDNS_RRTYPE_IPSECKEY; + return -1; + case 'S': + case 's': /* before "IS", final "DN" (GETDNS_RRTYPE_ISDN) */ + if ((t[2]|0x20) == 'd' && (t[3]|0x20) == 'n' && t[4] == '\0') + return GETDNS_RRTYPE_ISDN; + return -1; + case 'X': + case 'x': /* before "IX", final "FR" (GETDNS_RRTYPE_IXFR) */ + if ((t[2]|0x20) == 'f' && (t[3]|0x20) == 'r' && t[4] == '\0') + return GETDNS_RRTYPE_IXFR; + return -1; + default : return -1; + }; + case 'K': + case 'k': switch (t[1]) { + case 'E': + case 'e': /* before "KE", final "Y" (GETDNS_RRTYPE_KEY) */ + if ((t[2]|0x20) == 'y' && t[3] == '\0') + return GETDNS_RRTYPE_KEY; + return -1; + case 'X': + case 'x': if (t[2] == '\0') return GETDNS_RRTYPE_KX; + return -1; + default : return -1; + }; + case 'L': + case 'l': switch (t[1]) { + case '3': /* before "L3", final "2" (GETDNS_RRTYPE_L32) */ + if (t[2] == '2' && t[3] == '\0') + return GETDNS_RRTYPE_L32; + return -1; + case '6': /* before "L6", final "4" (GETDNS_RRTYPE_L64) */ + if (t[2] == '4' && t[3] == '\0') + return GETDNS_RRTYPE_L64; + return -1; + case 'O': + case 'o': /* before "LO", final "C" (GETDNS_RRTYPE_LOC) */ + if ((t[2]|0x20) == 'c' && t[3] == '\0') + return GETDNS_RRTYPE_LOC; + return -1; + case 'P': + case 'p': if (t[2] == '\0') return GETDNS_RRTYPE_LP; + return -1; + default : return -1; + }; + case 'M': + case 'm': switch (t[1]) { + case 'A': + case 'a': /* before "MA", next "IL" */ + if ((t[2]|0x20) != 'i' && (t[3]|0x20) != 'l') + return -1; + switch (t[4]) { + case 'A': + case 'a': if (t[5] == '\0') return GETDNS_RRTYPE_MAILA; + return -1; + case 'B': + case 'b': if (t[5] == '\0') return GETDNS_RRTYPE_MAILB; + return -1; + default : return -1; + }; + case 'B': + case 'b': if (t[2] == '\0') return GETDNS_RRTYPE_MB; + return -1; + case 'D': + case 'd': if (t[2] == '\0') return GETDNS_RRTYPE_MD; + return -1; + case 'F': + case 'f': if (t[2] == '\0') return GETDNS_RRTYPE_MF; + return -1; + case 'G': + case 'g': if (t[2] == '\0') return GETDNS_RRTYPE_MG; + return -1; + case 'I': + case 'i': /* before "MI", final "NFO" (GETDNS_RRTYPE_MINFO) */ + if ((t[2]|0x20) == 'n' && (t[3]|0x20) == 'f' && (t[4]|0x20) == 'o' && t[5] == '\0') + return GETDNS_RRTYPE_MINFO; + return -1; + case 'R': + case 'r': if (t[2] == '\0') return GETDNS_RRTYPE_MR; + return -1; + case 'X': + case 'x': if (t[2] == '\0') return GETDNS_RRTYPE_MX; + return -1; + default : return -1; + }; + case 'N': + case 'n': switch (t[1]) { + case 'A': + case 'a': /* before "NA", final "PTR" (GETDNS_RRTYPE_NAPTR) */ + if ((t[2]|0x20) == 'p' && (t[3]|0x20) == 't' && (t[4]|0x20) == 'r' && t[5] == '\0') + return GETDNS_RRTYPE_NAPTR; + return -1; + case 'I': + case 'i': switch (t[2]) { + case 'D': + case 'd': if (t[3] == '\0') return GETDNS_RRTYPE_NID; + return -1; + case 'M': + case 'm': /* before "NIM", final "LOC" (GETDNS_RRTYPE_NIMLOC) */ + if ((t[3]|0x20) == 'l' && (t[4]|0x20) == 'o' && (t[5]|0x20) == 'c' && t[6] == '\0') + return GETDNS_RRTYPE_NIMLOC; + return -1; + case 'N': + case 'n': /* before "NIN", final "FO" (GETDNS_RRTYPE_NINFO) */ + if ((t[3]|0x20) == 'f' && (t[4]|0x20) == 'o' && t[5] == '\0') + return GETDNS_RRTYPE_NINFO; + return -1; + default : return -1; + }; + case 'S': + case 's': switch (t[2]) { + case '\0': return GETDNS_RRTYPE_NS; + case 'A': + case 'a': /* before "NSA", final "P" (GETDNS_RRTYPE_NSAP) */ + if ((t[3]|0x20) == 'p' && t[4] == '\0') + return GETDNS_RRTYPE_NSAP; + return -1; + case 'E': + case 'e': /* before "NSE", final "C3PARAM" (GETDNS_RRTYPE_NSEC3PARAM) */ + if ((t[3]|0x20) == 'c' && t[4] == '3' && (t[5]|0x20) == 'p' && (t[6]|0x20) == 'a' && (t[7]|0x20) == 'r' && (t[8]|0x20) == 'a' && (t[9]|0x20) == 'm' && t[10] == '\0') + return GETDNS_RRTYPE_NSEC3PARAM; + return -1; + default : return -1; + }; + case 'U': + case 'u': /* before "NU", final "LL" (GETDNS_RRTYPE_NULL) */ + if ((t[2]|0x20) == 'l' && (t[3]|0x20) == 'l' && t[4] == '\0') + return GETDNS_RRTYPE_NULL; + return -1; + case 'X': + case 'x': /* before "NX", final "T" (GETDNS_RRTYPE_NXT) */ + if ((t[2]|0x20) == 't' && t[3] == '\0') + return GETDNS_RRTYPE_NXT; + return -1; + default : return -1; + }; + case 'O': + case 'o': /* before "O", next "P" */ + if ((t[1]|0x20) != 'p') + return -1; + switch (t[2]) { + case 'E': + case 'e': /* before "OPE", final "NPGPKEY" (GETDNS_RRTYPE_OPENPGPKEY) */ + if ((t[3]|0x20) == 'n' && (t[4]|0x20) == 'p' && (t[5]|0x20) == 'g' && (t[6]|0x20) == 'p' && (t[7]|0x20) == 'k' && (t[8]|0x20) == 'e' && (t[9]|0x20) == 'y' && t[10] == '\0') + return GETDNS_RRTYPE_OPENPGPKEY; + return -1; + case 'T': + case 't': if (t[3] == '\0') return GETDNS_RRTYPE_OPT; + return -1; + default : return -1; + }; + case 'P': + case 'p': switch (t[1]) { + case 'T': + case 't': /* before "PT", final "R" (GETDNS_RRTYPE_PTR) */ + if ((t[2]|0x20) == 'r' && t[3] == '\0') + return GETDNS_RRTYPE_PTR; + return -1; + case 'X': + case 'x': if (t[2] == '\0') return GETDNS_RRTYPE_PX; + return -1; + default : return -1; + }; + case 'R': + case 'r': switch (t[1]) { + case 'K': + case 'k': /* before "RK", final "EY" (GETDNS_RRTYPE_RKEY) */ + if ((t[2]|0x20) == 'e' && (t[3]|0x20) == 'y' && t[4] == '\0') + return GETDNS_RRTYPE_RKEY; + return -1; + case 'P': + case 'p': if (t[2] == '\0') return GETDNS_RRTYPE_RP; + return -1; + case 'R': + case 'r': /* before "RR", final "SIG" (GETDNS_RRTYPE_RRSIG) */ + if ((t[2]|0x20) == 's' && (t[3]|0x20) == 'i' && (t[4]|0x20) == 'g' && t[5] == '\0') + return GETDNS_RRTYPE_RRSIG; + return -1; + case 'T': + case 't': if (t[2] == '\0') return GETDNS_RRTYPE_RT; + return -1; + default : return -1; + }; + case 'S': + case 's': switch (t[1]) { + case 'I': + case 'i': switch (t[2]) { + case 'G': + case 'g': if (t[3] == '\0') return GETDNS_RRTYPE_SIG; + return -1; + case 'N': + case 'n': /* before "SIN", final "K" (GETDNS_RRTYPE_SINK) */ + if ((t[3]|0x20) == 'k' && t[4] == '\0') + return GETDNS_RRTYPE_SINK; + return -1; + default : return -1; + }; + case 'O': + case 'o': /* before "SO", final "A" (GETDNS_RRTYPE_SOA) */ + if ((t[2]|0x20) == 'a' && t[3] == '\0') + return GETDNS_RRTYPE_SOA; + return -1; + case 'P': + case 'p': /* before "SP", final "F" (GETDNS_RRTYPE_SPF) */ + if ((t[2]|0x20) == 'f' && t[3] == '\0') + return GETDNS_RRTYPE_SPF; + return -1; + case 'R': + case 'r': /* before "SR", final "V" (GETDNS_RRTYPE_SRV) */ + if ((t[2]|0x20) == 'v' && t[3] == '\0') + return GETDNS_RRTYPE_SRV; + return -1; + case 'S': + case 's': /* before "SS", final "HFP" (GETDNS_RRTYPE_SSHFP) */ + if ((t[2]|0x20) == 'h' && (t[3]|0x20) == 'f' && (t[4]|0x20) == 'p' && t[5] == '\0') + return GETDNS_RRTYPE_SSHFP; + return -1; + default : return -1; + }; + case 'T': + case 't': switch (t[1]) { + case 'A': + case 'a': /* before "TA", final "LINK" (GETDNS_RRTYPE_TALINK) */ + if ((t[2]|0x20) == 'l' && (t[3]|0x20) == 'i' && (t[4]|0x20) == 'n' && (t[5]|0x20) == 'k' && t[6] == '\0') + return GETDNS_RRTYPE_TALINK; + return -1; + case 'K': + case 'k': /* before "TK", final "EY" (GETDNS_RRTYPE_TKEY) */ + if ((t[2]|0x20) == 'e' && (t[3]|0x20) == 'y' && t[4] == '\0') + return GETDNS_RRTYPE_TKEY; + return -1; + case 'L': + case 'l': /* before "TL", final "SA" (GETDNS_RRTYPE_TLSA) */ + if ((t[2]|0x20) == 's' && (t[3]|0x20) == 'a' && t[4] == '\0') + return GETDNS_RRTYPE_TLSA; + return -1; + case 'S': + case 's': /* before "TS", final "IG" (GETDNS_RRTYPE_TSIG) */ + if ((t[2]|0x20) == 'i' && (t[3]|0x20) == 'g' && t[4] == '\0') + return GETDNS_RRTYPE_TSIG; + return -1; + case 'X': + case 'x': /* before "TX", final "T" (GETDNS_RRTYPE_TXT) */ + if ((t[2]|0x20) == 't' && t[3] == '\0') + return GETDNS_RRTYPE_TXT; + return -1; + case 'Y': + case 'y': /* before "TY", then "PE" followed by a number */ + if ((t[2]|0x20) == 'p' && (t[3]|0x20) == 'e' && t[4] != '\0') { + r = (int) strtol(t + 4, &endptr, 10); + if (*endptr == '\0') return r; + } + return -1; + default : return -1; + }; + case 'U': + case 'u': switch (t[1]) { + case 'I': + case 'i': switch (t[2]) { + case 'D': + case 'd': if (t[3] == '\0') return GETDNS_RRTYPE_UID; + return -1; + case 'N': + case 'n': /* before "UIN", final "FO" (GETDNS_RRTYPE_UINFO) */ + if ((t[3]|0x20) == 'f' && (t[4]|0x20) == 'o' && t[5] == '\0') + return GETDNS_RRTYPE_UINFO; + return -1; + default : return -1; + }; + case 'N': + case 'n': /* before "UN", final "SPEC" (GETDNS_RRTYPE_UNSPEC) */ + if ((t[2]|0x20) == 's' && (t[3]|0x20) == 'p' && (t[4]|0x20) == 'e' && (t[5]|0x20) == 'c' && t[6] == '\0') + return GETDNS_RRTYPE_UNSPEC; + return -1; + case 'R': + case 'r': /* before "UR", final "I" (GETDNS_RRTYPE_URI) */ + if ((t[2]|0x20) == 'i' && t[3] == '\0') + return GETDNS_RRTYPE_URI; + return -1; + default : return -1; + }; + case 'W': + case 'w': /* before "W", final "KS" (GETDNS_RRTYPE_WKS) */ + if ((t[1]|0x20) == 'k' && (t[2]|0x20) == 's' && t[3] == '\0') + return GETDNS_RRTYPE_WKS; + return -1; + case 'X': + case 'x': /* before "X", final "25" (GETDNS_RRTYPE_X25) */ + if (t[1] == '2' && t[2] == '5' && t[3] == '\0') + return GETDNS_RRTYPE_X25; + return -1; + default : return -1; + }; +} + diff --git a/src/stub.c b/src/stub.c index c0c254cc..8072d5da 100644 --- a/src/stub.c +++ b/src/stub.c @@ -46,6 +46,12 @@ #include "util-internal.h" #include "general.h" +#ifdef USE_WINSOCK +#define EINPROGRESS 112 +#define EWOULDBLOCK 140 +typedef u_short sa_family_t; +#endif + #define STUB_OUT_OF_OPTIONS -5 /* upstream options exceeded MAXIMUM_UPSTREAM_OPTION_SPACE */ #define STUB_TLS_SETUP_ERROR -4 #define STUB_TCP_AGAIN -3 @@ -741,8 +747,16 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq) upstream). We must let the handshake complete since non-blocking. */ errno == EINPROGRESS)) || written < pkt_len + 2) { +#else + +#ifdef USE_WINSOCK + written = sendto(fd, netreq->query - 2, pkt_len + 2, + 0, (struct sockaddr *)&(netreq->upstream->addr), + netreq->upstream->addr_len); + #else written = write(fd, netreq->query - 2, pkt_len + 2); +#endif if ((written == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) || written < pkt_len + 2) { diff --git a/src/util/winsock_event.c b/src/util/winsock_event.c new file mode 100644 index 00000000..f29c9f32 --- /dev/null +++ b/src/util/winsock_event.c @@ -0,0 +1,872 @@ +/* + * util/winsock_event.c - implementation of the getdns winsock event handler. + * + * Copyright (c) 2015, Verisign/NLnet Labs. All rights reserved. + * + * This software is open source. + * + * 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 NLNET LABS/Verisign 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 THE COPYRIGHT + * HOLDER OR CONTRIBUTORS 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. + */ +/** + * \file + * Implementation of the getdns WinSock2 API event notification handler + * for the getdns Windows port. + */ + +#include "config.h" +#ifdef USE_WINSOCK +#include +#ifdef HAVE_TIME_H +#include +#endif +#include +#include "util/winsock_event.h" +#include "util/fptr_wlist.h" + +/** +* implementation of log_err +* @param format: format string printf-style. +*/ +void +log_err(const char *format, ...) +{ + va_list args; + va_start(args, format); + fprintf(stderr, "error", format, args); + va_end(args); +} + +char* wsa_strerror(DWORD err) +{ + static char unknown[32]; + + switch (err) { + case WSA_INVALID_HANDLE: return "Specified event object handle is invalid."; + case WSA_NOT_ENOUGH_MEMORY: return "Insufficient memory available."; + case WSA_INVALID_PARAMETER: return "One or more parameters are invalid."; + case WSA_OPERATION_ABORTED: return "Overlapped operation aborted."; + case WSA_IO_INCOMPLETE: return "Overlapped I/O event object not in signaled state."; + case WSA_IO_PENDING: return "Overlapped operations will complete later."; + case WSAEINTR: return "Interrupted function call."; + case WSAEBADF: return "File handle is not valid."; + case WSAEACCES: return "Permission denied."; + case WSAEFAULT: return "Bad address."; + case WSAEINVAL: return "Invalid argument."; + case WSAEMFILE: return "Too many open files."; + case WSAEWOULDBLOCK: return "Resource temporarily unavailable."; + case WSAEINPROGRESS: return "Operation now in progress."; + case WSAEALREADY: return "Operation already in progress."; + case WSAENOTSOCK: return "Socket operation on nonsocket."; + case WSAEDESTADDRREQ: return "Destination address required."; + case WSAEMSGSIZE: return "Message too long."; + case WSAEPROTOTYPE: return "Protocol wrong type for socket."; + case WSAENOPROTOOPT: return "Bad protocol option."; + case WSAEPROTONOSUPPORT: return "Protocol not supported."; + case WSAESOCKTNOSUPPORT: return "Socket type not supported."; + case WSAEOPNOTSUPP: return "Operation not supported."; + case WSAEPFNOSUPPORT: return "Protocol family not supported."; + case WSAEAFNOSUPPORT: return "Address family not supported by protocol family."; + case WSAEADDRINUSE: return "Address already in use."; + case WSAEADDRNOTAVAIL: return "Cannot assign requested address."; + case WSAENETDOWN: return "Network is down."; + case WSAENETUNREACH: return "Network is unreachable."; + case WSAENETRESET: return "Network dropped connection on reset."; + case WSAECONNABORTED: return "Software caused connection abort."; + case WSAECONNRESET: return "Connection reset by peer."; + case WSAENOBUFS: return "No buffer space available."; + case WSAEISCONN: return "Socket is already connected."; + case WSAENOTCONN: return "Socket is not connected."; + case WSAESHUTDOWN: return "Cannot send after socket shutdown."; + case WSAETOOMANYREFS: return "Too many references."; + case WSAETIMEDOUT: return "Connection timed out."; + case WSAECONNREFUSED: return "Connection refused."; + case WSAELOOP: return "Cannot translate name."; + case WSAENAMETOOLONG: return "Name too long."; + case WSAEHOSTDOWN: return "Host is down."; + case WSAEHOSTUNREACH: return "No route to host."; + case WSAENOTEMPTY: return "Directory not empty."; + case WSAEPROCLIM: return "Too many processes."; + case WSAEUSERS: return "User quota exceeded."; + case WSAEDQUOT: return "Disk quota exceeded."; + case WSAESTALE: return "Stale file handle reference."; + case WSAEREMOTE: return "Item is remote."; + case WSASYSNOTREADY: return "Network subsystem is unavailable."; + case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range."; + case WSANOTINITIALISED: return "Successful WSAStartup not yet performed."; + case WSAEDISCON: return "Graceful shutdown in progress."; + case WSAENOMORE: return "No more results."; + case WSAECANCELLED: return "Call has been canceled."; + case WSAEINVALIDPROCTABLE: return "Procedure call table is invalid."; + case WSAEINVALIDPROVIDER: return "Service provider is invalid."; + case WSAEPROVIDERFAILEDINIT: return "Service provider failed to initialize."; + case WSASYSCALLFAILURE: return "System call failure."; + case WSASERVICE_NOT_FOUND: return "Service not found."; + case WSATYPE_NOT_FOUND: return "Class type not found."; + case WSA_E_NO_MORE: return "No more results."; + case WSA_E_CANCELLED: return "Call was canceled."; + case WSAEREFUSED: return "Database query was refused."; + case WSAHOST_NOT_FOUND: return "Host not found."; + case WSATRY_AGAIN: return "Nonauthoritative host not found."; + case WSANO_RECOVERY: return "This is a nonrecoverable error."; + case WSANO_DATA: return "Valid name, no data record of requested type."; + case WSA_QOS_RECEIVERS: return "QOS receivers."; + case WSA_QOS_SENDERS: return "QOS senders."; + case WSA_QOS_NO_SENDERS: return "No QOS senders."; + case WSA_QOS_NO_RECEIVERS: return "QOS no receivers."; + case WSA_QOS_REQUEST_CONFIRMED: return "QOS request confirmed."; + case WSA_QOS_ADMISSION_FAILURE: return "QOS admission error."; + case WSA_QOS_POLICY_FAILURE: return "QOS policy failure."; + case WSA_QOS_BAD_STYLE: return "QOS bad style."; + case WSA_QOS_BAD_OBJECT: return "QOS bad object."; + case WSA_QOS_TRAFFIC_CTRL_ERROR: return "QOS traffic control error."; + case WSA_QOS_GENERIC_ERROR: return "QOS generic error."; + case WSA_QOS_ESERVICETYPE: return "QOS service type error."; + case WSA_QOS_EFLOWSPEC: return "QOS flowspec error."; + case WSA_QOS_EPROVSPECBUF: return "Invalid QOS provider buffer."; + case WSA_QOS_EFILTERSTYLE: return "Invalid QOS filter style."; + case WSA_QOS_EFILTERTYPE: return "Invalid QOS filter type."; + case WSA_QOS_EFILTERCOUNT: return "Incorrect QOS filter count."; + case WSA_QOS_EOBJLENGTH: return "Invalid QOS object length."; + case WSA_QOS_EFLOWCOUNT: return "Incorrect QOS flow count."; + /*case WSA_QOS_EUNKOWNPSOBJ: return "Unrecognized QOS object.";*/ + case WSA_QOS_EPOLICYOBJ: return "Invalid QOS policy object."; + case WSA_QOS_EFLOWDESC: return "Invalid QOS flow descriptor."; + case WSA_QOS_EPSFLOWSPEC: return "Invalid QOS provider-specific flowspec."; + case WSA_QOS_EPSFILTERSPEC: return "Invalid QOS provider-specific filterspec."; + case WSA_QOS_ESDMODEOBJ: return "Invalid QOS shape discard mode object."; + case WSA_QOS_ESHAPERATEOBJ: return "Invalid QOS shaping rate object."; + case WSA_QOS_RESERVED_PETYPE: return "Reserved policy QOS element type."; + default: + snprintf(unknown, sizeof(unknown), + "unknown WSA error code %d", (int)err); + return unknown; + } +} + +int _getdns_mini_ev_cmp(const void* a, const void* b) +{ + const struct _getdns_event *e = (const struct _getdns_event*)a; + const struct _getdns_event *f = (const struct _getdns_event*)b; + if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec) + return -1; + if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec) + return 1; + if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec) + return -1; + if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec) + return 1; + if(e < f) + return -1; + if(e > f) + return 1; + return 0; +} + +/** set time */ +static int +settime(struct _getdns_event_base* base) +{ + if(gettimeofday(base->time_tv, NULL) < 0) { + return -1; + } +#ifndef S_SPLINT_S + *base->time_secs = (time_t)base->time_tv->tv_sec; +#endif + return 0; +} + +#ifdef UNBOUND_DEBUG +/** + * Find a fd in the list of items. + * Note that not all items have a fd associated (those are -1). + * Signals are stored separately, and not searched. + * @param base: event base to look in. + * @param fd: what socket to look for. + * @return the index in the array, or -1 on failure. + */ +static int +find_fd(struct _getdns_event_base* base, int fd) +{ + int i; + for(i=0; imax; i++) { + if(base->items[i]->ev_fd == fd) + return i; + } + return -1; +} +#endif + +/** Find ptr in base array */ +static void +zero_waitfor(WSAEVENT waitfor[], WSAEVENT x) +{ + int i; + for(i=0; itime_secs = time_secs; + base->time_tv = time_tv; + if(settime(base) < 0) { + _getdns_event_base_free(base); + return NULL; + } + base->items = (struct _getdns_event**)calloc(WSK_MAX_ITEMS, + sizeof(struct _getdns_event*)); + if(!base->items) { + _getdns_event_base_free(base); + return NULL; + } + base->cap = WSK_MAX_ITEMS; + base->max = 0; + base->times = _getdns_rbtree_create(_getdns_mini_ev_cmp); + if(!base->times) { + _getdns_event_base_free(base); + return NULL; + } + base->signals = (struct _getdns_event**)calloc(MAX_SIG, sizeof(struct _getdns_event*)); + if(!base->signals) { + _getdns_event_base_free(base); + return NULL; + } + base->tcp_stickies = 0; + base->tcp_reinvigorated = 0; + //verbose(VERB_CLIENT, "winsock_event inited"); + return base; +} + +const char *_getdns_event_get_version(void) +{ + return "winsock-event-"PACKAGE_VERSION; +} + +const char *_getdns_event_get_method(void) +{ + return "WSAWaitForMultipleEvents"; +} + +//gowri remove static +/** call timeouts handlers, and return how long to wait for next one or -1 */ +void _getdns_handle_timeouts(struct _getdns_event_base* base, struct timeval* now, + struct timeval* wait) +{ + struct _getdns_event* p; +#ifndef S_SPLINT_S + wait->tv_sec = (time_t)-1; +#endif + //verbose(VERB_CLIENT, "winsock_event handle_timeouts"); + + while((_getdns_rbnode_t*)(p = (struct _getdns_event*)_getdns_rbtree_first(base->times)) + !=RBTREE_NULL) { +#ifndef S_SPLINT_S + if(p->ev_timeout.tv_sec > now->tv_sec || + (p->ev_timeout.tv_sec==now->tv_sec && + p->ev_timeout.tv_usec > now->tv_usec)) { + /* there is a next larger timeout. wait for it */ + wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec; + if(now->tv_usec > p->ev_timeout.tv_usec) { + wait->tv_sec--; + wait->tv_usec = 1000000 - (now->tv_usec - + p->ev_timeout.tv_usec); + } else { + wait->tv_usec = p->ev_timeout.tv_usec + - now->tv_usec; + } + //verbose(VERB_CLIENT, "winsock_event wait=" ARG_LL "d.%6.6d", + // (long long)wait->tv_sec, (int)wait->tv_usec); + return; + } +#endif + /* event times out, remove it */ + (void)_getdns_rbtree_delete(base->times, p); + p->ev_events &= ~EV_TIMEOUT; + fptr_ok(fptr_whitelist_event(p->ev_callback)); + (*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg); + } + //verbose(VERB_CLIENT, "winsock_event wait=(-1)"); +} + +/** handle is_signal events and see if signalled */ +static void _getdns_handle_signal(struct _getdns_event* ev) +{ + printf("In _getdns_handle_signal\n"); + DWORD ret; + //log_assert(ev->is_signal && ev->hEvent); + /* see if the event is signalled */ + ret = WSAWaitForMultipleEvents(1, &ev->hEvent, 0 /* any object */, + 0 /* return immediately */, 0 /* not alertable for IOcomple*/); + if(ret == WSA_WAIT_IO_COMPLETION || ret == WSA_WAIT_FAILED) { + log_err("getdns: WSAWaitForMultipleEvents(signal) failed: %s", + wsa_strerror(WSAGetLastError())); + return; + } + if(ret == WSA_WAIT_TIMEOUT) { + /* not signalled */ + return; + } + + /* reset the signal */ + if(!WSAResetEvent(ev->hEvent)) + log_err("getdns: WSAResetEvent failed: %s", + wsa_strerror(WSAGetLastError())); + /* do the callback (which may set the signal again) */ + fptr_ok(fptr_whitelist_event(ev->ev_callback)); + (*ev->ev_callback)(ev->ev_fd, ev->ev_events, ev->ev_arg); +} + +/** call select and callbacks for that */ +int _getdns_handle_select(struct _getdns_event_base* base, struct timeval* wait) +{ + DWORD timeout = 0; /* in milliseconds */ + DWORD ret; + WSANETWORKEVENTS netev; + struct _getdns_event* eventlist[WSK_MAX_ITEMS]; + int i, numwait = 0, startidx = 0, was_timeout = 0; + int newstickies = 0; + struct timeval nultm; + +#ifndef S_SPLINT_S + if(wait->tv_sec==(time_t)-1) + wait = NULL; + if (wait) + // timeout = 10 + wait->tv_usec / 1000; + timeout = wait->tv_sec * 1000 + wait->tv_usec / 1000; + if(base->tcp_stickies) { + wait = &nultm; + nultm.tv_sec = 0; + nultm.tv_usec = 0; + timeout = 0; /* no waiting, we have sticky events */ + } +#endif + + /* prepare event array */ + for(i=0; imax; i++) { + if(base->items[i]->ev_fd == -1 && !base->items[i]->is_signal) + continue; /* skip timer only events */ + eventlist[numwait] = base->items[i]; + base->waitfor[numwait++] = base->items[i]->hEvent; + printf("winsock_event bmax=%d numwait=%d wait=%x " + "timeout=%d hEvent %d\n", base->max, numwait, (int)wait, (int)timeout, base->items[i]->hEvent); + if (numwait == WSK_MAX_ITEMS) + break; /* sanity check */ + } + //log_assert(numwait <= WSA_MAXIMUM_WAIT_EVENTS); + + /* do the wait */ + if(numwait == 0) { + /* WSAWaitFor.. doesn't like 0 event objects */ + if(wait) { + Sleep(timeout); + } + was_timeout = 1; + } + else { + //gv: do not schedule udp write + for (i = 0; imax; i++) { + if (!base->items[i]->is_tcp && base->items[i]->ev_events&EV_WRITE) { + //gprintf("skip UDP sched\n"); + (*eventlist[i]->ev_callback)(eventlist[i]->ev_fd, + EV_WRITE & eventlist[i]->ev_events, + eventlist[i]->ev_arg); + return 0; + } + } + //gprintf("before wait %d\n", base->items[0]->ev_events); + ret = WSAWaitForMultipleEvents(numwait, base->waitfor, + 0 /* do not wait for all, just one will do */, + wait?timeout:WSA_INFINITE, + 0); /* we are not alertable (IO completion events) */ + //gprintf("after wait %d %d\n", ret, numwait); + if(ret == WSA_WAIT_IO_COMPLETION) { + //printf("getdns: WSAWaitForMultipleEvents failed: WSA_WAIT_IO_COMPLETION"); + return -1; + } else if(ret == WSA_WAIT_FAILED) { + //printf("getdns: WSAWaitForMultipleEvents failed: %s", + // wsa_strerror(WSAGetLastError())); + return -1; + } else if(ret == WSA_WAIT_TIMEOUT) { + //printf("timeout\n"); + was_timeout = 1; + } else + startidx = ret - WSA_WAIT_EVENT_0; + } + ////verbose(VERB_CLIENT, "winsock_event wake was_timeout=%d startidx=%d", + // was_timeout, startidx); + + /* get new time after wait */ + if(settime(base) < 0) + return -1; + + /* callbacks */ + if(base->tcp_stickies) + startidx = 0; /* process all events, some are sticky */ + for(i=startidx; ijust_checked = 1; + + //verbose(VERB_CLIENT, "winsock_event signals"); + for(i=startidx; iwaitfor[i]) + continue; /* was deleted */ + if(eventlist[i]->is_signal) { + eventlist[i]->just_checked = 0; + _getdns_handle_signal(eventlist[i]); + } + } + /* early exit - do not process network, exit quickly */ + if(base->need_to_exit) + return 0; + + //verbose(VERB_CLIENT, "winsock_event net"); + for(i=startidx; iwaitfor[i]) + continue; /* was deleted */ + if(!eventlist[i]->just_checked) + continue; /* added by other callback */ + if(eventlist[i]->is_signal) + continue; /* not a network event at all */ + eventlist[i]->just_checked = 0; + + if(WSAEnumNetworkEvents(eventlist[i]->ev_fd, + base->waitfor[i], /* reset the event handle */ + /*NULL,*/ /* do not reset the event handle */ + &netev) != 0) { + log_err("getdns: WSAEnumNetworkEvents failed: %s", + wsa_strerror(WSAGetLastError())); + return -1; + } + if((netev.lNetworkEvents & FD_READ)) { + if(netev.iErrorCode[FD_READ_BIT] != 0) + printf("FD_READ_BIT error: %s\n", + wsa_strerror(netev.iErrorCode[FD_READ_BIT])); + bits |= EV_READ; + printf("FD_READ_BIT\n"); + } + if((netev.lNetworkEvents & FD_WRITE)) { + if(netev.iErrorCode[FD_WRITE_BIT] != 0) + printf("FD_WRITE_BIT error: %s\n", + wsa_strerror(netev.iErrorCode[FD_WRITE_BIT])); + bits |= EV_WRITE; + printf("FD_WRITE_BIT\n"); + } + if((netev.lNetworkEvents & FD_CONNECT)) { + if(netev.iErrorCode[FD_CONNECT_BIT] != 0) + printf("FD_CONNECT_BIT error: %s\n", + wsa_strerror(netev.iErrorCode[FD_CONNECT_BIT])); + bits |= EV_READ; + bits |= EV_WRITE; + printf("FD_CONNECT_BIT\n"); + } + if((netev.lNetworkEvents & FD_ACCEPT)) { + if(netev.iErrorCode[FD_ACCEPT_BIT] != 0) + printf("FD_ACCEPT_BIT error: %s\n", + wsa_strerror(netev.iErrorCode[FD_ACCEPT_BIT])); + bits |= EV_READ; + printf("FD_ACCEPT_BIT\n"); + } + if((netev.lNetworkEvents & FD_CLOSE)) { + if(netev.iErrorCode[FD_CLOSE_BIT] != 0) + printf("FD_CLOSE_BIT error: %s\n", + wsa_strerror(netev.iErrorCode[FD_CLOSE_BIT])); + //g bits |= EV_READ; + //g bits |= EV_WRITE; + printf("FD_CLOSE_BIT\n"); + } + if(eventlist[i]->is_tcp && eventlist[i]->stick_events) { + /* + printf("winsock %d pass sticky %s%s\n", + eventlist[i]->ev_fd, + (eventlist[i]->old_events&EV_READ)?"EV_READ":"", + (eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":""); + */ + bits |= eventlist[i]->old_events; + } + if(eventlist[i]->is_tcp && bits) { + eventlist[i]->old_events = bits; + eventlist[i]->stick_events = 1; + if((eventlist[i]->ev_events & bits)) { + newstickies = 1; + } + /* + printf("winsock %d store sticky %s%s", + eventlist[i]->ev_fd, + (eventlist[i]->old_events&EV_READ)?"EV_READ":"", + (eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":""); + */ + } + if((bits & eventlist[i]->ev_events)) { + printf( "winsock event callback %p fd=%d " + "%s%s%s%s%s ; %s%s%s\n", + eventlist[i], eventlist[i]->ev_fd, + (netev.lNetworkEvents&FD_READ)?" FD_READ":"", + (netev.lNetworkEvents&FD_WRITE)?" FD_WRITE":"", + (netev.lNetworkEvents&FD_CONNECT)? + " FD_CONNECT":"", + (netev.lNetworkEvents&FD_ACCEPT)? + " FD_ACCEPT":"", + (netev.lNetworkEvents&FD_CLOSE)?" FD_CLOSE":"", + (bits&EV_READ)?" EV_READ":"", + (bits&EV_WRITE)?" EV_WRITE":"", + (bits&EV_TIMEOUT)?" EV_TIMEOUT":""); + + fptr_ok(fptr_whitelist_event( + eventlist[i]->ev_callback)); + (*eventlist[i]->ev_callback)(eventlist[i]->ev_fd, + bits & eventlist[i]->ev_events, + eventlist[i]->ev_arg); + } + /* + if(eventlist[i]->is_tcp && bits) + printf( "winsock %d got sticky %s%s\n", + eventlist[i]->ev_fd, + (eventlist[i]->old_events&EV_READ)?"EV_READ":"", + (eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":""); + */ + } + //verbose(VERB_CLIENT, "winsock_event net"); + if(base->tcp_reinvigorated) { + printf("winsock_event reinvigorated\n"); + base->tcp_reinvigorated = 0; + newstickies = 1; + } + base->tcp_stickies = newstickies; + //gprintf("winsock_event handle_select end\n"); + return 0; +} + +int _getdns_event_base_dispatch(struct _getdns_event_base *base) +{ + struct timeval wait; + if(settime(base) < 0) + return -1; + while(!base->need_to_exit) + { + /* see if timeouts need handling */ + _getdns_handle_timeouts(base, base->time_tv, &wait); + if(base->need_to_exit) + return 0; + /* do select */ + if(_getdns_handle_select(base, &wait) < 0) { + if(base->need_to_exit) + return 0; + return -1; + } + } + return 0; +} + +int _getdns_event_base_loopexit(struct _getdns_event_base *base, + struct timeval * ATTR_UNUSED(tv)) +{ + //verbose(VERB_CLIENT, "winsock_event loopexit"); + base->need_to_exit = 1; + return 0; +} + +void _getdns_event_base_free(struct _getdns_event_base *base) +{ + //verbose(VERB_CLIENT, "winsock_event event_base_free"); + if(!base) + return; + if(base->items) + free(base->items); + if(base->times) + free(base->times); + if(base->signals) + free(base->signals); + free(base); +} + +void _getdns_event_set(struct _getdns_event *ev, int fd, short bits, + void (*cb)(int, short, void *), void *arg) +{ + ev->node.key = ev; + ev->ev_fd = fd; + ev->ev_events = bits; + ev->ev_callback = cb; + fptr_ok(fptr_whitelist_event(ev->ev_callback)); + ev->ev_arg = arg; + ev->just_checked = 0; + ev->added = 0; +} + +int _getdns_event_base_set(struct _getdns_event_base *base, struct _getdns_event *ev) +{ + ev->ev_base = base; + ev->old_events = 0; + ev->stick_events = 0; + ev->added = 0; + return 0; +} + +/** +* Find a fd in the list of items. +* Note that not all items have a fd associated (those are -1). +* Signals are stored separately, and not searched. +* @param base: event base to look in. +* @param fd: what socket to look for. +* @return the index in the array, or -1 on failure. +*/ +static int +find_fd(struct _getdns_event_base* base, int fd) +{ + int i; + for (i = 0; imax; i++) { + if (base->items[i]->ev_fd == fd) + return i; + } + return -1; +} + +int _getdns_event_add(struct _getdns_event *ev, struct timeval *tv) +{ + printf( "event_add %p added=%d fd=%d tv=" ARG_LL "d %s%s%s\n", + ev, ev->added, ev->ev_fd, + (tv?(long long)tv->tv_sec*1000+(long long)tv->tv_usec/1000:-1), + (ev->ev_events&EV_READ)?" EV_READ":"", + (ev->ev_events&EV_WRITE)?" EV_WRITE":"", + (ev->ev_events&EV_TIMEOUT)?" EV_TIMEOUT":""); + + if(ev->added) + _getdns_event_del(ev); + + ev->is_tcp = 0; + ev->is_signal = 0; + ev->just_checked = 0; + + if ((ev->ev_events&(EV_READ | EV_WRITE)) && ev->ev_fd != -1) { + BOOL b = 0; + int t, l; + long events = 0; + //gprintf("\getdns_event_add %d %d\n", ev->ev_fd, events); + + if (ev->ev_base->max == ev->ev_base->cap) + return -1; + ev->idx = ev->ev_base->max++; + ev->ev_base->items[ev->idx] = ev; + + if ((ev->ev_events&EV_READ)) + events |= FD_READ; + if ((ev->ev_events&EV_WRITE)) + { + events |= FD_CONNECT; + events |= FD_WRITE; + } + + //printf("\getdns_event_add %d read = %d write = %d %d\n", ev->ev_fd, ev->ev_events&EV_READ, ev->ev_events&EV_WRITE, events); + + l = sizeof(t); + if(getsockopt(ev->ev_fd, SOL_SOCKET, SO_TYPE, + (void*)&t, &l) != 0) + log_err("getdns: getsockopt(SO_TYPE) failed: %s", + wsa_strerror(WSAGetLastError())); + if(t == SOCK_STREAM) { + /* TCP socket */ + ev->is_tcp = 1; + events |= FD_CLOSE; + if( (ev->ev_events&EV_WRITE) ) + events |= FD_CONNECT; + l = sizeof(b); + if(getsockopt(ev->ev_fd, SOL_SOCKET, SO_ACCEPTCONN, + (void*)&b, &l) != 0) + log_err("getdns: getsockopt(SO_ACCEPTCONN) failed: %s", + wsa_strerror(WSAGetLastError())); + if(b) /* TCP accept socket */ + events |= FD_ACCEPT; + } + ev->hEvent = WSACreateEvent(); + if(ev->hEvent == WSA_INVALID_EVENT) + log_err("getdns: WSACreateEvent failed: %s", + wsa_strerror(WSAGetLastError())); + /* automatically sets fd to nonblocking mode. + * nonblocking cannot be disabled, until wsaES(fd, NULL, 0) */ + printf("\nWSAEventSelect %d events %d hEvent %d\n", ev->ev_fd, events, ev->hEvent); + //gg if (WSAEventSelect(ev->ev_fd, ev->hEvent, FD_ACCEPT | FD_CONNECT | FD_READ | FD_CLOSE | FD_WRITE) != 0) { + //if (WSAEventSelect(ev->ev_fd, ev->hEvent,FD_READ | FD_WRITE) != 0) { + if (WSAEventSelect(ev->ev_fd, ev->hEvent, events) != 0) { + log_err("getdns: WSAEventSelect in getdns failed: %s", + wsa_strerror(WSAGetLastError())); + } + if(ev->is_tcp && ev->stick_events && + (ev->ev_events & ev->old_events)) { + /* go to processing the sticky event right away */ + ev->ev_base->tcp_reinvigorated = 1; + } + } + + if(tv && (ev->ev_events&EV_TIMEOUT)) { +#ifndef S_SPLINT_S + struct timeval *now = ev->ev_base->time_tv; + ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec; + ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec; + while(ev->ev_timeout.tv_usec > 1000000) { + ev->ev_timeout.tv_usec -= 1000000; + ev->ev_timeout.tv_sec++; + } +#endif + (void)_getdns_rbtree_insert(ev->ev_base->times, &ev->node); + } + ev->added = 1; + return 0; +} + +int _getdns_event_del(struct _getdns_event *ev) +{ + //verbose(VERB_ALGO, "event_del %p added=%d fd=%d tv=" ARG_LL "d %s%s%s", + // ev, ev->added, ev->ev_fd, + // (ev->ev_events&EV_TIMEOUT)?(long long)ev->ev_timeout.tv_sec*1000+ + // (long long)ev->ev_timeout.tv_usec/1000:-1, + // (ev->ev_events&EV_READ)?" EV_READ":"", + // (ev->ev_events&EV_WRITE)?" EV_WRITE":"", + // (ev->ev_events&EV_TIMEOUT)?" EV_TIMEOUT":""); + if(!ev->added) + return 0; + //log_assert(ev->added); + if((ev->ev_events&EV_TIMEOUT)) + (void)_getdns_rbtree_delete(ev->ev_base->times, &ev->node); + if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) { + //log_assert(ev->ev_base->max > 0); + /* remove item and compact the list */ + ev->ev_base->items[ev->idx] = + ev->ev_base->items[ev->ev_base->max-1]; + ev->ev_base->items[ev->ev_base->max-1] = NULL; + ev->ev_base->max--; + if(ev->idx < ev->ev_base->max) + ev->ev_base->items[ev->idx]->idx = ev->idx; + zero_waitfor(ev->ev_base->waitfor, ev->hEvent); + + if(WSAEventSelect(ev->ev_fd, ev->hEvent, 0) != 0) + log_err("getdns: WSAEventSelect(disable) failed: %s", + wsa_strerror(WSAGetLastError())); + if(!WSACloseEvent(ev->hEvent)) + log_err("getdns: WSACloseEvent failed: %s", + wsa_strerror(WSAGetLastError())); + } + ev->just_checked = 0; + ev->added = 0; + return 0; +} + +/** which base gets to handle signals */ +static struct _getdns_event_base* signal_base = NULL; +/** signal handler */ +static RETSIGTYPE sigh(int sig) +{ + struct getdns_event* ev; + if(!signal_base || sig < 0 || sig >= MAX_SIG) + return; + ev = signal_base->signals[sig]; + if(!ev) + return; + //g fptr_ok(fptr_whitelist_event(ev->ev_callback)); + //g (*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg); +} + +int _getdns_signal_add(struct _getdns_event *ev, struct timeval * ATTR_UNUSED(tv)) +{ + if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG) + return -1; + signal_base = ev->ev_base; + ev->ev_base->signals[ev->ev_fd] = ev; + ev->added = 1; + if(signal(ev->ev_fd, sigh) == SIG_ERR) { + return -1; + } + return 0; +} + +int _getdns_signal_del(struct _getdns_event *ev) +{ + if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG) + return -1; + ev->ev_base->signals[ev->ev_fd] = NULL; + ev->added = 0; + return 0; +} + +void winsock_tcp_wouldblock(struct _getdns_event* ev, int eventbits) +{ + //verbose(VERB_ALGO, "winsock: tcp wouldblock %s", + //eventbits==EV_READ?"EV_READ":"EV_WRITE"); + ev->old_events &= (~eventbits); + if(ev->old_events == 0) + ev->stick_events = 0; + /* in case this is the last sticky event, we could + * possibly run an empty handler loop to reset the base + * tcp_stickies variable + */ +} + +int winsock_register_wsaevent(struct _getdns_event_base* base, struct _getdns_event* ev, + WSAEVENT wsaevent, void (*cb)(int, short, void*), void* arg) +{ + if(base->max == base->cap) + return 0; + memset(ev, 0, sizeof(*ev)); + ev->ev_fd = -1; + ev->ev_events = EV_READ; + ev->ev_callback = cb; + ev->ev_arg = arg; + ev->is_signal = 1; + ev->hEvent = wsaevent; + ev->added = 1; + ev->ev_base = base; + ev->idx = ev->ev_base->max++; + ev->ev_base->items[ev->idx] = ev; + return 1; +} + +void winsock_unregister_wsaevent(struct _getdns_event* ev) +{ + if(!ev || !ev->added) return; + //log_assert(ev->added && ev->ev_base->max > 0) + /* remove item and compact the list */ + ev->ev_base->items[ev->idx] = ev->ev_base->items[ev->ev_base->max-1]; + ev->ev_base->items[ev->ev_base->max-1] = NULL; + ev->ev_base->max--; + if(ev->idx < ev->ev_base->max) + ev->ev_base->items[ev->idx]->idx = ev->idx; + ev->added = 0; +} + +#else /* USE_WINSOCK */ +/** symbol so this codefile defines symbols. pleasing ranlib on OSX 10.5 */ +int winsock_unused_symbol = 1; +#endif /* USE_WINSOCK */ diff --git a/src/util/winsock_event.h b/src/util/winsock_event.h new file mode 100644 index 00000000..595116b2 --- /dev/null +++ b/src/util/winsock_event.h @@ -0,0 +1,294 @@ +/* + * util/winsock_event.h - getdns event handling for winsock on windows + * extracted from Unbound source code and modified for getdns + * + * Copyright (c) 2015, NLnet Labs/Verisign. All rights reserved. + * + * This software is open source. + * + * 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 NLNET LABS or Verisign 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 THE COPYRIGHT + * HOLDER OR CONTRIBUTORS 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. + */ + +/** + * \file + * + * This file contains interface functions with the WinSock2 API on Windows. + * It uses the winsock WSAWaitForMultipleEvents interface on a number of + * sockets. + * + * Note that windows can only wait for max 64 events at one time. + * + * Also, file descriptors cannot be waited for. + * + * Named pipes are not easily available (and are not usable in select() ). + * For interprocess communication, it is possible to wait for a hEvent to + * be signaled by another thread. + * + * When a socket becomes readable, then it will not be flagged as + * readable again until you have gotten WOULDBLOCK from a recv routine. + * That means the event handler must store the readability (edge notify) + * and process the incoming data until it blocks. + * The function performing recv then has to inform the event handler that + * the socket has blocked, and the event handler can mark it as such. + * Thus, this file transforms the edge notify from windows to a level notify + * that is compatible with UNIX. + * The WSAEventSelect page says that it does do level notify, as long + * as you call a recv/write/accept at least once when it is signalled. + * This last bit is not true, even though documented in server2008 api docs + * from microsoft, it does not happen at all. Instead you have to test for + * WSAEWOULDBLOCK on a tcp stream, and only then retest the socket. + * And before that remember the previous result as still valid. + * + * To stay 'fair', instead of emptying a socket completely, the event handler + * can test the other (marked as blocking) sockets for new events. + * + * Additionally, TCP accept sockets get special event support. + * + * Socket numbers are not starting small, they can be any number (say 33060). + * Therefore, bitmaps are not used, but arrays. + * + * on winsock, you must use recv() and send() for TCP reads and writes, + * not read() and write(), those work only on files. + * + * Also fseek and fseeko do not work if a FILE is not fopen-ed in binary mode. + * + * When under a high load windows gives out lots of errors, from recvfrom + * on udp sockets for example (WSAECONNRESET). Even though the udp socket + * has no connection per se. + */ + +#ifndef UTIL_WINSOCK_EVENT_H +#define UTIL_WINSOCK_EVENT_H + +// Only enabled for Windows +#ifdef USE_WINSOCK + +#ifndef HAVE_EVENT_BASE_FREE +#define HAVE_EVENT_BASE_FREE +#endif + +/* redefine the calls to different names so that there is no name + * collision with other code that uses libevent names. (that uses libunbound)*/ +#define event_init winsockevent_init +#define event_get_version winsockevent_get_version +#define event_get_method winsockevent_get_method +#define _getdns_event_base_dispatch winsockevent_base_dispatch +#define event_base_loopexit winsockevent_base_loopexit +#define _getdns_event_base_free winsockevent_base_free +#define event_set winsockevent_set +#define event_base_set winsockevent_base_set +#define event_add winsockevent_add +#define _getdns_event_del winsockevent_del +#define signal_add winsocksignal_add +#define signal_del winsocksignal_del + +/** event timeout */ +#define EV_TIMEOUT 0x01 +/** event fd readable */ +#define EV_READ 0x02 +/** event fd writable */ +#define EV_WRITE 0x04 +/** event signal */ +#define EV_SIGNAL 0x08 +/** event must persist */ +#define EV_PERSIST 0x10 + +/* needs our redblack tree */ +#include "util/rbtree.h" + +/** max number of signals to support */ +#define MAX_SIG 32 + +/** The number of items that the winsock event handler can service. + * Windows cannot handle more anyway */ +#define WSK_MAX_ITEMS 64 + +/** + * event base for winsock event handler + */ +struct _getdns_event_base +{ + /** sorted by timeout (absolute), ptr */ + _getdns_rbtree_t* times; + /** array (first part in use) of handles to work on */ + struct _getdns_event** items; + /** number of items in use in array */ + int max; + /** capacity of array, size of array in items */ + int cap; + /** array of 0 - maxsig of ptr to event for it */ + struct _getdns_event** signals; + /** if we need to exit */ + int need_to_exit; + /** where to store time in seconds */ + time_t* time_secs; + /** where to store time in microseconds */ + struct timeval* time_tv; + /** + * TCP streams have sticky events to them, these are not + * reported by the windows event system anymore, we have to + * keep reporting those events as present until wouldblock() is + * signalled by the handler back to use. + */ + int tcp_stickies; + /** + * should next cycle process reinvigorated stickies, + * these are stickies that have been stored, but due to a new + * event_add a sudden interest in the event has incepted. + */ + int tcp_reinvigorated; + /** The list of events that is currently being processed. */ + WSAEVENT waitfor[WSK_MAX_ITEMS]; + + /* fdset for read write, for fds ready, and added */ + fd_set + /** fds for reading */ + reads, + /** fds for writing */ + writes, + /** fds determined ready for use */ + ready, + /** ready plus newly added events. */ + content; +}; + +/** + * Event structure. Has some of the event elements. + */ +struct _getdns_event { + /** node in timeout rbtree */ + _getdns_rbnode_t node; + /** is event already added */ + int added; + + /** event base it belongs to */ + struct _getdns_event_base *ev_base; + /** fd to poll or -1 for timeouts. signal number for sigs. */ + int ev_fd; + /** what events this event is interested in, see EV_.. above. */ + short ev_events; + /** timeout value */ + struct timeval ev_timeout; + + /** callback to call: fd, eventbits, userarg */ + void (*ev_callback)(int, short, void *); + /** callback user arg */ + void *ev_arg; + + /* ----- nonpublic part, for winsock_event only ----- */ + /** index of this event in the items array (if added) */ + int idx; + /** the event handle to wait for new events to become ready */ + WSAEVENT hEvent; + /** true if this filedes is a TCP socket and needs special attention */ + int is_tcp; + /** remembered EV_ values */ + short old_events; + /** should remembered EV_ values be used for TCP streams. + * Reset after WOULDBLOCK is signaled using the function. */ + int stick_events; + + /** true if this event is a signaling WSAEvent by the user. + * User created and user closed WSAEvent. Only signaled/unsigneled, + * no read/write/distinctions needed. */ + int is_signal; + /** used during callbacks to see which events were just checked */ + int just_checked; +}; + +char* wsa_strerror(DWORD err); +void log_err(const char *format, ...); +/** create event base */ +void *event_init(time_t* time_secs, struct timeval* time_tv); +/** get version */ +const char *event_get_version(void); +/** get polling method (select,epoll) */ +const char *event_get_method(void); +/** run select in a loop */ +int event_base_dispatch(struct _getdns_event_base *); +/** exit that loop */ +int event_base_loopexit(struct _getdns_event_base *, struct timeval *); +/** free event base. Free events yourself */ +void event_base_free(struct _getdns_event_base *); +/** set content of event */ +void event_set(struct _getdns_event *, int, short, void (*)(int, short, void *), void *); + +/** add event to a base. You *must* call this for every event. */ +int event_base_set(struct _getdns_event_base *, struct _getdns_event *); +/** add event to make it active. You may not change it with event_set anymore */ +int event_add(struct _getdns_event *, struct timeval *); +/** remove event. You may change it again */ +int event_del(struct _getdns_event *); + +#define evtimer_add(ev, tv) event_add(ev, tv) +#define evtimer_del(ev) event_del(ev) + +/* uses different implementation. Cannot mix fd/timeouts and signals inside + * the same struct event. create several event structs for that. */ +/** install signal handler */ +int signal_add(struct _getdns_event *, struct timeval *); +/** set signal event contents */ +#define signal_set(ev, x, cb, arg) \ + event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg) +/** remove signal handler */ +int signal_del(struct _getdns_event *); + +/** compare events in tree, based on timevalue, ptr for uniqueness */ +int getdns_mini_ev_cmp(const void* a, const void* b); + +/** + * Routine for windows only, where the handling layer can signal that + * a TCP stream encountered WSAEWOULDBLOCK for a stream and thus needs + * retesting the event. + * Pass if EV_READ or EV_WRITE gave wouldblock. + */ +static void winsock_tcp_wouldblock(struct _getdns_event* ev, int eventbit); + +/** + * Routine for windows only. where you pass a signal WSAEvent that + * you wait for. When the event is signaled, the callback gets called. + * The callback has to WSAResetEvent to disable the signal. + * @param base: the event base. + * @param ev: the event structure for data storage + * can be passed uninitialised. + * @param wsaevent: the WSAEvent that gets signaled. + * @param cb: callback routine. + * @param arg: user argument to callback routine. + * @return false on error. + */ +static int winsock_register_wsaevent(struct _getdns_event_base* base, struct _getdns_event* ev, + WSAEVENT wsaevent, void (*cb)(int, short, void*), void* arg); + +/** + * Unregister a wsaevent. User has to close the WSAEVENT itself. + * @param ev: event data storage. + */ +static void winsock_unregister_wsaevent(struct _getdns_event* ev); + +#endif /* USE_WINSOCK */ +#endif /* UTIL_WINSOCK_EVENT_H */