summaryrefslogtreecommitdiffstats
path: root/src/lib/asiolink/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:15:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:15:43 +0000
commitf5f56e1a1c4d9e9496fcb9d81131066a964ccd23 (patch)
tree49e44c6f87febed37efb953ab5485aa49f6481a7 /src/lib/asiolink/tests
parentInitial commit. (diff)
downloadisc-kea-f5f56e1a1c4d9e9496fcb9d81131066a964ccd23.tar.xz
isc-kea-f5f56e1a1c4d9e9496fcb9d81131066a964ccd23.zip
Adding upstream version 2.4.1.upstream/2.4.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib/asiolink/tests')
-rw-r--r--src/lib/asiolink/tests/Makefile.am75
-rw-r--r--src/lib/asiolink/tests/Makefile.in1303
-rw-r--r--src/lib/asiolink/tests/addr_utilities_unittest.cc388
-rw-r--r--src/lib/asiolink/tests/dummy_io_callback_unittest.cc27
-rw-r--r--src/lib/asiolink/tests/hash_address_unittest.cc58
-rw-r--r--src/lib/asiolink/tests/interval_timer_unittest.cc377
-rw-r--r--src/lib/asiolink/tests/io_address_unittest.cc370
-rw-r--r--src/lib/asiolink/tests/io_endpoint_unittest.cc286
-rw-r--r--src/lib/asiolink/tests/io_service_signal_unittests.cc282
-rw-r--r--src/lib/asiolink/tests/io_service_thread_pool_unittests.cc264
-rw-r--r--src/lib/asiolink/tests/io_service_unittest.cc48
-rw-r--r--src/lib/asiolink/tests/io_socket_unittest.cc25
-rw-r--r--src/lib/asiolink/tests/process_spawn_app.sh.in75
-rw-r--r--src/lib/asiolink/tests/process_spawn_unittest.cc350
-rw-r--r--src/lib/asiolink/tests/run_unittests.cc19
-rw-r--r--src/lib/asiolink/tests/tcp_acceptor_unittest.cc444
-rw-r--r--src/lib/asiolink/tests/tcp_endpoint_unittest.cc46
-rw-r--r--src/lib/asiolink/tests/tcp_socket_unittest.cc515
-rw-r--r--src/lib/asiolink/tests/tls_acceptor_unittest.cc450
-rw-r--r--src/lib/asiolink/tests/tls_socket_unittest.cc560
-rw-r--r--src/lib/asiolink/tests/tls_unittest.cc2695
-rw-r--r--src/lib/asiolink/tests/udp_endpoint_unittest.cc46
-rw-r--r--src/lib/asiolink/tests/udp_socket_unittest.cc324
-rw-r--r--src/lib/asiolink/tests/unix_domain_socket_unittest.cc310
24 files changed, 9337 insertions, 0 deletions
diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am
new file mode 100644
index 0000000..cad0d6d
--- /dev/null
+++ b/src/lib/asiolink/tests/Makefile.am
@@ -0,0 +1,75 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
+AM_CPPFLAGS += -DTEST_SCRIPT_SH=\"$(abs_top_builddir)/src/lib/asiolink/tests/process_spawn_app.sh\"
+AM_CPPFLAGS += -DINVALID_TEST_SCRIPT_SH=\"$(abs_top_srcdir)/README\"
+TEST_CA_DIR = $(abs_srcdir)/../testutils/ca
+AM_CPPFLAGS += -DTEST_CA_DIR=\"$(TEST_CA_DIR)\"
+
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda test-socket
+
+DISTCLEANFILES = process_spawn_app.sh
+
+noinst_SCRIPTS = process_spawn_app.sh
+
+TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += addr_utilities_unittest.cc
+run_unittests_SOURCES += io_address_unittest.cc
+run_unittests_SOURCES += hash_address_unittest.cc
+run_unittests_SOURCES += io_endpoint_unittest.cc
+run_unittests_SOURCES += io_socket_unittest.cc
+run_unittests_SOURCES += interval_timer_unittest.cc
+run_unittests_SOURCES += tcp_endpoint_unittest.cc
+run_unittests_SOURCES += tcp_socket_unittest.cc
+run_unittests_SOURCES += udp_endpoint_unittest.cc
+run_unittests_SOURCES += udp_socket_unittest.cc
+run_unittests_SOURCES += io_service_unittest.cc
+run_unittests_SOURCES += io_service_signal_unittests.cc
+run_unittests_SOURCES += io_service_thread_pool_unittests.cc
+run_unittests_SOURCES += dummy_io_callback_unittest.cc
+run_unittests_SOURCES += tcp_acceptor_unittest.cc
+run_unittests_SOURCES += unix_domain_socket_unittest.cc
+run_unittests_SOURCES += process_spawn_unittest.cc
+if HAVE_OPENSSL
+run_unittests_SOURCES += tls_unittest.cc
+run_unittests_SOURCES += tls_acceptor_unittest.cc
+run_unittests_SOURCES += tls_socket_unittest.cc
+endif
+if HAVE_BOTAN_BOOST
+run_unittests_SOURCES += tls_unittest.cc
+run_unittests_SOURCES += tls_acceptor_unittest.cc
+run_unittests_SOURCES += tls_socket_unittest.cc
+endif
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+
+run_unittests_LDADD = $(top_builddir)/src/lib/asiolink/testutils/libasiolinktest.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+run_unittests_LDADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(CRYPTO_LIBS)
+run_unittests_LDADD += $(GTEST_LDADD)
+
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
+
+# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
+# KEA_CXXFLAGS)
+run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_GXX
+run_unittests_CXXFLAGS += -Wno-unused-parameter -Wno-unused-private-field
+endif
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/asiolink/tests/Makefile.in b/src/lib/asiolink/tests/Makefile.in
new file mode 100644
index 0000000..b76d283
--- /dev/null
+++ b/src/lib/asiolink/tests/Makefile.in
@@ -0,0 +1,1303 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+TESTS = $(am__EXEEXT_1)
+@HAVE_GTEST_TRUE@am__append_1 = run_unittests
+@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@am__append_2 = tls_unittest.cc \
+@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@ tls_acceptor_unittest.cc \
+@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@ tls_socket_unittest.cc
+@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@am__append_3 = \
+@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@ tls_unittest.cc \
+@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@ tls_acceptor_unittest.cc \
+@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@ tls_socket_unittest.cc
+@HAVE_GTEST_TRUE@@USE_GXX_TRUE@am__append_4 = -Wno-unused-parameter -Wno-unused-private-field
+noinst_PROGRAMS = $(am__EXEEXT_2)
+subdir = src/lib/asiolink/tests
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \
+ $(top_srcdir)/m4macros/ax_cpp11.m4 \
+ $(top_srcdir)/m4macros/ax_cpp20.m4 \
+ $(top_srcdir)/m4macros/ax_crypto.m4 \
+ $(top_srcdir)/m4macros/ax_find_library.m4 \
+ $(top_srcdir)/m4macros/ax_gssapi.m4 \
+ $(top_srcdir)/m4macros/ax_gtest.m4 \
+ $(top_srcdir)/m4macros/ax_isc_rpath.m4 \
+ $(top_srcdir)/m4macros/ax_netconf.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = process_spawn_app.sh
+CONFIG_CLEAN_VPATH_FILES =
+@HAVE_GTEST_TRUE@am__EXEEXT_1 = run_unittests$(EXEEXT)
+am__EXEEXT_2 = $(am__EXEEXT_1)
+PROGRAMS = $(noinst_PROGRAMS)
+am__run_unittests_SOURCES_DIST = run_unittests.cc \
+ addr_utilities_unittest.cc io_address_unittest.cc \
+ hash_address_unittest.cc io_endpoint_unittest.cc \
+ io_socket_unittest.cc interval_timer_unittest.cc \
+ tcp_endpoint_unittest.cc tcp_socket_unittest.cc \
+ udp_endpoint_unittest.cc udp_socket_unittest.cc \
+ io_service_unittest.cc io_service_signal_unittests.cc \
+ io_service_thread_pool_unittests.cc \
+ dummy_io_callback_unittest.cc tcp_acceptor_unittest.cc \
+ unix_domain_socket_unittest.cc process_spawn_unittest.cc \
+ tls_unittest.cc tls_acceptor_unittest.cc \
+ tls_socket_unittest.cc
+@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@am__objects_1 = run_unittests-tls_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@ run_unittests-tls_acceptor_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@ run_unittests-tls_socket_unittest.$(OBJEXT)
+@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@am__objects_2 = run_unittests-tls_unittest.$(OBJEXT) \
+@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@ run_unittests-tls_acceptor_unittest.$(OBJEXT) \
+@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@ run_unittests-tls_socket_unittest.$(OBJEXT)
+@HAVE_GTEST_TRUE@am_run_unittests_OBJECTS = \
+@HAVE_GTEST_TRUE@ run_unittests-run_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-addr_utilities_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-io_address_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-hash_address_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-io_endpoint_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-io_socket_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-interval_timer_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-tcp_endpoint_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-tcp_socket_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-udp_endpoint_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-udp_socket_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-io_service_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-io_service_signal_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-io_service_thread_pool_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-dummy_io_callback_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-tcp_acceptor_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-unix_domain_socket_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-process_spawn_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ $(am__objects_1) $(am__objects_2)
+run_unittests_OBJECTS = $(am_run_unittests_OBJECTS)
+am__DEPENDENCIES_1 =
+@HAVE_GTEST_TRUE@run_unittests_DEPENDENCIES = $(top_builddir)/src/lib/asiolink/testutils/libasiolinktest.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+run_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \
+ $(run_unittests_CXXFLAGS) $(CXXFLAGS) $(run_unittests_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SCRIPTS = $(noinst_SCRIPTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = \
+ ./$(DEPDIR)/run_unittests-addr_utilities_unittest.Po \
+ ./$(DEPDIR)/run_unittests-dummy_io_callback_unittest.Po \
+ ./$(DEPDIR)/run_unittests-hash_address_unittest.Po \
+ ./$(DEPDIR)/run_unittests-interval_timer_unittest.Po \
+ ./$(DEPDIR)/run_unittests-io_address_unittest.Po \
+ ./$(DEPDIR)/run_unittests-io_endpoint_unittest.Po \
+ ./$(DEPDIR)/run_unittests-io_service_signal_unittests.Po \
+ ./$(DEPDIR)/run_unittests-io_service_thread_pool_unittests.Po \
+ ./$(DEPDIR)/run_unittests-io_service_unittest.Po \
+ ./$(DEPDIR)/run_unittests-io_socket_unittest.Po \
+ ./$(DEPDIR)/run_unittests-process_spawn_unittest.Po \
+ ./$(DEPDIR)/run_unittests-run_unittests.Po \
+ ./$(DEPDIR)/run_unittests-tcp_acceptor_unittest.Po \
+ ./$(DEPDIR)/run_unittests-tcp_endpoint_unittest.Po \
+ ./$(DEPDIR)/run_unittests-tcp_socket_unittest.Po \
+ ./$(DEPDIR)/run_unittests-tls_acceptor_unittest.Po \
+ ./$(DEPDIR)/run_unittests-tls_socket_unittest.Po \
+ ./$(DEPDIR)/run_unittests-tls_unittest.Po \
+ ./$(DEPDIR)/run_unittests-udp_endpoint_unittest.Po \
+ ./$(DEPDIR)/run_unittests-udp_socket_unittest.Po \
+ ./$(DEPDIR)/run_unittests-unix_domain_socket_unittest.Po
+am__mv = mv -f
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+SOURCES = $(run_unittests_SOURCES)
+DIST_SOURCES = $(am__run_unittests_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(srcdir)/process_spawn_app.sh.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_GSSAPI_CONFIGURE_FLAG = @DISTCHECK_GSSAPI_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG = @DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG@
+DISTCHECK_LIBYANG_CONFIGURE_FLAG = @DISTCHECK_LIBYANG_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_MYSQL_CONFIGURE_FLAG = @DISTCHECK_MYSQL_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PGSQL_CONFIGURE_FLAG = @DISTCHECK_PGSQL_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@
+DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG = @DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG@
+DISTCHECK_SYSREPO_CONFIGURE_FLAG = @DISTCHECK_SYSREPO_CONFIGURE_FLAG@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GTEST_CONFIG = @GTEST_CONFIG@
+GTEST_INCLUDES = @GTEST_INCLUDES@
+GTEST_LDADD = @GTEST_LDADD@
+GTEST_LDFLAGS = @GTEST_LDFLAGS@
+GTEST_SOURCE = @GTEST_SOURCE@
+HAVE_NETCONF = @HAVE_NETCONF@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KEA_CXXFLAGS = @KEA_CXXFLAGS@
+KEA_SRCID = @KEA_SRCID@
+KRB5_CONFIG = @KRB5_CONFIG@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBYANGCPP_CPPFLAGS = @LIBYANGCPP_CPPFLAGS@
+LIBYANGCPP_INCLUDEDIR = @LIBYANGCPP_INCLUDEDIR@
+LIBYANGCPP_LIBS = @LIBYANGCPP_LIBS@
+LIBYANGCPP_PREFIX = @LIBYANGCPP_PREFIX@
+LIBYANGCPP_VERSION = @LIBYANGCPP_VERSION@
+LIBYANG_CPPFLAGS = @LIBYANG_CPPFLAGS@
+LIBYANG_INCLUDEDIR = @LIBYANG_INCLUDEDIR@
+LIBYANG_LIBS = @LIBYANG_LIBS@
+LIBYANG_PREFIX = @LIBYANG_PREFIX@
+LIBYANG_VERSION = @LIBYANG_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@
+LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LIBS = @MYSQL_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PDFLATEX = @PDFLATEX@
+PERL = @PERL@
+PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PKGPYTHONDIR = @PKGPYTHONDIR@
+PKG_CONFIG = @PKG_CONFIG@
+PLANTUML = @PLANTUML@
+PREMIUM_DIR = @PREMIUM_DIR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SEP = @SEP@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@
+SR_PLUGINS_PATH = @SR_PLUGINS_PATH@
+SR_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_CPPFLAGS = @SYSREPOCPP_CPPFLAGS@
+SYSREPOCPP_INCLUDEDIR = @SYSREPOCPP_INCLUDEDIR@
+SYSREPOCPP_LIBS = @SYSREPOCPP_LIBS@
+SYSREPOCPP_PREFIX = @SYSREPOCPP_PREFIX@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+SYSREPO_PREFIX = @SYSREPO_PREFIX@
+SYSREPO_VERSION = @SYSREPO_VERSION@
+USE_LCOV = @USE_LCOV@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@
+YACC = @YACC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib \
+ $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) \
+ -DTEST_SCRIPT_SH=\"$(abs_top_builddir)/src/lib/asiolink/tests/process_spawn_app.sh\" \
+ -DINVALID_TEST_SCRIPT_SH=\"$(abs_top_srcdir)/README\" \
+ -DTEST_CA_DIR=\"$(TEST_CA_DIR)\"
+TEST_CA_DIR = $(abs_srcdir)/../testutils/ca
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static
+CLEANFILES = *.gcno *.gcda test-socket
+DISTCLEANFILES = process_spawn_app.sh
+noinst_SCRIPTS = process_spawn_app.sh
+TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+@HAVE_GTEST_TRUE@run_unittests_SOURCES = run_unittests.cc \
+@HAVE_GTEST_TRUE@ addr_utilities_unittest.cc \
+@HAVE_GTEST_TRUE@ io_address_unittest.cc \
+@HAVE_GTEST_TRUE@ hash_address_unittest.cc \
+@HAVE_GTEST_TRUE@ io_endpoint_unittest.cc io_socket_unittest.cc \
+@HAVE_GTEST_TRUE@ interval_timer_unittest.cc \
+@HAVE_GTEST_TRUE@ tcp_endpoint_unittest.cc \
+@HAVE_GTEST_TRUE@ tcp_socket_unittest.cc \
+@HAVE_GTEST_TRUE@ udp_endpoint_unittest.cc \
+@HAVE_GTEST_TRUE@ udp_socket_unittest.cc io_service_unittest.cc \
+@HAVE_GTEST_TRUE@ io_service_signal_unittests.cc \
+@HAVE_GTEST_TRUE@ io_service_thread_pool_unittests.cc \
+@HAVE_GTEST_TRUE@ dummy_io_callback_unittest.cc \
+@HAVE_GTEST_TRUE@ tcp_acceptor_unittest.cc \
+@HAVE_GTEST_TRUE@ unix_domain_socket_unittest.cc \
+@HAVE_GTEST_TRUE@ process_spawn_unittest.cc $(am__append_2) \
+@HAVE_GTEST_TRUE@ $(am__append_3)
+@HAVE_GTEST_TRUE@run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+@HAVE_GTEST_TRUE@run_unittests_LDADD = $(top_builddir)/src/lib/asiolink/testutils/libasiolinktest.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+@HAVE_GTEST_TRUE@ $(LOG4CPLUS_LIBS) $(BOOST_LIBS) \
+@HAVE_GTEST_TRUE@ $(CRYPTO_LIBS) $(GTEST_LDADD)
+@HAVE_GTEST_TRUE@run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
+
+# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
+# KEA_CXXFLAGS)
+@HAVE_GTEST_TRUE@run_unittests_CXXFLAGS = $(AM_CXXFLAGS) \
+@HAVE_GTEST_TRUE@ $(am__append_4)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/asiolink/tests/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib/asiolink/tests/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+process_spawn_app.sh: $(top_builddir)/config.status $(srcdir)/process_spawn_app.sh.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+run_unittests$(EXEEXT): $(run_unittests_OBJECTS) $(run_unittests_DEPENDENCIES) $(EXTRA_run_unittests_DEPENDENCIES)
+ @rm -f run_unittests$(EXEEXT)
+ $(AM_V_CXXLD)$(run_unittests_LINK) $(run_unittests_OBJECTS) $(run_unittests_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-addr_utilities_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-dummy_io_callback_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-hash_address_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-interval_timer_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-io_address_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-io_endpoint_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-io_service_signal_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-io_service_thread_pool_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-io_service_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-io_socket_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-process_spawn_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-run_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-tcp_acceptor_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-tcp_endpoint_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-tcp_socket_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-tls_acceptor_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-tls_socket_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-tls_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-udp_endpoint_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-udp_socket_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-unix_domain_socket_unittest.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.cc.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+run_unittests-run_unittests.o: run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-run_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-run_unittests.Tpo -c -o run_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-run_unittests.Tpo $(DEPDIR)/run_unittests-run_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='run_unittests-run_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc
+
+run_unittests-run_unittests.obj: run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-run_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-run_unittests.Tpo -c -o run_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-run_unittests.Tpo $(DEPDIR)/run_unittests-run_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='run_unittests-run_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi`
+
+run_unittests-addr_utilities_unittest.o: addr_utilities_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-addr_utilities_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-addr_utilities_unittest.Tpo -c -o run_unittests-addr_utilities_unittest.o `test -f 'addr_utilities_unittest.cc' || echo '$(srcdir)/'`addr_utilities_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-addr_utilities_unittest.Tpo $(DEPDIR)/run_unittests-addr_utilities_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='addr_utilities_unittest.cc' object='run_unittests-addr_utilities_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-addr_utilities_unittest.o `test -f 'addr_utilities_unittest.cc' || echo '$(srcdir)/'`addr_utilities_unittest.cc
+
+run_unittests-addr_utilities_unittest.obj: addr_utilities_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-addr_utilities_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-addr_utilities_unittest.Tpo -c -o run_unittests-addr_utilities_unittest.obj `if test -f 'addr_utilities_unittest.cc'; then $(CYGPATH_W) 'addr_utilities_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/addr_utilities_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-addr_utilities_unittest.Tpo $(DEPDIR)/run_unittests-addr_utilities_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='addr_utilities_unittest.cc' object='run_unittests-addr_utilities_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-addr_utilities_unittest.obj `if test -f 'addr_utilities_unittest.cc'; then $(CYGPATH_W) 'addr_utilities_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/addr_utilities_unittest.cc'; fi`
+
+run_unittests-io_address_unittest.o: io_address_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-io_address_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-io_address_unittest.Tpo -c -o run_unittests-io_address_unittest.o `test -f 'io_address_unittest.cc' || echo '$(srcdir)/'`io_address_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-io_address_unittest.Tpo $(DEPDIR)/run_unittests-io_address_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_address_unittest.cc' object='run_unittests-io_address_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-io_address_unittest.o `test -f 'io_address_unittest.cc' || echo '$(srcdir)/'`io_address_unittest.cc
+
+run_unittests-io_address_unittest.obj: io_address_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-io_address_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-io_address_unittest.Tpo -c -o run_unittests-io_address_unittest.obj `if test -f 'io_address_unittest.cc'; then $(CYGPATH_W) 'io_address_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/io_address_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-io_address_unittest.Tpo $(DEPDIR)/run_unittests-io_address_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_address_unittest.cc' object='run_unittests-io_address_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-io_address_unittest.obj `if test -f 'io_address_unittest.cc'; then $(CYGPATH_W) 'io_address_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/io_address_unittest.cc'; fi`
+
+run_unittests-hash_address_unittest.o: hash_address_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-hash_address_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-hash_address_unittest.Tpo -c -o run_unittests-hash_address_unittest.o `test -f 'hash_address_unittest.cc' || echo '$(srcdir)/'`hash_address_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-hash_address_unittest.Tpo $(DEPDIR)/run_unittests-hash_address_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='hash_address_unittest.cc' object='run_unittests-hash_address_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-hash_address_unittest.o `test -f 'hash_address_unittest.cc' || echo '$(srcdir)/'`hash_address_unittest.cc
+
+run_unittests-hash_address_unittest.obj: hash_address_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-hash_address_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-hash_address_unittest.Tpo -c -o run_unittests-hash_address_unittest.obj `if test -f 'hash_address_unittest.cc'; then $(CYGPATH_W) 'hash_address_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/hash_address_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-hash_address_unittest.Tpo $(DEPDIR)/run_unittests-hash_address_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='hash_address_unittest.cc' object='run_unittests-hash_address_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-hash_address_unittest.obj `if test -f 'hash_address_unittest.cc'; then $(CYGPATH_W) 'hash_address_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/hash_address_unittest.cc'; fi`
+
+run_unittests-io_endpoint_unittest.o: io_endpoint_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-io_endpoint_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-io_endpoint_unittest.Tpo -c -o run_unittests-io_endpoint_unittest.o `test -f 'io_endpoint_unittest.cc' || echo '$(srcdir)/'`io_endpoint_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-io_endpoint_unittest.Tpo $(DEPDIR)/run_unittests-io_endpoint_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_endpoint_unittest.cc' object='run_unittests-io_endpoint_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-io_endpoint_unittest.o `test -f 'io_endpoint_unittest.cc' || echo '$(srcdir)/'`io_endpoint_unittest.cc
+
+run_unittests-io_endpoint_unittest.obj: io_endpoint_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-io_endpoint_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-io_endpoint_unittest.Tpo -c -o run_unittests-io_endpoint_unittest.obj `if test -f 'io_endpoint_unittest.cc'; then $(CYGPATH_W) 'io_endpoint_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/io_endpoint_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-io_endpoint_unittest.Tpo $(DEPDIR)/run_unittests-io_endpoint_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_endpoint_unittest.cc' object='run_unittests-io_endpoint_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-io_endpoint_unittest.obj `if test -f 'io_endpoint_unittest.cc'; then $(CYGPATH_W) 'io_endpoint_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/io_endpoint_unittest.cc'; fi`
+
+run_unittests-io_socket_unittest.o: io_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-io_socket_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-io_socket_unittest.Tpo -c -o run_unittests-io_socket_unittest.o `test -f 'io_socket_unittest.cc' || echo '$(srcdir)/'`io_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-io_socket_unittest.Tpo $(DEPDIR)/run_unittests-io_socket_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_socket_unittest.cc' object='run_unittests-io_socket_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-io_socket_unittest.o `test -f 'io_socket_unittest.cc' || echo '$(srcdir)/'`io_socket_unittest.cc
+
+run_unittests-io_socket_unittest.obj: io_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-io_socket_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-io_socket_unittest.Tpo -c -o run_unittests-io_socket_unittest.obj `if test -f 'io_socket_unittest.cc'; then $(CYGPATH_W) 'io_socket_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/io_socket_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-io_socket_unittest.Tpo $(DEPDIR)/run_unittests-io_socket_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_socket_unittest.cc' object='run_unittests-io_socket_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-io_socket_unittest.obj `if test -f 'io_socket_unittest.cc'; then $(CYGPATH_W) 'io_socket_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/io_socket_unittest.cc'; fi`
+
+run_unittests-interval_timer_unittest.o: interval_timer_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-interval_timer_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-interval_timer_unittest.Tpo -c -o run_unittests-interval_timer_unittest.o `test -f 'interval_timer_unittest.cc' || echo '$(srcdir)/'`interval_timer_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-interval_timer_unittest.Tpo $(DEPDIR)/run_unittests-interval_timer_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='interval_timer_unittest.cc' object='run_unittests-interval_timer_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-interval_timer_unittest.o `test -f 'interval_timer_unittest.cc' || echo '$(srcdir)/'`interval_timer_unittest.cc
+
+run_unittests-interval_timer_unittest.obj: interval_timer_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-interval_timer_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-interval_timer_unittest.Tpo -c -o run_unittests-interval_timer_unittest.obj `if test -f 'interval_timer_unittest.cc'; then $(CYGPATH_W) 'interval_timer_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/interval_timer_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-interval_timer_unittest.Tpo $(DEPDIR)/run_unittests-interval_timer_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='interval_timer_unittest.cc' object='run_unittests-interval_timer_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-interval_timer_unittest.obj `if test -f 'interval_timer_unittest.cc'; then $(CYGPATH_W) 'interval_timer_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/interval_timer_unittest.cc'; fi`
+
+run_unittests-tcp_endpoint_unittest.o: tcp_endpoint_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tcp_endpoint_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-tcp_endpoint_unittest.Tpo -c -o run_unittests-tcp_endpoint_unittest.o `test -f 'tcp_endpoint_unittest.cc' || echo '$(srcdir)/'`tcp_endpoint_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tcp_endpoint_unittest.Tpo $(DEPDIR)/run_unittests-tcp_endpoint_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tcp_endpoint_unittest.cc' object='run_unittests-tcp_endpoint_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tcp_endpoint_unittest.o `test -f 'tcp_endpoint_unittest.cc' || echo '$(srcdir)/'`tcp_endpoint_unittest.cc
+
+run_unittests-tcp_endpoint_unittest.obj: tcp_endpoint_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tcp_endpoint_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-tcp_endpoint_unittest.Tpo -c -o run_unittests-tcp_endpoint_unittest.obj `if test -f 'tcp_endpoint_unittest.cc'; then $(CYGPATH_W) 'tcp_endpoint_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tcp_endpoint_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tcp_endpoint_unittest.Tpo $(DEPDIR)/run_unittests-tcp_endpoint_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tcp_endpoint_unittest.cc' object='run_unittests-tcp_endpoint_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tcp_endpoint_unittest.obj `if test -f 'tcp_endpoint_unittest.cc'; then $(CYGPATH_W) 'tcp_endpoint_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tcp_endpoint_unittest.cc'; fi`
+
+run_unittests-tcp_socket_unittest.o: tcp_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tcp_socket_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-tcp_socket_unittest.Tpo -c -o run_unittests-tcp_socket_unittest.o `test -f 'tcp_socket_unittest.cc' || echo '$(srcdir)/'`tcp_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tcp_socket_unittest.Tpo $(DEPDIR)/run_unittests-tcp_socket_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tcp_socket_unittest.cc' object='run_unittests-tcp_socket_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tcp_socket_unittest.o `test -f 'tcp_socket_unittest.cc' || echo '$(srcdir)/'`tcp_socket_unittest.cc
+
+run_unittests-tcp_socket_unittest.obj: tcp_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tcp_socket_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-tcp_socket_unittest.Tpo -c -o run_unittests-tcp_socket_unittest.obj `if test -f 'tcp_socket_unittest.cc'; then $(CYGPATH_W) 'tcp_socket_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tcp_socket_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tcp_socket_unittest.Tpo $(DEPDIR)/run_unittests-tcp_socket_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tcp_socket_unittest.cc' object='run_unittests-tcp_socket_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tcp_socket_unittest.obj `if test -f 'tcp_socket_unittest.cc'; then $(CYGPATH_W) 'tcp_socket_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tcp_socket_unittest.cc'; fi`
+
+run_unittests-udp_endpoint_unittest.o: udp_endpoint_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-udp_endpoint_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-udp_endpoint_unittest.Tpo -c -o run_unittests-udp_endpoint_unittest.o `test -f 'udp_endpoint_unittest.cc' || echo '$(srcdir)/'`udp_endpoint_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-udp_endpoint_unittest.Tpo $(DEPDIR)/run_unittests-udp_endpoint_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='udp_endpoint_unittest.cc' object='run_unittests-udp_endpoint_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-udp_endpoint_unittest.o `test -f 'udp_endpoint_unittest.cc' || echo '$(srcdir)/'`udp_endpoint_unittest.cc
+
+run_unittests-udp_endpoint_unittest.obj: udp_endpoint_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-udp_endpoint_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-udp_endpoint_unittest.Tpo -c -o run_unittests-udp_endpoint_unittest.obj `if test -f 'udp_endpoint_unittest.cc'; then $(CYGPATH_W) 'udp_endpoint_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/udp_endpoint_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-udp_endpoint_unittest.Tpo $(DEPDIR)/run_unittests-udp_endpoint_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='udp_endpoint_unittest.cc' object='run_unittests-udp_endpoint_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-udp_endpoint_unittest.obj `if test -f 'udp_endpoint_unittest.cc'; then $(CYGPATH_W) 'udp_endpoint_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/udp_endpoint_unittest.cc'; fi`
+
+run_unittests-udp_socket_unittest.o: udp_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-udp_socket_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-udp_socket_unittest.Tpo -c -o run_unittests-udp_socket_unittest.o `test -f 'udp_socket_unittest.cc' || echo '$(srcdir)/'`udp_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-udp_socket_unittest.Tpo $(DEPDIR)/run_unittests-udp_socket_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='udp_socket_unittest.cc' object='run_unittests-udp_socket_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-udp_socket_unittest.o `test -f 'udp_socket_unittest.cc' || echo '$(srcdir)/'`udp_socket_unittest.cc
+
+run_unittests-udp_socket_unittest.obj: udp_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-udp_socket_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-udp_socket_unittest.Tpo -c -o run_unittests-udp_socket_unittest.obj `if test -f 'udp_socket_unittest.cc'; then $(CYGPATH_W) 'udp_socket_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/udp_socket_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-udp_socket_unittest.Tpo $(DEPDIR)/run_unittests-udp_socket_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='udp_socket_unittest.cc' object='run_unittests-udp_socket_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-udp_socket_unittest.obj `if test -f 'udp_socket_unittest.cc'; then $(CYGPATH_W) 'udp_socket_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/udp_socket_unittest.cc'; fi`
+
+run_unittests-io_service_unittest.o: io_service_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-io_service_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-io_service_unittest.Tpo -c -o run_unittests-io_service_unittest.o `test -f 'io_service_unittest.cc' || echo '$(srcdir)/'`io_service_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-io_service_unittest.Tpo $(DEPDIR)/run_unittests-io_service_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_service_unittest.cc' object='run_unittests-io_service_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-io_service_unittest.o `test -f 'io_service_unittest.cc' || echo '$(srcdir)/'`io_service_unittest.cc
+
+run_unittests-io_service_unittest.obj: io_service_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-io_service_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-io_service_unittest.Tpo -c -o run_unittests-io_service_unittest.obj `if test -f 'io_service_unittest.cc'; then $(CYGPATH_W) 'io_service_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/io_service_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-io_service_unittest.Tpo $(DEPDIR)/run_unittests-io_service_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_service_unittest.cc' object='run_unittests-io_service_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-io_service_unittest.obj `if test -f 'io_service_unittest.cc'; then $(CYGPATH_W) 'io_service_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/io_service_unittest.cc'; fi`
+
+run_unittests-io_service_signal_unittests.o: io_service_signal_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-io_service_signal_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-io_service_signal_unittests.Tpo -c -o run_unittests-io_service_signal_unittests.o `test -f 'io_service_signal_unittests.cc' || echo '$(srcdir)/'`io_service_signal_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-io_service_signal_unittests.Tpo $(DEPDIR)/run_unittests-io_service_signal_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_service_signal_unittests.cc' object='run_unittests-io_service_signal_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-io_service_signal_unittests.o `test -f 'io_service_signal_unittests.cc' || echo '$(srcdir)/'`io_service_signal_unittests.cc
+
+run_unittests-io_service_signal_unittests.obj: io_service_signal_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-io_service_signal_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-io_service_signal_unittests.Tpo -c -o run_unittests-io_service_signal_unittests.obj `if test -f 'io_service_signal_unittests.cc'; then $(CYGPATH_W) 'io_service_signal_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/io_service_signal_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-io_service_signal_unittests.Tpo $(DEPDIR)/run_unittests-io_service_signal_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_service_signal_unittests.cc' object='run_unittests-io_service_signal_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-io_service_signal_unittests.obj `if test -f 'io_service_signal_unittests.cc'; then $(CYGPATH_W) 'io_service_signal_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/io_service_signal_unittests.cc'; fi`
+
+run_unittests-io_service_thread_pool_unittests.o: io_service_thread_pool_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-io_service_thread_pool_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-io_service_thread_pool_unittests.Tpo -c -o run_unittests-io_service_thread_pool_unittests.o `test -f 'io_service_thread_pool_unittests.cc' || echo '$(srcdir)/'`io_service_thread_pool_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-io_service_thread_pool_unittests.Tpo $(DEPDIR)/run_unittests-io_service_thread_pool_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_service_thread_pool_unittests.cc' object='run_unittests-io_service_thread_pool_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-io_service_thread_pool_unittests.o `test -f 'io_service_thread_pool_unittests.cc' || echo '$(srcdir)/'`io_service_thread_pool_unittests.cc
+
+run_unittests-io_service_thread_pool_unittests.obj: io_service_thread_pool_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-io_service_thread_pool_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-io_service_thread_pool_unittests.Tpo -c -o run_unittests-io_service_thread_pool_unittests.obj `if test -f 'io_service_thread_pool_unittests.cc'; then $(CYGPATH_W) 'io_service_thread_pool_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/io_service_thread_pool_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-io_service_thread_pool_unittests.Tpo $(DEPDIR)/run_unittests-io_service_thread_pool_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_service_thread_pool_unittests.cc' object='run_unittests-io_service_thread_pool_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-io_service_thread_pool_unittests.obj `if test -f 'io_service_thread_pool_unittests.cc'; then $(CYGPATH_W) 'io_service_thread_pool_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/io_service_thread_pool_unittests.cc'; fi`
+
+run_unittests-dummy_io_callback_unittest.o: dummy_io_callback_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-dummy_io_callback_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-dummy_io_callback_unittest.Tpo -c -o run_unittests-dummy_io_callback_unittest.o `test -f 'dummy_io_callback_unittest.cc' || echo '$(srcdir)/'`dummy_io_callback_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-dummy_io_callback_unittest.Tpo $(DEPDIR)/run_unittests-dummy_io_callback_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='dummy_io_callback_unittest.cc' object='run_unittests-dummy_io_callback_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-dummy_io_callback_unittest.o `test -f 'dummy_io_callback_unittest.cc' || echo '$(srcdir)/'`dummy_io_callback_unittest.cc
+
+run_unittests-dummy_io_callback_unittest.obj: dummy_io_callback_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-dummy_io_callback_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-dummy_io_callback_unittest.Tpo -c -o run_unittests-dummy_io_callback_unittest.obj `if test -f 'dummy_io_callback_unittest.cc'; then $(CYGPATH_W) 'dummy_io_callback_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/dummy_io_callback_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-dummy_io_callback_unittest.Tpo $(DEPDIR)/run_unittests-dummy_io_callback_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='dummy_io_callback_unittest.cc' object='run_unittests-dummy_io_callback_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-dummy_io_callback_unittest.obj `if test -f 'dummy_io_callback_unittest.cc'; then $(CYGPATH_W) 'dummy_io_callback_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/dummy_io_callback_unittest.cc'; fi`
+
+run_unittests-tcp_acceptor_unittest.o: tcp_acceptor_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tcp_acceptor_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-tcp_acceptor_unittest.Tpo -c -o run_unittests-tcp_acceptor_unittest.o `test -f 'tcp_acceptor_unittest.cc' || echo '$(srcdir)/'`tcp_acceptor_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tcp_acceptor_unittest.Tpo $(DEPDIR)/run_unittests-tcp_acceptor_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tcp_acceptor_unittest.cc' object='run_unittests-tcp_acceptor_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tcp_acceptor_unittest.o `test -f 'tcp_acceptor_unittest.cc' || echo '$(srcdir)/'`tcp_acceptor_unittest.cc
+
+run_unittests-tcp_acceptor_unittest.obj: tcp_acceptor_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tcp_acceptor_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-tcp_acceptor_unittest.Tpo -c -o run_unittests-tcp_acceptor_unittest.obj `if test -f 'tcp_acceptor_unittest.cc'; then $(CYGPATH_W) 'tcp_acceptor_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tcp_acceptor_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tcp_acceptor_unittest.Tpo $(DEPDIR)/run_unittests-tcp_acceptor_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tcp_acceptor_unittest.cc' object='run_unittests-tcp_acceptor_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tcp_acceptor_unittest.obj `if test -f 'tcp_acceptor_unittest.cc'; then $(CYGPATH_W) 'tcp_acceptor_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tcp_acceptor_unittest.cc'; fi`
+
+run_unittests-unix_domain_socket_unittest.o: unix_domain_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-unix_domain_socket_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-unix_domain_socket_unittest.Tpo -c -o run_unittests-unix_domain_socket_unittest.o `test -f 'unix_domain_socket_unittest.cc' || echo '$(srcdir)/'`unix_domain_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-unix_domain_socket_unittest.Tpo $(DEPDIR)/run_unittests-unix_domain_socket_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='unix_domain_socket_unittest.cc' object='run_unittests-unix_domain_socket_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-unix_domain_socket_unittest.o `test -f 'unix_domain_socket_unittest.cc' || echo '$(srcdir)/'`unix_domain_socket_unittest.cc
+
+run_unittests-unix_domain_socket_unittest.obj: unix_domain_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-unix_domain_socket_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-unix_domain_socket_unittest.Tpo -c -o run_unittests-unix_domain_socket_unittest.obj `if test -f 'unix_domain_socket_unittest.cc'; then $(CYGPATH_W) 'unix_domain_socket_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/unix_domain_socket_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-unix_domain_socket_unittest.Tpo $(DEPDIR)/run_unittests-unix_domain_socket_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='unix_domain_socket_unittest.cc' object='run_unittests-unix_domain_socket_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-unix_domain_socket_unittest.obj `if test -f 'unix_domain_socket_unittest.cc'; then $(CYGPATH_W) 'unix_domain_socket_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/unix_domain_socket_unittest.cc'; fi`
+
+run_unittests-process_spawn_unittest.o: process_spawn_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-process_spawn_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-process_spawn_unittest.Tpo -c -o run_unittests-process_spawn_unittest.o `test -f 'process_spawn_unittest.cc' || echo '$(srcdir)/'`process_spawn_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-process_spawn_unittest.Tpo $(DEPDIR)/run_unittests-process_spawn_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='process_spawn_unittest.cc' object='run_unittests-process_spawn_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-process_spawn_unittest.o `test -f 'process_spawn_unittest.cc' || echo '$(srcdir)/'`process_spawn_unittest.cc
+
+run_unittests-process_spawn_unittest.obj: process_spawn_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-process_spawn_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-process_spawn_unittest.Tpo -c -o run_unittests-process_spawn_unittest.obj `if test -f 'process_spawn_unittest.cc'; then $(CYGPATH_W) 'process_spawn_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/process_spawn_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-process_spawn_unittest.Tpo $(DEPDIR)/run_unittests-process_spawn_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='process_spawn_unittest.cc' object='run_unittests-process_spawn_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-process_spawn_unittest.obj `if test -f 'process_spawn_unittest.cc'; then $(CYGPATH_W) 'process_spawn_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/process_spawn_unittest.cc'; fi`
+
+run_unittests-tls_unittest.o: tls_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tls_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-tls_unittest.Tpo -c -o run_unittests-tls_unittest.o `test -f 'tls_unittest.cc' || echo '$(srcdir)/'`tls_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tls_unittest.Tpo $(DEPDIR)/run_unittests-tls_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tls_unittest.cc' object='run_unittests-tls_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tls_unittest.o `test -f 'tls_unittest.cc' || echo '$(srcdir)/'`tls_unittest.cc
+
+run_unittests-tls_unittest.obj: tls_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tls_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-tls_unittest.Tpo -c -o run_unittests-tls_unittest.obj `if test -f 'tls_unittest.cc'; then $(CYGPATH_W) 'tls_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tls_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tls_unittest.Tpo $(DEPDIR)/run_unittests-tls_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tls_unittest.cc' object='run_unittests-tls_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tls_unittest.obj `if test -f 'tls_unittest.cc'; then $(CYGPATH_W) 'tls_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tls_unittest.cc'; fi`
+
+run_unittests-tls_acceptor_unittest.o: tls_acceptor_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tls_acceptor_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-tls_acceptor_unittest.Tpo -c -o run_unittests-tls_acceptor_unittest.o `test -f 'tls_acceptor_unittest.cc' || echo '$(srcdir)/'`tls_acceptor_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tls_acceptor_unittest.Tpo $(DEPDIR)/run_unittests-tls_acceptor_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tls_acceptor_unittest.cc' object='run_unittests-tls_acceptor_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tls_acceptor_unittest.o `test -f 'tls_acceptor_unittest.cc' || echo '$(srcdir)/'`tls_acceptor_unittest.cc
+
+run_unittests-tls_acceptor_unittest.obj: tls_acceptor_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tls_acceptor_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-tls_acceptor_unittest.Tpo -c -o run_unittests-tls_acceptor_unittest.obj `if test -f 'tls_acceptor_unittest.cc'; then $(CYGPATH_W) 'tls_acceptor_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tls_acceptor_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tls_acceptor_unittest.Tpo $(DEPDIR)/run_unittests-tls_acceptor_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tls_acceptor_unittest.cc' object='run_unittests-tls_acceptor_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tls_acceptor_unittest.obj `if test -f 'tls_acceptor_unittest.cc'; then $(CYGPATH_W) 'tls_acceptor_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tls_acceptor_unittest.cc'; fi`
+
+run_unittests-tls_socket_unittest.o: tls_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tls_socket_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-tls_socket_unittest.Tpo -c -o run_unittests-tls_socket_unittest.o `test -f 'tls_socket_unittest.cc' || echo '$(srcdir)/'`tls_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tls_socket_unittest.Tpo $(DEPDIR)/run_unittests-tls_socket_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tls_socket_unittest.cc' object='run_unittests-tls_socket_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tls_socket_unittest.o `test -f 'tls_socket_unittest.cc' || echo '$(srcdir)/'`tls_socket_unittest.cc
+
+run_unittests-tls_socket_unittest.obj: tls_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tls_socket_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-tls_socket_unittest.Tpo -c -o run_unittests-tls_socket_unittest.obj `if test -f 'tls_socket_unittest.cc'; then $(CYGPATH_W) 'tls_socket_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tls_socket_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tls_socket_unittest.Tpo $(DEPDIR)/run_unittests-tls_socket_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tls_socket_unittest.cc' object='run_unittests-tls_socket_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tls_socket_unittest.obj `if test -f 'tls_socket_unittest.cc'; then $(CYGPATH_W) 'tls_socket_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tls_socket_unittest.cc'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ $(am__tty_colors); \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=XPASS; \
+ ;; \
+ *) \
+ col=$$grn; res=PASS; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ col=$$lgn; res=XFAIL; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=FAIL; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ col=$$blu; res=SKIP; \
+ fi; \
+ echo "$${col}$$res$${std}: $$tst"; \
+ done; \
+ if test "$$all" -eq 1; then \
+ tests="test"; \
+ All=""; \
+ else \
+ tests="tests"; \
+ All="All "; \
+ fi; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="$$All$$all $$tests passed"; \
+ else \
+ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all $$tests failed"; \
+ else \
+ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ if test "$$skip" -eq 1; then \
+ skipped="($$skip test was not run)"; \
+ else \
+ skipped="($$skip tests were not run)"; \
+ fi; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ if test "$$failed" -eq 0; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ fi; \
+ echo "$${col}$$dashes$${std}"; \
+ echo "$${col}$$banner$${std}"; \
+ test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+ test -z "$$report" || echo "$${col}$$report$${std}"; \
+ echo "$${col}$$dashes$${std}"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(PROGRAMS) $(SCRIPTS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/run_unittests-addr_utilities_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-dummy_io_callback_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-hash_address_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-interval_timer_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-io_address_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-io_endpoint_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-io_service_signal_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-io_service_thread_pool_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-io_service_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-io_socket_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-process_spawn_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-run_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-tcp_acceptor_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-tcp_endpoint_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-tcp_socket_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-tls_acceptor_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-tls_socket_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-tls_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-udp_endpoint_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-udp_socket_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-unix_domain_socket_unittest.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/run_unittests-addr_utilities_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-dummy_io_callback_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-hash_address_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-interval_timer_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-io_address_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-io_endpoint_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-io_service_signal_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-io_service_thread_pool_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-io_service_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-io_socket_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-process_spawn_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-run_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-tcp_acceptor_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-tcp_endpoint_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-tcp_socket_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-tls_acceptor_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-tls_socket_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-tls_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-udp_endpoint_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-udp_socket_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-unix_domain_socket_unittest.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \
+ check-am clean clean-generic clean-libtool \
+ clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/lib/asiolink/tests/addr_utilities_unittest.cc b/src/lib/asiolink/tests/addr_utilities_unittest.cc
new file mode 100644
index 0000000..30a9817
--- /dev/null
+++ b/src/lib/asiolink/tests/addr_utilities_unittest.cc
@@ -0,0 +1,388 @@
+// Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/addr_utilities.h>
+#include <exceptions/exceptions.h>
+#include <util/bigints.h>
+
+#include <gtest/gtest.h>
+
+#include <vector>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+using namespace std;
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace {
+
+// This test verifies that lastAddrInPrefix is able to handle IPv4 operations.
+TEST(AddrUtilitiesTest, lastAddrInPrefix4) {
+ IOAddress addr1("192.0.2.1");
+
+ // Prefixes rounded to addresses are easy...
+ EXPECT_EQ("192.255.255.255", lastAddrInPrefix(addr1, 8).toText());
+ EXPECT_EQ("192.0.255.255", lastAddrInPrefix(addr1, 16).toText());
+ EXPECT_EQ("192.0.2.255", lastAddrInPrefix(addr1, 24).toText());
+
+ // these are trickier
+ EXPECT_EQ("192.0.2.127", lastAddrInPrefix(addr1, 25).toText());
+ EXPECT_EQ("192.0.2.63", lastAddrInPrefix(addr1, 26).toText());
+ EXPECT_EQ("192.0.2.31", lastAddrInPrefix(addr1, 27).toText());
+ EXPECT_EQ("192.0.2.15", lastAddrInPrefix(addr1, 28).toText());
+ EXPECT_EQ("192.0.2.7", lastAddrInPrefix(addr1, 29).toText());
+ EXPECT_EQ("192.0.2.3", lastAddrInPrefix(addr1, 30).toText());
+
+ // that doesn't make much sense as /31 subnet consists of network address
+ // and a broadcast address, with 0 usable addresses.
+ EXPECT_EQ("192.0.2.1", lastAddrInPrefix(addr1, 31).toText());
+ EXPECT_EQ("192.0.2.1", lastAddrInPrefix(addr1, 32).toText());
+
+ // Let's check extreme cases
+ IOAddress anyAddr("0.0.0.0");
+ EXPECT_EQ("127.255.255.255", lastAddrInPrefix(anyAddr, 1).toText());
+ EXPECT_EQ("255.255.255.255", lastAddrInPrefix(anyAddr, 0).toText());
+ EXPECT_EQ("0.0.0.0", lastAddrInPrefix(anyAddr, 32).toText());
+}
+
+// This test checks if firstAddrInPrefix is able to handle IPv4 operations.
+TEST(AddrUtilitiesTest, firstAddrInPrefix4) {
+ IOAddress addr1("192.223.2.255");
+
+ // Prefixes rounded to addresses are easy...
+ EXPECT_EQ("192.0.0.0", firstAddrInPrefix(addr1, 8).toText());
+ EXPECT_EQ("192.223.0.0", firstAddrInPrefix(addr1, 16).toText());
+ EXPECT_EQ("192.223.2.0", firstAddrInPrefix(addr1, 24).toText());
+
+ // these are trickier
+ EXPECT_EQ("192.223.2.128", firstAddrInPrefix(addr1, 25).toText());
+ EXPECT_EQ("192.223.2.192", firstAddrInPrefix(addr1, 26).toText());
+ EXPECT_EQ("192.223.2.224", firstAddrInPrefix(addr1, 27).toText());
+ EXPECT_EQ("192.223.2.240", firstAddrInPrefix(addr1, 28).toText());
+ EXPECT_EQ("192.223.2.248", firstAddrInPrefix(addr1, 29).toText());
+ EXPECT_EQ("192.223.2.252", firstAddrInPrefix(addr1, 30).toText());
+
+ // that doesn't make much sense as /31 subnet consists of network address
+ // and a broadcast address, with 0 usable addresses.
+ EXPECT_EQ("192.223.2.254", firstAddrInPrefix(addr1, 31).toText());
+ EXPECT_EQ("192.223.2.255", firstAddrInPrefix(addr1, 32).toText());
+
+ // Let's check extreme cases.
+ IOAddress bcast("255.255.255.255");
+ EXPECT_EQ("128.0.0.0", firstAddrInPrefix(bcast, 1).toText());
+ EXPECT_EQ("0.0.0.0", firstAddrInPrefix(bcast, 0).toText());
+ EXPECT_EQ("255.255.255.255", firstAddrInPrefix(bcast, 32).toText());
+
+}
+
+/// This test checks if lastAddrInPrefix properly supports IPv6 operations
+TEST(AddrUtilitiesTest, lastAddrInPrefix6) {
+ IOAddress addr1("2001:db8:1:1234:5678:abcd:1234:beef");
+
+ // Prefixes rounded to nibbles are easy...
+ EXPECT_EQ("2001:db8:1:1234:5678:abcd:1234:ffff",
+ lastAddrInPrefix(addr1, 112).toText());
+ EXPECT_EQ("2001:db8:1:1234:5678:abcd:123f:ffff",
+ lastAddrInPrefix(addr1, 108).toText());
+ EXPECT_EQ("2001:db8:1:1234:5678:abcd:12ff:ffff",
+ lastAddrInPrefix(addr1, 104).toText());
+ EXPECT_EQ("2001:db8:1:1234:ffff:ffff:ffff:ffff",
+ lastAddrInPrefix(addr1, 64).toText());
+
+ IOAddress addr2("2001::");
+
+ // These are trickier, though, as they are done in 1 bit increments
+
+ // the last address in 2001::/127 pool should be 2001::1
+ EXPECT_EQ("2001::1", lastAddrInPrefix(addr2, 127).toText());
+
+ EXPECT_EQ("2001::3", lastAddrInPrefix(addr2, 126).toText());
+ EXPECT_EQ("2001::7", lastAddrInPrefix(addr2, 125).toText());
+ EXPECT_EQ("2001::f", lastAddrInPrefix(addr2, 124).toText());
+ EXPECT_EQ("2001::1f", lastAddrInPrefix(addr2, 123).toText());
+ EXPECT_EQ("2001::3f", lastAddrInPrefix(addr2, 122).toText());
+ EXPECT_EQ("2001::7f", lastAddrInPrefix(addr2, 121).toText());
+ EXPECT_EQ("2001::ff", lastAddrInPrefix(addr2, 120).toText());
+
+ // Let's check extreme cases
+ IOAddress anyAddr("::");
+ EXPECT_EQ("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+ lastAddrInPrefix(anyAddr, 1).toText());
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+ lastAddrInPrefix(anyAddr, 0).toText());
+ EXPECT_EQ("::", lastAddrInPrefix(anyAddr, 128).toText());
+}
+
+/// This test checks if firstAddrInPrefix properly supports IPv6 operations
+TEST(AddrUtilitiesTest, firstAddrInPrefix6) {
+ IOAddress addr1("2001:db8:1:1234:5678:1234:abcd:beef");
+
+ // Prefixes rounded to nibbles are easy...
+ EXPECT_EQ("2001:db8:1:1234:5678:1234::",
+ firstAddrInPrefix(addr1, 96).toText());
+ EXPECT_EQ("2001:db8:1:1234:5678:1230::",
+ firstAddrInPrefix(addr1, 92).toText());
+ EXPECT_EQ("2001:db8:1:1234:5678:1200::",
+ firstAddrInPrefix(addr1, 88).toText());
+ EXPECT_EQ("2001:db8:1:1234::",
+ firstAddrInPrefix(addr1, 64).toText());
+
+ IOAddress addr2("2001::ffff");
+
+ // These are trickier, though, as they are done in 1 bit increments
+
+ // the first address in 2001::/127 pool should be 2001::1
+ EXPECT_EQ("2001::fffe", firstAddrInPrefix(addr2, 127).toText());
+
+ EXPECT_EQ("2001::fffc", firstAddrInPrefix(addr2, 126).toText());
+ EXPECT_EQ("2001::fff8", firstAddrInPrefix(addr2, 125).toText());
+ EXPECT_EQ("2001::fff0", firstAddrInPrefix(addr2, 124).toText());
+ EXPECT_EQ("2001::ffe0", firstAddrInPrefix(addr2, 123).toText());
+ EXPECT_EQ("2001::ffc0", firstAddrInPrefix(addr2, 122).toText());
+ EXPECT_EQ("2001::ff80", firstAddrInPrefix(addr2, 121).toText());
+ EXPECT_EQ("2001::ff00", firstAddrInPrefix(addr2, 120).toText());
+}
+
+// Checks if IPv4 netmask is generated properly
+TEST(AddrUtilitiesTest, getNetmask4) {
+ EXPECT_EQ("0.0.0.0", getNetmask4(0).toText());
+ EXPECT_EQ("128.0.0.0", getNetmask4(1).toText());
+ EXPECT_EQ("192.0.0.0", getNetmask4(2).toText());
+ EXPECT_EQ("224.0.0.0", getNetmask4(3).toText());
+ EXPECT_EQ("240.0.0.0", getNetmask4(4).toText());
+ EXPECT_EQ("248.0.0.0", getNetmask4(5).toText());
+ EXPECT_EQ("252.0.0.0", getNetmask4(6).toText());
+ EXPECT_EQ("254.0.0.0", getNetmask4(7).toText());
+ EXPECT_EQ("255.0.0.0", getNetmask4(8).toText());
+
+ EXPECT_EQ("255.128.0.0", getNetmask4(9).toText());
+ EXPECT_EQ("255.192.0.0", getNetmask4(10).toText());
+ EXPECT_EQ("255.224.0.0", getNetmask4(11).toText());
+ EXPECT_EQ("255.240.0.0", getNetmask4(12).toText());
+ EXPECT_EQ("255.248.0.0", getNetmask4(13).toText());
+ EXPECT_EQ("255.252.0.0", getNetmask4(14).toText());
+ EXPECT_EQ("255.254.0.0", getNetmask4(15).toText());
+ EXPECT_EQ("255.255.0.0", getNetmask4(16).toText());
+
+ EXPECT_EQ("255.255.128.0", getNetmask4(17).toText());
+ EXPECT_EQ("255.255.192.0", getNetmask4(18).toText());
+ EXPECT_EQ("255.255.224.0", getNetmask4(19).toText());
+ EXPECT_EQ("255.255.240.0", getNetmask4(20).toText());
+ EXPECT_EQ("255.255.248.0", getNetmask4(21).toText());
+ EXPECT_EQ("255.255.252.0", getNetmask4(22).toText());
+ EXPECT_EQ("255.255.254.0", getNetmask4(23).toText());
+ EXPECT_EQ("255.255.255.0", getNetmask4(24).toText());
+
+ EXPECT_EQ("255.255.255.128", getNetmask4(25).toText());
+ EXPECT_EQ("255.255.255.192", getNetmask4(26).toText());
+ EXPECT_EQ("255.255.255.224", getNetmask4(27).toText());
+ EXPECT_EQ("255.255.255.240", getNetmask4(28).toText());
+ EXPECT_EQ("255.255.255.248", getNetmask4(29).toText());
+ EXPECT_EQ("255.255.255.252", getNetmask4(30).toText());
+ EXPECT_EQ("255.255.255.254", getNetmask4(31).toText());
+ EXPECT_EQ("255.255.255.255", getNetmask4(32).toText());
+
+ EXPECT_THROW(getNetmask4(33), isc::BadValue);
+}
+
+// Checks if the calculation for IPv4 addresses in range are correct.
+TEST(AddrUtilitiesTest, addrsInRange4) {
+
+ // Let's start with something simple
+ EXPECT_EQ(1, addrsInRange(IOAddress("192.0.2.0"), IOAddress("192.0.2.0")));
+ EXPECT_EQ(10, addrsInRange(IOAddress("192.0.2.10"), IOAddress("192.0.2.19")));
+ EXPECT_EQ(256, addrsInRange(IOAddress("192.0.2.0"), IOAddress("192.0.2.255")));
+ EXPECT_EQ(65536, addrsInRange(IOAddress("192.0.0.0"), IOAddress("192.0.255.255")));
+ EXPECT_EQ(16777216, addrsInRange(IOAddress("10.0.0.0"), IOAddress("10.255.255.255")));
+
+ // Let's check if the network boundaries are crossed correctly.
+ EXPECT_EQ(3, addrsInRange(IOAddress("10.0.0.255"), IOAddress("10.0.1.1")));
+
+ // Let's go a bit overboard with this! How many addresses are there in
+ // IPv4 address space? That's a slightly tricky question, as the answer
+ // cannot be stored in uint32_t.
+ EXPECT_EQ(uint64_t(std::numeric_limits<uint32_t>::max()) + 1,
+ addrsInRange(IOAddress("0.0.0.0"), IOAddress("255.255.255.255")));
+
+ // The upper bound cannot be smaller than the lower bound.
+ EXPECT_THROW(addrsInRange(IOAddress("192.0.2.5"), IOAddress("192.0.2.4")),
+ isc::BadValue);
+}
+
+// Checks if the calculation for IPv6 addresses in range are correct.
+TEST(AddrUtilitiesTest, addrsInRange6) {
+
+ // Let's start with something simple
+ EXPECT_EQ(1, addrsInRange(IOAddress("::"), IOAddress("::")));
+ EXPECT_EQ(16, addrsInRange(IOAddress("fe80::1"), IOAddress("fe80::10")));
+ EXPECT_EQ(65536, addrsInRange(IOAddress("fe80::"), IOAddress("fe80::ffff")));
+ EXPECT_EQ(uint64_t(std::numeric_limits<uint32_t>::max()) + 1,
+ addrsInRange(IOAddress("fe80::"), IOAddress("fe80::ffff:ffff")));
+
+ // There's 2^80 addresses between those. Due to uint64_t limits, this method is
+ // capped at 2^64 -1.
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+ addrsInRange(IOAddress("2001:db8:1::"), IOAddress("2001:db8:2::")));
+
+ // Let's check if the network boundaries are crossed correctly.
+ EXPECT_EQ(3, addrsInRange(IOAddress("2001:db8::ffff"), IOAddress("2001:db8::1:1")));
+
+ // Let's go a bit overboard with this! How many addresses are there in
+ // IPv6 address space? That's a really tricky question, as the answer
+ // wouldn't fit even in uint128_t (if we had it). This method is capped
+ // at max value of uint64_t.
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max(), addrsInRange(IOAddress("::"),
+ IOAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+
+ EXPECT_THROW(addrsInRange(IOAddress("fe80::5"), IOAddress("fe80::4")),
+ isc::BadValue);
+}
+
+// Checks if IPv4 address ranges can be converted to prefix / prefix_len
+TEST(AddrUtilitiesTest, prefixLengthFromRange4) {
+ // Use a shorter name
+ const auto& plfr = prefixLengthFromRange;
+
+ // Let's start with something simple
+ EXPECT_EQ(32, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.0")));
+ EXPECT_EQ(31, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.1")));
+ EXPECT_EQ(30, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.3")));
+ EXPECT_EQ(29, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.7")));
+ EXPECT_EQ(28, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.15")));
+ EXPECT_EQ(27, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.31")));
+ EXPECT_EQ(26, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.63")));
+ EXPECT_EQ(25, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.127")));
+ EXPECT_EQ(24, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.255")));
+ EXPECT_EQ(23, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.3.255")));
+ EXPECT_EQ(16, plfr(IOAddress("10.0.0.0"), IOAddress("10.0.255.255")));
+ EXPECT_EQ(8, plfr(IOAddress("10.0.0.0"), IOAddress("10.255.255.255")));
+ EXPECT_EQ(0, plfr(IOAddress("0.0.0.0"), IOAddress("255.255.255.255")));
+
+ // Fail if a network boundary is crossed
+ EXPECT_EQ(-1, plfr(IOAddress("10.0.0.255"), IOAddress("10.0.1.1")));
+
+ // Fail if first is not at the begin
+ EXPECT_EQ(-1, plfr(IOAddress("10.0.0.2"), IOAddress("10.0.0.5")));
+
+ // The upper bound cannot be smaller than the lower bound
+ EXPECT_THROW(plfr(IOAddress("192.0.2.5"), IOAddress("192.0.2.4")),
+ isc::BadValue);
+}
+
+// Checks if IPv6 address ranges can be converted to prefix / prefix_len
+TEST(AddrUtilitiesTest, prefixLengthFromRange6) {
+ // Use a shorter name
+ const auto& plfr = prefixLengthFromRange;
+
+ // Let's start with something simple
+ EXPECT_EQ(128, plfr(IOAddress("::"), IOAddress("::")));
+ EXPECT_EQ(112, plfr(IOAddress("fe80::"), IOAddress("fe80::ffff")));
+ EXPECT_EQ(96, plfr(IOAddress("fe80::"), IOAddress("fe80::ffff:ffff")));
+ EXPECT_EQ(80, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::ffff:ffff:ffff")));
+ EXPECT_EQ(64, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(63, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::1:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(62, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::3:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(61, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::7:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(60, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::f:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(59, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::1f:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(58, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::3f:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(57, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::7f:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(56, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::ff:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(55, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::1ff:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(54, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::3ff:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(53, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::7ff:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(52, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::fff:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(51, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::1fff:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(50, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::3fff:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(49, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::7fff:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(48, plfr(IOAddress("fe80::"),
+ IOAddress("fe80::ffff:ffff:ffff:ffff:ffff")));
+ EXPECT_EQ(0, plfr(IOAddress("::"),
+ IOAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+
+ // Fail if a network boundary is crossed
+ EXPECT_EQ(-1, plfr(IOAddress("2001:db8::ffff"),
+ IOAddress("2001:db8::1:1")));
+
+ // Fail if first is not at the begin
+ EXPECT_EQ(-1, plfr(IOAddress("2001:db8::2"), IOAddress("2001:db8::5")));
+ EXPECT_EQ(-1, plfr(IOAddress("2001:db8::2:0"),
+ IOAddress("2001:db8::5:ffff")));
+ EXPECT_EQ(-1, plfr(IOAddress("2001:db8::2:ff00:0"),
+ IOAddress("2001:db8::3:00ff:ffff")));
+
+ // The upper bound cannot be smaller than the lower bound
+ EXPECT_THROW(plfr(IOAddress("fe80::5"), IOAddress("fe80::4")),
+ isc::BadValue);
+
+ // Address family must match
+ EXPECT_THROW(plfr(IOAddress("192.0.2.0"), IOAddress("fe80::1")),
+ isc::BadValue);
+}
+
+// Checks if prefixInRange returns valid number of prefixes in specified range.
+TEST(AddrUtilitiesTest, prefixesInRange) {
+
+ // How many /64 prefixes are in /64 pool?
+ EXPECT_EQ(1, prefixesInRange(64, 64));
+
+ // How many /63 prefixes are in /64 pool?
+ EXPECT_EQ(2, prefixesInRange(63, 64));
+
+ // How many /64 prefixes are in /48 pool?
+ EXPECT_EQ(65536, prefixesInRange(48, 64));
+
+ // How many /127 prefixes are in /64 pool?
+ EXPECT_EQ(uint64_t(9223372036854775808ull), prefixesInRange(64, 127));
+
+ // How many /128 prefixes are in /64 pool?
+ EXPECT_EQ(uint128_t(1) << 64, prefixesInRange(64, 128));
+
+ // Let's go overboard again. How many IPv6 addresses are there?
+ EXPECT_EQ(uint128_t(1) << 127, prefixesInRange(1, 128));
+
+ // Let's go overboard again. How many IPv6 addresses are there?
+ EXPECT_EQ(uint128_t(-1), prefixesInRange(0, 128));
+}
+
+// Checks the function which finds an IPv4 address from input address and offset.
+TEST(AddrUtilitiesTest, offsetIPv4Address) {
+ EXPECT_EQ("10.1.2.46", offsetAddress(IOAddress("10.1.1.45"), 257).toText());
+ EXPECT_EQ("10.1.7.9", offsetAddress(IOAddress("10.1.1.45"), 1500).toText());
+ // Using very large offset. The maximum IPv4 address should be returned.
+ EXPECT_EQ("255.255.255.255", offsetAddress(IOAddress("255.255.254.254"), 0xFFFFFFFFFFFFFFFA).toText());
+}
+
+// Checks the function which finds an IPv6 address from input address and offset.
+TEST(AddrUtilitiesTest, offsetIPv6Address) {
+ EXPECT_EQ("2001:db8:1::4", offsetAddress(IOAddress("2001:db8:1::4"), 0).toText());
+ EXPECT_EQ("2001:db8:1::10:3", offsetAddress(IOAddress("2001:db8:1::4"), 0xFFFFF).toText());
+ EXPECT_EQ("2001:db8:2::", offsetAddress(IOAddress("2001:db8:1:FFFF::1"), 0xFFFFFFFFFFFFFFFF).toText());
+ EXPECT_EQ("3000::1c", offsetAddress(IOAddress("3000::15"), 7).toText());
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/asiolink/tests/dummy_io_callback_unittest.cc b/src/lib/asiolink/tests/dummy_io_callback_unittest.cc
new file mode 100644
index 0000000..b836893
--- /dev/null
+++ b/src/lib/asiolink/tests/dummy_io_callback_unittest.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/dummy_io_cb.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::asiolink;
+using namespace boost::asio;
+
+namespace { // begin unnamed namespace
+
+TEST(DummyIOCallbackTest, throws) {
+ DummyIOCallback cb;
+ boost::system::error_code error_code;
+
+ // All methods should throw isc::Unexpected.
+ EXPECT_THROW(cb(error_code), isc::Unexpected);
+ EXPECT_THROW(cb(error_code, 42), isc::Unexpected);
+}
+
+} // end of unnamed namespace
diff --git a/src/lib/asiolink/tests/hash_address_unittest.cc b/src/lib/asiolink/tests/hash_address_unittest.cc
new file mode 100644
index 0000000..50fa43f
--- /dev/null
+++ b/src/lib/asiolink/tests/hash_address_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright (C) 2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/io_address.h>
+#include <gtest/gtest.h>
+#include <boost/functional/hash.hpp>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+// In fact the goal i.e. is to check if the file compiles.
+
+using namespace isc::asiolink;
+
+typedef boost::hash<IOAddress> hash_address;
+
+TEST(HashAddressTest, unorderedSet) {
+ std::unordered_set<IOAddress, hash_address> set;
+ EXPECT_TRUE(set.empty());
+ EXPECT_EQ(0, set.size());
+
+ IOAddress addr("192.168.2.1");
+ EXPECT_EQ(set.end(), set.find(addr));
+ EXPECT_EQ(0, set.count(addr));
+
+ EXPECT_NO_THROW(set.insert(addr));
+ EXPECT_FALSE(set.empty());
+ EXPECT_EQ(1, set.size());
+ EXPECT_NE(set.end(), set.find(addr));
+ EXPECT_EQ(1, set.count(addr));
+
+ EXPECT_NO_THROW(set.clear());
+ EXPECT_TRUE(set.empty());
+}
+
+TEST(HashAddressTest, unorderedMap) {
+ std::unordered_map<IOAddress, std::string, hash_address> map;
+ EXPECT_TRUE(map.empty());
+ EXPECT_EQ(0, map.size());
+
+ IOAddress addr("192.168.2.1");
+ EXPECT_EQ(map.end(), map.find(addr));
+ EXPECT_EQ(0, map.count(addr));
+
+ std::string str("my-address");
+ EXPECT_NO_THROW(map[addr] = str);
+ EXPECT_FALSE(map.empty());
+ EXPECT_EQ(1, map.size());
+ EXPECT_NE(map.end(), map.find(addr));
+ EXPECT_EQ(1, map.count(addr));
+
+ EXPECT_NO_THROW(map.clear());
+ EXPECT_TRUE(map.empty());
+}
diff --git a/src/lib/asiolink/tests/interval_timer_unittest.cc b/src/lib/asiolink/tests/interval_timer_unittest.cc
new file mode 100644
index 0000000..757bba4
--- /dev/null
+++ b/src/lib/asiolink/tests/interval_timer_unittest.cc
@@ -0,0 +1,377 @@
+// Copyright (C) 2011-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/asiolink.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <gtest/gtest.h>
+
+namespace {
+// TODO: Consider this margin
+const boost::posix_time::time_duration TIMER_MARGIN_MSEC =
+ boost::posix_time::milliseconds(50);
+}
+
+using namespace isc::asiolink;
+
+// This fixture is for testing IntervalTimer. Some callback functors are
+// registered as callback function of the timer to test if they are called
+// or not.
+class IntervalTimerTest : public ::testing::Test {
+protected:
+ IntervalTimerTest() :
+ io_service_(), timer_called_(false), timer_cancel_success_(false)
+ {}
+ ~IntervalTimerTest() {}
+ class TimerCallBack {
+ public:
+ TimerCallBack(IntervalTimerTest* test_obj) : test_obj_(test_obj) {}
+ void operator()() const {
+ test_obj_->timer_called_ = true;
+ test_obj_->io_service_.stop();
+ return;
+ }
+ private:
+ IntervalTimerTest* test_obj_;
+ };
+ class TimerCallBackCounter {
+ public:
+ TimerCallBackCounter(IntervalTimerTest* test_obj) :
+ test_obj_(test_obj)
+ {
+ counter_ = 0;
+ }
+ void operator()() {
+ ++counter_;
+ return;
+ }
+ int counter_;
+ private:
+ IntervalTimerTest* test_obj_;
+ };
+ class TimerCallBackCancelDeleter {
+ public:
+ TimerCallBackCancelDeleter(IntervalTimerTest* test_obj,
+ IntervalTimer* timer,
+ TimerCallBackCounter& counter)
+ : test_obj_(test_obj), timer_(timer), counter_(counter), count_(0),
+ prev_counter_(-1)
+ {}
+ void operator()() {
+ ++count_;
+ if (count_ == 1) {
+ // First time of call back.
+ // Store the value of counter_.counter_.
+ prev_counter_ = counter_.counter_;
+ delete timer_;
+ } else if (count_ == 2) {
+ // Second time of call back.
+ // Stop io_service to stop all timers.
+ test_obj_->io_service_.stop();
+ // Compare the value of counter_.counter_ with stored one.
+ // If TimerCallBackCounter was not called (expected behavior),
+ // they are same.
+ if (counter_.counter_ == prev_counter_) {
+ test_obj_->timer_cancel_success_ = true;
+ }
+ }
+ return;
+ }
+ private:
+ IntervalTimerTest* test_obj_;
+ IntervalTimer* timer_;
+ TimerCallBackCounter& counter_;
+ int count_;
+ int prev_counter_;
+ };
+ class TimerCallBackCanceller {
+ public:
+ TimerCallBackCanceller(unsigned int& counter, IntervalTimer& itimer) :
+ counter_(counter), itimer_(itimer)
+ {}
+ void operator()() {
+ ++counter_;
+ itimer_.cancel();
+ }
+ private:
+ unsigned int& counter_;
+ IntervalTimer& itimer_;
+ };
+ class TimerCallBackOverwriter {
+ public:
+ TimerCallBackOverwriter(IntervalTimerTest* test_obj,
+ IntervalTimer& timer)
+ : test_obj_(test_obj), timer_(timer), count_(0)
+ {}
+ void operator()() {
+ ++count_;
+ if (count_ == 1) {
+ // First time of call back.
+ // Call setup() to update callback function to TimerCallBack.
+ test_obj_->timer_called_ = false;
+ timer_.setup(TimerCallBack(test_obj_), 100);
+ } else if (count_ == 2) {
+ // Second time of call back.
+ // If it reaches here, re-setup() is failed (unexpected).
+ // We should stop here.
+ test_obj_->io_service_.stop();
+ }
+ return;
+ }
+ private:
+ IntervalTimerTest* test_obj_;
+ IntervalTimer& timer_;
+ int count_;
+ };
+ class TimerCallBackAccumulator {
+ public:
+ TimerCallBackAccumulator(IntervalTimerTest* test_obj, int &counter) :
+ test_obj_(test_obj), counter_(counter) {
+ }
+ void operator()() {
+ ++counter_;
+ return;
+ }
+ private:
+ IntervalTimerTest* test_obj_;
+ // Reference to integer accumulator
+ int& counter_;
+ };
+protected:
+ IOService io_service_;
+ bool timer_called_;
+ bool timer_cancel_success_;
+};
+
+TEST_F(IntervalTimerTest, invalidArgumentToIntervalTimer) {
+ // Create asio_link::IntervalTimer and setup.
+ IntervalTimer itimer(io_service_);
+ // expect throw if call back function is empty
+ EXPECT_THROW(itimer.setup(IntervalTimer::Callback(), 1),
+ isc::InvalidParameter);
+ // expect throw if interval is negative.
+ EXPECT_THROW(itimer.setup(TimerCallBack(this), -1), isc::BadValue);
+}
+
+TEST_F(IntervalTimerTest, startIntervalTimer) {
+ // Create asio_link::IntervalTimer and setup.
+ // Then run IOService and test if the callback function is called.
+ IntervalTimer itimer(io_service_);
+ timer_called_ = false;
+ // store start time
+ boost::posix_time::ptime start;
+ start = boost::posix_time::microsec_clock::universal_time();
+ // setup timer
+ itimer.setup(TimerCallBack(this), 100);
+ EXPECT_EQ(100, itimer.getInterval());
+ io_service_.run();
+ // Control reaches here after io_service_ was stopped by TimerCallBack.
+
+ // delta: difference between elapsed time and 100 milliseconds.
+ boost::posix_time::time_duration test_runtime =
+ boost::posix_time::microsec_clock::universal_time() - start;
+ EXPECT_FALSE(test_runtime.is_negative()) <<
+ "test duration " << test_runtime <<
+ " negative - clock skew?";
+ // Expect TimerCallBack is called; timer_called_ is true
+ EXPECT_TRUE(timer_called_);
+ // Expect test_runtime is 100 milliseconds or longer.
+ // Allow 1% of clock skew
+ EXPECT_TRUE(test_runtime >= boost::posix_time::milliseconds(99)) <<
+ "test runtime " << test_runtime.total_milliseconds() <<
+ "msec >= 100";
+}
+
+TEST_F(IntervalTimerTest, destructIntervalTimer) {
+ // This code isn't exception safe, but we'd rather keep the code
+ // simpler and more readable as this is only for tests and if it throws
+ // the program would immediately terminate anyway.
+
+ // The call back function will not be called after the timer is
+ // destroyed.
+ //
+ // There are two timers:
+ // itimer_counter (A)
+ // (Calls TimerCallBackCounter)
+ // - increments internal counter in callback function
+ // itimer_canceller (B)
+ // (Calls TimerCallBackCancelDeleter)
+ // - first time of callback, it stores the counter value of
+ // callback_canceller and destroys itimer_counter
+ // - second time of callback, it compares the counter value of
+ // callback_canceller with stored value
+ // if they are same the timer was not called; expected result
+ // if they are different the timer was called after destroyed
+ //
+ // 0 100 200 300 400 500 600 (ms)
+ // (A) i--------+----x
+ // ^
+ // |destroy itimer_counter
+ // (B) i-------------+--------------s
+ // ^stop io_service
+ // and check if itimer_counter have been
+ // stopped
+
+ // itimer_counter will be deleted in TimerCallBackCancelDeleter
+ IntervalTimer* itimer_counter = new IntervalTimer(io_service_);
+ IntervalTimer itimer_canceller(io_service_);
+ timer_cancel_success_ = false;
+ TimerCallBackCounter callback_canceller(this);
+ itimer_counter->setup(callback_canceller, 200);
+ itimer_canceller.setup(
+ TimerCallBackCancelDeleter(this, itimer_counter, callback_canceller),
+ 300);
+ io_service_.run();
+ EXPECT_TRUE(timer_cancel_success_);
+}
+
+TEST_F(IntervalTimerTest, cancel) {
+ // Similar to destructIntervalTimer test, but the first timer explicitly
+ // cancels itself on first callback.
+ IntervalTimer itimer_counter(io_service_);
+ IntervalTimer itimer_watcher(io_service_);
+ unsigned int counter = 0;
+ itimer_counter.setup(TimerCallBackCanceller(counter, itimer_counter), 100);
+ itimer_watcher.setup(TimerCallBack(this), 200);
+ io_service_.run();
+ EXPECT_EQ(1, counter);
+ EXPECT_EQ(0, itimer_counter.getInterval());
+
+ // canceling an already canceled timer shouldn't cause any surprise.
+ EXPECT_NO_THROW(itimer_counter.cancel());
+}
+
+TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
+ // Call setup() multiple times to update call back function and interval.
+ //
+ // There are two timers:
+ // itimer (A)
+ // (Calls TimerCallBackCounter / TimerCallBack)
+ // - increments internal counter in callback function
+ // (TimerCallBackCounter)
+ // interval: 300 milliseconds
+ // - io_service_.stop() (TimerCallBack)
+ // interval: 100 milliseconds
+ // itimer_overwriter (B)
+ // (Calls TimerCallBackOverwriter)
+ // - first time of callback, it calls setup() to change call back
+ // function to TimerCallBack and interval of itimer to 100
+ // milliseconds
+ // after 300 + 100 milliseconds from the beginning of this test,
+ // TimerCallBack() will be called and io_service_ stops.
+ // - second time of callback, it means the test fails.
+ //
+ // 0 100 200 300 400 500 600 700 800 (ms)
+ // (A) i-------------+----C----s
+ // ^ ^stop io_service
+ // |change call back function and interval
+ // (B) i------------------+-------------------S
+ // ^(stop io_service on fail)
+ //
+
+ IntervalTimer itimer(io_service_);
+ IntervalTimer itimer_overwriter(io_service_);
+ // store start time
+ boost::posix_time::ptime start;
+ start = boost::posix_time::microsec_clock::universal_time();
+ itimer.setup(TimerCallBackCounter(this), 300);
+ itimer_overwriter.setup(TimerCallBackOverwriter(this, itimer), 400);
+ io_service_.run();
+ // Control reaches here after io_service_ was stopped by
+ // TimerCallBackCounter or TimerCallBackOverwriter.
+
+ // Expect callback function is updated: TimerCallBack is called
+ EXPECT_TRUE(timer_called_);
+ // Expect interval is updated: return value of getInterval() is updated
+ EXPECT_EQ(itimer.getInterval(), 100);
+}
+
+// This test verifies that timers operate correctly based on their mode.
+TEST_F(IntervalTimerTest, intervalModeTest) {
+ // Create a timer to control the duration of the test.
+ IntervalTimer test_timer(io_service_);
+ test_timer.setup(TimerCallBack(this), 2000);
+
+ // Create an timer which automatically reschedules itself. Use the
+ // accumulator callback to increment local counter for it.
+ int repeater_count = 0;
+ IntervalTimer repeater(io_service_);
+ repeater.setup(TimerCallBackAccumulator(this, repeater_count), 10);
+
+ // Create a one-shot timer. Use the accumulator callback to increment
+ // local counter variable for it.
+ int one_shot_count = 0;
+ IntervalTimer one_shot(io_service_);
+ one_shot.setup(TimerCallBackAccumulator(this, one_shot_count), 10,
+ IntervalTimer::ONE_SHOT);
+
+ // As long as service runs at least one event handler, loop until
+ // we've hit our goals. It won't return zero unless is out of
+ // work or the service has been stopped by the test timer.
+ int cnt = 0;
+ while (((cnt = io_service_.get_io_service().run_one()) > 0)
+ && (repeater_count < 5)) {
+ // deliberately empty
+ };
+
+ // If cnt is zero, then something went wrong.
+ EXPECT_TRUE(cnt > 0);
+
+ // The loop stopped make sure it was for the right reason.
+ EXPECT_EQ(repeater_count, 5);
+ EXPECT_EQ(one_shot_count, 1);
+}
+
+// This test verifies that the same timer can be reused in either mode.
+TEST_F(IntervalTimerTest, timerReuseTest) {
+ // Create a timer to control the duration of the test.
+ IntervalTimer test_timer(io_service_);
+ test_timer.setup(TimerCallBack(this), 2000);
+
+ // Create a one-shot timer. Use the accumulator callback to increment
+ // local counter variable for it.
+ int one_shot_count = 0;
+ IntervalTimer one_shot(io_service_);
+ TimerCallBackAccumulator callback(this, one_shot_count);
+ one_shot.setup(callback, 10, IntervalTimer::ONE_SHOT);
+
+ // Run until a single event handler executes. This should be our
+ // one-shot expiring.
+ io_service_.run_one();
+
+ // Verify the timer expired once.
+ ASSERT_EQ(one_shot_count, 1);
+
+ // Setup the one-shot to go again.
+ one_shot.setup(callback, 10, IntervalTimer::ONE_SHOT);
+
+ // Run until a single event handler executes. This should be our
+ // one-shot expiring.
+ io_service_.run_one();
+
+ // Verify the timer expired once.
+ ASSERT_EQ(one_shot_count, 2);
+
+ // Setup the timer to be repeating.
+ one_shot.setup(callback, 10, IntervalTimer::REPEATING);
+
+ // As long as service runs at least one event handler, loop until
+ // we've hit our goals. It won't return zero unless is out of
+ // work or the service has been stopped by the test timer.
+ int cnt = 0;
+ while ((cnt = io_service_.get_io_service().run_one())
+ && (one_shot_count < 4)) {
+ // deliberately empty
+ };
+
+ // If cnt is zero, then something went wrong.
+ EXPECT_TRUE(cnt > 0);
+
+ // Verify the timer repeated.
+ EXPECT_GE(one_shot_count, 4);
+}
diff --git a/src/lib/asiolink/tests/io_address_unittest.cc b/src/lib/asiolink/tests/io_address_unittest.cc
new file mode 100644
index 0000000..b113940
--- /dev/null
+++ b/src/lib/asiolink/tests/io_address_unittest.cc
@@ -0,0 +1,370 @@
+// Copyright (C) 2011-2023 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <gtest/gtest.h>
+
+#include <asiolink/io_error.h>
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
+
+#include <algorithm>
+#include <cstring>
+#include <vector>
+#include <sstream>
+#include <unordered_set>
+
+using namespace isc::asiolink;
+
+TEST(IOAddressHashTest, hashIPv4) {
+ IOAddress::Hash hash;
+ std::unordered_set<size_t> results;
+ for (uint32_t i = 0; i < 10; i++) {
+ IOAddress address(i);
+ auto result = hash(address);
+ results.insert(result);
+ }
+ // Make sure that the hashing function generated a unique hash for
+ // each address.
+ EXPECT_EQ(10, results.size());
+}
+
+TEST(IOAddressHashTest, hashIPv6) {
+ IOAddress::Hash hash;
+ std::unordered_set<size_t> results;
+ for (auto i = 0; i < 10; i++) {
+ std::ostringstream s;
+ s << "2001:db8:" << i << "::ffff";
+ IOAddress address(s.str());
+ auto result = hash(address);
+ results.insert(result);
+ }
+ // Make sure that the hashing function generated a unique hash for
+ // each address.
+ EXPECT_EQ(10, results.size());
+}
+
+TEST(IOAddressTest, fromText) {
+ IOAddress io_address_v4("192.0.2.1");
+ EXPECT_EQ("192.0.2.1", io_address_v4.toText());
+
+ IOAddress io_address_v6("2001:db8::1234");
+ EXPECT_EQ("2001:db8::1234", io_address_v6.toText());
+
+ // bogus IPv4 address-like input
+ EXPECT_THROW(IOAddress("192.0.2.2.1"), IOError);
+
+ // bogus IPv4 address-like input: out-of-range octet
+ EXPECT_THROW(IOAddress("192.0.2.300"), IOError);
+
+ // bogus IPv6 address-like input
+ EXPECT_THROW(IOAddress("2001:db8:::1234"), IOError);
+
+ // bogus IPv6 address-like input
+ EXPECT_THROW(IOAddress("2001:db8::efgh"), IOError);
+}
+
+TEST(IOAddressTest, Equality) {
+ EXPECT_TRUE(IOAddress("192.0.2.1") == IOAddress("192.0.2.1"));
+ EXPECT_FALSE(IOAddress("192.0.2.1") != IOAddress("192.0.2.1"));
+
+ EXPECT_TRUE(IOAddress("192.0.2.1") != IOAddress("192.0.2.2"));
+ EXPECT_FALSE(IOAddress("192.0.2.1") == IOAddress("192.0.2.2"));
+
+ EXPECT_TRUE(IOAddress("2001:db8::12") == IOAddress("2001:0DB8:0:0::0012"));
+ EXPECT_FALSE(IOAddress("2001:db8::12") != IOAddress("2001:0DB8:0:0::0012"));
+
+ EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("2001:db8::1235"));
+ EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("2001:db8::1235"));
+
+ EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("192.0.2.3"));
+ EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("192.0.2.3"));
+}
+
+TEST(IOAddressTest, Family) {
+ EXPECT_EQ(AF_INET, IOAddress("192.0.2.1").getFamily());
+ EXPECT_EQ(AF_INET6, IOAddress("2001:0DB8:0:0::0012").getFamily());
+}
+
+TEST(IOAddressTest, fromBytes) {
+ // 2001:db8:1::dead:beef
+ uint8_t v6[] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0, 0,
+ 0, 0, 0, 0, 0xde, 0xad, 0xbe, 0xef };
+
+ uint8_t v4[] = { 192, 0 , 2, 3 };
+
+ IOAddress addr("::");
+ EXPECT_NO_THROW({
+ addr = IOAddress::fromBytes(AF_INET6, v6);
+ });
+ EXPECT_EQ("2001:db8:1::dead:beef", addr.toText());
+
+ EXPECT_NO_THROW({
+ addr = IOAddress::fromBytes(AF_INET, v4);
+ });
+ EXPECT_EQ(addr, IOAddress("192.0.2.3"));
+}
+
+TEST(IOAddressTest, toBytesV4) {
+ // Address and network byte-order representation of the address.
+ const char* V4STRING = "192.0.2.1";
+ uint8_t V4[] = {0xc0, 0x00, 0x02, 0x01};
+
+ std::vector<uint8_t> actual = IOAddress(V4STRING).toBytes();
+ ASSERT_EQ(sizeof(V4), actual.size());
+ EXPECT_TRUE(std::equal(actual.begin(), actual.end(), V4));
+}
+
+TEST(IOAddressTest, toBytesV6) {
+ // Address and network byte-order representation of the address.
+ const char* V6STRING = "2001:db8:1::dead:beef";
+ uint8_t V6[] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+ };
+
+ std::vector<uint8_t> actual = IOAddress(V6STRING).toBytes();
+ ASSERT_EQ(sizeof(V6), actual.size());
+ EXPECT_TRUE(std::equal(actual.begin(), actual.end(), V6));
+}
+
+TEST(IOAddressTest, isV4) {
+ const IOAddress address4("192.0.2.1");
+ const IOAddress address6("2001:db8:1::dead:beef");
+
+ EXPECT_TRUE(address4.isV4());
+ EXPECT_FALSE(address6.isV4());
+}
+
+TEST(IOAddressTest, isV4Zero) {
+ // 0.0.0.0
+ const IOAddress address_zero("0.0.0.0");
+ EXPECT_TRUE(address_zero.isV4Zero());
+ // :: (v6 zero address)
+ const IOAddress address_zero_v6("::");
+ EXPECT_FALSE(address_zero_v6.isV4Zero());
+ // 192.0.2.3
+ const IOAddress address_non_zero("192.0.2.3");
+ EXPECT_FALSE(address_non_zero.isV4Zero());
+ // 0.0.0.100
+ const IOAddress address_non_zero1("0.0.0.100");
+ EXPECT_FALSE(address_non_zero1.isV4Zero());
+ // 64.0.0.0
+ const IOAddress address_non_zero2("64.0.0.0");
+ EXPECT_FALSE(address_non_zero2.isV4Zero());
+}
+
+TEST(IOAddressTest, isV4Bcast) {
+ // 255.255.255.255
+ const IOAddress address_bcast("255.255.255.255");
+ EXPECT_TRUE(address_bcast.isV4Bcast());
+ // 10.2.3.4
+ const IOAddress address_non_bcast("10.2.3.4");
+ EXPECT_FALSE(address_non_bcast.isV4Bcast());
+ // 255.255.255.23
+ const IOAddress address_non_bcast1("255.255.255.23");
+ EXPECT_FALSE(address_non_bcast1.isV4Bcast());
+ // 123.255.255.255
+ const IOAddress address_non_bcast2("123.255.255.255");
+ EXPECT_FALSE(address_non_bcast2.isV4Bcast());
+
+}
+
+TEST(IOAddressTest, isV6) {
+ const IOAddress address4("192.0.2.1");
+ const IOAddress address6("2001:db8:1::dead:beef");
+
+ EXPECT_FALSE(address4.isV6());
+ EXPECT_TRUE(address6.isV6());
+}
+
+TEST(IOAddressTest, isV6Zero) {
+ // ::
+ const IOAddress address_zero("::");
+ EXPECT_TRUE(address_zero.isV6Zero());
+ // 0.0.0.0
+ const IOAddress address_non_zero("0.0.0.0");
+ EXPECT_FALSE(address_non_zero.isV6Zero());
+ // ::ff
+ const IOAddress address_non_zero1("::ff");
+ EXPECT_FALSE(address_non_zero1.isV6Zero());
+ // ff::
+ const IOAddress address_non_zero2("ff::");
+ EXPECT_FALSE(address_non_zero2.isV6Zero());
+}
+
+TEST(IOAddressTest, uint32) {
+ IOAddress addr1("192.0.2.5");
+
+ // operator uint_32() is used here
+ uint32_t tmp = addr1.toUint32();
+
+ uint32_t expected = (192U << 24) + (0U << 16) + (2U << 8) + 5U;
+
+ EXPECT_EQ(expected, tmp);
+
+ // now let's try opposite conversion
+ IOAddress addr3 = IOAddress(expected);
+
+ EXPECT_EQ(addr3.toText(), "192.0.2.5");
+}
+
+TEST(IOAddressTest, lessThanEqual) {
+ IOAddress addr1("192.0.2.5");
+ IOAddress addr2("192.0.2.6");
+ IOAddress addr3("0.0.0.0");
+
+ IOAddress addr4("::");
+ IOAddress addr5("2001:db8::1");
+ IOAddress addr6("2001:db8::1:0");
+ IOAddress addr7("2001:db8::1:0"); // the same as 6
+
+ // v4 comparisons
+ EXPECT_TRUE(addr1 < addr2);
+ EXPECT_FALSE(addr2 < addr1);
+ EXPECT_FALSE(addr2 <= addr1);
+ EXPECT_TRUE(addr3 < addr1);
+ EXPECT_TRUE(addr3 < addr2);
+ EXPECT_TRUE(addr3 <= addr2);
+
+ // v6 comparisons
+ EXPECT_TRUE(addr4 < addr5);
+ EXPECT_TRUE(addr5 < addr6);
+ EXPECT_FALSE(addr6 < addr5);
+ EXPECT_FALSE(addr6 <= addr5);
+
+ // v4 to v6 - v4 should always be smaller
+ EXPECT_TRUE(addr1 < addr4);
+ EXPECT_TRUE(addr3 < addr4);
+ EXPECT_TRUE(addr2 < addr5);
+
+ EXPECT_TRUE(addr6 <= addr7);
+}
+
+// test operator<<. We simply confirm it appends the result of toText().
+TEST(IOAddressTest, LeftShiftOperator) {
+ const IOAddress addr("192.0.2.5");
+
+ std::ostringstream oss;
+ oss << addr;
+ EXPECT_EQ(addr.toText(), oss.str());
+}
+
+// Tests address classification methods (which were previously used by accessing
+// underlying asio objects directly)
+TEST(IOAddressTest, accessClassificationMethods) {
+ IOAddress addr1("192.0.2.5"); // IPv4
+ IOAddress addr2("::"); // IPv6
+ IOAddress addr3("2001:db8::1"); // global IPv6
+ IOAddress addr4("fe80::1234"); // link-local
+ IOAddress addr5("ff02::1:2"); // multicast
+
+ EXPECT_TRUE (addr1.isV4());
+ EXPECT_FALSE(addr1.isV6());
+ EXPECT_FALSE(addr1.isV6LinkLocal());
+ EXPECT_FALSE(addr1.isV6Multicast());
+
+ EXPECT_FALSE(addr2.isV4());
+ EXPECT_TRUE (addr2.isV6());
+ EXPECT_FALSE(addr2.isV6LinkLocal());
+ EXPECT_FALSE(addr2.isV6Multicast());
+
+ EXPECT_FALSE(addr3.isV4());
+ EXPECT_TRUE (addr3.isV6());
+ EXPECT_FALSE(addr3.isV6LinkLocal());
+ EXPECT_FALSE(addr3.isV6Multicast());
+
+ EXPECT_FALSE(addr4.isV4());
+ EXPECT_TRUE (addr4.isV6());
+ EXPECT_TRUE (addr4.isV6LinkLocal());
+ EXPECT_FALSE(addr4.isV6Multicast());
+
+ EXPECT_FALSE(addr5.isV4());
+ EXPECT_TRUE (addr5.isV6());
+ EXPECT_FALSE(addr5.isV6LinkLocal());
+ EXPECT_TRUE (addr5.isV6Multicast());
+}
+
+TEST(IOAddressTest, staticAddresses) {
+ EXPECT_EQ(IOAddress("0.0.0.0"), IOAddress::IPV4_ZERO_ADDRESS());
+ EXPECT_EQ(IOAddress("255.255.255.255"), IOAddress::IPV4_BCAST_ADDRESS());
+ EXPECT_EQ(IOAddress("::"), IOAddress::IPV6_ZERO_ADDRESS());
+}
+
+// Tests whether address subtraction works correctly.
+TEST(IOAddressTest, subtract) {
+ IOAddress addr1("192.0.2.12");
+ IOAddress addr2("192.0.2.5");
+ IOAddress addr3("192.0.2.0");
+ IOAddress addr4("0.0.2.1");
+ IOAddress any4("0.0.0.0");
+ IOAddress bcast("255.255.255.255");
+
+ EXPECT_EQ("0.0.0.7", IOAddress::subtract(addr1, addr2).toText());
+ EXPECT_EQ("0.0.0.12", IOAddress::subtract(addr1, addr3).toText());
+
+ // Subtracting 0.0.0.0 is like subtracting 0.
+ EXPECT_EQ("192.0.2.12", IOAddress::subtract(addr1, any4).toText());
+ EXPECT_EQ("192.0.2.13", IOAddress::subtract(addr1, bcast).toText());
+ EXPECT_EQ("191.255.255.255", IOAddress::subtract(addr3, addr4).toText());
+
+ // Let's check if we can subtract greater address from smaller.
+ // This will check if we can "loop"
+ EXPECT_EQ("255.255.255.251", IOAddress::subtract(addr3, addr2).toText());
+
+ IOAddress addr6("fe80::abcd");
+ IOAddress addr7("fe80::");
+ IOAddress addr8("fe80::1234");
+ IOAddress addr9("2001:db8::face");
+ IOAddress addr10("2001:db8::ffff:ffff:ffff:ffff");
+ IOAddress addr11("::1");
+ IOAddress any6("::");
+
+ EXPECT_EQ(IOAddress("::abcd"), IOAddress::subtract(addr6, addr7));
+ EXPECT_EQ(IOAddress("::9999"), IOAddress::subtract(addr6, addr8));
+ EXPECT_EQ("::ffff:ffff:ffff:531", IOAddress::subtract(addr10, addr9).toText());
+
+ // Subtract with borrow, extreme edition. Need to borrow one bit
+ // 112 times.
+ EXPECT_EQ("fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+ IOAddress::subtract(addr7, addr11).toText());
+
+ // Now check if we can loop beyond :: (:: - ::1 is a lot of F's)
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+ IOAddress::subtract(any6, addr11).toText());
+
+ // Subtracting :: is like subtracting 0.
+ EXPECT_EQ("2001:db8::face", IOAddress::subtract(addr9, any6).toText());
+
+ // Let's check if we can subtract greater address from smaller.
+ // This will check if we can "loop"
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:edcc",
+ IOAddress::subtract(addr7, addr8).toText());
+
+ // Inter-family relations are not allowed.
+ EXPECT_THROW(IOAddress::subtract(addr1, addr6), isc::BadValue);
+ EXPECT_THROW(IOAddress::subtract(addr6, addr1), isc::BadValue);
+}
+
+// Test checks whether an address can be increased.
+TEST(IOAddressTest, increaseAddr) {
+ IOAddress addr1("192.0.2.12");
+ IOAddress any4("0.0.0.0");
+ IOAddress bcast("255.255.255.255");
+ IOAddress addr6("2001:db8::ffff:ffff:ffff:ffff");
+ IOAddress addr11("::1");
+ IOAddress any6("::");
+ IOAddress the_last_one("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+
+ EXPECT_EQ("192.0.2.13", IOAddress::increase(addr1).toText());
+ EXPECT_EQ("0.0.0.1", IOAddress::increase(any4).toText());
+ EXPECT_EQ("0.0.0.0", IOAddress::increase(bcast).toText());
+ EXPECT_EQ("2001:db8:0:1::", IOAddress::increase(addr6).toText());
+ EXPECT_EQ(IOAddress("::2"), IOAddress::increase(addr11));
+ EXPECT_EQ(IOAddress("::1"), IOAddress::increase(any6));
+ EXPECT_EQ(IOAddress("::"), IOAddress::increase(the_last_one));
+}
diff --git a/src/lib/asiolink/tests/io_endpoint_unittest.cc b/src/lib/asiolink/tests/io_endpoint_unittest.cc
new file mode 100644
index 0000000..314a6a0
--- /dev/null
+++ b/src/lib/asiolink/tests/io_endpoint_unittest.cc
@@ -0,0 +1,286 @@
+// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_error.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <sstream>
+#include <string>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <string.h>
+
+using namespace isc::asiolink;
+
+namespace {
+typedef boost::shared_ptr<const IOEndpoint> ConstIOEndpointPtr;
+
+TEST(IOEndpointTest, createUDPv4) {
+ ConstIOEndpointPtr ep(IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.1"), 53210));
+ EXPECT_EQ("192.0.2.1", ep->getAddress().toText());
+ EXPECT_EQ(53210, ep->getPort());
+ EXPECT_EQ(AF_INET, ep->getFamily());
+ EXPECT_EQ(AF_INET, ep->getAddress().getFamily());
+ EXPECT_EQ(static_cast<short>(IPPROTO_UDP), ep->getProtocol());
+}
+
+TEST(IOEndpointTest, createTCPv4) {
+ ConstIOEndpointPtr ep(IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("192.0.2.1"), 5301));
+ EXPECT_EQ("192.0.2.1", ep->getAddress().toText());
+ EXPECT_EQ(5301, ep->getPort());
+ EXPECT_EQ(AF_INET, ep->getFamily());
+ EXPECT_EQ(AF_INET, ep->getAddress().getFamily());
+ EXPECT_EQ(static_cast<short>(IPPROTO_TCP), ep->getProtocol());
+}
+
+TEST(IOEndpointTest, createUDPv6) {
+ ConstIOEndpointPtr ep(IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1234"),
+ 5302));
+ EXPECT_EQ("2001:db8::1234", ep->getAddress().toText());
+ EXPECT_EQ(5302, ep->getPort());
+ EXPECT_EQ(AF_INET6, ep->getFamily());
+ EXPECT_EQ(AF_INET6, ep->getAddress().getFamily());
+ EXPECT_EQ(static_cast<short>(IPPROTO_UDP), ep->getProtocol());
+}
+
+TEST(IOEndpointTest, createTCPv6) {
+ ConstIOEndpointPtr ep(IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1234"),
+ 5303));
+ EXPECT_EQ("2001:db8::1234", ep->getAddress().toText());
+ EXPECT_EQ(5303, ep->getPort());
+ EXPECT_EQ(AF_INET6, ep->getFamily());
+ EXPECT_EQ(AF_INET6, ep->getAddress().getFamily());
+ EXPECT_EQ(static_cast<short>(IPPROTO_TCP), ep->getProtocol());
+}
+
+TEST(IOEndpointTest, equality) {
+ std::vector<ConstIOEndpointPtr> epv;
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1234"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1234"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1234"), 5304)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1234"), 5304)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1235"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1235"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1235"), 5304)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1235"), 5304)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("192.0.2.1"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.1"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("192.0.2.1"), 5304)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.1"), 5304)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("192.0.2.2"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.2"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("192.0.2.2"), 5304)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.2"), 5304)));
+
+ for (size_t i = 0; i < epv.size(); ++i) {
+ for (size_t j = 0; j < epv.size(); ++j) {
+ if (i != j) {
+ // We use EXPECT_TRUE/FALSE instead of _EQ here, since
+ // _EQ requires there is an operator<< as well
+ EXPECT_FALSE(*epv[i] == *epv[j]);
+ EXPECT_TRUE(*epv[i] != *epv[j]);
+ }
+ }
+ }
+
+ // Create a second array with exactly the same values. We use create()
+ // again to make sure we get different endpoints
+ std::vector<ConstIOEndpointPtr> epv2;
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1234"), 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1234"), 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1234"), 5304)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1234"), 5304)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1235"), 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1235"), 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1235"), 5304)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1235"), 5304)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("192.0.2.1"), 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.1"), 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.1"),
+ 5304)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.1"),
+ 5304)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.2"),
+ 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.2"),
+ 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.2"),
+ 5304)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.2"),
+ 5304)));
+
+ for (size_t i = 0; i < epv.size(); ++i) {
+ EXPECT_TRUE(*epv[i] == *epv2[i]);
+ EXPECT_FALSE(*epv[i] != *epv2[i]);
+ }
+}
+
+TEST(IOEndpointTest, createIPProto) {
+ EXPECT_THROW(IOEndpoint::create(IPPROTO_IP, IOAddress("192.0.2.1"),
+ 53210)->getAddress().toText(),
+ IOError);
+}
+
+void
+sockAddrMatch(const struct sockaddr& actual_sa,
+ const char* const expected_addr_text,
+ const char* const expected_port_text)
+{
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM; // this shouldn't matter
+ hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+
+ struct addrinfo* res;
+ ASSERT_EQ(0, getaddrinfo(expected_addr_text, expected_port_text, &hints,
+ &res));
+ EXPECT_EQ(res->ai_family, actual_sa.sa_family);
+#ifdef HAVE_SA_LEN
+ // ASIO doesn't seem to set sa_len, so we set it to the expected value
+ res->ai_addr->sa_len = actual_sa.sa_len;
+#endif
+ EXPECT_EQ(0, memcmp(res->ai_addr, &actual_sa, res->ai_addrlen));
+ freeaddrinfo(res);
+}
+
+TEST(IOEndpointTest, getSockAddr) {
+ // UDP/IPv4
+ ConstIOEndpointPtr ep(IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.1"), 53210));
+ sockAddrMatch(ep->getSockAddr(), "192.0.2.1", "53210");
+
+ // UDP/IPv6
+ ep.reset(IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::53"), 53));
+ sockAddrMatch(ep->getSockAddr(), "2001:db8::53", "53");
+
+ // TCP/IPv4
+ ep.reset(IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.2"), 53211));
+ sockAddrMatch(ep->getSockAddr(), "192.0.2.2", "53211");
+
+ // TCP/IPv6
+ ep.reset(IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::5300"), 35));
+ sockAddrMatch(ep->getSockAddr(), "2001:db8::5300", "35");
+}
+
+// A faked IOEndpoint for an uncommon address family. It wouldn't be possible
+// to create via the normal factory, so we define a special derived class
+// for it.
+class TestIOEndpoint : public IOEndpoint {
+ virtual IOAddress getAddress() const {
+ return IOAddress("2001:db8::bad:add");
+ }
+ virtual uint16_t getPort() const { return (42); }
+ virtual short getProtocol() const { return (IPPROTO_UDP); }
+ virtual short getFamily() const { return (AF_UNSPEC); }
+ virtual const struct sockaddr& getSockAddr() const {
+ static struct sockaddr sa_placeholder;
+ return (sa_placeholder);
+ }
+};
+
+void
+checkEndpointText(const std::string& expected, const IOEndpoint& ep) {
+ std::ostringstream oss;
+ oss << ep;
+ EXPECT_EQ(expected, oss.str());
+}
+
+// test operator<<. We simply confirm it appends the result of toText().
+TEST(IOEndpointTest, LeftShiftOperator) {
+ // UDP/IPv4
+ ConstIOEndpointPtr ep(IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.1"), 53210));
+ checkEndpointText("192.0.2.1:53210", *ep);
+
+ // UDP/IPv6
+ ep.reset(IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::53"), 53));
+ checkEndpointText("[2001:db8::53]:53", *ep);
+
+ // Same for TCP: shouldn't be different
+ ep.reset(IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.1"), 53210));
+ checkEndpointText("192.0.2.1:53210", *ep);
+ ep.reset(IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::53"), 53));
+ checkEndpointText("[2001:db8::53]:53", *ep);
+
+ // Uncommon address family. The actual behavior doesn't matter much
+ // in practice, but we check such input doesn't make it crash.
+ // We explicitly instantiate the test EP because otherwise some compilers
+ // would be confused and complain.
+ TestIOEndpoint test_ep;
+ checkEndpointText("2001:db8::bad:add:42", test_ep);
+}
+}
diff --git a/src/lib/asiolink/tests/io_service_signal_unittests.cc b/src/lib/asiolink/tests/io_service_signal_unittests.cc
new file mode 100644
index 0000000..1dd41a6
--- /dev/null
+++ b/src/lib/asiolink/tests/io_service_signal_unittests.cc
@@ -0,0 +1,282 @@
+// Copyright (C) 2014-2023 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <asiolink/interval_timer.h>
+#include <asiolink/io_service_signal.h>
+#include <asiolink/testutils/timed_signal.h>
+
+#include <gtest/gtest.h>
+
+#include <functional>
+#include <queue>
+
+using namespace isc::asiolink::test;
+namespace ph = std::placeholders;
+
+namespace isc {
+namespace asiolink {
+
+/// @brief Test fixture for testing the use of IO service signals.
+///
+/// This fixture is exercises IO service signaling.
+class IOSignalTest : public ::testing::Test {
+public:
+ /// @brief IOService instance to process IO.
+ asiolink::IOServicePtr io_service_;
+
+ /// @brief Failsafe timer to ensure test(s) do not hang.
+ isc::asiolink::IntervalTimer test_timer_;
+
+ /// @brief Maximum time should be allowed to run.
+ int test_time_ms_;
+
+ /// @brief IOSignalSet object.
+ IOSignalSetPtr io_signal_set_;
+
+ /// @brief Vector to record the signal values received.
+ std::vector<int> processed_signals_;
+
+ /// @brief The number of signals that must be received to stop the test.
+ int stop_at_count_;
+
+ /// @brief Boolean which causes IOSignalHandler to throw if true.
+ bool handler_throw_error_;
+
+ /// @brief Constructor.
+ IOSignalTest() :
+ io_service_(new asiolink::IOService()), test_timer_(*io_service_),
+ test_time_ms_(0), io_signal_set_(),
+ processed_signals_(), stop_at_count_(0), handler_throw_error_(false) {
+
+ io_signal_set_.reset(new IOSignalSet(io_service_,
+ std::bind(&IOSignalTest::processSignal,
+ this, ph::_1)));
+ }
+
+ /// @brief Destructor.
+ ~IOSignalTest() {
+ io_signal_set_.reset();
+ // Make sure the cancel handler for the IOSignalSet is called.
+ io_service_->poll();
+ }
+
+ /// @brief Method used as the IOSignalSet handler.
+ ///
+ /// Records the value of the given signal and checks if the desired
+ /// number of signals have been received. If so, the IOService is
+ /// stopped which will cause IOService::run() to exit, returning control
+ /// to the test.
+ ///
+ /// @param signum signal number.
+ void processSignal(int signum) {
+ // Remember the signal we got.
+ processed_signals_.push_back(signum);
+
+ // If the flag is on, force a throw to test error handling.
+ if (handler_throw_error_) {
+ handler_throw_error_ = false;
+ isc_throw(BadValue, "processSignal throwing simulated error");
+ }
+
+ // If we've hit the number we want stop the IOService. This will cause
+ // run to exit.
+ if (processed_signals_.size() >= stop_at_count_) {
+ io_service_->stop();
+ }
+ }
+
+ /// @brief Sets the failsafe timer for the test to the given time.
+ ///
+ /// @param test_time_ms maximum time in milliseconds the test should
+ /// be allowed to run.
+ void setTestTime(int test_time_ms) {
+ // Fail safe shutdown
+ test_time_ms_ = test_time_ms;
+ test_timer_.setup(std::bind(&IOSignalTest::testTimerHandler, this),
+ test_time_ms_, asiolink::IntervalTimer::ONE_SHOT);
+ }
+
+ /// @brief Failsafe timer expiration handler.
+ void testTimerHandler() {
+ io_service_->stop();
+ FAIL() << "Test Time: " << test_time_ms_ << " expired";
+ }
+};
+
+// Test the basic mechanics of IOSignal by handling one signal occurrence.
+TEST_F(IOSignalTest, singleSignalTest) {
+ // Set test fail safe.
+ setTestTime(1000);
+
+ // Register to receive SIGINT.
+ ASSERT_NO_THROW(io_signal_set_->add(SIGINT));
+
+ // Use TimedSignal to generate SIGINT 100 ms after we start IOService::run.
+ TimedSignal sig_int(*io_service_, SIGINT, 100);
+
+ // The first handler executed is the IOSignal's internal timer expire
+ // callback.
+ io_service_->run_one();
+
+ // The next handler executed is IOSignal's handler.
+ io_service_->run_one();
+
+ // Polling once to be sure.
+ io_service_->poll();
+
+ // Verify that we processed the signal.
+ ASSERT_EQ(1, processed_signals_.size());
+
+ // Now check that signal value is correct.
+ EXPECT_EQ(SIGINT, processed_signals_[0]);
+
+ // Set test fail safe.
+ setTestTime(1000);
+
+ // Unregister the receive of SIGINT.
+ ASSERT_NO_THROW(io_signal_set_->remove(SIGINT));
+
+ // Use TimedSignal to generate SIGINT 100 ms after we start IOService::run.
+ TimedSignal sig_int_too_late(*io_service_, SIGINT, 100);
+
+ // The first handler executed is the IOSignal's internal timer expire
+ // callback.
+ io_service_->run_one();
+
+ // The next handler executed is IOSignal's handler.
+ io_service_->run_one();
+
+ // Polling once to be sure.
+ io_service_->poll();
+
+ // Verify that we did not process the signal.
+ ASSERT_EQ(1, processed_signals_.size());
+}
+
+// Test verifies that signals can be delivered rapid-fire without falling over.
+TEST_F(IOSignalTest, hammer) {
+ // Set test fail safe. We want to allow at least 100 ms per signal,
+ // plus a bit more so 6 seconds ought to be enough.
+ setTestTime(6000);
+
+ // Register to receive SIGINT.
+ ASSERT_NO_THROW(io_signal_set_->add(SIGINT));
+
+ // Stop the test after 50 signals. This allows 100ms+ per signal
+ // so even sluggish VMs should handle it.
+ stop_at_count_ = 50;
+
+ // User a repeating TimedSignal so we should generate a signal every 1 ms
+ // until we hit our stop count.
+ TimedSignal sig_int(*io_service_, SIGINT, 1,
+ asiolink::IntervalTimer::REPEATING);
+
+ // Start processing IO. This should continue until we stop either by
+ // hitting the stop count or if things go wrong, max test time.
+ io_service_->run();
+
+ // Verify we received the expected number of signals.
+ EXPECT_EQ(stop_at_count_, processed_signals_.size());
+
+ // Now check that each signal value is correct. This is sort of a silly
+ // check but it does ensure things didn't go off the rails somewhere.
+ for (int i = 0; i < processed_signals_.size(); ++i) {
+ EXPECT_EQ(SIGINT, processed_signals_[i]);
+ }
+}
+
+// Verifies that handler exceptions are caught.
+TEST_F(IOSignalTest, handlerThrow) {
+ // Set test fail safe.
+ setTestTime(1000);
+
+ // Register to receive SIGINT.
+ ASSERT_NO_THROW(io_signal_set_->add(SIGINT));
+
+ // Set the stop after we've done at least 1 all the way through.
+ stop_at_count_ = 1;
+
+ // Use TimedSignal to generate SIGINT after we start IOService::run.
+ TimedSignal sig_int(*io_service_, SIGINT, 100,
+ asiolink::IntervalTimer::REPEATING);
+
+ // Set the test flag to cause the handler to throw an exception.
+ handler_throw_error_ = true;
+
+ // Start processing IO. This should fail with the handler exception.
+ ASSERT_NO_THROW(io_service_->run());
+
+ // Verify that the we hit the throw block. The flag will be false
+ // we will have skipped the stop count check so number signals processed
+ // is stop_at_count_ + 1.
+ EXPECT_FALSE(handler_throw_error_);
+ EXPECT_EQ(stop_at_count_ + 1, processed_signals_.size());
+}
+
+// Verifies that we can handle a mixed set of signals.
+TEST_F(IOSignalTest, mixedSignals) {
+ // Set test fail safe.
+ setTestTime(1000);
+
+ // Register to receive SIGINT, SIGUSR1, and SIGUSR2.
+ ASSERT_NO_THROW(io_signal_set_->add(SIGINT));
+ ASSERT_NO_THROW(io_signal_set_->add(SIGUSR1));
+ ASSERT_NO_THROW(io_signal_set_->add(SIGUSR2));
+
+ stop_at_count_ = 8;
+
+ // User a repeating TimedSignal so we should generate a signal every 3, 5
+ // and 7 ms until we hit our stop count.
+ TimedSignal sig_1(*io_service_, SIGINT, 3,
+ asiolink::IntervalTimer::REPEATING);
+ TimedSignal sig_2(*io_service_, SIGUSR1, 5,
+ asiolink::IntervalTimer::REPEATING);
+ TimedSignal sig_3(*io_service_, SIGUSR2, 7,
+ asiolink::IntervalTimer::REPEATING);
+
+ // Start processing IO. This should continue until we stop either by
+ // hitting the stop count or if things go wrong, max test time.
+ io_service_->run();
+
+ // Verify we received the expected number of signals.
+ ASSERT_EQ(stop_at_count_, processed_signals_.size());
+
+ // There is no guarantee that the signals will always be delivered in the
+ // order they are raised, but all of them should get delivered. Loop
+ // through and tally them up.
+ int sigint_cnt = 0;
+ int sigusr1_cnt = 0;
+ int sigusr2_cnt = 0;
+ for (int i = 0; i < stop_at_count_; ++i) {
+ switch (processed_signals_[i]) {
+ case SIGINT:
+ ++sigint_cnt;
+ break;
+ case SIGUSR1:
+ ++sigusr1_cnt;
+ break;
+ case SIGUSR2:
+ ++sigusr2_cnt;
+ break;
+ default:
+ FAIL() << "Invalid signal value: "
+ << processed_signals_[i]
+ << " at i:" << i;
+ break;
+ }
+ }
+
+ // See if our counts are correct.
+ EXPECT_EQ(sigint_cnt, 4);
+ EXPECT_EQ(sigusr1_cnt, 2);
+ EXPECT_EQ(sigusr2_cnt, 2);
+}
+
+} // namespace asiolink
+} // namespace isc
diff --git a/src/lib/asiolink/tests/io_service_thread_pool_unittests.cc b/src/lib/asiolink/tests/io_service_thread_pool_unittests.cc
new file mode 100644
index 0000000..d867011
--- /dev/null
+++ b/src/lib/asiolink/tests/io_service_thread_pool_unittests.cc
@@ -0,0 +1,264 @@
+// Copyright (C) 2021-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/interval_timer.h>
+#include <asiolink/io_service.h>
+#include <asiolink/io_service_thread_pool.h>
+#include <exceptions/exceptions.h>
+#include <testutils/gtest_utils.h>
+
+#include <gtest/gtest.h>
+#include <string>
+
+using namespace isc;
+using namespace isc::asiolink;
+
+namespace {
+
+/// @brief Test timeout (ms).
+const long TEST_TIMEOUT = 10000;
+
+/// @brief Simple test fixture for testing IoServiceThreadPool.
+class IoServiceThreadPoolTest : public ::testing::Test {
+public:
+ /// @brief Constructor.
+ IoServiceThreadPoolTest()
+ : io_service_(new IOService()) {
+ }
+
+ /// @brief Destructor.
+ virtual ~IoServiceThreadPoolTest() {
+ io_service_->stop();
+ }
+
+ /// @brief IOService instance used by thread pools.
+ IOServicePtr io_service_;
+};
+
+// URL contains scheme and hostname.
+TEST_F(IoServiceThreadPoolTest, invalidConstruction) {
+ IoServiceThreadPoolPtr pool;
+
+ // Constructing with pool size of 0 should fail.
+ ASSERT_THROW_MSG(pool.reset(new IoServiceThreadPool(io_service_, 0)),
+ BadValue, "pool_size must be non 0");
+}
+
+// Verifies that a pool can be created without starting it.
+TEST_F(IoServiceThreadPoolTest, deferredStartConstruction) {
+ IoServiceThreadPoolPtr pool;
+
+ ASSERT_NO_THROW_LOG(pool.reset(new IoServiceThreadPool(io_service_, 3, true)));
+
+ // State should be stopped.
+ // Pool size should be 3.
+ // IOService should be there.
+ // IOService is new, so it should not be stopped.
+ // No threads in the pool.
+ ASSERT_TRUE(pool->isStopped());
+ EXPECT_EQ(pool->getPoolSize(), 3);
+ ASSERT_TRUE(pool->getIOService());
+ EXPECT_FALSE(pool->getIOService()->stopped());
+ EXPECT_EQ(pool->getThreadCount(), 0);
+
+ // Destructor should not throw.
+ ASSERT_NO_THROW_LOG(pool.reset());
+}
+
+// Verifies that a pool can be started within the constructor.
+TEST_F(IoServiceThreadPoolTest, startDuringConstruction) {
+ IoServiceThreadPoolPtr pool;
+
+ ASSERT_NO_THROW_LOG(pool.reset(new IoServiceThreadPool(io_service_, 3)));
+
+ // State should be running.
+ // Pool size should be 3.
+ // IOService should be there.
+ // IOService is new, so it should not be stopped.
+ // Should have 3 threads in the pool.
+ ASSERT_TRUE(pool->isRunning());
+ EXPECT_EQ(pool->getPoolSize(), 3);
+ ASSERT_TRUE(pool->getIOService());
+ EXPECT_FALSE(pool->getIOService()->stopped());
+ EXPECT_EQ(pool->getThreadCount(), 3);
+
+ // Destructor should not throw.
+ ASSERT_NO_THROW_LOG(pool.reset());
+}
+
+// Verifies that pool can move from STOPPED to RUNNING.
+TEST_F(IoServiceThreadPoolTest, stoppedToRunning) {
+ IoServiceThreadPoolPtr pool;
+
+ // Create a stopped pool.
+ ASSERT_NO_THROW_LOG(pool.reset(new IoServiceThreadPool(io_service_, 3, true)));
+ ASSERT_TRUE(pool->isStopped());
+
+ // Call run from STOPPED.
+ ASSERT_NO_THROW_LOG(pool->run());
+
+ // State should be RUNNING, IOService should not be stopped, we should
+ // have 3 threads in the pool.
+ ASSERT_TRUE(pool->isRunning());
+ EXPECT_FALSE(pool->getIOService()->stopped());
+ EXPECT_EQ(pool->getThreadCount(), 3);
+
+ // Calling run again should be harmless.
+ ASSERT_NO_THROW_LOG(pool->run());
+
+ // State should be RUNNING, IOService should not be stopped, we should
+ // have 3 threads in the pool.
+ ASSERT_TRUE(pool->isRunning());
+ EXPECT_FALSE(pool->getIOService()->stopped());
+ EXPECT_EQ(pool->getThreadCount(), 3);
+
+ // Destroying the pool should be fine.
+ ASSERT_NO_THROW_LOG(pool.reset());
+}
+
+// Verifies that pool can move from RUNNING to STOPPED.
+TEST_F(IoServiceThreadPoolTest, runningToStopped) {
+ IoServiceThreadPoolPtr pool;
+
+ // Create a running pool.
+ ASSERT_NO_THROW_LOG(pool.reset(new IoServiceThreadPool(io_service_, 3, false)));
+ ASSERT_TRUE(pool->isRunning());
+
+ // Call stop.
+ ASSERT_NO_THROW_LOG(pool->stop());
+
+ // State should be STOPPED, IOService should be stopped, we should
+ // have 0 threads in the pool.
+ ASSERT_TRUE(pool->isStopped());
+ EXPECT_TRUE(pool->getIOService()->stopped());
+ EXPECT_EQ(pool->getThreadCount(), 0);
+
+ // Calling stop again should be harmless.
+ ASSERT_NO_THROW_LOG(pool->stop());
+
+ // State should be STOPPED, IOService should be stopped, we should
+ // have 0 threads in the pool.
+ ASSERT_TRUE(pool->isStopped());
+ EXPECT_TRUE(pool->getIOService()->stopped());
+ EXPECT_EQ(pool->getThreadCount(), 0);
+
+ // Destroying the pool should be fine.
+ ASSERT_NO_THROW_LOG(pool.reset());
+}
+
+// Verifies that pool can move from RUNNING to PAUSED.
+TEST_F(IoServiceThreadPoolTest, runningToPaused) {
+ IoServiceThreadPoolPtr pool;
+
+ // Create a running pool.
+ ASSERT_NO_THROW_LOG(pool.reset(new IoServiceThreadPool(io_service_, 3, false)));
+ ASSERT_TRUE(pool->isRunning());
+
+ // Call pause from RUNNING.
+ ASSERT_NO_THROW_LOG(pool->pause());
+
+ // State should be PAUSED, IOService should be stopped, we should
+ // have 3 threads in the pool.
+ ASSERT_TRUE(pool->isPaused());
+ EXPECT_TRUE(pool->getIOService()->stopped());
+ EXPECT_EQ(pool->getThreadCount(), 3);
+
+ // Calling pause again should be harmless.
+ ASSERT_NO_THROW_LOG(pool->pause());
+
+ // State should be PAUSED, IOService should be stopped, we should
+ // have 3 threads in the pool.
+ ASSERT_TRUE(pool->isPaused());
+ EXPECT_TRUE(pool->getIOService()->stopped());
+ EXPECT_EQ(pool->getThreadCount(), 3);
+
+ // Destroying the pool should be fine.
+ ASSERT_NO_THROW_LOG(pool.reset());
+}
+
+// Verifies that pool can move from PAUSED to RUNNING.
+TEST_F(IoServiceThreadPoolTest, pausedToRunning) {
+ IoServiceThreadPoolPtr pool;
+
+ // Create a running pool.
+ ASSERT_NO_THROW_LOG(pool.reset(new IoServiceThreadPool(io_service_, 3, false)));
+ ASSERT_TRUE(pool->isRunning());
+
+ // Call pause from RUNNING.
+ ASSERT_NO_THROW_LOG(pool->pause());
+ ASSERT_TRUE(pool->isPaused());
+
+ // Call run.
+ ASSERT_NO_THROW_LOG(pool->run());
+
+ // State should be RUNNING, IOService should not be stopped, we should
+ // have 3 threads in the pool.
+ ASSERT_TRUE(pool->isRunning());
+ EXPECT_FALSE(pool->getIOService()->stopped());
+ EXPECT_EQ(pool->getThreadCount(), 3);
+
+ // Destroying the pool should be fine.
+ ASSERT_NO_THROW_LOG(pool.reset());
+}
+
+// Verifies that pool can move from PAUSED to STOPPED.
+TEST_F(IoServiceThreadPoolTest, pausedToStopped) {
+ IoServiceThreadPoolPtr pool;
+
+ // Create a running pool.
+ ASSERT_NO_THROW_LOG(pool.reset(new IoServiceThreadPool(io_service_, 3, false)));
+ ASSERT_TRUE(pool->isRunning());
+
+ // Call pause from RUNNING.
+ ASSERT_NO_THROW_LOG(pool->pause());
+ ASSERT_TRUE(pool->isPaused());
+
+ // Call stop.
+ ASSERT_NO_THROW_LOG(pool->stop());
+
+ // State should be STOPPED, IOService should be stopped, we should
+ // have 0 threads in the pool.
+ ASSERT_TRUE(pool->isStopped());
+ EXPECT_TRUE(pool->getIOService()->stopped());
+ EXPECT_EQ(pool->getThreadCount(), 0);
+
+ // Destroying the pool should be fine.
+ ASSERT_NO_THROW_LOG(pool.reset());
+}
+
+// Verifies that attempting to pause a STOPPED pool has no effect.
+TEST_F(IoServiceThreadPoolTest, stoppedToPaused) {
+ IoServiceThreadPoolPtr pool;
+
+ // Create a stopped pool.
+ ASSERT_NO_THROW_LOG(pool.reset(new IoServiceThreadPool(io_service_, 3, true)));
+ ASSERT_TRUE(pool->isStopped());
+
+ // State should be STOPPED, IOService won't be stopped because it was
+ // never started. We should have 0 threads in the pool.
+ ASSERT_TRUE(pool->isStopped());
+ EXPECT_FALSE(pool->getIOService()->stopped());
+ EXPECT_EQ(pool->getThreadCount(), 0);
+
+ // Call pause from STOPPED.
+ ASSERT_NO_THROW_LOG(pool->pause());
+
+ // Should have no effect.
+ ASSERT_TRUE(pool->isStopped());
+
+ // State should be STOPPED, IOService won't be stopped because it was
+ // never started. We should have 0 threads in the pool.
+ ASSERT_TRUE(pool->isStopped());
+ EXPECT_FALSE(pool->getIOService()->stopped());
+ EXPECT_EQ(pool->getThreadCount(), 0);
+
+ // Destroying the pool should be fine.
+ ASSERT_NO_THROW_LOG(pool.reset());
+}
+
+}
diff --git a/src/lib/asiolink/tests/io_service_unittest.cc b/src/lib/asiolink/tests/io_service_unittest.cc
new file mode 100644
index 0000000..05fc5ac
--- /dev/null
+++ b/src/lib/asiolink/tests/io_service_unittest.cc
@@ -0,0 +1,48 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/io_service.h>
+
+#include <gtest/gtest.h>
+#include <functional>
+#include <vector>
+
+using namespace isc::asiolink;
+
+namespace {
+
+void
+postedEvent(std::vector<int>* destination, int value) {
+ destination->push_back(value);
+}
+
+// Check the posted events are called, in the same order they are posted.
+TEST(IOService, post) {
+ std::vector<int> called;
+ IOService service;
+ // Post two events
+ service.post(std::bind(&postedEvent, &called, 1));
+ service.post(std::bind(&postedEvent, &called, 2));
+ service.post(std::bind(&postedEvent, &called, 3));
+ // They have not yet been called
+ EXPECT_TRUE(called.empty());
+ // Process two events
+ service.run_one();
+ service.run_one();
+ // Both events were called in the right order
+ ASSERT_EQ(2, called.size());
+ EXPECT_EQ(1, called[0]);
+ EXPECT_EQ(2, called[1]);
+
+ // Test that poll() executes the last handler.
+ service.poll();
+ ASSERT_EQ(3, called.size());
+ EXPECT_EQ(3, called[2]);
+}
+
+}
diff --git a/src/lib/asiolink/tests/io_socket_unittest.cc b/src/lib/asiolink/tests/io_socket_unittest.cc
new file mode 100644
index 0000000..659a6a6
--- /dev/null
+++ b/src/lib/asiolink/tests/io_socket_unittest.cc
@@ -0,0 +1,25 @@
+// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_socket.h>
+
+#include <gtest/gtest.h>
+#include <netinet/in.h>
+
+using namespace isc::asiolink;
+
+TEST(IOSocketTest, dummySockets) {
+ EXPECT_EQ(static_cast<short>(IPPROTO_UDP),
+ IOSocket::getDummyUDPSocket().getProtocol());
+ EXPECT_EQ(static_cast<short>(IPPROTO_TCP),
+ IOSocket::getDummyTCPSocket().getProtocol());
+ EXPECT_EQ(-1, IOSocket::getDummyUDPSocket().getNative());
+ EXPECT_EQ(-1, IOSocket::getDummyTCPSocket().getNative());
+}
+
+
diff --git a/src/lib/asiolink/tests/process_spawn_app.sh.in b/src/lib/asiolink/tests/process_spawn_app.sh.in
new file mode 100644
index 0000000..eab7310
--- /dev/null
+++ b/src/lib/asiolink/tests/process_spawn_app.sh.in
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+# Copyright (C) 2015-2021 Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+# This script is used for testing the ProcessSpawn utility class. This
+# class is used to fork and execute a new process. It also allows for
+# checking the exit code returned when the process terminates.
+# The unit tests execute this script via ProcessSpawn class with
+# different command line parameters to test the class functionality.
+#
+# In particular, they check if the class correctly records the exit code
+# returned. The exit code returned is controlled by the caller. It is
+# possible to explicitly specify the exit code to be returned using
+# the command line options. It is also possible to specify that the
+# exit code is "unique" for the process, so as the test can check
+# that two distinct processes spawned by the same ProcessSpawn
+# object may return different status code. The command line of this
+# script also allows for forcing the process to sleep so as the
+# test has much enough time to verify that the convenience methods
+# checking the state of the process, i.e. process running or not.
+
+# Exit with error if commands exit with non-zero and if undefined variables are
+# used.
+set -eu
+
+exit_code=
+
+while test "${#}" -gt 0
+do
+ option=${1}
+ case ${option} in
+ -p)
+ exit_code=${$}
+ ;;
+ -e)
+ shift
+ exit_code=${1-}
+ ;;
+ -s)
+ shift
+ sleep "${1}"
+ ;;
+ -v)
+ shift
+ VAR_NAME=${1}
+ shift
+ VAR_VALUE=${1}
+ EXPECTED=$(env | grep -E "^${VAR_NAME}=")
+ if ! test "${VAR_NAME}=${VAR_VALUE}" = "${EXPECTED}"; then
+ exit 123
+ fi
+ ;;
+ *)
+ exit 123
+ ;;
+ esac
+ # We've shifted in the loop so we may have run out of parameters in the
+ # meantime. Check again.
+ if test "${#}" -gt 0; then
+ shift
+ fi
+done
+
+# The exit code of 32 is returned when no args specified or
+# when only the -s arg has been specified.
+if [ -z "${exit_code}" ]; then
+ exit 32
+fi
+
+exit "${exit_code}"
diff --git a/src/lib/asiolink/tests/process_spawn_unittest.cc b/src/lib/asiolink/tests/process_spawn_unittest.cc
new file mode 100644
index 0000000..0fda5a1
--- /dev/null
+++ b/src/lib/asiolink/tests/process_spawn_unittest.cc
@@ -0,0 +1,350 @@
+// Copyright (C) 2015-2023 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/io_service_signal.h>
+#include <asiolink/interval_timer.h>
+#include <asiolink/process_spawn.h>
+#include <gtest/gtest.h>
+#include <signal.h>
+#include <stdint.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <testutils/gtest_utils.h>
+
+namespace {
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace std;
+namespace ph = std::placeholders;
+
+/// @brief Test fixture for testing the use of ProcessSpawn with IO service
+/// signals.
+///
+/// This fixture is exercises ProcessSpawn using IO service signaling.
+class ProcessSpawnTest : public ::testing::Test {
+public:
+ /// @brief IOService instance to process IO.
+ asiolink::IOServicePtr io_service_;
+
+ /// @brief Single instance of IOService.
+ static asiolink::IOServicePtr getIOService() {
+ static asiolink::IOServicePtr io_service(new asiolink::IOService());
+ return (io_service);
+ }
+
+ /// @brief Failsafe timer to ensure test(s) do not hang.
+ isc::asiolink::IntervalTimer test_timer_;
+
+ /// @brief Maximum time should be allowed to run.
+ int test_time_ms_;
+
+ /// @brief IOSignalSet object.
+ IOSignalSetPtr io_signal_set_;
+
+ /// @brief Vector to record the signal values received.
+ std::vector<int> processed_signals_;
+
+ /// @brief Constructor.
+ ProcessSpawnTest() :
+ io_service_(getIOService()), test_timer_(*io_service_),
+ test_time_ms_(0), io_signal_set_(), processed_signals_() {
+
+ io_signal_set_.reset(new IOSignalSet(io_service_,
+ std::bind(&ProcessSpawnTest::processSignal,
+ this, ph::_1)));
+ io_signal_set_->add(SIGCHLD);
+ }
+
+ /// @brief Destructor.
+ ~ProcessSpawnTest() {
+ io_signal_set_->remove(SIGCHLD);
+ io_signal_set_.reset();
+ // Make sure the cancel handler for the IOSignalSet is called.
+ io_service_->poll();
+ }
+
+ /// @brief Method used as the IOSignalSet handler.
+ ///
+ /// Records the value of the given signal and checks if the desired
+ /// number of signals have been received. If so, the IOService is
+ /// stopped which will cause IOService::run() to exit, returning control
+ /// to the test.
+ ///
+ /// @param signum signal number.
+ void processSignal(int signum) {
+ // Remember the signal we got.
+ processed_signals_.push_back(signum);
+ }
+
+ /// @brief Sets the failsafe timer for the test to the given time.
+ ///
+ /// @param test_time_ms maximum time in milliseconds the test should
+ /// be allowed to run.
+ void setTestTime(int test_time_ms) {
+ // Fail safe shutdown
+ test_time_ms_ = test_time_ms;
+ test_timer_.setup(std::bind(&ProcessSpawnTest::testTimerHandler, this),
+ test_time_ms_, asiolink::IntervalTimer::ONE_SHOT);
+ }
+
+ /// @brief Failsafe timer expiration handler.
+ void testTimerHandler() {
+ io_service_->stop();
+ }
+};
+
+// This test verifies that the external application can be ran with
+// arguments and that the exit code is gathered.
+TEST_F(ProcessSpawnTest, spawnWithArgs) {
+ vector<string> args;
+ args.push_back("-e");
+ args.push_back("64");
+
+ ProcessSpawn process(io_service_, TEST_SCRIPT_SH, args);
+ pid_t pid = 0;
+ ASSERT_NO_THROW(pid = process.spawn());
+
+ // Set test fail safe.
+ setTestTime(1000);
+
+ // The next handler executed is IOSignal's handler.
+ io_service_->run_one();
+
+ // The first handler executed is the IOSignal's internal timer expire
+ // callback.
+ io_service_->run_one();
+
+ // Polling once to be sure.
+ io_service_->poll();
+
+ ASSERT_EQ(1, processed_signals_.size());
+ ASSERT_EQ(SIGCHLD, processed_signals_[0]);
+
+ EXPECT_EQ(64, process.getExitStatus(pid));
+}
+
+// This test verifies that the external application can be ran with
+// arguments and environment variables that the exit code is gathered.
+TEST_F(ProcessSpawnTest, spawnWithArgsAndEnvVars) {
+ vector<string> args;
+ vector<string> vars;
+ args.push_back("-v");
+ args.push_back("TEST_VARIABLE_NAME");
+ args.push_back("TEST_VARIABLE_VALUE");
+ vars.push_back("TEST_VARIABLE_NAME=TEST_VARIABLE_VALUE");
+
+ ProcessSpawn process(io_service_, TEST_SCRIPT_SH, args, vars);
+ pid_t pid = 0;
+ ASSERT_NO_THROW(pid = process.spawn());
+
+ // Set test fail safe.
+ setTestTime(1000);
+
+ // The next handler executed is IOSignal's handler.
+ io_service_->run_one();
+
+ // The first handler executed is the IOSignal's internal timer expire
+ // callback.
+ io_service_->run_one();
+
+ // Polling once to be sure.
+ io_service_->poll();
+
+ ASSERT_EQ(1, processed_signals_.size());
+ ASSERT_EQ(SIGCHLD, processed_signals_[0]);
+
+ EXPECT_EQ(32, process.getExitStatus(pid));
+}
+
+// This test verifies that the single ProcessSpawn object can be used
+// to start two processes and that their status codes can be gathered.
+// It also checks that it is possible to clear the status of the
+// process.
+TEST_F(ProcessSpawnTest, spawnTwoProcesses) {
+ vector<string> args;
+ args.push_back("-p");
+
+ ProcessSpawn process(io_service_, TEST_SCRIPT_SH, args);
+ pid_t pid1 = 0;
+ ASSERT_NO_THROW(pid1 = process.spawn());
+
+ // Set test fail safe.
+ setTestTime(1000);
+
+ // The next handler executed is IOSignal's handler.
+ io_service_->run_one();
+
+ // The first handler executed is the IOSignal's internal timer expire
+ // callback.
+ io_service_->run_one();
+
+ // Polling once to be sure.
+ io_service_->poll();
+
+ pid_t pid2 = 0;
+ ASSERT_NO_THROW(pid2 = process.spawn());
+
+ // Set test fail safe.
+ setTestTime(1000);
+
+ // The next handler executed is IOSignal's handler.
+ io_service_->run_one();
+
+ // The first handler executed is the IOSignal's internal timer expire
+ // callback.
+ io_service_->run_one();
+
+ // Polling once to be sure.
+ io_service_->poll();
+
+ ASSERT_EQ(2, processed_signals_.size());
+ ASSERT_EQ(SIGCHLD, processed_signals_[0]);
+ ASSERT_EQ(SIGCHLD, processed_signals_[1]);
+
+ EXPECT_NE(process.getExitStatus(pid1), process.getExitStatus(pid2));
+
+ // Clear the status of the first process. An attempt to get the status
+ // for the cleared process should result in exception. But, there should
+ // be no exception for the second process.
+ process.clearState(pid1);
+ EXPECT_THROW(process.getExitStatus(pid1), InvalidOperation);
+ EXPECT_NO_THROW(process.getExitStatus(pid2));
+
+ process.clearState(pid2);
+ EXPECT_THROW(process.getExitStatus(pid2), InvalidOperation);
+}
+
+// This test verifies that the external application can be ran without
+// arguments and that the exit code is gathered.
+TEST_F(ProcessSpawnTest, spawnNoArgs) {
+ ProcessSpawn process(io_service_, TEST_SCRIPT_SH);
+ pid_t pid = 0;
+ ASSERT_NO_THROW(pid = process.spawn());
+
+ // Set test fail safe.
+ setTestTime(1000);
+
+ // The next handler executed is IOSignal's handler.
+ io_service_->run_one();
+
+ // The first handler executed is the IOSignal's internal timer expire
+ // callback.
+ io_service_->run_one();
+
+ // Polling once to be sure.
+ io_service_->poll();
+
+ ASSERT_EQ(1, processed_signals_.size());
+ ASSERT_EQ(SIGCHLD, processed_signals_[0]);
+
+ EXPECT_EQ(32, process.getExitStatus(pid));
+
+ ASSERT_NO_THROW(pid = process.spawn(true));
+
+ // Set test fail safe.
+ setTestTime(1000);
+
+ // The next handler executed is IOSignal's handler.
+ io_service_->run_one();
+
+ // The first handler executed is the IOSignal's internal timer expire
+ // callback.
+ io_service_->run_one();
+
+ // Polling once to be sure.
+ io_service_->poll();
+
+ ASSERT_EQ(2, processed_signals_.size());
+ ASSERT_EQ(SIGCHLD, processed_signals_[0]);
+ ASSERT_EQ(SIGCHLD, processed_signals_[1]);
+
+ EXPECT_THROW(process.getExitStatus(pid), InvalidOperation);
+}
+
+// This test verifies that the EXIT_FAILURE code is returned when
+// application can't be executed.
+TEST_F(ProcessSpawnTest, invalidExecutable) {
+ std::string expected = "File not found: foo";
+ ASSERT_THROW_MSG(ProcessSpawn process(io_service_, "foo"),
+ ProcessSpawnError, expected);
+
+ std::string name = INVALID_TEST_SCRIPT_SH;
+
+ expected = "File not executable: ";
+ expected += name;
+ ASSERT_THROW_MSG(ProcessSpawn process(io_service_, name),
+ ProcessSpawnError, expected);
+}
+
+// This test verifies that the full command line for the process is
+// returned.
+TEST_F(ProcessSpawnTest, getCommandLine) {
+ // Note that cases below are enclosed in separate scopes to make
+ // sure that the ProcessSpawn object is destroyed before a new
+ // object is created. Current implementation doesn't allow for
+ // having two ProcessSpawn objects simultaneously as they will
+ // both try to allocate a signal handler for SIGCHLD.
+ {
+ // Case 1: arguments present.
+ ProcessArgs args;
+ args.push_back("-x");
+ args.push_back("-y");
+ args.push_back("foo");
+ args.push_back("bar");
+ ProcessSpawn process(io_service_, TEST_SCRIPT_SH, args);
+ std::string expected = TEST_SCRIPT_SH;
+ expected += " -x -y foo bar";
+ EXPECT_EQ(expected, process.getCommandLine());
+ }
+
+ {
+ // Case 2: no arguments.
+ ProcessSpawn process(io_service_, TEST_SCRIPT_SH);
+ EXPECT_EQ(TEST_SCRIPT_SH, process.getCommandLine());
+ }
+}
+
+// This test verifies that it is possible to check if the process is
+// running.
+TEST_F(ProcessSpawnTest, isRunning) {
+ // Run the process which sleeps for 10 seconds, so as we have
+ // enough time to check if it is running.
+ vector<string> args;
+ args.push_back("-s");
+ args.push_back("10");
+
+ ProcessSpawn process(io_service_, TEST_SCRIPT_SH, args);
+ pid_t pid = 0;
+ ASSERT_NO_THROW(pid = process.spawn());
+
+ EXPECT_TRUE(process.isRunning(pid));
+
+ // Kill the process.
+ ASSERT_EQ(0, kill(pid, SIGKILL));
+
+ // Set test fail safe.
+ setTestTime(1000);
+
+ // The next handler executed is IOSignal's handler.
+ io_service_->run_one();
+
+ // The first handler executed is the IOSignal's internal timer expire
+ // callback.
+ io_service_->run_one();
+
+ // Polling once to be sure.
+ io_service_->poll();
+
+ ASSERT_EQ(1, processed_signals_.size());
+ ASSERT_EQ(SIGCHLD, processed_signals_[0]);
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/asiolink/tests/run_unittests.cc b/src/lib/asiolink/tests/run_unittests.cc
new file mode 100644
index 0000000..566c7c9
--- /dev/null
+++ b/src/lib/asiolink/tests/run_unittests.cc
@@ -0,0 +1,19 @@
+// Copyright (C) 2009-2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+#include <log/logger_manager.h>
+
+int
+main(int argc, char* argv[])
+{
+ ::testing::InitGoogleTest(&argc, argv); // Initialize Google test
+ isc::log::LoggerManager::init("unittest"); // Set a root logger name
+ return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/asiolink/tests/tcp_acceptor_unittest.cc b/src/lib/asiolink/tests/tcp_acceptor_unittest.cc
new file mode 100644
index 0000000..5a925cc
--- /dev/null
+++ b/src/lib/asiolink/tests/tcp_acceptor_unittest.cc
@@ -0,0 +1,444 @@
+// Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/interval_timer.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_acceptor.h>
+#include <asiolink/tcp_endpoint.h>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+#include <functional>
+#include <list>
+#include <netinet/in.h>
+#include <string>
+
+using namespace isc::asiolink;
+namespace ph = std::placeholders;
+
+namespace {
+
+/// @brief Local server address used for testing.
+const char SERVER_ADDRESS[] = "127.0.0.1";
+
+/// @brief Local server port used for testing.
+const unsigned short SERVER_PORT = 18123;
+
+/// @brief Test timeout in ms.
+const long TEST_TIMEOUT = 10000;
+
+/// @brief Simple class representing TCP socket callback.
+class SocketCallback {
+public:
+
+ /// @brief Implements callback for the asynchronous operation on the socket.
+ ///
+ /// This callback merely checks if error has occurred and reports this
+ /// error. It does nothing in case of success.
+ ///
+ /// @param ec Error code.
+ /// @param length Length of received data.
+ void operator()(boost::system::error_code ec, size_t length = 0) {
+ if (ec) {
+ ADD_FAILURE() << "error occurred for a socket with data of length "
+ << length << ": " << ec.message();
+ }
+ }
+
+};
+
+/// @brief Entity which can connect to the TCP server endpoint and close the
+/// connection.
+class TCPClient : public boost::noncopyable {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// This constructor creates new socket instance. It doesn't connect. Call
+ /// connect() to connect to the server.
+ ///
+ /// @param io_service IO service to be stopped on error.
+ explicit TCPClient(IOService& io_service)
+ : io_service_(io_service.get_io_service()), socket_(io_service_) {
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Closes the underlying socket if it is open.
+ ~TCPClient() {
+ close();
+ }
+
+ /// @brief Connect to the test server address and port.
+ ///
+ /// This method asynchronously connects to the server endpoint and uses the
+ /// connectHandler as a callback function.
+ void connect() {
+ boost::asio::ip::tcp::endpoint
+ endpoint(boost::asio::ip::address::from_string(SERVER_ADDRESS),
+ SERVER_PORT);
+ socket_.async_connect(endpoint,
+ std::bind(&TCPClient::connectHandler, this,
+ ph::_1));
+ }
+
+ /// @brief Callback function for connect().
+ ///
+ /// This function stops the IO service upon error.
+ ///
+ /// @param ec Error code.
+ void connectHandler(const boost::system::error_code& ec) {
+ if (ec) {
+ // One would expect that async_connect wouldn't return EINPROGRESS
+ // error code, but simply wait for the connection to get
+ // established before the handler is invoked. It turns out, however,
+ // that on some OSes the connect handler may receive this error code
+ // which doesn't necessarily indicate a problem. Making an attempt
+ // to write and read from this socket will typically succeed. So,
+ // we ignore this error.
+ if (ec.value() != boost::asio::error::in_progress) {
+ ADD_FAILURE() << "error occurred while connecting: "
+ << ec.message();
+ io_service_.stop();
+ }
+ }
+ }
+
+ /// @brief Close connection.
+ void close() {
+ socket_.close();
+ }
+
+private:
+
+ /// @brief Holds reference to the IO service.
+ boost::asio::io_service& io_service_;
+
+ /// @brief A socket used for the connection.
+ boost::asio::ip::tcp::socket socket_;
+
+};
+
+/// @brief Pointer to the TCPClient.
+typedef boost::shared_ptr<TCPClient> TCPClientPtr;
+
+/// @brief A signature of the function implementing callback for the
+/// TCPAcceptor.
+typedef std::function<void(const boost::system::error_code&)> TCPAcceptorCallback;
+
+/// @brief TCPAcceptor using TCPAcceptorCallback.
+typedef TCPAcceptor<TCPAcceptorCallback> TestTCPAcceptor;
+
+/// @brief Implements asynchronous TCP acceptor service.
+///
+/// It creates a new socket into which connection is accepted. The socket
+/// is retained until class instance exists.
+class Acceptor {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param io_service IO service.
+ /// @param acceptor Reference to the TCP acceptor on which asyncAccept
+ /// will be called.
+ /// @param callback Callback function for the asyncAccept.
+ explicit Acceptor(IOService& io_service, TestTCPAcceptor& acceptor,
+ const TCPAcceptorCallback& callback)
+ : socket_(io_service), acceptor_(acceptor), callback_(callback) {
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Closes socket.
+ ~Acceptor() {
+ socket_.close();
+ }
+
+ /// @brief Asynchronous accept new connection.
+ void accept() {
+ acceptor_.asyncAccept(socket_, callback_);
+ }
+
+ /// @brief Close connection.
+ void close() {
+ socket_.close();
+ }
+
+private:
+
+ /// @brief Socket into which connection is accepted.
+ TCPSocket<SocketCallback> socket_;
+
+ /// @brief Reference to the TCPAcceptor on which asyncAccept is called.
+ TestTCPAcceptor& acceptor_;
+
+ /// @brief Instance of the callback used for asyncAccept.
+ TCPAcceptorCallback callback_;
+
+};
+
+/// @brief Pointer to the Acceptor object.
+typedef boost::shared_ptr<Acceptor> AcceptorPtr;
+
+/// @brief Test fixture class for TCPAcceptor.
+///
+/// This class provides means for creating new TCP connections, i.e. simulates
+/// clients connecting to the servers via TCPAcceptor. It is possible to create
+/// multiple simultaneous connections, which are retained by the test fixture
+/// class and closed cleanly when the test fixture is destroyed.
+class TCPAcceptorTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Besides initializing class members it also sets the test timer to guard
+ /// against endlessly running IO service when TCP connections are
+ /// unsuccessful.
+ TCPAcceptorTest()
+ : io_service_(), acceptor_(io_service_),
+ asio_endpoint_(boost::asio::ip::address::from_string(SERVER_ADDRESS),
+ SERVER_PORT),
+ endpoint_(asio_endpoint_), test_timer_(io_service_), connections_(),
+ clients_(), connections_num_(0), aborted_connections_num_(0),
+ max_connections_(1) {
+ test_timer_.setup(std::bind(&TCPAcceptorTest::timeoutHandler, this),
+ TEST_TIMEOUT, IntervalTimer::ONE_SHOT);
+ }
+
+ /// @brief Destructor.
+ virtual ~TCPAcceptorTest() {
+ }
+
+ /// @brief Specifies how many new connections are expected before the IO
+ /// service is stopped.
+ ///
+ /// @param max_connections Connections limit.
+ void setMaxConnections(const unsigned int max_connections) {
+ max_connections_ = max_connections;
+ }
+
+ /// @brief Create ASIO endpoint from the provided endpoint by retaining the
+ /// IP address and modifying the port.
+ ///
+ /// This convenience method is useful to create new endpoint from the
+ /// existing endpoint to test reusing IP address for multiple acceptors.
+ /// The returned endpoint has the same IP address but different port.
+ ///
+ /// @param endpoint Source endpoint.
+ ///
+ /// @return New endpoint with the port number increased by 1.
+ boost::asio::ip::tcp::endpoint
+ createSiblingEndpoint(const boost::asio::ip::tcp::endpoint& endpoint) const {
+ boost::asio::ip::tcp::endpoint endpoint_copy(endpoint);
+ endpoint_copy.port(endpoint.port() + 1);
+ return (endpoint_copy);
+ }
+
+ /// @brief Opens TCP acceptor and sets 'reuse address' option.
+ void acceptorOpen() {
+ acceptor_.open(endpoint_);
+ acceptor_.setOption(TestTCPAcceptor::ReuseAddress(true));
+ }
+
+ /// @brief Starts accepting TCP connections.
+ ///
+ /// This method creates new Acceptor instance and calls accept() to start
+ /// accepting new connections. The instance of the Acceptor object is
+ /// retained in the connections_ list.
+ void accept() {
+ TCPAcceptorCallback cb = std::bind(&TCPAcceptorTest::acceptHandler,
+ this, ph::_1);
+ AcceptorPtr conn(new Acceptor(io_service_, acceptor_, cb));
+ connections_.push_back(conn);
+ connections_.back()->accept();
+ }
+
+ /// @brief Connect to the endpoint.
+ ///
+ /// This method creates TCPClient instance and retains it in the clients_
+ /// list.
+ void connect() {
+ TCPClientPtr client(new TCPClient(io_service_));
+ clients_.push_back(client);
+ clients_.back()->connect();
+ }
+
+ /// @brief Callback function for asynchronous accept calls.
+ ///
+ /// It stops the IO service upon error or when the number of accepted
+ /// connections reaches the max_connections_ value. Otherwise it calls
+ /// accept() to start accepting next connections.
+ ///
+ /// @param ec Error code.
+ void acceptHandler(const boost::system::error_code& ec) {
+ if (ec) {
+ if (ec.value() != boost::asio::error::operation_aborted) {
+ ADD_FAILURE() << "error occurred while accepting connection: "
+ << ec.message();
+ } else {
+ ++aborted_connections_num_;
+ }
+ io_service_.stop();
+ }
+
+ // We have reached the maximum number of connections - end the test.
+ if (++connections_num_ >= max_connections_) {
+ io_service_.stop();
+ return;
+ }
+
+ accept();
+ }
+
+ /// @brief Callback function invoke upon test timeout.
+ ///
+ /// It stops the IO service and reports test timeout.
+ void timeoutHandler() {
+ ADD_FAILURE() << "Timeout occurred while running the test!";
+ io_service_.stop();
+ }
+
+ /// @brief IO service.
+ IOService io_service_;
+
+ /// @brief TCPAcceptor under test.
+ TestTCPAcceptor acceptor_;
+
+ /// @brief Server endpoint.
+ boost::asio::ip::tcp::endpoint asio_endpoint_;
+
+ /// @brief asiolink server endpoint (uses asio_endpoint_).
+ TCPEndpoint endpoint_;
+
+ /// @brief Asynchronous timer service to detect timeouts.
+ IntervalTimer test_timer_;
+
+ /// @brief List of connections on the server side.
+ std::list<AcceptorPtr> connections_;
+
+ /// @brief List of client connections.
+ std::list<TCPClientPtr> clients_;
+
+ /// @brief Current number of established connections.
+ unsigned int connections_num_;
+
+ /// @brief Current number of aborted connections.
+ unsigned int aborted_connections_num_;
+
+ /// @brief Connections limit.
+ unsigned int max_connections_;
+};
+
+// Test TCPAcceptor::asyncAccept.
+TEST_F(TCPAcceptorTest, asyncAccept) {
+ // Establish up to 10 connections.
+ setMaxConnections(10);
+
+ // Initialize acceptor.
+ acceptorOpen();
+ acceptor_.bind(endpoint_);
+ acceptor_.listen();
+
+ // Start accepting new connections.
+ accept();
+
+ // Create 10 new TCP connections (client side).
+ for (unsigned int i = 0; i < 10; ++i) {
+ connect();
+ }
+
+ // Run the IO service until we have accepted 10 connections, an error
+ // or test timeout occurred.
+ io_service_.run();
+
+ // Make sure that all accepted connections have been recorded.
+ EXPECT_EQ(10, connections_num_);
+ EXPECT_EQ(10, connections_.size());
+}
+
+// Check that it is possible to set SO_REUSEADDR flag for the TCPAcceptor.
+TEST_F(TCPAcceptorTest, reuseAddress) {
+ // We need at least two acceptors using common address. Let's create the
+ // second endpoint which has the same address but different port.
+ boost::asio::ip::tcp::endpoint asio_endpoint2(createSiblingEndpoint(asio_endpoint_));
+ TCPEndpoint endpoint2(asio_endpoint2);
+
+ // Create and open two acceptors.
+ TestTCPAcceptor acceptor1(io_service_);
+ TestTCPAcceptor acceptor2(io_service_);
+ ASSERT_NO_THROW(acceptor1.open(endpoint_));
+ ASSERT_NO_THROW(acceptor2.open(endpoint2));
+
+ // Set SO_REUSEADDR socket option so as acceptors can bind to the
+ /// same address.
+ ASSERT_NO_THROW(
+ acceptor1.setOption(TestTCPAcceptor::ReuseAddress(true))
+ );
+ ASSERT_NO_THROW(
+ acceptor2.setOption(TestTCPAcceptor::ReuseAddress(true))
+ );
+ ASSERT_NO_THROW(acceptor1.bind(endpoint_));
+ ASSERT_NO_THROW(acceptor2.bind(endpoint2));
+
+ // Create third acceptor, but don't set the SO_REUSEADDR. It should
+ // refuse to bind.
+ TCPEndpoint endpoint3(createSiblingEndpoint(asio_endpoint2));
+ TestTCPAcceptor acceptor3(io_service_);
+ ASSERT_NO_THROW(acceptor3.open(endpoint3));
+ EXPECT_THROW(acceptor3.bind(endpoint_), boost::system::system_error);
+}
+
+// Test that TCPAcceptor::getProtocol returns IPPROTO_TCP.
+TEST_F(TCPAcceptorTest, getProtocol) {
+ EXPECT_EQ(IPPROTO_TCP, acceptor_.getProtocol());
+}
+
+// Test that TCPAcceptor::getNative returns valid socket descriptor.
+TEST_F(TCPAcceptorTest, getNative) {
+ // Initially the descriptor should be invalid (negative).
+ ASSERT_LT(acceptor_.getNative(), 0);
+ // Now open the socket and make sure the returned descriptor is now valid.
+ ASSERT_NO_THROW(acceptorOpen());
+ EXPECT_GE(acceptor_.getNative(), 0);
+}
+
+// macOS 10.12.3 has a bug which causes the connections to not enter
+// the TIME-WAIT state and they never get closed.
+#if !defined (OS_OSX)
+
+// Test that TCPAcceptor::close works properly.
+TEST_F(TCPAcceptorTest, close) {
+ // Initialize acceptor.
+ acceptorOpen();
+ acceptor_.bind(endpoint_);
+ acceptor_.listen();
+
+ // Start accepting new connections.
+ accept();
+
+ // Create 10 new TCP connections (client side).
+ for (unsigned int i = 0; i < 10; ++i) {
+ connect();
+ }
+
+ // Close the acceptor before connections are accepted.
+ acceptor_.close();
+
+ // Run the IO service.
+ io_service_.run();
+
+ // The connections should have been aborted.
+ EXPECT_EQ(1, connections_num_);
+ EXPECT_EQ(1, aborted_connections_num_);
+ EXPECT_EQ(1, connections_.size());
+}
+
+#endif
+
+}
diff --git a/src/lib/asiolink/tests/tcp_endpoint_unittest.cc b/src/lib/asiolink/tests/tcp_endpoint_unittest.cc
new file mode 100644
index 0000000..0d838d4
--- /dev/null
+++ b/src/lib/asiolink/tests/tcp_endpoint_unittest.cc
@@ -0,0 +1,46 @@
+// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_address.h>
+#include <asiolink/tcp_endpoint.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+using namespace isc::asiolink;
+using namespace std;
+
+// This test checks that the endpoint can manage its own internal
+// boost::asio::ip::tcp::endpoint object.
+
+TEST(TCPEndpointTest, v4Address) {
+ const string test_address("192.0.2.1");
+ const unsigned short test_port = 5301;
+
+ IOAddress address(test_address);
+ TCPEndpoint endpoint(address, test_port);
+
+ EXPECT_TRUE(address == endpoint.getAddress());
+ EXPECT_EQ(test_port, endpoint.getPort());
+ EXPECT_EQ(static_cast<short>(IPPROTO_TCP), endpoint.getProtocol());
+ EXPECT_EQ(AF_INET, endpoint.getFamily());
+}
+
+TEST(TCPEndpointTest, v6Address) {
+ const string test_address("2001:db8::1235");
+ const unsigned short test_port = 5302;
+
+ IOAddress address(test_address);
+ TCPEndpoint endpoint(address, test_port);
+
+ EXPECT_TRUE(address == endpoint.getAddress());
+ EXPECT_EQ(test_port, endpoint.getPort());
+ EXPECT_EQ(static_cast<short>(IPPROTO_TCP), endpoint.getProtocol());
+ EXPECT_EQ(AF_INET6, endpoint.getFamily());
+}
diff --git a/src/lib/asiolink/tests/tcp_socket_unittest.cc b/src/lib/asiolink/tests/tcp_socket_unittest.cc
new file mode 100644
index 0000000..7531af6
--- /dev/null
+++ b/src/lib/asiolink/tests/tcp_socket_unittest.cc
@@ -0,0 +1,515 @@
+// Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @brief Test of TCPSocket
+///
+/// Tests the functionality of a TCPSocket by working through an open-send-
+/// receive-close sequence and checking that the asynchronous notifications
+/// work.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/tcp_socket.h>
+#include <util/buffer.h>
+#include <util/io_utilities.h>
+
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <arpa/inet.h>
+#include <cstddef>
+#include <cstdlib>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string>
+#include <vector>
+
+using namespace boost::asio;
+using namespace boost::asio::ip;
+using namespace isc::util;
+using namespace isc::asiolink;
+using namespace std;
+
+namespace {
+
+const char SERVER_ADDRESS[] = "127.0.0.1";
+const unsigned short SERVER_PORT = 5303;
+
+/// @todo Shouldn't we send something that is real message?
+const char OUTBOUND_DATA[] = "Data sent from client to server";
+const char INBOUND_DATA[] = "Returned data from server to client";
+}
+
+/// An instance of this object is passed to the asynchronous I/O functions
+/// and the operator() method is called when when an asynchronous I/O completes.
+/// The arguments to the completion callback are stored for later retrieval.
+class TCPCallback {
+public:
+ /// @brief Operations the server is doing
+ enum Operation {
+ ACCEPT = 0, ///< accept() was issued
+ OPEN = 1, /// Client connected to server
+ READ = 2, ///< Asynchronous read completed
+ WRITE = 3, ///< Asynchronous write completed
+ NONE = 4 ///< "Not set" state
+ };
+
+ /// @brief Minimum size of buffers
+ enum {
+ MIN_SIZE = (64 * 1024 + 2) ///< 64kB + two bytes for a count
+ };
+
+ struct PrivateData {
+ PrivateData() :
+ error_code_(), length_(0), cumulative_(0), expected_(0), offset_(0),
+ name_(""), queued_(NONE), called_(NONE), data_(MIN_SIZE, 0)
+ {}
+
+ boost::system::error_code error_code_; ///< Completion error code
+ size_t length_; ///< Bytes transferred in this I/O
+ size_t cumulative_; ///< Cumulative bytes transferred
+ size_t expected_; ///< Expected amount of data
+ size_t offset_; ///< Where to put data in buffer
+ std::string name_; ///< Which of the objects this is
+ Operation queued_; ///< Queued operation
+ Operation called_; ///< Which callback called
+ std::vector<uint8_t> data_; ///< Receive buffer
+ };
+
+ /// @brief Constructor
+ ///
+ /// Constructs the object. It also creates the data member pointed to by
+ /// a shared pointer. When used as a callback object, this is copied as it
+ /// is passed into the asynchronous function. This means that there are two
+ /// objects and inspecting the one we passed in does not tell us anything.
+ ///
+ /// Therefore we use a boost::shared_ptr. When the object is copied, the
+ /// shared pointer is copied, which leaves both objects pointing to the same
+ /// data.
+ ///
+ /// @param which Which of the two callback objects this is
+ TCPCallback(std::string which) : ptr_(new PrivateData())
+ {
+ ptr_->name_ = which;
+ }
+
+ /// @brief Destructor
+ ///
+ /// No code needed, destroying the shared pointer destroys the private data.
+ virtual ~TCPCallback()
+ {}
+
+ /// @brief Client Callback Function
+ ///
+ /// Called when an asynchronous operation is completed by the client, this
+ /// stores the origin of the operation in the client_called_ data member.
+ ///
+ /// @param ec I/O completion error code passed to callback function.
+ /// @param length Number of bytes transferred
+ void operator()(boost::system::error_code ec = boost::system::error_code(),
+ size_t length = 0)
+ {
+ setCode(ec.value());
+ ptr_->called_ = ptr_->queued_;
+ ptr_->length_ = length;
+ }
+
+ /// @brief Get I/O completion error code
+ int getCode() {
+ return (ptr_->error_code_.value());
+ }
+
+ /// @brief Set I/O completion code
+ ///
+ /// @param code New value of completion code
+ void setCode(int code) {
+ ptr_->error_code_ = boost::system::error_code(code, boost::system::error_code().category());
+ }
+
+ /// @brief Get number of bytes transferred in I/O
+ size_t& length() {
+ return (ptr_->length_);
+ }
+
+ /// @brief Get cumulative number of bytes transferred in I/O
+ size_t& cumulative() {
+ return (ptr_->cumulative_);
+ }
+
+ /// @brief Get expected amount of data
+ size_t& expected() {
+ return (ptr_->expected_);
+ }
+
+ /// @brief Get offset into data
+ size_t& offset() {
+ return (ptr_->offset_);
+ }
+
+ /// @brief Get data member
+ uint8_t* data() {
+ return (&ptr_->data_[0]);
+ }
+
+ /// @brief Get flag to say what was queued
+ Operation& queued() {
+ return (ptr_->queued_);
+ }
+
+ /// @brief Get flag to say when callback was called
+ Operation& called() {
+ return (ptr_->called_);
+ }
+
+ /// @brief Return instance of callback name
+ std::string& name() {
+ return (ptr_->name_);
+ }
+
+private:
+ boost::shared_ptr<PrivateData> ptr_; ///< Pointer to private data
+};
+
+
+// Read Server Data
+//
+// Called in the part of the test that has the client send a message to the
+// server, this loops until all the data has been read (synchronously) by the
+// server.
+//
+// "All the data read" means that the server has received a message that is
+// preceded by a two-byte count field and that the total amount of data received
+// from the remote end is equal to the value in the count field plus two bytes
+// for the count field itself.
+//
+// @param socket Socket on which the server is reading data
+// @param server_cb Structure in which server data is held.
+void
+serverRead(tcp::socket& socket, TCPCallback& server_cb) {
+
+ // As we may need to read multiple times, keep a count of the cumulative
+ // amount of data read and do successive reads into the appropriate part
+ // of the buffer.
+ //
+ // Note that there are no checks for buffer overflow - this is a test
+ // program and we have sized the buffer to be large enough for the test.
+ server_cb.cumulative() = 0;
+
+ bool complete = false;
+ while (!complete) {
+
+ // Read block of data and update cumulative amount of data received.
+ server_cb.length() = socket.receive(
+ boost::asio::buffer(server_cb.data() + server_cb.cumulative(),
+ TCPCallback::MIN_SIZE - server_cb.cumulative()));
+ server_cb.cumulative() += server_cb.length();
+
+ // If we have read at least two bytes, we can work out how much we
+ // should be reading.
+ if (server_cb.cumulative() >= 2) {
+ server_cb.expected() = readUint16(server_cb.data(), server_cb.length());
+ if ((server_cb.expected() + 2) == server_cb.cumulative()) {
+
+ // Amount of data read from socket equals the size of the
+ // message (as indicated in the first two bytes of the message)
+ // plus the size of the count field. Therefore we have received
+ // all the data.
+ complete = true;
+ }
+ }
+ }
+}
+
+// Receive complete method should return true only if the count in the first
+// two bytes is equal to the size of the rest if the buffer.
+
+TEST(TCPSocket, processReceivedData) {
+ const uint16_t PACKET_SIZE = 16382; // Amount of "real" data in the buffer
+
+ IOService service; // Used to instantiate socket
+ TCPSocket<TCPCallback> test(service); // Socket under test
+ uint8_t inbuff[PACKET_SIZE + 2]; // Buffer to check
+ OutputBufferPtr outbuff(new OutputBuffer(16));
+ // Where data is put
+ size_t expected; // Expected amount of data
+ size_t offset; // Where to put next data
+ size_t cumulative; // Cumulative data received
+
+ // Set some dummy values in the buffer to check
+ for (size_t i = 0; i < sizeof(inbuff); ++i) {
+ inbuff[i] = i % 256;
+ }
+
+ // Check that the method will handle various receive sizes.
+ writeUint16(PACKET_SIZE, inbuff, sizeof(inbuff));
+
+ cumulative = 0;
+ offset = 0;
+ expected = 0;
+ outbuff->clear();
+ bool complete = test.processReceivedData(inbuff, 1, cumulative, offset,
+ expected, outbuff);
+ EXPECT_FALSE(complete);
+ EXPECT_EQ(1, cumulative);
+ EXPECT_EQ(1, offset);
+ EXPECT_EQ(0, expected);
+ EXPECT_EQ(0, outbuff->getLength());
+
+ // Now pretend that we've received one more byte.
+ complete = test.processReceivedData(inbuff, 1, cumulative, offset, expected,
+ outbuff);
+ EXPECT_FALSE(complete);
+ EXPECT_EQ(2, cumulative);
+ EXPECT_EQ(0, offset);
+ EXPECT_EQ(PACKET_SIZE, expected);
+ EXPECT_EQ(0, outbuff->getLength());
+
+ // Add another two bytes. However, this time note that we have to offset
+ // in the input buffer because it is expected that the next chunk of data
+ // from the connection will be read into the start of the buffer.
+ complete = test.processReceivedData(inbuff + cumulative, 2, cumulative,
+ offset, expected, outbuff);
+ EXPECT_FALSE(complete);
+ EXPECT_EQ(4, cumulative);
+ EXPECT_EQ(0, offset);
+ EXPECT_EQ(PACKET_SIZE, expected);
+ EXPECT_EQ(2, outbuff->getLength());
+
+ const uint8_t* dataptr = static_cast<const uint8_t*>(outbuff->getData());
+ EXPECT_TRUE(equal(inbuff + 2, inbuff + cumulative, dataptr));
+
+ // And add the remaining data. Remember that "inbuff" is "PACKET_SIZE + 2"
+ // long.
+ complete = test.processReceivedData(inbuff + cumulative,
+ PACKET_SIZE + 2 - cumulative,
+ cumulative, offset, expected, outbuff);
+ EXPECT_TRUE(complete);
+ EXPECT_EQ(PACKET_SIZE + 2, cumulative);
+ EXPECT_EQ(0, offset);
+ EXPECT_EQ(PACKET_SIZE, expected);
+ EXPECT_EQ(PACKET_SIZE, outbuff->getLength());
+ dataptr = static_cast<const uint8_t*>(outbuff->getData());
+ EXPECT_TRUE(equal(inbuff + 2, inbuff + cumulative, dataptr));
+}
+
+/// @todo Need to add a test to check the cancel() method
+
+// Tests the operation of a TCPSocket by opening it, sending an asynchronous
+// message to a server, receiving an asynchronous message from the server and
+// closing.
+TEST(TCPSocket, sequenceTest) {
+
+ // Common objects.
+ IOService service; // Service object for async control
+
+ // The client - the TCPSocket being tested
+ TCPSocket<TCPCallback> client(service);// Socket under test
+ TCPCallback client_cb("Client"); // Async I/O callback function
+ TCPEndpoint client_remote_endpoint; // Where client receives message from
+ OutputBufferPtr client_buffer(new OutputBuffer(128));
+ // Received data is put here
+
+ // The server - with which the client communicates.
+ IOAddress server_address(SERVER_ADDRESS);
+ // Address of target server
+ TCPCallback server_cb("Server"); // Server callback
+ TCPEndpoint server_endpoint(server_address, SERVER_PORT);
+ // Endpoint describing server
+ TCPEndpoint server_remote_endpoint; // Address where server received message from
+ tcp::socket server_socket(service.get_io_service());
+ // Socket used for server
+
+ // Step 1. Create the connection between the client and the server. Set
+ // up the server to accept incoming connections and have the client open
+ // a channel to it.
+
+ // Set up server - open socket and queue an accept.
+ server_cb.queued() = TCPCallback::ACCEPT;
+ server_cb.called() = TCPCallback::NONE;
+ server_cb.setCode(42); // Some error
+ tcp::acceptor acceptor(service.get_io_service(),
+ tcp::endpoint(tcp::v4(), SERVER_PORT));
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ acceptor.async_accept(server_socket, server_cb);
+
+ // Set up client - connect to the server.
+ client_cb.queued() = TCPCallback::OPEN;
+ client_cb.called() = TCPCallback::NONE;
+ client_cb.setCode(43); // Some error
+ EXPECT_FALSE(client.isOpenSynchronous());
+ client.open(&server_endpoint, client_cb);
+
+ // Run the open and the accept callback and check that they ran.
+ service.run_one();
+ service.run_one();
+
+ EXPECT_EQ(TCPCallback::ACCEPT, server_cb.called());
+ EXPECT_EQ(0, server_cb.getCode());
+
+ EXPECT_EQ(TCPCallback::OPEN, client_cb.called());
+
+ // On some operating system the async_connect may return EINPROGRESS.
+ // This doesn't necessarily indicate an error. In most cases trying
+ // to asynchronously write and read from the socket would work just
+ // fine.
+ if ((client_cb.getCode()) != 0 && (client_cb.getCode() != EINPROGRESS)) {
+ ADD_FAILURE() << "expected error code of 0 or " << EINPROGRESS
+ << " as a result of async_connect, got " << client_cb.getCode();
+ }
+
+ // Step 2. Get the client to write to the server asynchronously. The
+ // server will loop reading the data synchronously.
+
+ // Write asynchronously to the server.
+ client_cb.called() = TCPCallback::NONE;
+ client_cb.queued() = TCPCallback::WRITE;
+ client_cb.setCode(143); // Arbitrary number
+ client_cb.length() = 0;
+ client.asyncSend(OUTBOUND_DATA, sizeof(OUTBOUND_DATA), &server_endpoint, client_cb);
+
+ // Wait for the client callback to complete. (Must do this first on
+ // Solaris: if we do the synchronous read first, the test hangs.)
+ service.run_one();
+
+ // Synchronously read the data from the server.;
+ serverRead(server_socket, server_cb);
+
+ // Check the client state
+ EXPECT_EQ(TCPCallback::WRITE, client_cb.called());
+ EXPECT_EQ(0, client_cb.getCode());
+ EXPECT_EQ(sizeof(OUTBOUND_DATA) + 2, client_cb.length());
+
+ // ... and check what the server received.
+ EXPECT_EQ(sizeof(OUTBOUND_DATA) + 2, server_cb.cumulative());
+ EXPECT_TRUE(equal(OUTBOUND_DATA,
+ (OUTBOUND_DATA + (sizeof(OUTBOUND_DATA) - 1)),
+ (server_cb.data() + 2)));
+
+ // Step 3. Get the server to write all the data asynchronously and have the
+ // client loop (asynchronously) reading the data. Note that we copy the
+ // data into the server's internal buffer in order to precede it with a two-
+ // byte count field.
+
+ // Have the server write asynchronously to the client.
+ server_cb.called() = TCPCallback::NONE;
+ server_cb.queued() = TCPCallback::WRITE;
+ server_cb.length() = 0;
+ server_cb.cumulative() = 0;
+
+ writeUint16(sizeof(INBOUND_DATA), server_cb.data(), TCPCallback::MIN_SIZE);
+ copy(INBOUND_DATA, (INBOUND_DATA + sizeof(INBOUND_DATA) - 1),
+ (server_cb.data() + 2));
+ server_socket.async_send(boost::asio::buffer(server_cb.data(),
+ (sizeof(INBOUND_DATA) + 2)),
+ server_cb);
+
+ // Have the client read asynchronously.
+ client_cb.called() = TCPCallback::NONE;
+ client_cb.queued() = TCPCallback::READ;
+ client_cb.length() = 0;
+ client_cb.cumulative() = 0;
+ client_cb.expected() = 0;
+ client_cb.offset() = 0;
+
+ client.asyncReceive(client_cb.data(), TCPCallback::MIN_SIZE,
+ client_cb.offset(), &client_remote_endpoint,
+ client_cb);
+
+ // Run the callbacks. Several options are possible depending on how ASIO
+ // is implemented and whether the message gets fragmented:
+ //
+ // 1) The send handler may complete immediately, regardless of whether the
+ // data has been read by the client. (This is the most likely.)
+ // 2) The send handler may only run after all the data has been read by
+ // the client. (This could happen if the client's TCP buffers were too
+ // small so the data was not transferred to the "remote" system until the
+ // remote buffer has been emptied one or more times.)
+ // 3) The client handler may be run a number of times to handle the message
+ // fragments and the server handler may run between calls of the client
+ // handler.
+ //
+ // So loop, running one handler at a time until we are certain that all the
+ // handlers have run.
+
+ bool server_complete = false;
+ bool client_complete = false;
+ while (!server_complete || !client_complete) {
+ service.run_one();
+
+ // Has the server run?
+ if (!server_complete) {
+ if (server_cb.called() == server_cb.queued()) {
+
+ // Yes. Check that the send completed successfully and that
+ // all the data that was expected to have been sent was in fact
+ // sent.
+ EXPECT_EQ(0, server_cb.getCode());
+ EXPECT_EQ((sizeof(INBOUND_DATA) + 2), server_cb.length());
+ server_complete = true;
+ }
+ }
+
+ // Has the client run?
+ if (!client_complete) {
+
+ if (client_cb.called() != client_cb.queued()) {
+ // No. Run the service another time.
+ continue;
+ }
+
+ // Client callback must have run. Check that it ran OK.
+ EXPECT_EQ(TCPCallback::READ, client_cb.called());
+ EXPECT_EQ(0, client_cb.getCode());
+
+ // Check if we need to queue another read, copying the data into
+ // the output buffer as we do so.
+ client_complete = client.processReceivedData(client_cb.data(),
+ client_cb.length(),
+ client_cb.cumulative(),
+ client_cb.offset(),
+ client_cb.expected(),
+ client_buffer);
+
+ // If the data is not complete, queue another read.
+ if (! client_complete) {
+ client_cb.called() = TCPCallback::NONE;
+ client_cb.queued() = TCPCallback::READ;
+ client_cb.length() = 0;
+ client.asyncReceive(client_cb.data(), TCPCallback::MIN_SIZE ,
+ client_cb.offset(), &client_remote_endpoint,
+ client_cb);
+ }
+ }
+ }
+
+ // Both the send and the receive have completed. Check that the received
+ // is what was sent.
+
+ // Check the client state
+ EXPECT_EQ(TCPCallback::READ, client_cb.called());
+ EXPECT_EQ(0, client_cb.getCode());
+ EXPECT_EQ(sizeof(INBOUND_DATA) + 2, client_cb.cumulative());
+ EXPECT_EQ(sizeof(INBOUND_DATA), client_buffer->getLength());
+
+ // ... and check what the server sent.
+ EXPECT_EQ(TCPCallback::WRITE, server_cb.called());
+ EXPECT_EQ(0, server_cb.getCode());
+ EXPECT_EQ(sizeof(INBOUND_DATA) + 2, server_cb.length());
+
+ // ... and that what was sent is what was received.
+ const uint8_t* received = static_cast<const uint8_t*>(client_buffer->getData());
+ EXPECT_TRUE(equal(INBOUND_DATA, (INBOUND_DATA + (sizeof(INBOUND_DATA) - 1)),
+ received));
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.close());
+ EXPECT_NO_THROW(server_socket.close());
+}
diff --git a/src/lib/asiolink/tests/tls_acceptor_unittest.cc b/src/lib/asiolink/tests/tls_acceptor_unittest.cc
new file mode 100644
index 0000000..d9d9ccd
--- /dev/null
+++ b/src/lib/asiolink/tests/tls_acceptor_unittest.cc
@@ -0,0 +1,450 @@
+// Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/interval_timer.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/tls_acceptor.h>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+#include <functional>
+#include <list>
+#include <netinet/in.h>
+#include <string>
+
+using namespace isc::asiolink;
+using namespace boost::asio;
+namespace ph = std::placeholders;
+
+namespace {
+
+/// @brief Local server address used for testing.
+const char SERVER_ADDRESS[] = "127.0.0.1";
+
+/// @brief Local server port used for testing.
+const unsigned short SERVER_PORT = 18123;
+
+/// @brief Test timeout in ms.
+const long TEST_TIMEOUT = 10000;
+
+/// @brief Simple class representing TCP socket callback.
+class SocketCallback {
+public:
+
+ /// @brief Implements callback for the asynchronous operation on the socket.
+ ///
+ /// This callback merely checks if error has occurred and reports this
+ /// error. It does nothing in case of success.
+ ///
+ /// @param ec Error code.
+ /// @param length Length of received data.
+ void operator()(boost::system::error_code ec, size_t length = 0) {
+ if (ec) {
+ ADD_FAILURE() << "error occurred for a socket with data of length "
+ << length << ": " << ec.message();
+ }
+ }
+
+};
+
+/// @brief Entity which can connect to the TLS server endpoint and close the
+/// connection.
+class TLSClient : public boost::noncopyable {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// This constructor creates new socket instance. It doesn't connect. Call
+ /// connect() to connect to the server.
+ ///
+ /// @param io_service IO service to be stopped on error.
+ explicit TLSClient(IOService& io_service)
+ : io_service_(io_service.get_io_service()), socket_(io_service_) {
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Closes the underlying socket if it is open.
+ ~TLSClient() {
+ close();
+ }
+
+ /// @brief Connect to the test server address and port.
+ ///
+ /// This method asynchronously connects to the server endpoint and uses the
+ /// connectHandler as a callback function.
+ void connect() {
+ ip::tcp::endpoint
+ endpoint(ip::address::from_string(SERVER_ADDRESS),
+ SERVER_PORT);
+ socket_.async_connect(endpoint,
+ std::bind(&TLSClient::connectHandler, this,
+ ph::_1));
+ }
+
+ /// @brief Callback function for connect().
+ ///
+ /// This function stops the IO service upon error.
+ ///
+ /// @param ec Error code.
+ void connectHandler(const boost::system::error_code& ec) {
+ if (ec) {
+ // One would expect that async_connect wouldn't return EINPROGRESS
+ // error code, but simply wait for the connection to get
+ // established before the handler is invoked. It turns out, however,
+ // that on some OSes the connect handler may receive this error code
+ // which doesn't necessarily indicate a problem. Making an attempt
+ // to write and read from this socket will typically succeed. So,
+ // we ignore this error.
+ if (ec.value() != error::in_progress) {
+ ADD_FAILURE() << "error occurred while connecting: "
+ << ec.message();
+ io_service_.stop();
+ }
+ }
+ }
+
+ /// @brief Close connection.
+ void close() {
+ socket_.close();
+ }
+
+private:
+
+ /// @brief Holds reference to the IO service.
+ io_service& io_service_;
+
+ /// @brief A socket used for the connection.
+ ip::tcp::socket socket_;
+
+};
+
+/// @brief Pointer to the TLSClient.
+typedef boost::shared_ptr<TLSClient> TLSClientPtr;
+
+/// @brief A signature of the function implementing callback for the
+/// TLSAcceptor.
+typedef std::function<void(const boost::system::error_code&)> TLSAcceptorCallback;
+
+/// @brief TLSAcceptor using TLSAcceptorCallback.
+typedef TLSAcceptor<TLSAcceptorCallback> TestTLSAcceptor;
+
+/// @brief Implements asynchronous TLS acceptor service.
+///
+/// It creates a new socket into which connection is accepted. The socket
+/// is retained until class instance exists.
+class Acceptor {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param io_service IO service.
+ /// @param context Pointer to TLS context.
+ /// @param acceptor Reference to the TLS acceptor on which asyncAccept
+ /// will be called.
+ /// @param callback Callback function for the asyncAccept.
+ explicit Acceptor(IOService& io_service,
+ TlsContextPtr context,
+ TestTLSAcceptor& acceptor,
+ const TLSAcceptorCallback& callback)
+ : socket_(io_service, context), acceptor_(acceptor),
+ callback_(callback) {
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Closes socket.
+ ~Acceptor() {
+ socket_.close();
+ }
+
+ /// @brief Asynchronous accept new connection.
+ void accept() {
+ acceptor_.asyncAccept(socket_, callback_);
+ }
+
+ /// @brief Close connection.
+ void close() {
+ socket_.close();
+ }
+
+private:
+
+ /// @brief Socket into which connection is accepted.
+ TLSSocket<SocketCallback> socket_;
+
+ /// @brief Reference to the TLSAcceptor on which asyncAccept is called.
+ TestTLSAcceptor& acceptor_;
+
+ /// @brief Instance of the callback used for asyncAccept.
+ TLSAcceptorCallback callback_;
+
+};
+
+/// @brief Pointer to the Acceptor object.
+typedef boost::shared_ptr<Acceptor> AcceptorPtr;
+
+/// @brief Test fixture class for TLSAcceptor.
+///
+/// This class provides means for creating new TLS connections, i.e. simulates
+/// clients connecting to the servers via TLSAcceptor. It is possible to create
+/// multiple simultaneous connections, which are retained by the test fixture
+/// class and closed cleanly when the test fixture is destroyed.
+class TLSAcceptorTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Besides initializing class members it also sets the test timer to guard
+ /// against endlessly running IO service when TLS connections are
+ /// unsuccessful.
+ TLSAcceptorTest()
+ : io_service_(), acceptor_(io_service_),
+ asio_endpoint_(ip::address::from_string(SERVER_ADDRESS),
+ SERVER_PORT),
+ endpoint_(asio_endpoint_), test_timer_(io_service_), connections_(),
+ clients_(), connections_num_(0), aborted_connections_num_(0),
+ max_connections_(1) {
+ test_timer_.setup(std::bind(&TLSAcceptorTest::timeoutHandler, this),
+ TEST_TIMEOUT, IntervalTimer::ONE_SHOT);
+ }
+
+ /// @brief Destructor.
+ virtual ~TLSAcceptorTest() {
+ }
+
+ /// @brief Specifies how many new connections are expected before the IO
+ /// service is stopped.
+ ///
+ /// @param max_connections Connections limit.
+ void setMaxConnections(const unsigned int max_connections) {
+ max_connections_ = max_connections;
+ }
+
+ /// @brief Create ASIO endpoint from the provided endpoint by retaining the
+ /// IP address and modifying the port.
+ ///
+ /// This convenience method is useful to create new endpoint from the
+ /// existing endpoint to test reusing IP address for multiple acceptors.
+ /// The returned endpoint has the same IP address but different port.
+ ///
+ /// @param endpoint Source endpoint.
+ ///
+ /// @return New endpoint with the port number increased by 1.
+ ip::tcp::endpoint
+ createSiblingEndpoint(const ip::tcp::endpoint& endpoint) const {
+ ip::tcp::endpoint endpoint_copy(endpoint);
+ endpoint_copy.port(endpoint.port() + 1);
+ return (endpoint_copy);
+ }
+
+ /// @brief Opens TLS acceptor and sets 'reuse address' option.
+ void acceptorOpen() {
+ acceptor_.open(endpoint_);
+ acceptor_.setOption(TestTLSAcceptor::ReuseAddress(true));
+ }
+
+ /// @brief Starts accepting TLS connections.
+ ///
+ /// This method creates new Acceptor instance and calls accept() to start
+ /// accepting new connections. The instance of the Acceptor object is
+ /// retained in the connections_ list.
+ void accept() {
+ TLSAcceptorCallback cb = std::bind(&TLSAcceptorTest::acceptHandler,
+ this, ph::_1);
+ TlsContextPtr ctx(new TlsContext(SERVER));
+ AcceptorPtr conn(new Acceptor(io_service_, ctx, acceptor_, cb));
+ connections_.push_back(conn);
+ connections_.back()->accept();
+ }
+
+ /// @brief Connect to the endpoint.
+ ///
+ /// This method creates TLSClient instance and retains it in the clients_
+ /// list.
+ void connect() {
+ TLSClientPtr client(new TLSClient(io_service_));
+ clients_.push_back(client);
+ clients_.back()->connect();
+ }
+
+ /// @brief Callback function for asynchronous accept calls.
+ ///
+ /// It stops the IO service upon error or when the number of accepted
+ /// connections reaches the max_connections_ value. Otherwise it calls
+ /// accept() to start accepting next connections.
+ ///
+ /// @param ec Error code.
+ void acceptHandler(const boost::system::error_code& ec) {
+ if (ec) {
+ if (ec.value() != error::operation_aborted) {
+ ADD_FAILURE() << "error occurred while accepting connection: "
+ << ec.message();
+ } else {
+ ++aborted_connections_num_;
+ }
+ io_service_.stop();
+ }
+
+ // We have reached the maximum number of connections - end the test.
+ if (++connections_num_ >= max_connections_) {
+ io_service_.stop();
+ return;
+ }
+
+ accept();
+ }
+
+ /// @brief Callback function invoke upon test timeout.
+ ///
+ /// It stops the IO service and reports test timeout.
+ void timeoutHandler() {
+ ADD_FAILURE() << "Timeout occurred while running the test!";
+ io_service_.stop();
+ }
+
+ /// @brief IO service.
+ IOService io_service_;
+
+ /// @brief TLSAcceptor under test.
+ TestTLSAcceptor acceptor_;
+
+ /// @brief Server endpoint.
+ ip::tcp::endpoint asio_endpoint_;
+
+ /// @brief asiolink server endpoint (uses asio_endpoint_).
+ TCPEndpoint endpoint_;
+
+ /// @brief Asynchronous timer service to detect timeouts.
+ IntervalTimer test_timer_;
+
+ /// @brief List of connections on the server side.
+ std::list<AcceptorPtr> connections_;
+
+ /// @brief List of client connections.
+ std::list<TLSClientPtr> clients_;
+
+ /// @brief Current number of established connections.
+ unsigned int connections_num_;
+
+ /// @brief Current number of aborted connections.
+ unsigned int aborted_connections_num_;
+
+ /// @brief Connections limit.
+ unsigned int max_connections_;
+};
+
+// Test TLSAcceptor::asyncAccept.
+TEST_F(TLSAcceptorTest, asyncAccept) {
+ // Establish up to 10 connections.
+ setMaxConnections(10);
+
+ // Initialize acceptor.
+ acceptorOpen();
+ acceptor_.bind(endpoint_);
+ acceptor_.listen();
+
+ // Start accepting new connections.
+ accept();
+
+ // Create 10 new TLS connections (client side).
+ for (unsigned int i = 0; i < 10; ++i) {
+ connect();
+ }
+
+ // Run the IO service until we have accepted 10 connections, an error
+ // or test timeout occurred.
+ io_service_.run();
+
+ // Make sure that all accepted connections have been recorded.
+ EXPECT_EQ(10, connections_num_);
+ EXPECT_EQ(10, connections_.size());
+}
+
+// Check that it is possible to set SO_REUSEADDR flag for the TLSAcceptor.
+TEST_F(TLSAcceptorTest, reuseAddress) {
+ // We need at least two acceptors using common address. Let's create the
+ // second endpoint which has the same address but different port.
+ ip::tcp::endpoint asio_endpoint2(createSiblingEndpoint(asio_endpoint_));
+ TCPEndpoint endpoint2(asio_endpoint2);
+
+ // Create and open two acceptors.
+ TestTLSAcceptor acceptor1(io_service_);
+ TestTLSAcceptor acceptor2(io_service_);
+ ASSERT_NO_THROW(acceptor1.open(endpoint_));
+ ASSERT_NO_THROW(acceptor2.open(endpoint2));
+
+ // Set SO_REUSEADDR socket option so as acceptors can bind to the
+ /// same address.
+ ASSERT_NO_THROW(
+ acceptor1.setOption(TestTLSAcceptor::ReuseAddress(true))
+ );
+ ASSERT_NO_THROW(
+ acceptor2.setOption(TestTLSAcceptor::ReuseAddress(true))
+ );
+ ASSERT_NO_THROW(acceptor1.bind(endpoint_));
+ ASSERT_NO_THROW(acceptor2.bind(endpoint2));
+
+ // Create third acceptor, but don't set the SO_REUSEADDR. It should
+ // refuse to bind.
+ TCPEndpoint endpoint3(createSiblingEndpoint(asio_endpoint2));
+ TestTLSAcceptor acceptor3(io_service_);
+ ASSERT_NO_THROW(acceptor3.open(endpoint3));
+ EXPECT_THROW(acceptor3.bind(endpoint_), boost::system::system_error);
+}
+
+// Test that TLSAcceptor::getProtocol returns IPPROTO_TCP.
+TEST_F(TLSAcceptorTest, getProtocol) {
+ EXPECT_EQ(IPPROTO_TCP, acceptor_.getProtocol());
+}
+
+// Test that TLSAcceptor::getNative returns valid socket descriptor.
+TEST_F(TLSAcceptorTest, getNative) {
+ // Initially the descriptor should be invalid (negative).
+ ASSERT_LT(acceptor_.getNative(), 0);
+ // Now open the socket and make sure the returned descriptor is now valid.
+ ASSERT_NO_THROW(acceptorOpen());
+ EXPECT_GE(acceptor_.getNative(), 0);
+}
+
+// macOS 10.12.3 has a bug which causes the connections to not enter
+// the TIME-WAIT state and they never get closed.
+#if !defined (OS_OSX)
+
+// Test that TLSAcceptor::close works properly.
+TEST_F(TLSAcceptorTest, close) {
+ // Initialize acceptor.
+ acceptorOpen();
+ acceptor_.bind(endpoint_);
+ acceptor_.listen();
+
+ // Start accepting new connections.
+ accept();
+
+ // Create 10 new TLS connections (client side).
+ for (unsigned int i = 0; i < 10; ++i) {
+ connect();
+ }
+
+ // Close the acceptor before connections are accepted.
+ acceptor_.close();
+
+ // Run the IO service.
+ io_service_.run();
+
+ // The connections should have been aborted.
+ EXPECT_EQ(1, connections_num_);
+ EXPECT_EQ(1, aborted_connections_num_);
+ EXPECT_EQ(1, connections_.size());
+}
+
+#endif
+
+}
diff --git a/src/lib/asiolink/tests/tls_socket_unittest.cc b/src/lib/asiolink/tests/tls_socket_unittest.cc
new file mode 100644
index 0000000..c2572c0
--- /dev/null
+++ b/src/lib/asiolink/tests/tls_socket_unittest.cc
@@ -0,0 +1,560 @@
+// Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @brief Test of TLSSocket
+///
+/// Tests the functionality of a TLSSocket by working through an open-send-
+/// receive-close sequence and checking that the asynchronous notifications
+/// work.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/tls_socket.h>
+#include <asiolink/testutils/test_tls.h>
+#include <util/buffer.h>
+#include <util/io_utilities.h>
+
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <arpa/inet.h>
+#include <cstddef>
+#include <cstdlib>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string>
+#include <vector>
+
+using namespace boost::asio;
+using namespace boost::asio::ip;
+using namespace isc::util;
+using namespace isc::asiolink;
+using namespace std;
+
+namespace {
+
+const char SERVER_ADDRESS[] = "127.0.0.1";
+const unsigned short SERVER_PORT = 5303;
+
+/// @todo Shouldn't we send something that is real message?
+const char OUTBOUND_DATA[] = "Data sent from client to server";
+const char INBOUND_DATA[] = "Returned data from server to client";
+}
+
+/// An instance of this object is passed to the asynchronous I/O functions
+/// and the operator() method is called when when an asynchronous I/O completes.
+/// The arguments to the completion callback are stored for later retrieval.
+class TLSCallback {
+public:
+ /// @brief Operations the server is doing
+ enum Operation {
+ ACCEPT = 0, ///< accept() was issued
+ OPEN = 1, ///< Client connected to server
+ HANDSHAKE = 2, ///< TLS handshake completed
+ READ = 3, ///< Asynchronous read completed
+ WRITE = 4, ///< Asynchronous write completed
+ NONE = 5 ///< "Not set" state
+ };
+
+ /// @brief Minimum size of buffers
+ enum {
+ MIN_SIZE = (64 * 1024 + 2) ///< 64kB + two bytes for a count
+ };
+
+ struct PrivateData {
+ PrivateData() :
+ error_code_(), length_(0), cumulative_(0), expected_(0), offset_(0),
+ name_(""), queued_(NONE), called_(NONE), data_(MIN_SIZE, 0)
+ {}
+
+ boost::system::error_code error_code_; ///< Completion error code
+ size_t length_; ///< Bytes transferred in this I/O
+ size_t cumulative_; ///< Cumulative bytes transferred
+ size_t expected_; ///< Expected amount of data
+ size_t offset_; ///< Where to put data in buffer
+ std::string name_; ///< Which of the objects this is
+ Operation queued_; ///< Queued operation
+ Operation called_; ///< Which callback called
+ std::vector<uint8_t> data_; ///< Receive buffer
+ };
+
+ /// @brief Constructor
+ ///
+ /// Constructs the object. It also creates the data member pointed to by
+ /// a shared pointer. When used as a callback object, this is copied as it
+ /// is passed into the asynchronous function. This means that there are two
+ /// objects and inspecting the one we passed in does not tell us anything.
+ ///
+ /// Therefore we use a boost::shared_ptr. When the object is copied, the
+ /// shared pointer is copied, which leaves both objects pointing to the same
+ /// data.
+ ///
+ /// @param which Which of the two callback objects this is
+ TLSCallback(std::string which) : ptr_(new PrivateData())
+ {
+ ptr_->name_ = which;
+ }
+
+ /// @brief Destructor
+ ///
+ /// No code needed, destroying the shared pointer destroys the private data.
+ virtual ~TLSCallback()
+ {}
+
+ /// @brief Client Callback Function
+ ///
+ /// Called when an asynchronous operation is completed by the client, this
+ /// stores the origin of the operation in the client_called_ data member.
+ ///
+ /// @param ec I/O completion error code passed to callback function.
+ /// @param length Number of bytes transferred
+ void operator()(boost::system::error_code ec = boost::system::error_code(),
+ size_t length = 0)
+ {
+ setCode(ec.value());
+ ptr_->called_ = ptr_->queued_;
+ ptr_->length_ = length;
+ }
+
+ /// @brief Get I/O completion error code
+ int getCode() {
+ return (ptr_->error_code_.value());
+ }
+
+ /// @brief Set I/O completion code
+ ///
+ /// @param code New value of completion code
+ void setCode(int code) {
+ ptr_->error_code_ = boost::system::error_code(code, boost::system::error_code().category());
+ }
+
+ /// @brief Get number of bytes transferred in I/O
+ size_t& length() {
+ return (ptr_->length_);
+ }
+
+ /// @brief Get cumulative number of bytes transferred in I/O
+ size_t& cumulative() {
+ return (ptr_->cumulative_);
+ }
+
+ /// @brief Get expected amount of data
+ size_t& expected() {
+ return (ptr_->expected_);
+ }
+
+ /// @brief Get offset into data
+ size_t& offset() {
+ return (ptr_->offset_);
+ }
+
+ /// @brief Get data member
+ uint8_t* data() {
+ return (&ptr_->data_[0]);
+ }
+
+ /// @brief Get flag to say what was queued
+ Operation& queued() {
+ return (ptr_->queued_);
+ }
+
+ /// @brief Get flag to say when callback was called
+ Operation& called() {
+ return (ptr_->called_);
+ }
+
+ /// @brief Return instance of callback name
+ std::string& name() {
+ return (ptr_->name_);
+ }
+
+private:
+ boost::shared_ptr<PrivateData> ptr_; ///< Pointer to private data
+};
+
+
+// Read Server Data
+//
+// Called in the part of the test that has the client send a message to the
+// server, this loops until all the data has been read (synchronously) by the
+// server.
+//
+// "All the data read" means that the server has received a message that is
+// preceded by a two-byte count field and that the total amount of data received
+// from the remote end is equal to the value in the count field plus two bytes
+// for the count field itself.
+//
+// @param stream Stream on which the server is reading data
+// @param server_cb Structure in which server data is held.
+void
+serverRead(TlsStreamImpl& stream, TLSCallback& server_cb) {
+
+ // As we may need to read multiple times, keep a count of the cumulative
+ // amount of data read and do successive reads into the appropriate part
+ // of the buffer.
+ //
+ // Note that there are no checks for buffer overflow - this is a test
+ // program and we have sized the buffer to be large enough for the test.
+ server_cb.cumulative() = 0;
+
+ bool complete = false;
+ while (!complete) {
+
+ // Read block of data and update cumulative amount of data received.
+ server_cb.length() = stream.read_some(
+ boost::asio::buffer(server_cb.data() + server_cb.cumulative(),
+ TLSCallback::MIN_SIZE - server_cb.cumulative()));
+ server_cb.cumulative() += server_cb.length();
+
+ // If we have read at least two bytes, we can work out how much we
+ // should be reading.
+ if (server_cb.cumulative() >= 2) {
+ server_cb.expected() = readUint16(server_cb.data(), server_cb.length());
+ if ((server_cb.expected() + 2) == server_cb.cumulative()) {
+
+ // Amount of data read from stream equals the size of the
+ // message (as indicated in the first two bytes of the message)
+ // plus the size of the count field. Therefore we have received
+ // all the data.
+ complete = true;
+ }
+ }
+ }
+}
+
+// Receive complete method should return true only if the count in the first
+// two bytes is equal to the size of the rest if the buffer.
+
+TEST(TLSSocket, processReceivedData) {
+ // Amount of "real" data in the buffer
+ const uint16_t PACKET_SIZE = 16382;
+
+ // Used to instantiate socket
+ IOService service;
+ TlsContextPtr context(new TlsContext(CLIENT));
+ // Socket under test
+ TLSSocket<TLSCallback> test(service, context);
+ // Buffer to check
+ uint8_t inbuff[PACKET_SIZE + 2];
+ // Where data is put
+ OutputBufferPtr outbuff(new OutputBuffer(16));
+ // Expected amount of data
+ size_t expected;
+ // Where to put next data
+ size_t offset;
+ // Cumulative data received
+ size_t cumulative;
+
+ // Set some dummy values in the buffer to check
+ for (size_t i = 0; i < sizeof(inbuff); ++i) {
+ inbuff[i] = i % 256;
+ }
+
+ // Check that the method will handle various receive sizes.
+ writeUint16(PACKET_SIZE, inbuff, sizeof(inbuff));
+
+ cumulative = 0;
+ offset = 0;
+ expected = 0;
+ outbuff->clear();
+ bool complete = test.processReceivedData(inbuff, 1, cumulative, offset,
+ expected, outbuff);
+ EXPECT_FALSE(complete);
+ EXPECT_EQ(1, cumulative);
+ EXPECT_EQ(1, offset);
+ EXPECT_EQ(0, expected);
+ EXPECT_EQ(0, outbuff->getLength());
+
+ // Now pretend that we've received one more byte.
+ complete = test.processReceivedData(inbuff, 1, cumulative, offset, expected,
+ outbuff);
+ EXPECT_FALSE(complete);
+ EXPECT_EQ(2, cumulative);
+ EXPECT_EQ(0, offset);
+ EXPECT_EQ(PACKET_SIZE, expected);
+ EXPECT_EQ(0, outbuff->getLength());
+
+ // Add another two bytes. However, this time note that we have to offset
+ // in the input buffer because it is expected that the next chunk of data
+ // from the connection will be read into the start of the buffer.
+ complete = test.processReceivedData(inbuff + cumulative, 2, cumulative,
+ offset, expected, outbuff);
+ EXPECT_FALSE(complete);
+ EXPECT_EQ(4, cumulative);
+ EXPECT_EQ(0, offset);
+ EXPECT_EQ(PACKET_SIZE, expected);
+ EXPECT_EQ(2, outbuff->getLength());
+
+ const uint8_t* dataptr = static_cast<const uint8_t*>(outbuff->getData());
+ EXPECT_TRUE(equal(inbuff + 2, inbuff + cumulative, dataptr));
+
+ // And add the remaining data. Remember that "inbuff" is "PACKET_SIZE + 2"
+ // long.
+ complete = test.processReceivedData(inbuff + cumulative,
+ PACKET_SIZE + 2 - cumulative,
+ cumulative, offset, expected, outbuff);
+ EXPECT_TRUE(complete);
+ EXPECT_EQ(PACKET_SIZE + 2, cumulative);
+ EXPECT_EQ(0, offset);
+ EXPECT_EQ(PACKET_SIZE, expected);
+ EXPECT_EQ(PACKET_SIZE, outbuff->getLength());
+ dataptr = static_cast<const uint8_t*>(outbuff->getData());
+ EXPECT_TRUE(equal(inbuff + 2, inbuff + cumulative, dataptr));
+}
+
+/// @todo Need to add a test to check the cancel() method
+
+// Tests the operation of a TLSSocket by opening it, sending an asynchronous
+// message to a server, receiving an asynchronous message from the server and
+// closing.
+TEST(TLSSocket, sequenceTest) {
+
+ // Common objects.
+ // Service object for async control
+ IOService service;
+
+ // The client - the TLSSocket being tested
+ TlsContextPtr client_ctx;
+ test::configClient(client_ctx);
+ // Socket under test
+ TLSSocket<TLSCallback> client(service, client_ctx);
+ // Async I/O callback function
+ TLSCallback client_cb("Client");
+ // Where client receives message from
+ TCPEndpoint client_remote_endpoint;
+ // Received data is put here
+ OutputBufferPtr client_buffer(new OutputBuffer(128));
+ // The server - with which the client communicates.
+ // Address of target server
+ IOAddress server_address(SERVER_ADDRESS);
+ // Server callback
+ TLSCallback server_cb("Server");
+ // Endpoint describing server
+ TCPEndpoint server_endpoint(server_address, SERVER_PORT);
+ // Address where server received message from
+ TCPEndpoint server_remote_endpoint;
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ // Stream used for server.
+ TlsStreamImpl server(service.get_io_service(), server_ctx->getContext());
+
+ // Step 1. Create the connection between the client and the server. Set
+ // up the server to accept incoming connections and have the client open
+ // a channel to it.
+
+ // Set up server - open socket and queue an accept.
+ server_cb.queued() = TLSCallback::ACCEPT;
+ server_cb.called() = TLSCallback::NONE;
+ server_cb.setCode(42); // Some error
+ tcp::acceptor acceptor(service.get_io_service(),
+ tcp::endpoint(tcp::v4(), SERVER_PORT));
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ acceptor.async_accept(server.lowest_layer(), server_cb);
+
+ // Set up client - connect to the server.
+ client_cb.queued() = TLSCallback::OPEN;
+ client_cb.called() = TLSCallback::NONE;
+ client_cb.setCode(43); // Some error
+ EXPECT_FALSE(client.isOpenSynchronous());
+ client.open(&server_endpoint, client_cb);
+
+ // Run the open and the accept callback and check that they ran.
+ while ((server_cb.called() == TLSCallback::NONE) ||
+ (client_cb.called() == TLSCallback::NONE)) {
+ service.run_one();
+ }
+ EXPECT_EQ(TLSCallback::ACCEPT, server_cb.called());
+ EXPECT_EQ(0, server_cb.getCode());
+
+ EXPECT_EQ(TLSCallback::OPEN, client_cb.called());
+
+ // On some operating system the async_connect may return EINPROGRESS.
+ // This doesn't necessarily indicate an error. In most cases trying
+ // to asynchronously write and read from the socket would work just
+ // fine.
+ if ((client_cb.getCode()) != 0 && (client_cb.getCode() != EINPROGRESS)) {
+ ADD_FAILURE() << "expected error code of 0 or " << EINPROGRESS
+ << " as a result of async_connect, got " << client_cb.getCode();
+ }
+
+ // Perform handshake.
+ client_cb.queued() = TLSCallback::HANDSHAKE;
+ client_cb.called() = TLSCallback::NONE;
+ client_cb.setCode(43); // Some error
+ client.handshake(client_cb);
+
+ server_cb.queued() = TLSCallback::HANDSHAKE;
+ server_cb.called() = TLSCallback::NONE;
+ server_cb.setCode(42); // Some error
+ server.async_handshake(roleToImpl(TlsRole::SERVER), server_cb);
+
+ while ((server_cb.called() == TLSCallback::NONE) ||
+ (client_cb.called() == TLSCallback::NONE)) {
+ service.run_one();
+ }
+ EXPECT_EQ(TLSCallback::HANDSHAKE, client_cb.called());
+ EXPECT_EQ(0, client_cb.getCode());
+
+ EXPECT_EQ(TLSCallback::HANDSHAKE, server_cb.called());
+ EXPECT_EQ(0, server_cb.getCode());
+
+ // Step 2. Get the client to write to the server asynchronously. The
+ // server will loop reading the data synchronously.
+
+ // Write asynchronously to the server.
+ client_cb.called() = TLSCallback::NONE;
+ client_cb.queued() = TLSCallback::WRITE;
+ client_cb.setCode(143); // Arbitrary number
+ client_cb.length() = 0;
+ client.asyncSend(OUTBOUND_DATA, sizeof(OUTBOUND_DATA), &server_endpoint, client_cb);
+
+ // Wait for the client callback to complete. (Must do this first on
+ // Solaris: if we do the synchronous read first, the test hangs.)
+ while (client_cb.called() == TLSCallback::NONE) {
+ service.run_one();
+ }
+
+ // Synchronously read the data from the server.;
+ serverRead(server, server_cb);
+
+ // Check the client state
+ EXPECT_EQ(TLSCallback::WRITE, client_cb.called());
+ EXPECT_EQ(0, client_cb.getCode());
+ EXPECT_EQ(sizeof(OUTBOUND_DATA) + 2, client_cb.length());
+
+ // ... and check what the server received.
+ EXPECT_EQ(sizeof(OUTBOUND_DATA) + 2, server_cb.cumulative());
+ EXPECT_TRUE(equal(OUTBOUND_DATA,
+ (OUTBOUND_DATA + (sizeof(OUTBOUND_DATA) - 1)),
+ (server_cb.data() + 2)));
+
+ // Step 3. Get the server to write all the data asynchronously and have the
+ // client loop (asynchronously) reading the data. Note that we copy the
+ // data into the server's internal buffer in order to precede it with a two-
+ // byte count field.
+
+ // Have the server write asynchronously to the client.
+ server_cb.called() = TLSCallback::NONE;
+ server_cb.queued() = TLSCallback::WRITE;
+ server_cb.length() = 0;
+ server_cb.cumulative() = 0;
+
+ writeUint16(sizeof(INBOUND_DATA), server_cb.data(), TLSCallback::MIN_SIZE);
+ copy(INBOUND_DATA, (INBOUND_DATA + sizeof(INBOUND_DATA) - 1),
+ (server_cb.data() + 2));
+ boost::asio::async_write(server,
+ boost::asio::buffer(server_cb.data(),
+ (sizeof(INBOUND_DATA) + 2)),
+ server_cb);
+
+ // Have the client read asynchronously.
+ client_cb.called() = TLSCallback::NONE;
+ client_cb.queued() = TLSCallback::READ;
+ client_cb.length() = 0;
+ client_cb.cumulative() = 0;
+ client_cb.expected() = 0;
+ client_cb.offset() = 0;
+
+ client.asyncReceive(client_cb.data(), TLSCallback::MIN_SIZE,
+ client_cb.offset(), &client_remote_endpoint,
+ client_cb);
+
+ // Run the callbacks. Several options are possible depending on how ASIO
+ // is implemented and whether the message gets fragmented:
+ //
+ // 1) The send handler may complete immediately, regardless of whether the
+ // data has been read by the client. (This is the most likely.)
+ // 2) The send handler may only run after all the data has been read by
+ // the client. (This could happen if the client's TCP buffers were too
+ // small so the data was not transferred to the "remote" system until the
+ // remote buffer has been emptied one or more times.)
+ // 3) The client handler may be run a number of times to handle the message
+ // fragments and the server handler may run between calls of the client
+ // handler.
+ //
+ // So loop, running one handler at a time until we are certain that all the
+ // handlers have run.
+
+ bool server_complete = false;
+ bool client_complete = false;
+ while (!server_complete || !client_complete) {
+ service.run_one();
+
+ // Has the server run?
+ if (!server_complete) {
+ if (server_cb.called() != TLSCallback::NONE) {
+
+ // Yes. Check that the send completed successfully and that
+ // all the data that was expected to have been sent was in fact
+ // sent.
+ EXPECT_EQ(TLSCallback::WRITE, server_cb.called());
+ EXPECT_EQ(0, server_cb.getCode());
+ EXPECT_EQ((sizeof(INBOUND_DATA) + 2), server_cb.length());
+ server_complete = true;
+ }
+ }
+
+ // Has the client run?
+ if (!client_complete) {
+
+ if (client_cb.called() == TLSCallback::NONE) {
+ // No. Run the service another time.
+ continue;
+ }
+
+ // Client callback must have run. Check that it ran OK.
+ EXPECT_EQ(TLSCallback::READ, client_cb.called());
+ EXPECT_EQ(0, client_cb.getCode());
+
+ // Check if we need to queue another read, copying the data into
+ // the output buffer as we do so.
+ client_complete = client.processReceivedData(client_cb.data(),
+ client_cb.length(),
+ client_cb.cumulative(),
+ client_cb.offset(),
+ client_cb.expected(),
+ client_buffer);
+
+ // If the data is not complete, queue another read.
+ if (!client_complete) {
+ client_cb.called() = TLSCallback::NONE;
+ client_cb.queued() = TLSCallback::READ;
+ client_cb.length() = 0;
+ client.asyncReceive(client_cb.data(), TLSCallback::MIN_SIZE ,
+ client_cb.offset(), &client_remote_endpoint,
+ client_cb);
+ }
+ }
+ }
+
+ // Both the send and the receive have completed. Check that the received
+ // is what was sent.
+
+ // Check the client state
+ EXPECT_EQ(TLSCallback::READ, client_cb.called());
+ EXPECT_EQ(0, client_cb.getCode());
+ EXPECT_EQ(sizeof(INBOUND_DATA) + 2, client_cb.cumulative());
+ EXPECT_EQ(sizeof(INBOUND_DATA), client_buffer->getLength());
+
+ // ... and check what the server sent.
+ EXPECT_EQ(TLSCallback::WRITE, server_cb.called());
+ EXPECT_EQ(0, server_cb.getCode());
+ EXPECT_EQ(sizeof(INBOUND_DATA) + 2, server_cb.length());
+
+ // ... and that what was sent is what was received.
+ const uint8_t* received = static_cast<const uint8_t*>(client_buffer->getData());
+ EXPECT_TRUE(equal(INBOUND_DATA, (INBOUND_DATA + (sizeof(INBOUND_DATA) - 1)),
+ received));
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
diff --git a/src/lib/asiolink/tests/tls_unittest.cc b/src/lib/asiolink/tests/tls_unittest.cc
new file mode 100644
index 0000000..35fc8c4
--- /dev/null
+++ b/src/lib/asiolink/tests/tls_unittest.cc
@@ -0,0 +1,2695 @@
+// Copyright (C) 2021-2023 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_service.h>
+#include <asiolink/interval_timer.h>
+#include <asiolink/crypto_tls.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/testutils/test_tls.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <cstdlib>
+#include <list>
+#include <string>
+#include <vector>
+
+#ifdef WITH_OPENSSL
+#include <openssl/opensslv.h>
+#endif
+
+using namespace boost::asio;
+using namespace boost::asio::ip;
+using namespace isc::asiolink;
+using namespace isc::cryptolink;
+using namespace std;
+
+namespace { // anonymous namespace.
+
+/// @brief Local server address used for testing.
+const char SERVER_ADDRESS[] = "127.0.0.1";
+
+/// @brief Local server port used for testing.
+const unsigned short SERVER_PORT = 18123;
+
+/// @brief Name of the environment variable controlling the display
+/// (default off) of TLS error messages.
+const char KEA_TLS_CHECK_VERBOSE[] = "KEA_TLS_CHECK_VERBOSE";
+
+/// @brief Test TLS context class exposing protected methods.
+class TestTlsContext : public TlsContext {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param role The TLS role client or server.
+ explicit TestTlsContext(TlsRole role) : TlsContext(role) { }
+
+ /// @brief Destructor.
+ virtual ~TestTlsContext() { }
+
+ /// @brief Make protected methods visible in tests.
+ using TlsContext::setCertRequired;
+ using TlsContext::loadCaFile;
+ using TlsContext::loadCaPath;
+ using TlsContext::loadCertFile;
+ using TlsContext::loadKeyFile;
+};
+
+/// @brief Class of test callbacks.
+class TestCallback {
+public:
+ /// @brief State part.
+ class State {
+ public:
+ /// @brief Constructor.
+ State() : called_(false), error_code_() {
+ }
+
+ /// @brief Destructor.
+ virtual ~State() {
+ }
+
+ /// @brief Called flag.
+ ///
+ /// Initialized to false, set to true when the callback is called.
+ bool called_;
+
+ /// @brief Last error code.
+ boost::system::error_code error_code_;
+ };
+
+ /// @brief Constructor.
+ ///
+ /// Used to shared pointer to state to allow the callback object to
+ /// be copied keeping the state member values.
+ TestCallback()
+ : state_(new State()), tcpp_(0) {
+ }
+
+ /// @brief Close on error constructor.
+ ///
+ /// An overload which takes the stream to close on error.
+ ///
+ /// @param tcpp Pointer to the stream to close on error.
+ TestCallback(TlsStream<TestCallback>::lowest_layer_type* tcpp)
+ : state_(new State()), tcpp_(tcpp) {
+ }
+
+ /// @brief Destructor.
+ virtual ~TestCallback() {
+ }
+
+ /// @brief Callback function (one argument).
+ ///
+ /// @parame ec Boost completion code.
+ void operator()(const boost::system::error_code& ec) {
+ state_->called_ = true;
+ state_->error_code_ = ec;
+ if (ec && tcpp_) {
+ tcpp_->close();
+ }
+ }
+
+ /// @brief Callback function (two arguments).
+ ///
+ /// @parame ec Boost completion code.
+ void operator()(const boost::system::error_code& ec, size_t) {
+ state_->called_ = true;
+ state_->error_code_ = ec;
+ if (ec && tcpp_) {
+ tcpp_->close();
+ }
+ }
+
+ /// @brief Get called value.
+ inline bool getCalled() const {
+ return (state_->called_);
+ }
+
+ /// @brief Get error code.
+ inline const boost::system::error_code& getCode() const {
+ return (state_->error_code_);
+ }
+
+protected:
+ /// @brief Pointer to state.
+ boost::shared_ptr<State> state_;
+
+ /// @brief Pointer to the stream to close on error.
+ TlsStream<TestCallback>::lowest_layer_type* tcpp_;
+};
+
+/// @brief The type of a test to be run.
+typedef function<void()> Test;
+
+/// @brief Class of an expected behavior.
+///
+/// Some TLS tests can not use the standard GTEST macros because they
+/// show different behaviors depending on the crypto backend and the
+/// boost library versions. Worse in some cases the behavior can not
+/// be deduced from them so #ifdef's do not work...
+///
+/// Until this is adopted / widespread the policy is to use these flexible
+/// expected behavior tests ONLY when needed.
+class Expected {
+private:
+ /// Constructor.
+ ///
+ /// @param throwing True when an exception should be thrown.
+ /// @param timeout True when a timeout should occur.
+ /// @param no_error True when no error should be returned.
+ /// @param message Expected message.
+ Expected(bool throwing, bool timeout, bool no_error,
+ const string& message)
+ : throwing_(throwing), timeout_(timeout), no_error_(no_error),
+ message_(message) {
+ }
+
+ /// @brief The throwing flag.
+ bool throwing_;
+
+ /// @brief The timeout flag.
+ bool timeout_;
+
+ /// @brief The no error flag.
+ bool no_error_;
+
+ /// @brief The expected error message.
+ string message_;
+
+public:
+ /// @brief Create an expected throwing exception behavior.
+ ///
+ /// @param message Expected message.
+ static Expected createThrow(const string& message) {
+ return (Expected(true, false, false, message));
+ }
+
+ /// @brief Create an expected timeout behavior.
+ static Expected createTimeout() {
+ return (Expected(false, true, false, ""));
+ }
+
+ /// @brief Create an expected no error behavior.
+ static Expected createNoError() {
+ return (Expected(false, false, true, ""));
+ }
+
+ /// @brief Create an expected error message behavior.
+ ///
+ /// @param message Expected message.
+ static Expected createError(const string& message) {
+ return (Expected(false, false, false, message));
+ }
+
+ /// @brief Get the throwing flag.
+ ///
+ /// @return The throwing flag.
+ bool getThrow() const {
+ return (throwing_);
+ }
+
+ /// @brief Get the timeout flag.
+ ///
+ /// @return The timeout flag.
+ bool getTimeout() const {
+ return (timeout_);
+ }
+
+ /// @brief Get the no error flag.
+ ///
+ /// @return The no error flag.
+ bool getNoError() const {
+ return (no_error_);
+ }
+
+ /// @brief Get the expected error message.
+ ///
+ /// @return The expected error message.
+ const string& getMessage() const {
+ return (message_);
+ }
+};
+
+/// @brief Class of expected behaviors.
+class Expecteds {
+private:
+ /// @brief List of expected behaviors.
+ list<Expected> list_;
+
+ /// @brief The error message for the verbose mode.
+ string errmsg_;
+
+public:
+ /// Constructor.
+ ///
+ /// @return An empty expected behavior list.
+ Expecteds() : list_(), errmsg_("") {
+ }
+
+ /// @brief Clear the list.
+ void clear() {
+ list_.clear();
+ errmsg_.clear();
+ }
+
+ /// @brief Add an expected throwing exception behavior.
+ ///
+ /// @param message Expected message.
+ void addThrow(const string& message) {
+ list_.push_back(Expected::createThrow(message));
+ }
+
+ /// @brief Add an expected timeout behavior.
+ void addTimeout() {
+ list_.push_back(Expected::createTimeout());
+ }
+
+ /// @brief Add an expected no error behavior.
+ void addNoError() {
+ list_.push_back(Expected::createNoError());
+ }
+
+ /// @brief Add an expected error message behavior.
+ ///
+ /// @param message Expected message.
+ void addError(const string& message) {
+ list_.push_back(Expected::createError(message));
+ }
+
+ /// @brief Display error messages.
+ ///
+ /// @return True if error messages are displayed.
+ static bool displayErrMsg() {
+ return (getenv(KEA_TLS_CHECK_VERBOSE));
+ }
+
+ /// @brief Has an error message.
+ ///
+ /// @return True when there is a cached error message.
+ bool hasErrMsg() const {
+ return (!errmsg_.empty());
+ }
+
+ /// @brief Get error message.
+ ///
+ /// @return The cached error message.
+ const string& getErrMsg() const {
+ return (errmsg_);
+ }
+
+ /// @brief Run a test which can throw.
+ ///
+ /// @param test The test to run.
+ void runCanThrow(const Test& test) {
+ // Check consistency.
+ for (auto const& exp : list_) {
+ if (!exp.getThrow() && !exp.getNoError()) {
+ ADD_FAILURE() << "inconsistent runCanThrow settings";
+ }
+ }
+
+ // Collect the test behavior.
+ bool thrown = false;
+ try {
+ test();
+ } catch (const LibraryError& ex) {
+ thrown = true;
+ errmsg_ = ex.what();
+ } catch (const isc::BadValue& ex) {
+ thrown = true;
+ errmsg_ = ex.what();
+ } catch (const exception& ex) {
+ thrown = true;
+ errmsg_ = ex.what();
+ ADD_FAILURE() << "expect only LibraryError or BadValue exception";
+ }
+
+ // Check the no error case.
+ if (!thrown) {
+ for (auto const& exp : list_) {
+ if (exp.getNoError()) {
+ // No error was expected: good.
+ return;
+ }
+ }
+ // No error was not expected: bad.
+ ADD_FAILURE() << "no exception?";
+ return;
+ }
+
+ // Check the thrown message.
+ for (auto const& exp : list_) {
+ if (!exp.getThrow()) {
+ continue;
+ }
+ if (errmsg_ == exp.getMessage()) {
+ // Got an expected message: good.
+ return;
+ }
+ }
+ // The message was not expected: bad.
+ ADD_FAILURE() << "exception with unknown '" << errmsg_ << "'";
+ }
+
+ /// @brief Check the result of an asynchronous operation.
+ ///
+ /// @param party The name of the party.
+ /// @param callback The test callback of the an asynchronous.
+ void checkAsync(const string& party, const TestCallback& callback) {
+ // Check timeout i.e. the callback was not called.
+ if (!callback.getCalled()) {
+ bool expected = false;
+ for (auto const& exp : list_) {
+ if (exp.getTimeout()) {
+ expected = true;
+ break;
+ }
+ }
+ if (!expected) {
+ ADD_FAILURE() << "unexpected timeout";
+ }
+ }
+
+ // Check the no error case.
+ const boost::system::error_code& ec = callback.getCode();
+ if (!ec) {
+ for (auto const& exp : list_) {
+ if (exp.getTimeout() || exp.getNoError()) {
+ // Expected timeout or no error: good.
+ return;
+ }
+ }
+ // Should have failed but did not: bad.
+ ADD_FAILURE() << party << " did not failed as expected";
+ return;
+ }
+
+ // Got an error but was this one expected?
+ errmsg_ = ec.message();
+ for (auto const& exp : list_) {
+ if (exp.getTimeout() || exp.getNoError()) {
+ continue;
+ }
+ if (errmsg_ == exp.getMessage()) {
+ // This error message was expected: good.
+ return;
+ }
+ }
+ ADD_FAILURE() << party << " got unexpected error '" << errmsg_ << "'";
+ }
+
+};
+
+////////////////////////////////////////////////////////////////////////
+// TlsContext tests //
+////////////////////////////////////////////////////////////////////////
+
+// Test if we can get a client context.
+TEST(TLSTest, clientContext) {
+ TlsContextPtr ctx;
+ EXPECT_NO_THROW(ctx.reset(new TlsContext(TlsRole::CLIENT)));
+}
+
+// Test if we can get a server context.
+TEST(TLSTest, serverContext) {
+ TlsContextPtr ctx;
+ EXPECT_NO_THROW(ctx.reset(new TlsContext(TlsRole::SERVER)));
+}
+
+// Test if the cert required flag is handled as expected.
+TEST(TLSTest, certRequired) {
+ auto check = [] (TlsContext& ctx) -> bool {
+#ifdef WITH_BOTAN
+ return (ctx.getCertRequired());
+#else // WITH_OPENSSL
+ ::SSL_CTX* ssl_ctx = ctx.getNativeContext();
+ if (!ssl_ctx) {
+ ADD_FAILURE() << "null SSL_CTX";
+ return (false);
+ }
+ int mode = SSL_CTX_get_verify_mode(ssl_ctx);
+ switch (mode) {
+ case SSL_VERIFY_NONE:
+ return (false);
+ case (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT):
+ return (true);
+ default:
+ ADD_FAILURE() << "unknown ssl_verify_mode: " << mode;
+ return (false);
+ }
+#endif
+ };
+
+ TestTlsContext ctx(TlsRole::SERVER);
+ EXPECT_TRUE(ctx.getCertRequired());
+ EXPECT_TRUE(check(ctx));
+ ASSERT_NO_THROW(ctx.setCertRequired(false));
+ EXPECT_FALSE(ctx.getCertRequired());
+ EXPECT_FALSE(check(ctx));
+ ASSERT_NO_THROW(ctx.setCertRequired(true));
+ EXPECT_TRUE(ctx.getCertRequired());
+ EXPECT_TRUE(check(ctx));
+
+ // Client role is different so test it too.
+ TestTlsContext cctx(TlsRole::CLIENT);
+ EXPECT_TRUE(cctx.getCertRequired());
+ EXPECT_TRUE(check(cctx));
+ ASSERT_NO_THROW(cctx.setCertRequired(true));
+ EXPECT_TRUE(cctx.getCertRequired());
+ EXPECT_TRUE(check(cctx));
+ EXPECT_THROW(cctx.setCertRequired(false), isc::BadValue);
+}
+
+// Test if the certificate authority can be loaded.
+TEST(TLSTest, loadCAFile) {
+ string ca(string(TEST_CA_DIR) + "/kea-ca.crt");
+ TestTlsContext ctx(TlsRole::CLIENT);
+ EXPECT_NO_THROW(ctx.loadCaFile(ca));
+}
+
+// Test that no certificate authority gives an error.
+TEST(TLSTest, loadNoCAFile) {
+ Expecteds exps;
+ // Botan error.
+ exps.addThrow("I/O error: DataSource: Failure opening file /no-such-file");
+ // OpenSSL errors.
+ exps.addThrow("No such file or directory");
+ exps.addThrow("No such file or directory (system library)");
+ exps.addThrow("No such file or directory (system library, fopen)");
+ exps.runCanThrow([] {
+ string ca("/no-such-file");
+ TestTlsContext ctx(TlsRole::CLIENT);
+ ctx.loadCaFile(ca);
+ });
+ if (Expecteds::displayErrMsg()) {
+ std::cout << exps.getErrMsg() << "\n";
+ }
+}
+
+#ifdef WITH_BOTAN
+// Test that Botan requires a real CA certificate so fails with
+// trusted self-signed client.
+/// @note: convert to GTEST when gtest_utils.h will be moved.
+TEST(TLSTest, loadTrustedSelfCAFile) {
+ Expecteds exps;
+ // Botan error.
+ string botan_error = "Flatfile_Certificate_Store received non CA cert ";
+ botan_error += "C=\"US\",X520.State=\"Some-State\",O=\"ISC Inc.\",";
+ botan_error += "CN=\"kea-self\"";
+ exps.addThrow(botan_error);
+ exps.runCanThrow([] {
+ string ca(string(TEST_CA_DIR) + "/kea-self.crt");
+ TestTlsContext ctx(TlsRole::CLIENT);
+ ctx.loadCaFile(ca);
+ });
+ if (Expecteds::displayErrMsg()) {
+ std::cout << exps.getErrMsg() << "\n";
+ }
+}
+#endif // WITH_BOTAN
+
+// Test that a directory can be loaded.
+TEST(TLSTest, loadCAPath) {
+ string ca(TEST_CA_DIR);
+ TestTlsContext ctx(TlsRole::CLIENT);
+ EXPECT_NO_THROW(ctx.loadCaPath(ca));
+}
+
+// Test that a certificate is wanted.
+TEST(TLSTest, loadKeyCA) {
+ Expecteds exps;
+ // Botan error.
+ exps.addThrow("Flatfile_Certificate_Store::Flatfile_Certificate_Store cert file is empty");
+ // LibreSSL or old OpenSSL does not check.
+ exps.addNoError();
+ // Recent OpenSSL errors.
+ exps.addThrow("no certificate or crl found");
+ exps.addThrow("no certificate or crl found (x509 certificate routines)");
+ exps.addThrow("no certificate or crl found (x509 certificate routines, CRYPTO_internal)");
+ exps.addThrow("no certificate or crl found (x509 certificate routines, X509_load_cert_crl_file)");
+ exps.runCanThrow([] {
+ string ca(string(TEST_CA_DIR) + "/kea-ca.key");
+ TestTlsContext ctx(TlsRole::CLIENT);
+ ctx.loadCaFile(ca);
+ });
+ if (Expecteds::displayErrMsg()) {
+ std::cout << exps.getErrMsg() << "\n";
+ }
+}
+
+// Test if the end entity certificate can be loaded.
+TEST(TLSTest, loadCertFile) {
+ string cert(string(TEST_CA_DIR) + "/kea-client.crt");
+ TestTlsContext ctx(TlsRole::CLIENT);
+ EXPECT_NO_THROW(ctx.loadCertFile(cert));
+}
+
+// Test that no end entity certificate gives an error.
+TEST(TLSTest, loadNoCertFile) {
+ Expecteds exps;
+ // Botan error.
+ exps.addThrow("I/O error: DataSource: Failure opening file /no-such-file");
+ // OpenSSL errors.
+ exps.addThrow("No such file or directory");
+ exps.addThrow("No such file or directory (system library)");
+ exps.addThrow("No such file or directory (system library, fopen)");
+ exps.runCanThrow([] {
+ string cert("/no-such-file");
+ TestTlsContext ctx(TlsRole::CLIENT);
+ ctx.loadCertFile(cert);
+ });
+ if (Expecteds::displayErrMsg()) {
+ std::cout << exps.getErrMsg() << "\n";
+ }
+}
+
+// Test that a certificate is wanted.
+TEST(TLSTest, loadCsrCertFile) {
+ Expecteds exps;
+ // Botan error.
+ exps.addThrow("Expected a certificate, got 'CERTIFICATE REQUEST'");
+ // OpenSSL errors.
+ exps.addThrow("no start line");
+ exps.addThrow("no start line (PEM routines)");
+ exps.addThrow("no start line (PEM routines, get_name)");
+ exps.addThrow("no start line (PEM routines, CRYPTO_internal)");
+ exps.runCanThrow([] {
+ string cert(string(TEST_CA_DIR) + "/kea-client.csr");
+ TestTlsContext ctx(TlsRole::CLIENT);
+ ctx.loadCertFile(cert);
+ });
+ if (Expecteds::displayErrMsg()) {
+ std::cout << exps.getErrMsg() << "\n";
+ }
+}
+
+// Test if the private key can be loaded.
+TEST(TLSTest, loadKeyFile) {
+ string key(string(TEST_CA_DIR) + "/kea-client.key");
+ TestTlsContext ctx(TlsRole::CLIENT);
+ EXPECT_NO_THROW(ctx.loadKeyFile(key));
+}
+
+// Test that no private key gives an error.
+TEST(TLSTest, loadNoKeyFile) {
+ Expecteds exps;
+ // Botan error.
+ exps.addThrow("I/O error: DataSource: Failure opening file /no-such-file");
+ // OpenSSL errors.
+ exps.addThrow("No such file or directory");
+ exps.addThrow("No such file or directory (system library)");
+ exps.addThrow("No such file or directory (system library, fopen)");
+ // Another possible error.
+ exps.addThrow("PEM lib");
+ exps.runCanThrow([] {
+ string key("/no-such-file");
+ TestTlsContext ctx(TlsRole::CLIENT);
+ ctx.loadKeyFile(key);
+ });
+ if (Expecteds::displayErrMsg()) {
+ std::cout << exps.getErrMsg() << "\n";
+ }
+}
+
+// Test that a private key is wanted.
+TEST(TLSTest, loadCertKeyFile) {
+ Expecteds exps;
+ // Botan error.
+ string botan_error = "PKCS #8 private key decoding failed with PKCS #8: ";
+ botan_error += "Unknown PEM label CERTIFICATE";
+ exps.addThrow(botan_error);
+ // OpenSSL errors.
+ exps.addThrow("no start line");
+ exps.addThrow("no start line (PEM routines)");
+ exps.addThrow("no start line (PEM routines, get_name)");
+ exps.addThrow("no start line (PEM routines, CRYPTO_internal)");
+ exps.addThrow("PEM lib");
+ exps.addThrow("PEM lib (SSL routines)");
+ exps.addThrow("unsupported");
+ exps.addThrow("unsupported (DECODER routines)");
+ // Another possible error.
+ exps.addThrow("No such file or directory");
+ exps.runCanThrow([] {
+ string key(string(TEST_CA_DIR) + "/kea-client.crt");
+ TestTlsContext ctx(TlsRole::CLIENT);
+ ctx.loadKeyFile(key);
+ });
+ if (Expecteds::displayErrMsg()) {
+ std::cout << exps.getErrMsg() << "\n";
+ }
+}
+
+// Test that the certificate and private key must match.
+TEST(TLSTest, loadMismatch) {
+ Expecteds exps;
+ exps.addNoError();
+ exps.runCanThrow([] {
+ string cert(string(TEST_CA_DIR) + "/kea-server.crt");
+ TestTlsContext ctx(TlsRole::SERVER);
+ ctx.loadCertFile(cert);
+ });
+ // Keep no error for at least Botan.
+ // OpenSSL error.
+ exps.addThrow("key values mismatch");
+ exps.runCanThrow([] {
+ string key(string(TEST_CA_DIR) + "/kea-client.key");
+ TestTlsContext ctx(TlsRole::SERVER);
+ // In fact OpenSSL checks only RSA key values...
+ // The explicit check function is SSL_CTX_check_private_key.
+ ctx.loadKeyFile(key);
+ });
+ if (Expecteds::displayErrMsg()) {
+ std::cout << exps.getErrMsg() << "\n";
+ }
+}
+
+// Test the configure class method.
+TEST(TLSTest, configure) {
+ TlsContextPtr ctx;
+ string ca(string(TEST_CA_DIR) + "/kea-ca.crt");
+ string cert(string(TEST_CA_DIR) + "/kea-client.crt");
+ string key(string(TEST_CA_DIR) + "/kea-client.key");
+ EXPECT_NO_THROW(TlsContext::configure(ctx, TlsRole::CLIENT,
+ ca, cert, key, true));
+ ASSERT_TRUE(ctx);
+ EXPECT_EQ(TlsRole::CLIENT, ctx->getRole());
+ EXPECT_TRUE(ctx->getCertRequired());
+
+ // Retry using the directory and the server.
+ ctx.reset();
+ ca = TEST_CA_DIR;
+ cert = string(TEST_CA_DIR) + "/kea-server.crt";
+ key = string(TEST_CA_DIR) + "/kea-server.key";
+ EXPECT_NO_THROW(TlsContext::configure(ctx, TlsRole::SERVER,
+ ca, cert, key, false));
+ ASSERT_TRUE(ctx);
+ EXPECT_EQ(TlsRole::SERVER, ctx->getRole());
+ EXPECT_FALSE(ctx->getCertRequired());
+}
+
+// Test the configure class method error case.
+TEST(TLSTest, configureError) {
+ // The error case.
+ Expecteds exps;
+ // Common part of the error message.
+ string common_error = "load of cert file '/no-such-file' failed: ";
+ // Botan error.
+ string botan_error = "I/O error: DataSource: Failure opening file /no-such-file";
+ exps.addThrow(common_error + botan_error);
+ // OpenSSL errors.
+ string openssl_error = "No such file or directory";
+ exps.addThrow(common_error + openssl_error);
+ exps.addThrow(common_error + "No such file or directory (system library)");
+ exps.addThrow(common_error + "No such file or directory (system library, fopen)");
+ exps.runCanThrow([] {
+ TlsContextPtr ctx1;
+ string ca(string(TEST_CA_DIR) + "/kea-ca.crt");
+ string cert = "/no-such-file";
+ string key = string(TEST_CA_DIR) + "/kea-client.key";
+ TlsContext::configure(ctx1, TlsRole::CLIENT,
+ ca, cert, key, true);
+ // The context is reset on errors.
+ EXPECT_FALSE(ctx1);
+ });
+ if (Expecteds::displayErrMsg()) {
+ std::cout << exps.getErrMsg() << "\n";
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// Basic handshake failures //
+////////////////////////////////////////////////////////////////////////
+
+// Test if we can get a stream.
+TEST(TLSTest, stream) {
+ IOService service;
+ TlsContextPtr ctx(new TlsContext(TlsRole::CLIENT));
+ boost::scoped_ptr<TlsStream<TestCallback> > st;
+ EXPECT_NO_THROW(st.reset(new TlsStream<TestCallback>(service, ctx)));
+}
+
+// Test what happens when handshake is forgotten.
+TEST(TLSTest, noHandshake) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ TlsContextPtr client_ctx;
+ test::configClient(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer1(service);
+ bool timeout = false;
+ timer1.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Send on the client.
+ char send_buf[] = "some text...";
+ TestCallback send_cb;
+ async_write(client, boost::asio::buffer(send_buf), send_cb);
+ while (!timeout && !send_cb.getCalled()) {
+ service.run_one();
+ }
+ timer1.cancel();
+
+ Expecteds exps;
+ // Botan error.
+ exps.addError("InvalidObjectState");
+ // OpenSSL errors.
+ exps.addError("uninitialized");
+ exps.addError("uninitialized (SSL routines)");
+ exps.addError("uninitialized (SSL routines, ST_BEFORE_ACCEPT)");
+ exps.addError("uninitialized (SSL routines, ssl_write_internal)");
+ exps.checkAsync("send", send_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "send: " << exps.getErrMsg() << "\n";
+ }
+
+ // Setup a second timeout.
+ IntervalTimer timer2(service);
+ timeout = false;
+ timer2.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Receive on the server.
+ vector<char> receive_buf(64);
+ TestCallback receive_cb;
+ server.async_read_some(boost::asio::buffer(receive_buf), receive_cb);
+ while (!timeout && !receive_cb.getCalled()) {
+ service.run_one();
+ }
+ timer2.cancel();
+
+ exps.clear();
+ // On Botan and some OpenSSL the receive party hangs.
+ exps.addTimeout();
+ // OpenSSL errors.
+ exps.addError("uninitialized");
+ exps.addError("uninitialized (SSL routines)");
+ exps.addError("uninitialized (SSL routines, ST_BEFORE_ACCEPT)");
+ exps.addError("uninitialized (SSL routines, ssl_read_internal)");
+ exps.checkAsync("receive", receive_cb);
+ if (Expecteds::displayErrMsg()) {
+ if (timeout) {
+ std::cout << "receive timeout\n";
+ } else {
+ std::cout << "receive: " << exps.getErrMsg() << "\n";
+ }
+ }
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the server was not configured.
+TEST(TLSTest, serverNotConfigured) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx(new TlsContext(TlsRole::SERVER));
+ // Skip config.
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ TlsContextPtr client_ctx;
+ test::configClient(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb;
+ server.handshake(server_cb);
+ TestCallback client_cb;
+ client.handshake(client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ Expecteds exps;
+ // Botan error.
+ exps.addError("handshake_failure");
+ // Old LibreSSL error.
+ exps.addError("no shared cipher");
+ // OpenSSL errors.
+ exps.addError("sslv3 alert handshake failure");
+ exps.addError("no shared cipher (SSL routines)");
+ exps.addError("no shared cipher (SSL routines, ACCEPT_SR_CLNT_HELLO_C)");
+ exps.addError("no shared cipher (SSL routines, tls_post_process_client_hello)");
+ // Recent LibreSSL error.
+ exps.addError("missing rsa certificate (SSL routines, (UNKNOWN)SSL_internal)");
+ exps.checkAsync("server", server_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "server: " << exps.getErrMsg() << "\n";
+ }
+
+ exps.clear();
+ // On Botan and some OpenSSL the client hangs.
+ exps.addTimeout();
+ // OpenSSL errors.
+ exps.addError("sslv3 alert handshake failure");
+ exps.addError("sslv3 alert handshake failure (SSL routines)");
+ exps.addError("sslv3 alert handshake failure (SSL routines, CONNECT_CR_SRVR_HELLO)");
+ exps.addError("sslv3 alert handshake failure (SSL routines, CONNECT_CR_CERT)");
+ exps.addError("sslv3 alert handshake failure (SSL routines, ssl3_read_bytes)");
+ exps.checkAsync("client", client_cb);
+ if (Expecteds::displayErrMsg()) {
+ if (timeout) {
+ std::cout << "client timeout\n";
+ } else {
+ std::cout << "client: " << exps.getErrMsg() << "\n";
+ }
+ }
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the client was not configured.
+TEST(TLSTest, clientNotConfigured) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ TlsContextPtr client_ctx(new TlsContext(TlsRole::CLIENT));
+ // Skip config.
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb;
+ server.async_handshake(roleToImpl(TlsRole::SERVER), server_cb);
+ TestCallback client_cb;
+ client.async_handshake(roleToImpl(TlsRole::CLIENT), client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ Expecteds exps;
+ // On Botan and some OpenSSL the server hangs.
+ exps.addTimeout();
+ // OpenSSL errors.
+ exps.addError("tlsv1 alert unknown ca");
+ exps.addError("tlsv1 alert unknown ca (SSL routines)");
+ exps.addError("tlsv1 alert unknown ca (SSL routines, ACCEPT_SR_CERT)");
+ exps.addError("tlsv1 alert unknown ca (SSL routines, ACCEPT_SR_CERT_VRFY)");
+ exps.addError("tlsv1 alert unknown ca (SSL routines, ssl3_read_bytes)");
+ exps.checkAsync("server", server_cb);
+ if (Expecteds::displayErrMsg()) {
+ if (timeout) {
+ std::cout << "server timeout\n";
+ } else {
+ std::cout << "server: " << exps.getErrMsg() << "\n";
+ }
+ }
+
+ exps.clear();
+ // Botan error (unfortunately a bit generic).
+ exps.addError("bad_certificate");
+ // Old LibreSSL error.
+ exps.addError("tlsv1 alert unknown ca");
+ // OpenSSL errors.
+ exps.addError("certificate verify failed");
+ exps.addError("certificate verify failed (SSL routines)");
+ exps.addError("certificate verify failed (SSL routines, CONNECT_CR_CERT)");
+ exps.addError("certificate verify failed (SSL routines, (UNKNOWN)SSL_internal)");
+ exps.addError("certificate verify failed (SSL routines, tls_process_server_certificate)");
+ // The client should not hang.
+ exps.checkAsync("client", client_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "client: " << exps.getErrMsg() << "\n";
+ }
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the client is HTTP (vs HTTPS).
+TEST(TLSTest, clientHTTPnoS) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ tcp::socket client(service.get_io_service());
+
+ // Connect to.
+ client.open(tcp::v4());
+ TestCallback connect_cb;
+ client.async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform server TLS handshake.
+ TestCallback server_cb;
+ server.async_handshake(roleToImpl(TlsRole::SERVER), server_cb);
+
+ // Client sending a HTTP GET.
+ char send_buf[] = "GET / HTTP/1.1\r\n";
+ TestCallback client_cb;
+ client.async_send(boost::asio::buffer(send_buf), client_cb);
+
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ Expecteds exps;
+ // Botan server hangs before 2.18.
+ exps.addTimeout();
+ // Botan error.
+ exps.addError("protocol_version");
+ // Old LibreSSL error.
+ exps.addError("tlsv1 alert protocol version");
+ // OpenSSL errors (OpenSSL recognizes HTTP).
+ exps.addError("http request");
+ exps.addError("http request (SSL routines)");
+ exps.addError("http request (SSL routines, ACCEPT_SR_CLNT_HELLO)");
+ exps.addError("http request (SSL routines, ssl3_get_record)");
+ // Another OpenSSL error (not all OpenSSL recognizes HTTP).
+ exps.addError("wrong version number");
+ // Recent LibreSSL error.
+ exps.addError("tlsv1 alert protocol version (SSL routines, ACCEPT_SR_CLNT_HELLO)");
+ exps.checkAsync("server", server_cb);
+ if (Expecteds::displayErrMsg()) {
+ if (timeout) {
+ std::cout << "server timeout\n";
+ } else {
+ std::cout << "server: " << exps.getErrMsg() << "\n";
+ }
+ }
+
+ // No error at the client.
+ EXPECT_TRUE(client_cb.getCalled());
+ EXPECT_FALSE(client_cb.getCode());
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the client does not use HTTP nor HTTP.
+TEST(TLSTest, unknownClient) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ tcp::socket client(service.get_io_service());
+
+ // Connect to.
+ client.open(tcp::v4());
+ TestCallback connect_cb;
+ client.async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform server TLS handshake.
+ TestCallback server_cb;
+ server.async_handshake(roleToImpl(TlsRole::SERVER), server_cb);
+
+ // Client sending something which is not a TLS ClientHello.
+ char send_buf[] = "hello my server...";
+ TestCallback client_cb;
+ client.async_send(boost::asio::buffer(send_buf), client_cb);
+
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ Expecteds exps;
+ // Botan errors.
+ exps.addError("record_overflow");
+ exps.addError("protocol_version");
+ // Old LibreSSL error.
+ exps.addError("tlsv1 alert protocol version");
+ // Old OpenSSL error.
+ exps.addError("unknown protocol");
+ // Recent OpenSSL errors.
+ exps.addError("wrong version number");
+ exps.addError("wrong version number (SSL routines)");
+ exps.addError("wrong version number (SSL routines, ssl3_get_record)");
+ // Recent LibreSSL error.
+ exps.addError("unknown protocol (SSL routines, ACCEPT_SR_CLNT_HELLO)");
+ exps.addError("tlsv1 alert protocol version (SSL routines, ACCEPT_SR_CLNT_HELLO)");
+ exps.checkAsync("server", server_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "server: " << exps.getErrMsg() << "\n";
+ }
+
+ // No error on the client side.
+ EXPECT_TRUE(client_cb.getCalled());
+ EXPECT_FALSE(client_cb.getCode());
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the client uses a certificate from another CA.
+TEST(TLSTest, anotherClient) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part using a certificate signed by another CA.
+ TlsContextPtr client_ctx;
+ test::configOther(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb;
+ server.async_handshake(roleToImpl(TlsRole::SERVER), server_cb);
+ TestCallback client_cb;
+ client.async_handshake(roleToImpl(TlsRole::CLIENT), client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ Expecteds exps;
+ // Botan error.
+ exps.addError("bad_certificate");
+ // Old LibreSSL error.
+ exps.addError("tlsv1 alert unknown ca");
+ // OpenSSL errors.
+ // Full error is:
+ // error 20 at 0 depth lookup:unable to get local issuer certificate
+ exps.addError("certificate verify failed");
+ exps.addError("certificate verify failed (SSL routines)");
+ exps.addError("certificate verify failed (SSL routines, tls_process_client_certificate)");
+ // Recent LibreSSL errors.
+ exps.addError("no certificate returned (SSL routines, ACCEPT_SR_CERT)");
+ exps.addError("certificate verify failed (SSL routines, (UNKNOWN)SSL_internal)");
+ exps.checkAsync("server", server_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "server: " << exps.getErrMsg() << "\n";
+ }
+
+ exps.clear();
+ // Botan client hangs.
+ exps.addTimeout();
+ // Old LibreSSL and recent OpenSSL do not fail.
+ exps.addNoError();
+ // Old OpenSSL error.
+ exps.addError("tlsv1 alert unknown ca");
+ // Recent LibreSSL error.
+ exps.addError("tlsv1 alert unknown ca (SSL routines, CONNECT_CR_SESSION_TICKET)");
+ exps.checkAsync("client", client_cb);
+ if (Expecteds::displayErrMsg()) {
+ if (timeout) {
+ std::cout << "client timeout\n";
+ } else if (exps.hasErrMsg()) {
+ std::cout << "client: " << exps.getErrMsg() << "\n";
+ }
+ }
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the client uses a self-signed certificate.
+TEST(TLSTest, selfSigned) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part using a self-signed certificate.
+ TlsContextPtr client_ctx;
+ test::configSelf(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb;
+ server.async_handshake(roleToImpl(TlsRole::SERVER), server_cb);
+ TestCallback client_cb;
+ client.async_handshake(roleToImpl(TlsRole::CLIENT), client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ Expecteds exps;
+ // Botan error.
+ exps.addError("bad_certificate");
+ // Old LibreSSL error.
+ exps.addError("tlsv1 alert unknown ca");
+ // OpenSSL errors.
+ // Full error is:
+ // error 18 at 0 depth lookup:self signed certificate
+ exps.addError("certificate verify failed");
+ exps.addError("certificate verify failed (SSL routines)");
+ exps.addError("certificate verify failed (SSL routines, tls_process_client_certificate)");
+ // Recent LibreSSL errors.
+ exps.addError("no certificate returned (SSL routines, ACCEPT_SR_CERT)");
+ exps.addError("certificate verify failed (SSL routines, (UNKNOWN)SSL_internal)");
+ exps.checkAsync("server", server_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "server: " << exps.getErrMsg() << "\n";
+ }
+
+ exps.clear();
+ // Botan client hangs.
+ exps.addTimeout();
+ // Old LibreSSL and recent OpenSSL do not fail.
+ exps.addNoError();
+ // Old OpenSSL error.
+ exps.addError("tlsv1 alert unknown ca");
+ // Recent LibreSSL error.
+ exps.addError("tlsv1 alert unknown ca (SSL routines, CONNECT_CR_SESSION_TICKET)");
+ exps.checkAsync("client", client_cb);
+ if (Expecteds::displayErrMsg()) {
+ if (timeout) {
+ std::cout << "client timeout\n";
+ } else if (exps.hasErrMsg()) {
+ std::cout << "client: " << exps.getErrMsg() << "\n";
+ }
+ }
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+////////////////////////////////////////////////////////////////////////
+// Close on error handshake failures //
+////////////////////////////////////////////////////////////////////////
+
+// Investigate what happens when a peer closes its streams when the
+// handshake callback returns an error. In particular does still
+// the other peer timeout?
+
+// Test what happens when handshake is forgotten.
+TEST(TLSTest, noHandshakeCloseonError) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ TlsContextPtr client_ctx;
+ test::configClient(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer1(service);
+ bool timeout = false;
+ timer1.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Send on the client.
+ char send_buf[] = "some text...";
+ TestCallback send_cb(&client.lowest_layer());
+ async_write(client, boost::asio::buffer(send_buf), send_cb);
+ while (!timeout && !send_cb.getCalled()) {
+ service.run_one();
+ }
+ timer1.cancel();
+
+ Expecteds exps;
+ // Botan error.
+ exps.addError("InvalidObjectState");
+ // OpenSSL errors.
+ exps.addError("uninitialized");
+ exps.addError("uninitialized (SSL routines)");
+ exps.addError("uninitialized (SSL routines, ST_BEFORE_ACCEPT)");
+ exps.addError("uninitialized (SSL routines, ssl_write_internal)");
+ exps.checkAsync("send", send_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "send: " << exps.getErrMsg() << "\n";
+ }
+
+ // Setup a second timeout.
+ IntervalTimer timer2(service);
+ timeout = false;
+ timer2.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Receive on the server.
+ vector<char> receive_buf(64);
+ TestCallback receive_cb;
+ server.async_read_some(boost::asio::buffer(receive_buf), receive_cb);
+ while (!timeout && !receive_cb.getCalled()) {
+ service.run_one();
+ }
+ timer2.cancel();
+
+ exps.clear();
+ // Botan and some OpenSSL.
+ exps.addError("stream truncated");
+ // OpenSSL errors.
+ exps.addError("uninitialized");
+ exps.addError("uninitialized (SSL routines)");
+ exps.addError("uninitialized (SSL routines, ST_BEFORE_ACCEPT)");
+ exps.addError("uninitialized (SSL routines, ssl_read_internal)");
+ exps.checkAsync("receive", receive_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "receive: " << exps.getErrMsg() << "\n";
+ }
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the server was not configured.
+TEST(TLSTest, serverNotConfiguredCloseonError) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx(new TlsContext(TlsRole::SERVER));
+ // Skip config.
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ TlsContextPtr client_ctx;
+ test::configClient(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb(&server.lowest_layer());
+ server.handshake(server_cb);
+ TestCallback client_cb;
+ client.handshake(client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ Expecteds exps;
+ // Botan error.
+ exps.addError("handshake_failure");
+ // Old LibreSSL error.
+ exps.addError("no shared cipher");
+ // OpenSSL errors.
+ exps.addError("sslv3 alert handshake failure");
+ exps.addError("no shared cipher (SSL routines)");
+ exps.addError("no shared cipher (SSL routines, ACCEPT_SR_CLNT_HELLO_C)");
+ exps.addError("no shared cipher (SSL routines, tls_post_process_client_hello)");
+ // Recent LibreSSL error.
+ exps.addError("missing rsa certificate (SSL routines, (UNKNOWN)SSL_internal)");
+ exps.checkAsync("server", server_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "server: " << exps.getErrMsg() << "\n";
+ }
+
+ exps.clear();
+ // Botan and some OpenSSL.
+ exps.addError("stream truncated");
+ // Alias on old OpenSSL.
+ exps.addError("short read");
+ // OpenSSL errors.
+ exps.addError("sslv3 alert handshake failure");
+ exps.addError("sslv3 alert handshake failure (SSL routines)");
+ exps.addError("sslv3 alert handshake failure (SSL routines, CONNECT_CR_SRVR_HELLO)");
+ exps.addError("sslv3 alert handshake failure (SSL routines, ssl3_read_bytes)");
+ // Recent LibreSSL error.
+ exps.addError("sslv3 alert handshake failure (SSL routines, CONNECT_CR_CERT)");
+ exps.checkAsync("client", client_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "client: " << exps.getErrMsg() << "\n";
+ }
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the client was not configured.
+TEST(TLSTest, clientNotConfiguredCloseonError) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ TlsContextPtr client_ctx(new TlsContext(TlsRole::CLIENT));
+ // Skip config.
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb;
+ server.async_handshake(roleToImpl(TlsRole::SERVER), server_cb);
+ TestCallback client_cb(&client.lowest_layer());
+ client.async_handshake(roleToImpl(TlsRole::CLIENT), client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ Expecteds exps;
+ // Botan and some OpenSSL.
+ exps.addError("stream truncated");
+ // Alias on old OpenSSL.
+ exps.addError("short read");
+ // OpenSSL errors.
+ exps.addError("tlsv1 alert unknown ca");
+ exps.addError("tlsv1 alert unknown ca (SSL routines)");
+ exps.addError("tlsv1 alert unknown ca (SSL routines, ACCEPT_SR_CERT)");
+ exps.addError("tlsv1 alert unknown ca (SSL routines, ACCEPT_SR_CERT_VRFY)");
+ exps.addError("tlsv1 alert unknown ca (SSL routines, ssl3_read_bytes)");
+ exps.checkAsync("server", server_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "server: " << exps.getErrMsg() << "\n";
+ }
+
+ exps.clear();
+ // Botan error (unfortunately a bit generic).
+ exps.addError("bad_certificate");
+ // Old LibreSSL error.
+ exps.addError("tlsv1 alert unknown ca");
+ // OpenSSL errors.
+ exps.addError("certificate verify failed");
+ exps.addError("certificate verify failed (SSL routines)");
+ exps.addError("certificate verify failed (SSL routines, CONNECT_CR_CERT)");
+ exps.addError("certificate verify failed (SSL routines, tls_process_server_certificate)");
+ // Recent LibreSSL error.
+ exps.addError("certificate verify failed (SSL routines, (UNKNOWN)SSL_internal)");
+ // The client should not hang.
+ exps.checkAsync("client", client_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "client: " << exps.getErrMsg() << "\n";
+ }
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the client is HTTP (vs HTTPS).
+TEST(TLSTest, clientHTTPnoSCloseonError) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ tcp::socket client(service.get_io_service());
+
+ // Connect to.
+ client.open(tcp::v4());
+ TestCallback connect_cb;
+ client.async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform server TLS handshake.
+ TestCallback server_cb;
+ server.async_handshake(roleToImpl(TlsRole::SERVER), server_cb);
+
+ // Client sending a HTTP GET.
+ char send_buf[] = "GET / HTTP/1.1\r\n";
+ TestCallback client_cb;
+ client.async_send(boost::asio::buffer(send_buf), client_cb);
+
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ Expecteds exps;
+ // Botan server hangs before 2.18.
+ exps.addTimeout();
+ // Botan behavior was reported and fixed.
+ exps.addError("protocol_version");
+ // Old LibreSSL error.
+ exps.addError("tlsv1 alert protocol version");
+ // OpenSSL errors when OpenSSL recognizes HTTP.
+ exps.addError("http request");
+ exps.addError("http request (SSL routines)");
+ exps.addError("http request (SSL routines, ACCEPT_SR_CLNT_HELLO)");
+ exps.addError("http request (SSL routines, ssl3_get_record)");
+ // Another OpenSSL error (not all OpenSSL recognizes HTTP).
+ exps.addError("wrong version number");
+ // Recent LibreSSL error.
+ exps.addError("tlsv1 alert protocol version (SSL routines, ACCEPT_SR_CLNT_HELLO)");
+ exps.checkAsync("server", server_cb);
+ if (Expecteds::displayErrMsg()) {
+ if (timeout) {
+ std::cout << "server timeout\n";
+ } else {
+ std::cout << "server: " << exps.getErrMsg() << "\n";
+ }
+ }
+
+ // No error at the client.
+ EXPECT_TRUE(client_cb.getCalled());
+ EXPECT_FALSE(client_cb.getCode());
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the client uses a certificate from another CA.
+TEST(TLSTest, anotherClientCloseonError) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part using a certificate signed by another CA.
+ TlsContextPtr client_ctx;
+ test::configOther(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb(&server.lowest_layer());
+ server.async_handshake(roleToImpl(TlsRole::SERVER), server_cb);
+ TestCallback client_cb;
+ client.async_handshake(roleToImpl(TlsRole::CLIENT), client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ Expecteds exps;
+ // Botan error.
+ exps.addError("bad_certificate");
+ // Old LibreSSL error.
+ exps.addError("tlsv1 alert unknown ca");
+ // OpenSSL errors.
+ // Full error is:
+ // error 20 at 0 depth lookup:unable to get local issuer certificate
+ exps.addError("certificate verify failed");
+ exps.addError("certificate verify failed (SSL routines)");
+ exps.addError("certificate verify failed (SSL routines, tls_process_client_certificate)");
+ // Recent LibreSSL errors.
+ exps.addError("no certificate returned (SSL routines, ACCEPT_SR_CERT)");
+ exps.addError("certificate verify failed (SSL routines, (UNKNOWN)SSL_internal)");
+ exps.checkAsync("server", server_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "server: " << exps.getErrMsg() << "\n";
+ }
+
+ exps.clear();
+ // Botan and some OpenSSL.
+ exps.addError("stream truncated");
+ // Alias on old OpenSSL.
+ exps.addError("short read");
+ // Old LibreSSL and recent OpenSSL do not fail.
+ exps.addNoError();
+ // Old OpenSSL error.
+ exps.addError("tlsv1 alert unknown ca");
+ // Recent LibreSSL error.
+ exps.addError("tlsv1 alert unknown ca (SSL routines, CONNECT_CR_SESSION_TICKET)");
+ exps.checkAsync("client", client_cb);
+ if (Expecteds::displayErrMsg() && exps.hasErrMsg()) {
+ std::cout << "client: " << exps.getErrMsg() << "\n";
+ }
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the client uses a self-signed certificate.
+TEST(TLSTest, selfSignedCloseonError) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part using a self-signed certificate.
+ TlsContextPtr client_ctx;
+ test::configSelf(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb(&server.lowest_layer());
+ server.async_handshake(roleToImpl(TlsRole::SERVER), server_cb);
+ TestCallback client_cb;
+ client.async_handshake(roleToImpl(TlsRole::CLIENT), client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ Expecteds exps;
+ // Botan error.
+ exps.addError("bad_certificate");
+ // Old LibreSSL error.
+ exps.addError("tlsv1 alert unknown ca");
+ // OpenSSL errors.
+ // Full error is:
+ // error 18 at 0 depth lookup:self signed certificate
+ exps.addError("certificate verify failed");
+ exps.addError("certificate verify failed (SSL routines)");
+ exps.addError("certificate verify failed (SSL routines, tls_process_client_certificate)");
+ // Recent LibreSSL errors.
+ exps.addError("no certificate returned (SSL routines, ACCEPT_SR_CERT)");
+ exps.addError("certificate verify failed (SSL routines, (UNKNOWN)SSL_internal)");
+ exps.checkAsync("server", server_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "server: " << exps.getErrMsg() << "\n";
+ }
+
+ exps.clear();
+ // Botan and some OpenSSL.
+ exps.addError("stream truncated");
+ // Alias on old OpenSSL.
+ exps.addError("short read");
+ // Old LibreSSL and recent OpenSSL do not fail.
+ exps.addNoError();
+ // Old OpenSSL error.
+ exps.addError("tlsv1 alert unknown ca");
+ // Recent LibreSSL error.
+ exps.addError("tlsv1 alert unknown ca (SSL routines, CONNECT_CR_SESSION_TICKET)");
+ exps.checkAsync("client", client_cb);
+ if (Expecteds::displayErrMsg() && exps.hasErrMsg()) {
+ std::cout << "client: " << exps.getErrMsg() << "\n";
+ }
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+////////////////////////////////////////////////////////////////////////
+// TLS handshake corner case //
+////////////////////////////////////////////////////////////////////////
+
+// Some special cases.
+
+// Test what happens when the client uses a certificate from another CA
+// but the client certificate request and validation are disabled.
+TEST(TLSTest, anotherClientNoReq) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServerNoReq(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part using a certificate signed by another CA.
+ TlsContextPtr client_ctx;
+ test::configOther(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb;
+ server.async_handshake(roleToImpl(TlsRole::SERVER), server_cb);
+ TestCallback client_cb;
+ client.async_handshake(roleToImpl(TlsRole::CLIENT), client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ // Should not fail.
+ EXPECT_FALSE(timeout);
+ EXPECT_TRUE(server_cb.getCalled());
+ EXPECT_FALSE(server_cb.getCode());
+ EXPECT_TRUE(client_cb.getCalled());
+ EXPECT_FALSE(client_cb.getCode());
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the server uses a certificate without subject
+// alternative name (but still a version 3 certificate).
+TEST(TLSTest, serverRaw) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServerRaw(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ TlsContextPtr client_ctx;
+ test::configClient(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb;
+ server.async_handshake(roleToImpl(TlsRole::SERVER), server_cb);
+ TestCallback client_cb;
+ client.async_handshake(roleToImpl(TlsRole::CLIENT), client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ // Should not fail.
+ EXPECT_FALSE(timeout);
+ EXPECT_TRUE(server_cb.getCalled());
+ EXPECT_FALSE(server_cb.getCode());
+ EXPECT_TRUE(client_cb.getCalled());
+ EXPECT_FALSE(client_cb.getCode());
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+#ifdef WITH_OPENSSL
+// Test what happens when the client uses a trusted self-signed certificate.
+// Not really a failure case as it works...
+TEST(TLSTest, trustedSelfSigned) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configTrustedSelf(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part using a self-signed certificate.
+ TlsContextPtr client_ctx;
+ test::configSelf(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb;
+ server.async_handshake(roleToImpl(TlsRole::SERVER), server_cb);
+ TestCallback client_cb;
+ client.async_handshake(roleToImpl(TlsRole::CLIENT), client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ // It should work for all OpenSSL.
+ EXPECT_FALSE(timeout);
+ EXPECT_TRUE(server_cb.getCalled());
+ EXPECT_FALSE(server_cb.getCode());
+ EXPECT_TRUE(client_cb.getCalled());
+ EXPECT_FALSE(client_cb.getCode());
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+#endif // WITH_OPENSSL
+
+////////////////////////////////////////////////////////////////////////
+// TLS shutdown //
+////////////////////////////////////////////////////////////////////////
+
+// Investigate the TLS shutdown processing.
+
+// Test what happens when the shutdown receiver is inactive.
+TEST(TLSTest, shutdownInactive) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx(new TlsContext(TlsRole::SERVER));
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ TlsContextPtr client_ctx;
+ test::configClient(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb(&server.lowest_layer());
+ server.handshake(server_cb);
+ TestCallback client_cb;
+ client.handshake(client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ // No problem is expected.
+ EXPECT_FALSE(timeout);
+ EXPECT_TRUE(server_cb.getCalled());
+ EXPECT_FALSE(server_cb.getCode());
+ EXPECT_TRUE(client_cb.getCalled());
+ EXPECT_FALSE(client_cb.getCode());
+
+ // Setup a timeout for the shutdown.
+ IntervalTimer timer2(service);
+ timeout = false;
+ timer2.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Shutdown on the client leaving the server inactive.
+ TestCallback shutdown_cb;
+ client.shutdown(shutdown_cb);
+ while (!timeout && !shutdown_cb.getCalled()) {
+ service.run_one();
+ }
+ timer2.cancel();
+
+ Expecteds exps;
+ // Botan gets no error.
+ exps.addNoError();
+ // OpenSSL hangs.
+ exps.addTimeout();
+ exps.checkAsync("shutdown", shutdown_cb);
+ if (Expecteds::displayErrMsg()) {
+ if (timeout) {
+ std::cout << "shutdown timeout\n";
+ } else if (exps.hasErrMsg()) {
+ std::cout << "shutdown: " << exps.getErrMsg() << "\n";
+ }
+ }
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the shutdown receiver is active.
+TEST(TLSTest, shutdownActive) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx(new TlsContext(TlsRole::SERVER));
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ TlsContextPtr client_ctx;
+ test::configClient(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb(&server.lowest_layer());
+ server.handshake(server_cb);
+ TestCallback client_cb;
+ client.handshake(client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ // No problem is expected.
+ EXPECT_FALSE(timeout);
+ EXPECT_TRUE(server_cb.getCalled());
+ EXPECT_FALSE(server_cb.getCode());
+ EXPECT_TRUE(client_cb.getCalled());
+ EXPECT_FALSE(client_cb.getCode());
+
+ // Setup a timeout for the shutdown and receive.
+ IntervalTimer timer2(service);
+ timeout = false;
+ timer2.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Receive on the server.
+ vector<char> receive_buf(64);
+ TestCallback receive_cb;
+ server.async_read_some(boost::asio::buffer(receive_buf), receive_cb);
+
+ // Shutdown on the client.
+ TestCallback shutdown_cb;
+ client.shutdown(shutdown_cb);
+ while (!timeout && (!shutdown_cb.getCalled() || !receive_cb.getCalled())) {
+ service.run_one();
+ }
+ timer2.cancel();
+
+ Expecteds exps;
+ // Botan gets no error.
+ exps.addNoError();
+ // OpenSSL hangs.
+ exps.addTimeout();
+ exps.checkAsync("shutdown", shutdown_cb);
+ if (Expecteds::displayErrMsg()) {
+ if (timeout) {
+ std::cout << "shutdown timeout\n";
+ } else if (exps.hasErrMsg()) {
+ std::cout << "shutdown: " << exps.getErrMsg() << "\n";
+ }
+ }
+
+ exps.clear();
+ // End of file on the receive side.
+ exps.addError("End of file");
+ exps.checkAsync("receive", receive_cb);
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "receive: " << exps.getErrMsg() << "\n";
+ }
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the shutdown receiver is inactive on shutdown
+// and immediate close.
+TEST(TLSTest, shutdownCloseInactive) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx(new TlsContext(TlsRole::SERVER));
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ TlsContextPtr client_ctx;
+ test::configClient(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb(&server.lowest_layer());
+ server.handshake(server_cb);
+ TestCallback client_cb;
+ client.handshake(client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ // No problem is expected.
+ EXPECT_FALSE(timeout);
+ EXPECT_TRUE(server_cb.getCalled());
+ EXPECT_FALSE(server_cb.getCode());
+ EXPECT_TRUE(client_cb.getCalled());
+ EXPECT_FALSE(client_cb.getCode());
+
+ // Setup a timeout for the shutdown.
+ IntervalTimer timer2(service);
+ timeout = false;
+ timer2.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Shutdown on the client leaving the server inactive.
+ TestCallback shutdown_cb;
+ client.shutdown(shutdown_cb);
+
+ // Post a close which should be called after the shutdown.
+ service.post([&client] { client.lowest_layer().close(); });
+ while (!timeout && !shutdown_cb.getCalled()) {
+ service.run_one();
+ }
+ timer2.cancel();
+
+ Expecteds exps;
+ // Botan gets no error.
+ exps.addNoError();
+ // LibreSSL and some old OpenSSL gets Operation canceled.
+ exps.addError("Operation canceled");
+ // OpenSSL gets Bad file descriptor.
+ exps.addError("Bad file descriptor");
+ exps.checkAsync("shutdown", shutdown_cb);
+ if (Expecteds::displayErrMsg()) {
+ if (timeout) {
+ std::cout << "shutdown timeout\n";
+ } else if (exps.hasErrMsg()) {
+ std::cout << "shutdown: " << exps.getErrMsg() << "\n";
+ }
+ }
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the shutdown receiver is active with an
+// immediate close.
+TEST(TLSTest, shutdownCloseActive) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx(new TlsContext(TlsRole::SERVER));
+ test::configServer(server_ctx);
+ TlsStream<TestCallback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ TestCallback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ TlsContextPtr client_ctx;
+ test::configClient(client_ctx);
+ TlsStream<TestCallback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ TestCallback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Setup a timeout.
+ IntervalTimer timer(service);
+ bool timeout = false;
+ timer.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Perform TLS handshakes.
+ TestCallback server_cb(&server.lowest_layer());
+ server.handshake(server_cb);
+ TestCallback client_cb;
+ client.handshake(client_cb);
+ while (!timeout && (!server_cb.getCalled() || !client_cb.getCalled())) {
+ service.run_one();
+ }
+ timer.cancel();
+
+ // No problem is expected.
+ EXPECT_FALSE(timeout);
+ EXPECT_TRUE(server_cb.getCalled());
+ EXPECT_FALSE(server_cb.getCode());
+ EXPECT_TRUE(client_cb.getCalled());
+ EXPECT_FALSE(client_cb.getCode());
+
+ // Setup a timeout for the shutdown and receive.
+ IntervalTimer timer2(service);
+ timeout = false;
+ timer2.setup([&timeout] { timeout = true; }, 100, IntervalTimer::ONE_SHOT);
+
+ // Receive on the server.
+ vector<char> receive_buf(64);
+ TestCallback receive_cb;
+ server.async_read_some(boost::asio::buffer(receive_buf), receive_cb);
+
+ // Shutdown on the client.
+ TestCallback shutdown_cb;
+ client.shutdown(shutdown_cb);
+
+ // Post a close which should be called after the shutdown.
+ service.post([&client] { client.lowest_layer().close(); });
+ while (!timeout && (!shutdown_cb.getCalled() || !receive_cb.getCalled())) {
+ service.run_one();
+ }
+ timer2.cancel();
+
+ Expecteds exps;
+ // Botan gets no error.
+ exps.addNoError();
+ // LibreSSL and some old OpenSSL gets Operation canceled.
+ exps.addError("Operation canceled");
+ // OpenSSL gets Bad file descriptor.
+ exps.addError("Bad file descriptor");
+ exps.checkAsync("shutdown", shutdown_cb);
+ if (Expecteds::displayErrMsg()) {
+ if (timeout) {
+ std::cout << "shutdown timeout\n";
+ } else if (exps.hasErrMsg()) {
+ std::cout << "shutdown: " << exps.getErrMsg() << "\n";
+ }
+ }
+
+ // End of file on the receive side.
+ EXPECT_TRUE(receive_cb.getCalled());
+ EXPECT_TRUE(receive_cb.getCode());
+ EXPECT_EQ("End of file", receive_cb.getCode().message());
+ if (Expecteds::displayErrMsg()) {
+ std::cout << "receive: " << receive_cb.getCode().message() << "\n";
+ }
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Conclusion about the shutdown: do the close on completion (e.g. in the
+// handler) or on timeout (i.e. simulate an asynchronous shutdown with
+// timeout).
+
+} // end of anonymous namespace.
diff --git a/src/lib/asiolink/tests/udp_endpoint_unittest.cc b/src/lib/asiolink/tests/udp_endpoint_unittest.cc
new file mode 100644
index 0000000..cf932f1
--- /dev/null
+++ b/src/lib/asiolink/tests/udp_endpoint_unittest.cc
@@ -0,0 +1,46 @@
+// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_address.h>
+#include <asiolink/udp_endpoint.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+using namespace isc::asiolink;
+using namespace std;
+
+// This test checks that the endpoint can manage its own internal
+// boost::asio::ip::udp::endpoint object.
+
+TEST(UDPEndpointTest, v4Address) {
+ const string test_address("192.0.2.1");
+ const unsigned short test_port = 5301;
+
+ IOAddress address(test_address);
+ UDPEndpoint endpoint(address, test_port);
+
+ EXPECT_TRUE(address == endpoint.getAddress());
+ EXPECT_EQ(test_port, endpoint.getPort());
+ EXPECT_EQ(static_cast<short>(IPPROTO_UDP), endpoint.getProtocol());
+ EXPECT_EQ(AF_INET, endpoint.getFamily());
+}
+
+TEST(UDPEndpointTest, v6Address) {
+ const string test_address("2001:db8::1235");
+ const unsigned short test_port = 5302;
+
+ IOAddress address(test_address);
+ UDPEndpoint endpoint(address, test_port);
+
+ EXPECT_TRUE(address == endpoint.getAddress());
+ EXPECT_EQ(test_port, endpoint.getPort());
+ EXPECT_EQ(static_cast<short>(IPPROTO_UDP), endpoint.getProtocol());
+ EXPECT_EQ(AF_INET6, endpoint.getFamily());
+}
diff --git a/src/lib/asiolink/tests/udp_socket_unittest.cc b/src/lib/asiolink/tests/udp_socket_unittest.cc
new file mode 100644
index 0000000..1fb9616
--- /dev/null
+++ b/src/lib/asiolink/tests/udp_socket_unittest.cc
@@ -0,0 +1,324 @@
+// Copyright (C) 2011-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// \brief Test of UDPSocket
+///
+/// Tests the functionality of a UDPSocket by working through an open-send-
+/// receive-close sequence and checking that the asynchronous notifications
+/// work.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_service.h>
+#include <asiolink/udp_endpoint.h>
+#include <asiolink/udp_socket.h>
+#include <util/buffer.h>
+#include <util/io_utilities.h>
+
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <string>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <algorithm>
+#include <cstdlib>
+#include <cstddef>
+#include <vector>
+
+
+using namespace boost::asio;
+using namespace isc::util;
+using namespace isc::asiolink;
+using namespace std;
+
+namespace {
+
+const char SERVER_ADDRESS[] = "127.0.0.1";
+const unsigned short SERVER_PORT = 5301;
+
+// TODO: Shouldn't we send something that is real message?
+const char OUTBOUND_DATA[] = "Data sent from client to server";
+const char INBOUND_DATA[] = "Returned data from server to client";
+}
+
+///
+/// An instance of this object is passed to the asynchronous I/O functions
+/// and the operator() method is called when when an asynchronous I/O
+/// completes. The arguments to the completion callback are stored for later
+/// retrieval.
+class UDPCallback {
+public:
+
+ struct PrivateData {
+ PrivateData() :
+ error_code_(), length_(0), called_(false), name_("")
+ {}
+
+ boost::system::error_code error_code_; ///< Completion error code
+ size_t length_; ///< Number of bytes transferred
+ bool called_; ///< Set true when callback called
+ std::string name_; ///< Which of the objects this is
+ };
+
+ /// \brief Constructor
+ ///
+ /// Constructs the object. It also creates the data member pointed to by
+ /// a shared pointer. When used as a callback object, this is copied as it
+ /// is passed into the asynchronous function. This means that there are two
+ /// objects and inspecting the one we passed in does not tell us anything.
+ ///
+ /// Therefore we use a boost::shared_ptr. When the object is copied, the
+ /// shared pointer is copied, which leaves both objects pointing to the same
+ /// data.
+ ///
+ /// \param which Which of the two callback objects this is
+ UDPCallback(const std::string& which) : ptr_(new PrivateData())
+ {
+ setName(which);
+ }
+
+ /// \brief Destructor
+ ///
+ /// No code needed, destroying the shared pointer destroys the private data.
+ virtual ~UDPCallback()
+ {}
+
+ /// \brief Callback Function
+ ///
+ /// Called when an asynchronous I/O completes, this stores the
+ /// completion error code and the number of bytes transferred.
+ ///
+ /// \param ec I/O completion error code passed to callback function.
+ /// \param length Number of bytes transferred
+ virtual void operator()(boost::system::error_code ec, size_t length = 0) {
+ ptr_->error_code_ = ec;
+ setLength(length);
+ setCalled(true);
+ }
+
+ /// \brief Get I/O completion error code
+ int getCode() {
+ return (ptr_->error_code_.value());
+ }
+
+ /// \brief Set I/O completion code
+ ///
+ /// \param code New value of completion code
+ void setCode(int code) {
+ ptr_->error_code_ = boost::system::error_code(code, boost::system::error_code().category());
+ }
+
+ /// \brief Get number of bytes transferred in I/O
+ size_t getLength() const {
+ return (ptr_->length_);
+ }
+
+ /// \brief Set number of bytes transferred in I/O
+ ///
+ /// \param length New value of length parameter
+ void setLength(size_t length) {
+ ptr_->length_ = length;
+ }
+
+ /// \brief Get flag to say when callback was called
+ bool getCalled() const {
+ return (ptr_->called_);
+ }
+
+ /// \brief Set flag to say when callback was called
+ ///
+ /// \param called New value of called parameter
+ void setCalled(bool called) {
+ ptr_->called_ = called;
+ }
+
+ /// \brief Return instance of callback name
+ std::string getName() const {
+ return (ptr_->name_);
+ }
+
+ /// \brief Set callback name
+ ///
+ /// \param name New value of the callback name
+ void setName(const std::string& name) {
+ ptr_->name_ = name;
+ }
+
+private:
+ boost::shared_ptr<PrivateData> ptr_; ///< Pointer to private data
+};
+
+// Receive complete method should return true regardless of what is in the first
+// two bytes of a buffer.
+
+TEST(UDPSocket, processReceivedData) {
+ IOService service; // Used to instantiate socket
+ UDPSocket<UDPCallback> test(service); // Socket under test
+ uint8_t inbuff[32]; // Buffer to check
+ OutputBufferPtr outbuff(new OutputBuffer(16));
+ // Where data is put
+ // cppcheck-suppress variableScope
+ size_t expected; // Expected amount of data
+ // cppcheck-suppress variableScope
+ size_t offset; // Where to put next data
+ // cppcheck-suppress variableScope
+ size_t cumulative; // Cumulative data received
+
+ // Set some dummy values in the buffer to check
+ for (uint8_t i = 0; i < sizeof(inbuff); ++i) {
+ inbuff[i] = i;
+ }
+
+ // Expect that the value is true whatever number is written in the first
+ // two bytes of the buffer.
+ uint16_t count = 0;
+ for (uint32_t i = 0; i < (2 << 16); ++i, ++count) {
+ writeUint16(count, inbuff, sizeof(inbuff));
+
+ // Set some random values
+ cumulative = 5;
+ offset = 10;
+ expected = 15;
+ outbuff->clear();
+
+ bool completed = test.processReceivedData(inbuff, sizeof(inbuff),
+ cumulative, offset, expected,
+ outbuff);
+ EXPECT_TRUE(completed);
+ EXPECT_EQ(sizeof(inbuff), cumulative);
+ EXPECT_EQ(0, offset);
+ EXPECT_EQ(sizeof(inbuff), expected);
+
+ const uint8_t* dataptr = static_cast<const uint8_t*>(outbuff->getData());
+ EXPECT_TRUE(equal(inbuff, inbuff + sizeof(inbuff) - 1, dataptr));
+ }
+}
+
+// TODO: Need to add a test to check the cancel() method
+
+// Tests the operation of a UDPSocket by opening it, sending an asynchronous
+// message to a server, receiving an asynchronous message from the server and
+// closing.
+TEST(UDPSocket, SequenceTest) {
+
+ // Common objects.
+ IOService service; // Service object for async control
+
+ // Server
+ IOAddress server_address(SERVER_ADDRESS); // Address of target server
+ UDPCallback server_cb("Server"); // Server callback
+ UDPEndpoint server_endpoint( // Endpoint describing server
+ server_address, SERVER_PORT);
+ UDPEndpoint server_remote_endpoint; // Address where server received message from
+
+ // The client - the UDPSocket being tested
+ UDPSocket<UDPCallback> client(service);// Socket under test
+ UDPCallback client_cb("Client"); // Async I/O callback function
+ UDPEndpoint client_remote_endpoint; // Where client receives message from
+ size_t client_cumulative = 0; // Cumulative data received
+ size_t client_offset = 0; // Offset into buffer where data is put
+ size_t client_expected = 0; // Expected amount of data
+ OutputBufferPtr client_buffer(new OutputBuffer(16));
+ // Where data is put
+
+ // The server - with which the client communicates. For convenience, we
+ // use the same io_service, and use the endpoint object created for
+ // the client to send to as the endpoint object in the constructor.
+ boost::asio::ip::udp::socket server(service.get_io_service(),
+ server_endpoint.getASIOEndpoint());
+ server.set_option(socket_base::reuse_address(true));
+
+ // Assertion to ensure that the server buffer is large enough
+ char data[UDPSocket<UDPCallback>::MIN_SIZE];
+ ASSERT_GT(sizeof(data), sizeof(OUTBOUND_DATA));
+
+ // Open the client socket - the operation should be synchronous
+ EXPECT_TRUE(client.isOpenSynchronous());
+ client.open(&server_endpoint, client_cb);
+
+ // Issue read on the server. Completion callback should not have run.
+ server_cb.setCalled(false);
+ server_cb.setCode(42); // Answer to Life, the Universe and Everything!
+ server.async_receive_from(buffer(data, sizeof(data)),
+ server_remote_endpoint.getASIOEndpoint(), server_cb);
+ EXPECT_FALSE(server_cb.getCalled());
+
+ // Write something to the server using the client - the callback should not
+ // be called until we call the io_service.run() method.
+ client_cb.setCalled(false);
+ client_cb.setCode(7); // Arbitrary number
+ client.asyncSend(OUTBOUND_DATA, sizeof(OUTBOUND_DATA), &server_endpoint, client_cb);
+ EXPECT_FALSE(client_cb.getCalled());
+
+ // Execute the two callbacks.
+ service.run_one();
+ service.run_one();
+
+ EXPECT_TRUE(client_cb.getCalled());
+ EXPECT_EQ(0, client_cb.getCode());
+ EXPECT_EQ(sizeof(OUTBOUND_DATA), client_cb.getLength());
+
+ EXPECT_TRUE(server_cb.getCalled());
+ EXPECT_EQ(0, server_cb.getCode());
+ EXPECT_EQ(sizeof(OUTBOUND_DATA), server_cb.getLength());
+
+ EXPECT_TRUE(equal(&data[0], &data[server_cb.getLength() - 1], OUTBOUND_DATA));
+
+ // Now return data from the server to the client. Issue the read on the
+ // client.
+ client_cb.setLength(12345); // Arbitrary number
+ client_cb.setCalled(false);
+ client_cb.setCode(32); // Arbitrary number
+ client.asyncReceive(data, sizeof(data), client_cumulative,
+ &client_remote_endpoint, client_cb);
+
+ // Issue the write on the server side to the source of the data it received.
+ server_cb.setLength(22345); // Arbitrary number
+ server_cb.setCalled(false);
+ server_cb.setCode(232); // Arbitrary number
+ server.async_send_to(buffer(INBOUND_DATA, sizeof(INBOUND_DATA)),
+ server_remote_endpoint.getASIOEndpoint(), server_cb);
+
+ // Expect two callbacks to run.
+ service.run_one();
+ service.run_one();
+
+ EXPECT_TRUE(client_cb.getCalled());
+ EXPECT_EQ(0, client_cb.getCode());
+ EXPECT_EQ(sizeof(INBOUND_DATA), client_cb.getLength());
+
+ EXPECT_TRUE(server_cb.getCalled());
+ EXPECT_EQ(0, server_cb.getCode());
+ EXPECT_EQ(sizeof(INBOUND_DATA), server_cb.getLength());
+
+ EXPECT_TRUE(equal(&data[0], &data[server_cb.getLength() - 1], INBOUND_DATA));
+
+ // Check that the address/port received by the client corresponds to the
+ // address and port the server is listening on.
+ EXPECT_TRUE(server_address == client_remote_endpoint.getAddress());
+ EXPECT_EQ(SERVER_PORT, client_remote_endpoint.getPort());
+
+ // Check that the receive received a complete buffer's worth of data.
+ EXPECT_TRUE(client.processReceivedData(&data[0], client_cb.getLength(),
+ client_cumulative, client_offset,
+ client_expected, client_buffer));
+
+ EXPECT_EQ(client_cb.getLength(), client_cumulative);
+ EXPECT_EQ(0, client_offset);
+ EXPECT_EQ(client_cb.getLength(), client_expected);
+ EXPECT_EQ(client_cb.getLength(), client_buffer->getLength());
+
+ // ...and check that the data was copied to the output client buffer.
+ const char* client_char_data = static_cast<const char*>(client_buffer->getData());
+ EXPECT_TRUE(equal(&data[0], &data[client_cb.getLength() - 1], client_char_data));
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.close());
+ EXPECT_NO_THROW(server.close());
+}
diff --git a/src/lib/asiolink/tests/unix_domain_socket_unittest.cc b/src/lib/asiolink/tests/unix_domain_socket_unittest.cc
new file mode 100644
index 0000000..31c573d
--- /dev/null
+++ b/src/lib/asiolink/tests/unix_domain_socket_unittest.cc
@@ -0,0 +1,310 @@
+// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_service.h>
+#include <asiolink/unix_domain_socket.h>
+#include <asiolink/testutils/test_server_unix_socket.h>
+#include <gtest/gtest.h>
+#include <testutils/sandbox.h>
+#include <array>
+#include <cstdio>
+#include <cstdlib>
+#include <sstream>
+#include <string>
+
+using namespace isc::asiolink;
+
+namespace {
+
+/// @brief Test timeout in ms.
+const long TEST_TIMEOUT = 10000;
+
+/// @brief Test fixture class for @ref UnixDomainSocket class.
+class UnixDomainSocketTest : public ::testing::Test {
+public:
+ isc::test::Sandbox sandbox;
+
+ /// @brief Constructor.
+ ///
+ /// Removes unix socket descriptor before the test.
+ UnixDomainSocketTest() :
+ io_service_(),
+ test_socket_(new test::TestServerUnixSocket(io_service_,
+ unixSocketFilePath())),
+ response_(),
+ read_buf_() {
+ test_socket_->startTimer(TEST_TIMEOUT);
+ removeUnixSocketFile();
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Removes unix socket descriptor after the test.
+ virtual ~UnixDomainSocketTest() {
+ removeUnixSocketFile();
+ }
+
+ /// @brief Returns socket file path.
+ ///
+ /// If the KEA_SOCKET_TEST_DIR environment variable is specified, the
+ /// socket file is created in the location pointed to by this variable.
+ /// Otherwise, it is created in the build directory.
+ std::string unixSocketFilePath() {
+ std::string socket_path;
+ const char* env = getenv("KEA_SOCKET_TEST_DIR");
+ if (env) {
+ socket_path = std::string(env) + "/test-socket";
+ } else {
+ socket_path = sandbox.join("test-socket");
+ }
+ return (socket_path);
+ }
+
+ /// @brief Removes unix socket descriptor.
+ void removeUnixSocketFile() {
+ static_cast<void>(remove(unixSocketFilePath().c_str()));
+ }
+
+ /// @brief Performs asynchronous receive on unix domain socket.
+ ///
+ /// This function performs partial read from the unix domain socket.
+ /// It uses @c read_buf_ or small size to ensure that the buffer fills
+ /// in before all that have been read. The partial responses are
+ /// appended to the @c response_ class member.
+ ///
+ /// If the response received so far is shorter than the expected
+ /// response, another partial read is scheduled.
+ ///
+ /// @param socket Reference to the unix domain socket.
+ /// @param expected_response Expected response.
+ void doReceive(UnixDomainSocket& socket,
+ const std::string& expected_response) {
+ socket.asyncReceive(&read_buf_[0], read_buf_.size(),
+ [this, &socket, expected_response]
+ (const boost::system::error_code& ec, size_t length) {
+ if (!ec) {
+ // Append partial response received and see if the
+ // size of the response received so far is still
+ // smaller than expected. If it is, schedule another
+ // partial read.
+ response_.append(&read_buf_[0], length);
+ if (expected_response.size() > response_.size()) {
+ doReceive(socket, expected_response);
+ }
+
+ } else if (ec.value() != boost::asio::error::operation_aborted) {
+ ADD_FAILURE() << "error occurred while asynchronously receiving"
+ " data via unix domain socket: " << ec.message();
+ }
+ });
+ }
+
+ /// @brief IO service used by the tests.
+ IOService io_service_;
+
+ /// @brief Server side unix socket used in these tests.
+ test::TestServerUnixSocketPtr test_socket_;
+
+ /// @brief String containing a response received with @c doReceive.
+ std::string response_;
+
+ /// @brief Read buffer used by @c doReceive.
+ std::array<char, 2> read_buf_;
+};
+
+// This test verifies that the client can send data over the unix
+// domain socket and receive a response.
+TEST_F(UnixDomainSocketTest, sendReceive) {
+ // Start the server.
+ test_socket_->bindServerSocket();
+
+ // Setup client side.
+ UnixDomainSocket socket(io_service_);
+ ASSERT_NO_THROW(socket.connect(unixSocketFilePath()));
+
+ // Send "foo".
+ const std::string outbound_data = "foo";
+ size_t sent_size = 0;
+ ASSERT_NO_THROW(sent_size = socket.write(outbound_data.c_str(),
+ outbound_data.size()));
+ // Make sure all data have been sent.
+ ASSERT_EQ(outbound_data.size(), sent_size);
+
+ // Run IO service to generate server's response.
+ while ((test_socket_->getResponseNum() < 1) &&
+ (!test_socket_->isStopped())) {
+ io_service_.run_one();
+ }
+
+ // Receive response from the socket.
+ std::array<char, 1024> read_buf;
+ size_t bytes_read = 0;
+ ASSERT_NO_THROW(bytes_read = socket.receive(&read_buf[0], read_buf.size()));
+ std::string response(&read_buf[0], bytes_read);
+
+ // The server should prepend "received" to the data we had sent.
+ EXPECT_EQ("received foo", response);
+}
+
+// This test verifies that the client can send the data over the unix
+// domain socket and receive a response asynchronously.
+TEST_F(UnixDomainSocketTest, asyncSendReceive) {
+ // Start the server.
+ test_socket_->bindServerSocket();
+
+ // Setup client side.
+ UnixDomainSocket socket(io_service_);
+
+ // We're going to asynchronously connect to the server. The boolean value
+ // below will be modified by the connect handler function (lambda) invoked
+ // when the connection is established or if an error occurs.
+ bool connect_handler_invoked = false;
+ ASSERT_NO_THROW(socket.asyncConnect(unixSocketFilePath(),
+ [&connect_handler_invoked](const boost::system::error_code& ec) {
+ // Indicate that the handler has been called so as the loop below gets
+ // interrupted.
+ connect_handler_invoked = true;
+ // Operation aborted indicates that IO service has been stopped. This
+ // shouldn't happen here.
+ if (ec && (ec.value() != boost::asio::error::operation_aborted)) {
+ ADD_FAILURE() << "error occurred while asynchronously connecting"
+ " via unix domain socket: " << ec.message();
+ }
+ }
+ ));
+ // Run IO service until connect handler is invoked.
+ while (!connect_handler_invoked && (!test_socket_->isStopped())) {
+ io_service_.run_one();
+ }
+
+ // We are going to asynchronously send the 'foo' over the unix socket.
+ const std::string outbound_data = "foo";
+ size_t sent_size = 0;
+ ASSERT_NO_THROW(socket.asyncSend(outbound_data.c_str(), outbound_data.size(),
+ [&sent_size](const boost::system::error_code& ec, size_t length) {
+ // If we have been successful sending the data, record the number of
+ // bytes we have sent.
+ if (!ec) {
+ sent_size = length;
+
+ } else if (ec.value() != boost::asio::error::operation_aborted) {
+ ADD_FAILURE() << "error occurred while asynchronously sending the"
+ " data over unix domain socket: " << ec.message();
+ }
+ }
+ ));
+
+ // Run IO service to generate server's response.
+ while ((test_socket_->getResponseNum() < 1) &&
+ (!test_socket_->isStopped())) {
+ io_service_.run_one();
+ }
+
+ // There is no guarantee that all data have been sent so we only check that
+ // some data have been sent.
+ ASSERT_GT(sent_size, 0);
+
+ std::string expected_response = "received foo";
+ doReceive(socket, expected_response);
+
+ // Run IO service until we get the full response from the server.
+ while ((response_.size() < expected_response.size()) &&
+ !test_socket_->isStopped()) {
+ io_service_.run_one();
+ }
+
+ // Check that the entire response has been received and is correct.
+ EXPECT_EQ(expected_response, response_);
+}
+
+// This test verifies that UnixDomainSocketError exception is thrown
+// on attempt to connect, write or receive when the server socket
+// is not available.
+TEST_F(UnixDomainSocketTest, clientErrors) {
+ UnixDomainSocket socket(io_service_);
+ ASSERT_THROW(socket.connect(unixSocketFilePath()), UnixDomainSocketError);
+ const std::string outbound_data = "foo";
+ ASSERT_THROW(socket.write(outbound_data.c_str(), outbound_data.size()),
+ UnixDomainSocketError);
+ std::array<char, 1024> read_buf;
+ ASSERT_THROW(socket.receive(&read_buf[0], read_buf.size()),
+ UnixDomainSocketError);
+}
+
+// This test verifies that an error is returned on attempt to asynchronously
+// connect, write or receive when the server socket is not available.
+TEST_F(UnixDomainSocketTest, asyncClientErrors) {
+ UnixDomainSocket socket(io_service_);
+
+ // Asynchronous operations signal errors through boost::system::error_code
+ // object passed to the handler function. This object casts to boolean.
+ // In case of success the object casts to false. In case of an error it
+ // casts to true. The actual error codes can be retrieved by comparing the
+ // ec objects to predefined error objects. We don't check for the actual
+ // errors here, because it is not certain that the same error codes would
+ // be returned on various operating systems.
+
+ // In the following tests we use C++11 lambdas as callbacks.
+
+ // Connect
+ bool connect_handler_invoked = false;
+ socket.asyncConnect(unixSocketFilePath(),
+ [&connect_handler_invoked](const boost::system::error_code& ec) {
+ connect_handler_invoked = true;
+ EXPECT_TRUE(ec);
+ });
+ while (!connect_handler_invoked && !test_socket_->isStopped()) {
+ io_service_.run_one();
+ }
+
+ // Send
+ const std::string outbound_data = "foo";
+ bool send_handler_invoked = false;
+ socket.asyncSend(outbound_data.c_str(), outbound_data.size(),
+ [&send_handler_invoked]
+ (const boost::system::error_code& ec, size_t /* length */) {
+ send_handler_invoked = true;
+ EXPECT_TRUE(ec);
+ });
+ while (!send_handler_invoked && !test_socket_->isStopped()) {
+ io_service_.run_one();
+ }
+
+ // Receive
+ bool receive_handler_invoked = false;
+ std::array<char, 1024> read_buf;
+ socket.asyncReceive(&read_buf[0], read_buf.size(),
+ [&receive_handler_invoked]
+ (const boost::system::error_code& ec, size_t /* length */) {
+ receive_handler_invoked = true;
+ EXPECT_TRUE(ec);
+ });
+ while (!receive_handler_invoked && !test_socket_->isStopped()) {
+ io_service_.run_one();
+ }
+}
+
+// Check that native socket descriptor is returned correctly when
+// the socket is connected.
+TEST_F(UnixDomainSocketTest, getNative) {
+ // Start the server.
+ test_socket_->bindServerSocket();
+
+ // Setup client side.
+ UnixDomainSocket socket(io_service_);
+ ASSERT_NO_THROW(socket.connect(unixSocketFilePath()));
+ ASSERT_GE(socket.getNative(), 0);
+}
+
+// Check that protocol returned is 0.
+TEST_F(UnixDomainSocketTest, getProtocol) {
+ UnixDomainSocket socket(io_service_);
+ EXPECT_EQ(0, socket.getProtocol());
+}
+
+}