diff options
Diffstat (limited to '')
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='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + 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()); +} + +} |