Merge branch 'features/windows-support' into develop

This commit is contained in:
Willem Toorop 2015-12-24 14:44:18 +01:00
commit a2bdfb2f22
19 changed files with 2054 additions and 104 deletions

View File

@ -193,6 +193,7 @@ $(distdir):
cp $(srcdir)/install-sh $(distdir)
cp $(srcdir)/config.sub $(distdir)
cp $(srcdir)/config.guess $(distdir)
cp $(srcdir)/getdns.pc.in $(distdir)
cp libtool $(distdir)
cp $(srcdir)/ltmain.sh $(distdir)
cp $(srcdir)/m4/*.m4 $(distdir)/m4

View File

@ -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 <https://getdnsapi.net/spec.html>
* 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,34 @@ 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
This section has some Windows specific build instructions.
Build tested using Mingw(3.21.0) and Msys 1.0 (http://www.mingw.org/) on Windows 8.1
Dependencies:
The following dependencies are built from source on Mingw
openssl1.0.2a
libidn
The windows version of getdns currently only is supported in the stub only mode.
To configure:
./configure --enable-stub-only --with-trust-anchor="c:\\\MinGW\\\msys\\\1.0\\\etc\\\unbound\\\getdns-root.key" --with-ssl=/c/OpenSSL --with-getdns_query
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.
Example test queries:
./getdns_query.exe -s gmadkat.com A @64.6.64.6 +return_call_reporting (UDP)
./getdns_query.exe -s gmadkat.com A @64.6.64.6 -T +return_call_reporting (TCP)
./getdns_query.exe -s gmadkat.com A -l L @185.49.141.37 +return_call_reporting (TLS without authentication)
./getdns_query.exe -s www.huque.com A +dnssec_return_status +return_call_reporting (DNSSEC)
Contributors
============
* Theogene Bucuti

View File

@ -32,6 +32,7 @@
AC_PREREQ([2.56])
AC_CONFIG_MACRO_DIRS([m4])
sinclude(./m4/acx_openssl.m4)
sinclude(./m4/acx_getaddrinfo.m4)
sinclude(./m4/ax_check_compile_flag.m4)
sinclude(./m4/pkg.m4)
@ -444,37 +445,53 @@ case "$enable_stub_only" in
esac
# search to set include and library paths right
# find libidn
my_with_libidn=1
AC_ARG_WITH(libidn, AS_HELP_STRING([--with-libidn=pathname],
[path to libidn (default: search /usr/local ..)]),
[], [withval="yes"])
if test x_$withval = x_yes; then
for dir in /usr/local /opt/local /usr/pkg /usr/sfw; do
if test -f "$dir/include/idna.h"; then
CFLAGS="$CFLAGS -I$dir/include"
LDFLAGS="$LDFLAGS -L$dir/lib"
AC_MSG_NOTICE([Found libidn in $dir])
break
fi
if test -f "$dir/include/idn/idna.h"; then
CFLAGS="$CFLAGS -I$dir/include/idn"
LDFLAGS="$LDFLAGS -L$dir/lib"
AC_MSG_NOTICE([Found libidn in $dir])
break
fi
done
if test -f "/usr/include/idn/idna.h"; then
CFLAGS="$CFLAGS -I/usr/include/idn"
#LDFLAGS="$LDFLAGS -L/usr/lib"
AC_MSG_NOTICE([Found libidn in /usr])
fi
# find libidn (no libidn on windows though)
AC_CHECK_HEADERS([windows.h winsock.h stdio.h winsock2.h ws2tcpip.h],,, [AC_INCLUDES_DEFAULT])
ACX_CHECK_GETADDRINFO_WITH_INCLUDES
if test "$USE_WINSOCK" = 1; then
AC_MSG_NOTICE([ Building on Windows ... YES! ])
AC_DEFINE_UNQUOTED([GETDNS_ON_WINDOWS], [1], [Define this to enable Windows build.])
AC_DEFINE_UNQUOTED([STUB_NATIVE_DNSSEC], [1])
LIBS="$LIBS -lgdi32 -liphlpapi"
my_with_libunbound=0
my_with_libidn=0
else
if test x_$withval != x_no; then
CFLAGS="$CFLAGS -I$withval/include"
LDFLAGS="$LDFLAGS -L$withval/lib"
my_with_libidn=1
fi
if test $my_with_libidn = 1
then
AC_ARG_WITH(libidn, AS_HELP_STRING([--with-libidn=pathname],
[path to libidn (default: search /usr/local ..)]),
[], [withval="yes"])
if test x_$withval = x_yes; then
for dir in /usr/local /opt/local /usr/pkg /usr/sfw; do
if test -f "$dir/include/idna.h"; then
CFLAGS="$CFLAGS -I$dir/include"
LDFLAGS="$LDFLAGS -L$dir/lib"
AC_MSG_NOTICE([Found libidn in $dir])
break
fi
if test -f "$dir/include/idn/idna.h"; then
CFLAGS="$CFLAGS -I$dir/include/idn"
LDFLAGS="$LDFLAGS -L$dir/lib"
AC_MSG_NOTICE([Found libidn in $dir])
break
fi
done
if test -f "/usr/include/idn/idna.h"; then
CFLAGS="$CFLAGS -I/usr/include/idn"
#LDFLAGS="$LDFLAGS -L/usr/lib"
AC_MSG_NOTICE([Found libidn in /usr])
fi
else
my_with_libidn=0
if test x_$withval != x_no; then
CFLAGS="$CFLAGS -I$withval/include"
LDFLAGS="$LDFLAGS -L$withval/lib"
else
my_with_libidn=0
fi
fi
fi
@ -942,6 +959,8 @@ if test $ac_cv_c_unused_attribute = yes; then
AC_DEFINE(HAVE_ATTR_UNUSED, 1, [Whether the C compiler accepts the "unused" attribute])
fi
AC_CHECK_DECLS([strlcpy,arc4random,arc4random_uniform])
AC_REPLACE_FUNCS(inet_pton)
AC_REPLACE_FUNCS(inet_ntop)
AC_REPLACE_FUNCS(strlcpy)
AC_REPLACE_FUNCS(arc4random)
AC_REPLACE_FUNCS(arc4random_uniform)
@ -999,6 +1018,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 <winsock2.h>
#include<BaseTsd.h>
#endif
#ifdef HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
#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 <stdint.h>
#include <stdio.h>
#include <unistd.h>
@ -1055,6 +1103,14 @@ void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
unsigned char *SHA512(void* data, unsigned int data_len, unsigned char *digest);
#endif /* COMPAT_SHA512 */
#ifndef HAVE_INET_PTON
int inet_pton(int af, const char* src, void* dst);
#endif /* HAVE_INET_PTON */
#ifndef HAVE_INET_NTOP
const char *inet_ntop(int af, const void *src, char *dst, size_t size);
#endif
#ifdef __cplusplus
}
#endif
@ -1108,13 +1164,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 <sys/time.h>
# include <time.h>

81
m4/acx_getaddrinfo.m4 Normal file
View File

@ -0,0 +1,81 @@
# Taken from acx_nlnetlabs.m4 - common macros for configure checks
# Copyright 2009, Wouter Wijngaards, NLnet Labs.
# BSD licensed.
#
dnl Check getaddrinfo.
dnl Works on linux, solaris, bsd and windows(links winsock).
dnl defines HAVE_GETADDRINFO, USE_WINSOCK.
AC_DEFUN([ACX_CHECK_GETADDRINFO_WITH_INCLUDES],
[AC_REQUIRE([AC_PROG_CC])
AC_MSG_CHECKING(for getaddrinfo)
ac_cv_func_getaddrinfo=no
AC_LINK_IFELSE(
[AC_LANG_SOURCE([[
#ifdef __cplusplus
extern "C"
{
#endif
char* getaddrinfo();
char* (*f) () = getaddrinfo;
#ifdef __cplusplus
}
#endif
int main() {
;
return 0;
}
]])],
dnl this case on linux, solaris, bsd
[ac_cv_func_getaddrinfo="yes"
dnl see if on windows
if test "$ac_cv_header_windows_h" = "yes"; then
AC_DEFINE(USE_WINSOCK, 1, [Whether the windows socket API is used])
USE_WINSOCK="1"
LIBS="$LIBS -lws2_32"
fi
],
dnl no quick getaddrinfo, try mingw32 and winsock2 library.
ORIGLIBS="$LIBS"
LIBS="$LIBS -lws2_32"
AC_LINK_IFELSE(
[AC_LANG_PROGRAM(
[
#define _WIN32_WINNT 0x0501
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#ifdef HAVE_WINSOCK_H
#include <winsock.h>
#endif
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#include <stdio.h>
#ifdef HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
#endif
],
[
(void)getaddrinfo(NULL, NULL, NULL, NULL);
]
)],
[
ac_cv_func_getaddrinfo="yes"
dnl already: LIBS="$LIBS -lws2_32"
AC_DEFINE(USE_WINSOCK, 1, [Whether the windows socket API is used])
USE_WINSOCK="1"
],
[
ac_cv_func_getaddrinfo="no"
LIBS="$ORIGLIBS"
])
)
AC_MSG_RESULT($ac_cv_func_getaddrinfo)
if test $ac_cv_func_getaddrinfo = yes; then
AC_DEFINE(HAVE_GETADDRINFO, 1, [Whether getaddrinfo is available])
fi
])dnl Endof AC_CHECK_GETADDRINFO_WITH_INCLUDES
dnl End of file

View File

@ -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
@ -238,11 +238,11 @@ convert.lo convert.o: $(srcdir)/convert.c config.h getdns/getdns.h getdns/getdns
$(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)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h \
$(srcdir)/gldns/wire2str.h $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h $(srcdir)/dict.h $(srcdir)/list.h $(srcdir)/convert.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)/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
dict.lo dict.o: $(srcdir)/dict.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)/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 config.h $(srcdir)/debug.h getdns/getdns.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 \
@ -309,6 +309,8 @@ getentropy_linux.lo getentropy_linux.o: $(srcdir)/compat/getentropy_linux.c conf
getentropy_osx.lo getentropy_osx.o: $(srcdir)/compat/getentropy_osx.c config.h
getentropy_solaris.lo getentropy_solaris.o: $(srcdir)/compat/getentropy_solaris.c config.h
getentropy_win.lo getentropy_win.o: $(srcdir)/compat/getentropy_win.c
inet_ntop.lo inet_ntop.o: $(srcdir)/compat/inet_ntop.c config.h
inet_pton.lo inet_pton.o: $(srcdir)/compat/inet_pton.c config.h
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 \
@ -317,6 +319,7 @@ rbtree.lo rbtree.o: $(srcdir)/util/rbtree.c config.h $(srcdir)/util/log.h $(srcd
$(srcdir)/util/fptr_wlist.h $(srcdir)/util/rbtree.h
val_secalgo.lo val_secalgo.o: $(srcdir)/util/val_secalgo.c config.h $(srcdir)/util/val_secalgo.h $(srcdir)/util/log.h \
$(srcdir)/debug.h config.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/keyraw.h $(srcdir)/gldns/gbuffer.h
winsock_event.lo winsock_event.o: $(srcdir)/util/winsock_event.c config.h
libev.lo libev.o: $(srcdir)/extension/libev.c config.h $(srcdir)/types-internal.h getdns/getdns.h \
getdns/getdns_extra.h getdns/getdns.h $(srcdir)/util/rbtree.h \
$(srcdir)/getdns/getdns_ext_libev.h getdns/getdns_extra.h

View File

@ -33,8 +33,42 @@
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#ifndef UB_ON_WINDOWS
#ifndef GETDNS_ON_WINDOWS
#include <sys/mman.h>
#else
#include <wincrypt.h>
#include <process.h>
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();

View File

@ -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

218
src/compat/inet_ntop.c Normal file
View File

@ -0,0 +1,218 @@
/* From openssh 4.3p2 compat/inet_ntop.c */
/* Copyright (c) 1996 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/* OPENBSD ORIGINAL: lib/libc/net/inet_ntop.c */
#include <config.h>
#ifndef HAVE_INET_NTOP
#include <sys/param.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include <string.h>
#include <errno.h>
#include <stdio.h>
#ifndef IN6ADDRSZ
#define IN6ADDRSZ 16 /* IPv6 T_AAAA */
#endif
#ifndef INT16SZ
#define INT16SZ 2 /* for systems without 16-bit ints */
#endif
/*
* WARNING: Don't even consider trying to compile this on a system where
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
*/
static const char *inet_ntop4(const u_char *src, char *dst, size_t size);
static const char *inet_ntop6(const u_char *src, char *dst, size_t size);
/* char *
* inet_ntop(af, src, dst, size)
* convert a network format address to presentation format.
* return:
* pointer to presentation format address (`dst'), or NULL (see errno).
* author:
* Paul Vixie, 1996.
*/
const char *
inet_ntop(int af, const void *src, char *dst, size_t size)
{
switch (af) {
case AF_INET:
return (inet_ntop4(src, dst, size));
case AF_INET6:
return (inet_ntop6(src, dst, size));
default:
#ifdef EAFNOSUPPORT
errno = EAFNOSUPPORT;
#else
errno = ENOSYS;
#endif
return (NULL);
}
/* NOTREACHED */
}
/* const char *
* inet_ntop4(src, dst, size)
* format an IPv4 address, more or less like inet_ntoa()
* return:
* `dst' (as a const)
* notes:
* (1) uses no statics
* (2) takes a u_char* not an in_addr as input
* author:
* Paul Vixie, 1996.
*/
static const char *
inet_ntop4(const u_char *src, char *dst, size_t size)
{
static const char fmt[] = "%u.%u.%u.%u";
char tmp[sizeof "255.255.255.255"];
int l;
l = snprintf(tmp, size, fmt, src[0], src[1], src[2], src[3]);
if (l <= 0 || l >= (int)size) {
errno = ENOSPC;
return (NULL);
}
strlcpy(dst, tmp, size);
return (dst);
}
/* const char *
* inet_ntop6(src, dst, size)
* convert IPv6 binary address into presentation (printable) format
* author:
* Paul Vixie, 1996.
*/
static const char *
inet_ntop6(const u_char *src, char *dst, size_t size)
{
/*
* Note that int32_t and int16_t need only be "at least" large enough
* to contain a value of the specified size. On some systems, like
* Crays, there is no such thing as an integer variable with 16 bits.
* Keep this in mind if you think this function should have been coded
* to use pointer overlays. All the world's not a VAX.
*/
char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
char *tp, *ep;
struct { int base, len; } best, cur;
u_int words[IN6ADDRSZ / INT16SZ];
int i;
int advance;
/*
* Preprocess:
* Copy the input (bytewise) array into a wordwise array.
* Find the longest run of 0x00's in src[] for :: shorthanding.
*/
memset(words, '\0', sizeof words);
for (i = 0; i < IN6ADDRSZ; i++)
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
best.base = -1;
best.len = 0;
cur.base = -1;
cur.len = 0;
for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
if (words[i] == 0) {
if (cur.base == -1)
cur.base = i, cur.len = 1;
else
cur.len++;
} else {
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
cur.base = -1;
}
}
}
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
}
if (best.base != -1 && best.len < 2)
best.base = -1;
/*
* Format the result.
*/
tp = tmp;
ep = tmp + sizeof(tmp);
for (i = 0; i < (IN6ADDRSZ / INT16SZ) && tp < ep; i++) {
/* Are we inside the best run of 0x00's? */
if (best.base != -1 && i >= best.base &&
i < (best.base + best.len)) {
if (i == best.base) {
if (tp + 1 >= ep)
return (NULL);
*tp++ = ':';
}
continue;
}
/* Are we following an initial run of 0x00s or any real hex? */
if (i != 0) {
if (tp + 1 >= ep)
return (NULL);
*tp++ = ':';
}
/* Is this address an encapsulated IPv4? */
if (i == 6 && best.base == 0 &&
(best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
if (!inet_ntop4(src+12, tp, (size_t)(ep - tp)))
return (NULL);
tp += strlen(tp);
break;
}
advance = snprintf(tp, ep - tp, "%x", words[i]);
if (advance <= 0 || advance >= ep - tp)
return (NULL);
tp += advance;
}
/* Was it a trailing run of 0x00's? */
if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) {
if (tp + 1 >= ep)
return (NULL);
*tp++ = ':';
}
if (tp + 1 >= ep)
return (NULL);
*tp++ = '\0';
/*
* Check for overflow, copy, and we're done.
*/
if ((size_t)(tp - tmp) > size) {
errno = ENOSPC;
return (NULL);
}
strlcpy(dst, tmp, size);
return (dst);
}
#endif /* !HAVE_INET_NTOP */

230
src/compat/inet_pton.c Normal file
View File

@ -0,0 +1,230 @@
/* $KAME: inet_pton.c,v 1.5 2001/08/20 02:32:40 itojun Exp $ */
/* Copyright (c) 1996 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN 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>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/*
* WARNING: Don't even consider trying to compile this on a system where
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
*/
static int inet_pton4 (const char *src, uint8_t *dst);
static int inet_pton6 (const char *src, uint8_t *dst);
/*
*
* The definitions we might miss.
*
*/
#ifndef NS_INT16SZ
#define NS_INT16SZ 2
#endif
#ifndef NS_IN6ADDRSZ
#define NS_IN6ADDRSZ 16
#endif
#ifndef NS_INADDRSZ
#define NS_INADDRSZ 4
#endif
/* int
* inet_pton(af, src, dst)
* convert from presentation format (which usually means ASCII printable)
* to network format (which is usually some kind of binary format).
* return:
* 1 if the address was valid for the specified address family
* 0 if the address wasn't valid (`dst' is untouched in this case)
* -1 if some other error occurred (`dst' is untouched in this case, too)
* author:
* Paul Vixie, 1996.
*/
int
inet_pton(af, src, dst)
int af;
const char *src;
void *dst;
{
switch (af) {
case AF_INET:
return (inet_pton4(src, dst));
case AF_INET6:
return (inet_pton6(src, dst));
default:
#ifdef EAFNOSUPPORT
errno = EAFNOSUPPORT;
#else
errno = ENOSYS;
#endif
return (-1);
}
/* NOTREACHED */
}
/* int
* inet_pton4(src, dst)
* like inet_aton() but without all the hexadecimal and shorthand.
* return:
* 1 if `src' is a valid dotted quad, else 0.
* notice:
* does not touch `dst' unless it's returning 1.
* author:
* Paul Vixie, 1996.
*/
static int
inet_pton4(src, dst)
const char *src;
uint8_t *dst;
{
static const char digits[] = "0123456789";
int saw_digit, octets, ch;
uint8_t tmp[NS_INADDRSZ], *tp;
saw_digit = 0;
octets = 0;
*(tp = tmp) = 0;
while ((ch = *src++) != '\0') {
const char *pch;
if ((pch = strchr(digits, ch)) != NULL) {
uint32_t new = *tp * 10 + (pch - digits);
if (new > 255)
return (0);
*tp = new;
if (! saw_digit) {
if (++octets > 4)
return (0);
saw_digit = 1;
}
} else if (ch == '.' && saw_digit) {
if (octets == 4)
return (0);
*++tp = 0;
saw_digit = 0;
} else
return (0);
}
if (octets < 4)
return (0);
memcpy(dst, tmp, NS_INADDRSZ);
return (1);
}
/* int
* inet_pton6(src, dst)
* convert presentation level address to network order binary form.
* return:
* 1 if `src' is a valid [RFC1884 2.2] address, else 0.
* notice:
* (1) does not touch `dst' unless it's returning 1.
* (2) :: in a full address is silently ignored.
* credit:
* inspired by Mark Andrews.
* author:
* Paul Vixie, 1996.
*/
static int
inet_pton6(src, dst)
const char *src;
uint8_t *dst;
{
static const char xdigits_l[] = "0123456789abcdef",
xdigits_u[] = "0123456789ABCDEF";
uint8_t tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
const char *xdigits, *curtok;
int ch, saw_xdigit;
uint32_t val;
memset((tp = tmp), '\0', NS_IN6ADDRSZ);
endp = tp + NS_IN6ADDRSZ;
colonp = NULL;
/* Leading :: requires some special handling. */
if (*src == ':')
if (*++src != ':')
return (0);
curtok = src;
saw_xdigit = 0;
val = 0;
while ((ch = *src++) != '\0') {
const char *pch;
if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
pch = strchr((xdigits = xdigits_u), ch);
if (pch != NULL) {
val <<= 4;
val |= (pch - xdigits);
if (val > 0xffff)
return (0);
saw_xdigit = 1;
continue;
}
if (ch == ':') {
curtok = src;
if (!saw_xdigit) {
if (colonp)
return (0);
colonp = tp;
continue;
}
if (tp + NS_INT16SZ > endp)
return (0);
*tp++ = (uint8_t) (val >> 8) & 0xff;
*tp++ = (uint8_t) val & 0xff;
saw_xdigit = 0;
val = 0;
continue;
}
if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
inet_pton4(curtok, tp) > 0) {
tp += NS_INADDRSZ;
saw_xdigit = 0;
break; /* '\0' was seen by inet_pton4(). */
}
return (0);
}
if (saw_xdigit) {
if (tp + NS_INT16SZ > endp)
return (0);
*tp++ = (uint8_t) (val >> 8) & 0xff;
*tp++ = (uint8_t) val & 0xff;
}
if (colonp != NULL) {
/*
* Since some memmove()'s erroneously fail to handle
* overlapping regions, we'll do the shift by hand.
*/
const int n = tp - colonp;
int i;
for (i = 1; i <= n; i++) {
endp[- i] = colonp[n - i];
colonp[n - i] = 0;
}
tp = endp;
}
if (tp != endp)
return (0);
memcpy(dst, tmp, NS_IN6ADDRSZ);
return (1);
}

View File

@ -34,14 +34,24 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#ifndef USE_WINSOCK
#include <arpa/inet.h>
#include <sys/time.h>
#include <netdb.h>
#else
#include <winsock2.h>
#include <iphlpapi.h>
typedef unsigned short in_port_t;
#endif
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <assert.h>
#include <netdb.h>
#include <ctype.h>
#include "config.h"
@ -97,7 +107,6 @@ getdns_port_str_array[] = {
/* Private functions */
static getdns_return_t create_default_namespaces(struct getdns_context *context);
static getdns_return_t create_default_dns_transports(struct getdns_context *context);
static getdns_return_t set_os_defaults(struct getdns_context *);
static int transaction_id_cmp(const void *, const void *);
static void dispatch_updated(struct getdns_context *, uint16_t);
static void cancel_dns_req(getdns_dns_req *);
@ -358,7 +367,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 */
@ -700,6 +713,89 @@ upstream_init(getdns_upstream *upstream,
net_req_query_id_cmp);
}
#ifdef USE_WINSOCK
static getdns_return_t
set_os_defaults_windows(struct getdns_context *context)
{
char domain[1024];
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;
FIXED_INFO *info;
ULONG buflen = sizeof(*info);
IP_ADDR_STRING *ptr = 0;
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) {
ptr = info->DnsServerList.Next;
*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 */
#else
static getdns_return_t
set_os_defaults(struct getdns_context *context)
{
@ -811,6 +907,7 @@ set_os_defaults(struct getdns_context *context)
_getdns_list_append_string(context->suffix, domain);
return GETDNS_RETURN_GOOD;
} /* set_os_defaults */
#endif
/* compare of transaction ids in DESCENDING order
so that 0 comes last
@ -953,8 +1050,14 @@ getdns_context_create_with_extended_memory_functions(
result->fchg_resolvconf = NULL;
result->fchg_hosts = NULL;
// 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;
@ -2443,7 +2546,11 @@ _getdns_context_prepare_for_resolution(struct getdns_context *context,
/* 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)
#ifndef USE_WINSOCK
return GETDNS_RETURN_BAD_CONTEXT;
#else
printf("Warning! Bad TLS context, check openssl version on Windows!\n");;
#endif
/* 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";

View File

@ -34,9 +34,11 @@
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <locale.h>
#include "config.h"
#ifndef USE_WINSOCK
#include <arpa/inet.h>
#endif
#ifdef HAVE_LIBIDN
#include <stringprep.h>
#include <idna.h>

View File

@ -35,10 +35,13 @@
*/
#include <ctype.h>
#include "config.h"
#ifndef USE_WINSOCK
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include "types-internal.h"
#include "util-internal.h"

View File

@ -206,6 +206,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);

View File

@ -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 {

View File

@ -346,50 +346,50 @@ _getdns_hostname_loop(getdns_context *context, getdns_eventloop *loop,
switch (address_data->size) {
case 4:
(void)snprintf(name, sizeof(name),
"%hhu.%hhu.%hhu.%hhu.in-addr.arpa.",
((uint8_t *)address_data->data)[3],
((uint8_t *)address_data->data)[2],
((uint8_t *)address_data->data)[1],
((uint8_t *)address_data->data)[0]);
"%d.%d.%d.%d.in-addr.arpa.",
(int)((uint8_t *)address_data->data)[3],
(int)((uint8_t *)address_data->data)[2],
(int)((uint8_t *)address_data->data)[1],
(int)((uint8_t *)address_data->data)[0]);
break;
case 16:
(void)snprintf(name, sizeof(name),
"%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
"%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
"%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
"%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.ip6.arpa.",
(uint8_t)(((uint8_t *)address_data->data)[15] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[15] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[14] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[14] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[13] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[13] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[12] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[12] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[11] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[11] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[10] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[10] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[9] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[9] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[8] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[8] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[7] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[7] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[6] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[6] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[5] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[5] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[4] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[4] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[3] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[3] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[2] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[2] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[1] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[1] >> 4),
(uint8_t)(((uint8_t *)address_data->data)[0] & 0x0F),
(uint8_t)(((uint8_t *)address_data->data)[0] >> 4));
"%x.%x.%x.%x.%x.%x.%x.%x."
"%x.%x.%x.%x.%x.%x.%x.%x."
"%x.%x.%x.%x.%x.%x.%x.%x."
"%x.%x.%x.%x.%x.%x.%x.%x.ip6.arpa.",
(int)(((uint8_t *)address_data->data)[15] & 0x0F),
(int)(((uint8_t *)address_data->data)[15] >> 4),
(int)(((uint8_t *)address_data->data)[14] & 0x0F),
(int)(((uint8_t *)address_data->data)[14] >> 4),
(int)(((uint8_t *)address_data->data)[13] & 0x0F),
(int)(((uint8_t *)address_data->data)[13] >> 4),
(int)(((uint8_t *)address_data->data)[12] & 0x0F),
(int)(((uint8_t *)address_data->data)[12] >> 4),
(int)(((uint8_t *)address_data->data)[11] & 0x0F),
(int)(((uint8_t *)address_data->data)[11] >> 4),
(int)(((uint8_t *)address_data->data)[10] & 0x0F),
(int)(((uint8_t *)address_data->data)[10] >> 4),
(int)(((uint8_t *)address_data->data)[9] & 0x0F),
(int)(((uint8_t *)address_data->data)[9] >> 4),
(int)(((uint8_t *)address_data->data)[8] & 0x0F),
(int)(((uint8_t *)address_data->data)[8] >> 4),
(int)(((uint8_t *)address_data->data)[7] & 0x0F),
(int)(((uint8_t *)address_data->data)[7] >> 4),
(int)(((uint8_t *)address_data->data)[6] & 0x0F),
(int)(((uint8_t *)address_data->data)[6] >> 4),
(int)(((uint8_t *)address_data->data)[5] & 0x0F),
(int)(((uint8_t *)address_data->data)[5] >> 4),
(int)(((uint8_t *)address_data->data)[4] & 0x0F),
(int)(((uint8_t *)address_data->data)[4] >> 4),
(int)(((uint8_t *)address_data->data)[3] & 0x0F),
(int)(((uint8_t *)address_data->data)[3] >> 4),
(int)(((uint8_t *)address_data->data)[2] & 0x0F),
(int)(((uint8_t *)address_data->data)[2] >> 4),
(int)(((uint8_t *)address_data->data)[1] & 0x0F),
(int)(((uint8_t *)address_data->data)[1] >> 4),
(int)(((uint8_t *)address_data->data)[0] & 0x0F),
(int)(((uint8_t *)address_data->data)[0] >> 4));
break;
default:
return GETDNS_RETURN_INVALID_PARAMETER;

View File

@ -49,6 +49,13 @@
#include "general.h"
#include "pubkey-pinning.h"
#ifdef USE_WINSOCK
#define EINPROGRESS 112
#define EWOULDBLOCK 140
typedef u_short sa_family_t;
#include "util/winsock_event.h"
#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
@ -618,7 +625,7 @@ stub_tls_timeout_cb(void *userarg)
/****************************/
static int
stub_tcp_read(int fd, getdns_tcp_state *tcp, struct mem_funcs *mf)
stub_tcp_read(int fd, getdns_tcp_state *tcp, struct mem_funcs *mf, getdns_eventloop_event* event)
{
ssize_t read;
uint8_t *buf;
@ -633,12 +640,26 @@ stub_tcp_read(int fd, getdns_tcp_state *tcp, struct mem_funcs *mf)
tcp->read_pos = tcp->read_buf;
tcp->to_read = 2; /* Packet size */
}
read = recv(fd, tcp->read_pos, tcp->to_read, 0);
read = recv(fd, (void *)tcp->read_pos, tcp->to_read, 0);
if (read == -1) {
#ifdef USE_WINSOCK
printf("read (in tcp ) %s\n",
wsa_strerror(WSAGetLastError()));
if (WSAGetLastError() == WSAECONNRESET)
return STUB_TCP_AGAIN;
if (WSAGetLastError() == WSAEINPROGRESS)
return STUB_TCP_AGAIN;
if (WSAGetLastError() == WSAEWOULDBLOCK) {
winsock_tcp_wouldblock(event->ev, EV_READ);
return STUB_TCP_AGAIN;
}
#else
if (errno == EAGAIN || errno == EWOULDBLOCK)
return STUB_TCP_AGAIN;
else
return STUB_TCP_ERROR;
#endif
} else if (read == 0) {
/* Remote end closed the socket */
/* TODO: Try to reconnect */
@ -750,8 +771,17 @@ 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, (const char *)(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) {
@ -1264,7 +1294,7 @@ stub_udp_read_cb(void *userarg)
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
read = recvfrom(netreq->fd, netreq->response,
read = recvfrom(netreq->fd, (void *)netreq->response,
netreq->max_udp_payload_size + 1, /* If read == max_udp_payload_size
* then all is good. If read ==
* max_udp_payload_size + 1, then
@ -1344,7 +1374,8 @@ stub_udp_write_cb(void *userarg)
return; /* too many upstream options */
}
pkt_len = _getdns_network_req_add_tsig(netreq);
if ((ssize_t)pkt_len != sendto(netreq->fd, netreq->query, pkt_len, 0,
if ((ssize_t)pkt_len != sendto(
netreq->fd, (const void *)netreq->query, pkt_len, 0,
(struct sockaddr *)&netreq->upstream->addr,
netreq->upstream->addr_len)) {
close(netreq->fd);
@ -1375,7 +1406,7 @@ upstream_read_cb(void *userarg)
&upstream->upstreams->mf);
else
q = stub_tcp_read(upstream->fd, &upstream->tcp,
&upstream->upstreams->mf);
&upstream->upstreams->mf, &upstream->event);
switch (q) {
case STUB_TCP_AGAIN:

View File

@ -555,8 +555,7 @@ static getdns_return_t validate_chain(getdns_dict *response)
if ((r = getdns_list_set_dict(to_validate, 0, reply)))
goto error;
fprintf( stdout
, "reply %zu, dnssec_status: ", i);
printf("reply %u, dnssec_status: ", (unsigned)i);
switch ((s = getdns_validate_dnssec(
to_validate, validation_chain, trust_anchor))) {
@ -604,14 +603,13 @@ void callback(getdns_context *context, getdns_callback_type_t callback_type,
}
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);
printf("Response code was: GOOD. Status was: Callback with ID %"PRIu64" was successfull.\n",
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);
"An error occurred: The callback with ID %"PRIu64" was cancelled. Exiting.\n",
trans_id);
else {
fprintf(stderr,
"An error occurred: The callback got a callback_type of %d. Exiting.\n",

844
src/util/winsock_event.c Normal file
View File

@ -0,0 +1,844 @@
/*
* 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.
* Code is originally from the Unbound source for Windows.
*/
#include "config.h"
#ifdef USE_WINSOCK // only included for Windows builds
#include <signal.h>
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include <sys/time.h>
#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 ");
fprintf(stderr, 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 WINSOCK_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; i<base->max; 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; i<WSK_MAX_ITEMS; i++) {
if(waitfor[i] == x)
waitfor[i] = 0;
}
}
void *_getdns_event_init(time_t* time_secs, struct timeval* time_tv)
{
struct _getdns_event_base* base = (struct _getdns_event_base*)malloc(
sizeof(struct _getdns_event_base));
if(!base)
return NULL;
memset(base, 0, sizeof(*base));
base->time_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;
return base;
}
const char *_getdns_event_get_version(void)
{
return "winsock-event-"PACKAGE_VERSION;
}
const char *_getdns_event_get_method(void)
{
return "WSAWaitForMultipleEvents";
}
/** 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
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;
}
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);
}
}
/** 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;
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; i<base->max; 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, (int)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);
}
}
else {
//gv: do not schedule udp write
for (i = 0; i<base->max; i++) {
if (!base->items[i]->is_tcp && base->items[i]->ev_events&EV_WRITE) {
printf("skip UDP sched\n");
(*eventlist[i]->ev_callback)(eventlist[i]->ev_fd,
EV_WRITE & eventlist[i]->ev_events,
eventlist[i]->ev_arg);
return 0;
}
}
printf("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) */
printf("after wait %d %d\n", (int)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");
} 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; i<numwait; i++)
eventlist[i]->just_checked = 1;
//verbose(VERB_CLIENT, "winsock_event signals");
for(i=startidx; i<numwait; i++) {
if(!base->waitfor[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; i<numwait; i++) {
short bits = 0;
/* eventlist[i] fired */
/* see if eventlist[i] is still valid and just checked from
* WSAWaitForEvents */
if(!base->waitfor[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))
{
base->need_to_exit = 1;
return 0;
}
void _getdns_event_base_free(struct _getdns_event_base *base)
{
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;
}
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, (int)events, (int)ev->hEvent);
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 */
printf("\nWSAEventSelect sticky %d events %d hEvent %d\n", ev->ev_fd, (int)events, (int)ev->hEvent);
ev->ev_base->tcp_reinvigorated = 1;
}
}
if(tv && (ev->ev_events&EV_TIMEOUT)) {
printf("\nWSAEventSelect timeout %d hEvent %d\n", ev->ev_fd, (int)ev->hEvent);
#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)
{
if(!signal_base || sig < 0 || sig >= MAX_SIG)
return;
}
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)
{
printf("winsock: tcp wouldblock %s\n",
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 */

295
src/util/winsock_event.h Normal file
View File

@ -0,0 +1,295 @@
/*
* 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.
* Code is originally from the Unbound source for Windows.
*
* 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 _getdns_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 _getdns_event_set winsockevent_set
#define _getdns_event_base_set winsockevent_base_set
#define _getdns_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 *_getdns_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 _getdns_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 _getdns_event_base_free(struct _getdns_event_base *);
/** set content of event */
void _getdns_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 _getdns_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 _getdns_event_add(struct _getdns_event *, struct timeval *);
/** remove event. You may change it again */
int _getdns_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.
*/
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.
*/
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.
*/
void winsock_unregister_wsaevent(struct _getdns_event* ev);
#endif /* USE_WINSOCK */
#endif /* UTIL_WINSOCK_EVENT_H */