diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 11:36:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 11:36:04 +0000 |
commit | 040eee1aa49b49df4698d83a05af57c220127fd1 (patch) | |
tree | f635435954e6ccde5eee9893889e24f30ca68346 /src/lib/asiolink | |
parent | Initial commit. (diff) | |
download | isc-kea-upstream.tar.xz isc-kea-upstream.zip |
Adding upstream version 2.2.0.upstream/2.2.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
111 files changed, 21907 insertions, 0 deletions
diff --git a/src/lib/asiolink/Makefile.am b/src/lib/asiolink/Makefile.am new file mode 100644 index 0000000..acff3d0 --- /dev/null +++ b/src/lib/asiolink/Makefile.am @@ -0,0 +1,98 @@ +SUBDIRS = . testutils tests + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) -Wno-non-virtual-dtor + +EXTRA_DIST = asiolink.dox + +CLEANFILES = *.gcno *.gcda + +lib_LTLIBRARIES = libkea-asiolink.la + +libkea_asiolink_la_LDFLAGS = -no-undefined -version-info 40:0:0 +libkea_asiolink_la_LDFLAGS += $(CRYPTO_LDFLAGS) + +libkea_asiolink_la_SOURCES = asiolink.h +libkea_asiolink_la_SOURCES += asio_wrapper.h +libkea_asiolink_la_SOURCES += addr_utilities.cc addr_utilities.h +libkea_asiolink_la_SOURCES += botan_boost_tls.h botan_boost_wrapper.h +libkea_asiolink_la_SOURCES += botan_tls.h +libkea_asiolink_la_SOURCES += common_tls.cc common_tls.h +libkea_asiolink_la_SOURCES += crypto_tls.h +libkea_asiolink_la_SOURCES += dummy_io_cb.h +libkea_asiolink_la_SOURCES += interval_timer.cc interval_timer.h +libkea_asiolink_la_SOURCES += io_acceptor.h +libkea_asiolink_la_SOURCES += io_address.cc io_address.h +libkea_asiolink_la_SOURCES += io_asio_socket.h +libkea_asiolink_la_SOURCES += io_endpoint.cc io_endpoint.h +libkea_asiolink_la_SOURCES += io_error.h +libkea_asiolink_la_SOURCES += io_service.h io_service.cc +libkea_asiolink_la_SOURCES += io_service_signal.cc io_service_signal.h +libkea_asiolink_la_SOURCES += io_socket.h io_socket.cc +libkea_asiolink_la_SOURCES += openssl_tls.h +libkea_asiolink_la_SOURCES += process_spawn.h process_spawn.cc +libkea_asiolink_la_SOURCES += tcp_acceptor.h +libkea_asiolink_la_SOURCES += tcp_endpoint.h +libkea_asiolink_la_SOURCES += tcp_socket.h +libkea_asiolink_la_SOURCES += tls_acceptor.h +libkea_asiolink_la_SOURCES += tls_socket.h +libkea_asiolink_la_SOURCES += udp_endpoint.h +libkea_asiolink_la_SOURCES += udp_socket.h +libkea_asiolink_la_SOURCES += unix_domain_socket.cc unix_domain_socket.h +libkea_asiolink_la_SOURCES += unix_domain_socket_acceptor.h +libkea_asiolink_la_SOURCES += unix_domain_socket_endpoint.h + +if HAVE_BOTAN +if HAVE_BOTAN_BOOST +libkea_asiolink_la_SOURCES += botan_boost_tls.cc +else +libkea_asiolink_la_SOURCES += botan_tls.cc +endif +endif +if HAVE_OPENSSL +libkea_asiolink_la_SOURCES += openssl_tls.cc +endif + +# Note: the ordering matters: -Wno-... must follow -Wextra (defined in +# KEA_CXXFLAGS) +libkea_asiolink_la_CXXFLAGS = $(AM_CXXFLAGS) +libkea_asiolink_la_CPPFLAGS = $(AM_CPPFLAGS) +libkea_asiolink_la_LIBADD = $(top_builddir)/src/lib/util/libkea-util.la +libkea_asiolink_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libkea_asiolink_la_LIBADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +# Specify the headers for copying into the installation directory tree. +libkea_asiolink_includedir = $(pkgincludedir)/asiolink +libkea_asiolink_include_HEADERS = \ + addr_utilities.h \ + asio_wrapper.h \ + asiolink.h \ + botan_boost_tls.h \ + botan_boost_wrapper.h \ + botan_tls.h \ + common_tls.h \ + crypto_tls.h \ + dummy_io_cb.h \ + interval_timer.h \ + io_acceptor.h \ + io_address.h \ + io_asio_socket.h \ + io_endpoint.h \ + io_error.h \ + io_service.h \ + io_service_signal.h \ + io_socket.h \ + openssl_tls.h \ + process_spawn.h \ + tcp_acceptor.h \ + tcp_endpoint.h \ + tcp_socket.h \ + tls_acceptor.h \ + tls_socket.h \ + udp_endpoint.h \ + udp_socket.h \ + unix_domain_socket.h \ + unix_domain_socket_acceptor.h \ + unix_domain_socket_endpoint.h diff --git a/src/lib/asiolink/Makefile.in b/src/lib/asiolink/Makefile.in new file mode 100644 index 0000000..f2ebc29 --- /dev/null +++ b/src/lib/asiolink/Makefile.in @@ -0,0 +1,1138 @@ +# 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@ +@HAVE_BOTAN_BOOST_TRUE@@HAVE_BOTAN_TRUE@am__append_1 = botan_boost_tls.cc +@HAVE_BOTAN_BOOST_FALSE@@HAVE_BOTAN_TRUE@am__append_2 = botan_tls.cc +@HAVE_OPENSSL_TRUE@am__append_3 = openssl_tls.cc +subdir = src/lib/asiolink +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_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_sysrepo.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 $(libkea_asiolink_include_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" \ + "$(DESTDIR)$(libkea_asiolink_includedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libkea_asiolink_la_DEPENDENCIES = \ + $(top_builddir)/src/lib/util/libkea-util.la \ + $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am__libkea_asiolink_la_SOURCES_DIST = asiolink.h asio_wrapper.h \ + addr_utilities.cc addr_utilities.h botan_boost_tls.h \ + botan_boost_wrapper.h botan_tls.h common_tls.cc common_tls.h \ + crypto_tls.h dummy_io_cb.h interval_timer.cc interval_timer.h \ + io_acceptor.h io_address.cc io_address.h io_asio_socket.h \ + io_endpoint.cc io_endpoint.h io_error.h io_service.h \ + io_service.cc io_service_signal.cc io_service_signal.h \ + io_socket.h io_socket.cc openssl_tls.h process_spawn.h \ + process_spawn.cc tcp_acceptor.h tcp_endpoint.h tcp_socket.h \ + tls_acceptor.h tls_socket.h udp_endpoint.h udp_socket.h \ + unix_domain_socket.cc unix_domain_socket.h \ + unix_domain_socket_acceptor.h unix_domain_socket_endpoint.h \ + botan_boost_tls.cc botan_tls.cc openssl_tls.cc +@HAVE_BOTAN_BOOST_TRUE@@HAVE_BOTAN_TRUE@am__objects_1 = libkea_asiolink_la-botan_boost_tls.lo +@HAVE_BOTAN_BOOST_FALSE@@HAVE_BOTAN_TRUE@am__objects_2 = libkea_asiolink_la-botan_tls.lo +@HAVE_OPENSSL_TRUE@am__objects_3 = libkea_asiolink_la-openssl_tls.lo +am_libkea_asiolink_la_OBJECTS = libkea_asiolink_la-addr_utilities.lo \ + libkea_asiolink_la-common_tls.lo \ + libkea_asiolink_la-interval_timer.lo \ + libkea_asiolink_la-io_address.lo \ + libkea_asiolink_la-io_endpoint.lo \ + libkea_asiolink_la-io_service.lo \ + libkea_asiolink_la-io_service_signal.lo \ + libkea_asiolink_la-io_socket.lo \ + libkea_asiolink_la-process_spawn.lo \ + libkea_asiolink_la-unix_domain_socket.lo $(am__objects_1) \ + $(am__objects_2) $(am__objects_3) +libkea_asiolink_la_OBJECTS = $(am_libkea_asiolink_la_OBJECTS) +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 = +libkea_asiolink_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) \ + $(libkea_asiolink_la_LDFLAGS) $(LDFLAGS) -o $@ +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)/libkea_asiolink_la-addr_utilities.Plo \ + ./$(DEPDIR)/libkea_asiolink_la-botan_boost_tls.Plo \ + ./$(DEPDIR)/libkea_asiolink_la-botan_tls.Plo \ + ./$(DEPDIR)/libkea_asiolink_la-common_tls.Plo \ + ./$(DEPDIR)/libkea_asiolink_la-interval_timer.Plo \ + ./$(DEPDIR)/libkea_asiolink_la-io_address.Plo \ + ./$(DEPDIR)/libkea_asiolink_la-io_endpoint.Plo \ + ./$(DEPDIR)/libkea_asiolink_la-io_service.Plo \ + ./$(DEPDIR)/libkea_asiolink_la-io_service_signal.Plo \ + ./$(DEPDIR)/libkea_asiolink_la-io_socket.Plo \ + ./$(DEPDIR)/libkea_asiolink_la-openssl_tls.Plo \ + ./$(DEPDIR)/libkea_asiolink_la-process_spawn.Plo \ + ./$(DEPDIR)/libkea_asiolink_la-unix_domain_socket.Plo +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 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libkea_asiolink_la_SOURCES) +DIST_SOURCES = $(am__libkea_asiolink_la_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(libkea_asiolink_include_HEADERS) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +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 +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp README +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +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_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_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_SYSREPO = @HAVE_SYSREPO@ +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@ +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_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +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@ +SUBDIRS = . testutils tests +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib \ + $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) -Wno-non-virtual-dtor +EXTRA_DIST = asiolink.dox +CLEANFILES = *.gcno *.gcda +lib_LTLIBRARIES = libkea-asiolink.la +libkea_asiolink_la_LDFLAGS = -no-undefined -version-info 40:0:0 \ + $(CRYPTO_LDFLAGS) +libkea_asiolink_la_SOURCES = asiolink.h asio_wrapper.h \ + addr_utilities.cc addr_utilities.h botan_boost_tls.h \ + botan_boost_wrapper.h botan_tls.h common_tls.cc common_tls.h \ + crypto_tls.h dummy_io_cb.h interval_timer.cc interval_timer.h \ + io_acceptor.h io_address.cc io_address.h io_asio_socket.h \ + io_endpoint.cc io_endpoint.h io_error.h io_service.h \ + io_service.cc io_service_signal.cc io_service_signal.h \ + io_socket.h io_socket.cc openssl_tls.h process_spawn.h \ + process_spawn.cc tcp_acceptor.h tcp_endpoint.h tcp_socket.h \ + tls_acceptor.h tls_socket.h udp_endpoint.h udp_socket.h \ + unix_domain_socket.cc unix_domain_socket.h \ + unix_domain_socket_acceptor.h unix_domain_socket_endpoint.h \ + $(am__append_1) $(am__append_2) $(am__append_3) + +# Note: the ordering matters: -Wno-... must follow -Wextra (defined in +# KEA_CXXFLAGS) +libkea_asiolink_la_CXXFLAGS = $(AM_CXXFLAGS) +libkea_asiolink_la_CPPFLAGS = $(AM_CPPFLAGS) +libkea_asiolink_la_LIBADD = \ + $(top_builddir)/src/lib/util/libkea-util.la \ + $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ + $(BOOST_LIBS) $(CRYPTO_LIBS) + +# Specify the headers for copying into the installation directory tree. +libkea_asiolink_includedir = $(pkgincludedir)/asiolink +libkea_asiolink_include_HEADERS = \ + addr_utilities.h \ + asio_wrapper.h \ + asiolink.h \ + botan_boost_tls.h \ + botan_boost_wrapper.h \ + botan_tls.h \ + common_tls.h \ + crypto_tls.h \ + dummy_io_cb.h \ + interval_timer.h \ + io_acceptor.h \ + io_address.h \ + io_asio_socket.h \ + io_endpoint.h \ + io_error.h \ + io_service.h \ + io_service_signal.h \ + io_socket.h \ + openssl_tls.h \ + process_spawn.h \ + tcp_acceptor.h \ + tcp_endpoint.h \ + tcp_socket.h \ + tls_acceptor.h \ + tls_socket.h \ + udp_endpoint.h \ + udp_socket.h \ + unix_domain_socket.h \ + unix_domain_socket_acceptor.h \ + unix_domain_socket_endpoint.h + +all: all-recursive + +.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/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/asiolink/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): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libkea-asiolink.la: $(libkea_asiolink_la_OBJECTS) $(libkea_asiolink_la_DEPENDENCIES) $(EXTRA_libkea_asiolink_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libkea_asiolink_la_LINK) -rpath $(libdir) $(libkea_asiolink_la_OBJECTS) $(libkea_asiolink_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_asiolink_la-addr_utilities.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_asiolink_la-botan_boost_tls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_asiolink_la-botan_tls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_asiolink_la-common_tls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_asiolink_la-interval_timer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_asiolink_la-io_address.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_asiolink_la-io_endpoint.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_asiolink_la-io_service.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_asiolink_la-io_service_signal.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_asiolink_la-io_socket.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_asiolink_la-openssl_tls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_asiolink_la-process_spawn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_asiolink_la-unix_domain_socket.Plo@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 $@ $< + +libkea_asiolink_la-addr_utilities.lo: addr_utilities.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_asiolink_la-addr_utilities.lo -MD -MP -MF $(DEPDIR)/libkea_asiolink_la-addr_utilities.Tpo -c -o libkea_asiolink_la-addr_utilities.lo `test -f 'addr_utilities.cc' || echo '$(srcdir)/'`addr_utilities.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_asiolink_la-addr_utilities.Tpo $(DEPDIR)/libkea_asiolink_la-addr_utilities.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='addr_utilities.cc' object='libkea_asiolink_la-addr_utilities.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_asiolink_la-addr_utilities.lo `test -f 'addr_utilities.cc' || echo '$(srcdir)/'`addr_utilities.cc + +libkea_asiolink_la-common_tls.lo: common_tls.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_asiolink_la-common_tls.lo -MD -MP -MF $(DEPDIR)/libkea_asiolink_la-common_tls.Tpo -c -o libkea_asiolink_la-common_tls.lo `test -f 'common_tls.cc' || echo '$(srcdir)/'`common_tls.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_asiolink_la-common_tls.Tpo $(DEPDIR)/libkea_asiolink_la-common_tls.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='common_tls.cc' object='libkea_asiolink_la-common_tls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_asiolink_la-common_tls.lo `test -f 'common_tls.cc' || echo '$(srcdir)/'`common_tls.cc + +libkea_asiolink_la-interval_timer.lo: interval_timer.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_asiolink_la-interval_timer.lo -MD -MP -MF $(DEPDIR)/libkea_asiolink_la-interval_timer.Tpo -c -o libkea_asiolink_la-interval_timer.lo `test -f 'interval_timer.cc' || echo '$(srcdir)/'`interval_timer.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_asiolink_la-interval_timer.Tpo $(DEPDIR)/libkea_asiolink_la-interval_timer.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='interval_timer.cc' object='libkea_asiolink_la-interval_timer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_asiolink_la-interval_timer.lo `test -f 'interval_timer.cc' || echo '$(srcdir)/'`interval_timer.cc + +libkea_asiolink_la-io_address.lo: io_address.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_asiolink_la-io_address.lo -MD -MP -MF $(DEPDIR)/libkea_asiolink_la-io_address.Tpo -c -o libkea_asiolink_la-io_address.lo `test -f 'io_address.cc' || echo '$(srcdir)/'`io_address.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_asiolink_la-io_address.Tpo $(DEPDIR)/libkea_asiolink_la-io_address.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_address.cc' object='libkea_asiolink_la-io_address.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_asiolink_la-io_address.lo `test -f 'io_address.cc' || echo '$(srcdir)/'`io_address.cc + +libkea_asiolink_la-io_endpoint.lo: io_endpoint.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_asiolink_la-io_endpoint.lo -MD -MP -MF $(DEPDIR)/libkea_asiolink_la-io_endpoint.Tpo -c -o libkea_asiolink_la-io_endpoint.lo `test -f 'io_endpoint.cc' || echo '$(srcdir)/'`io_endpoint.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_asiolink_la-io_endpoint.Tpo $(DEPDIR)/libkea_asiolink_la-io_endpoint.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_endpoint.cc' object='libkea_asiolink_la-io_endpoint.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_asiolink_la-io_endpoint.lo `test -f 'io_endpoint.cc' || echo '$(srcdir)/'`io_endpoint.cc + +libkea_asiolink_la-io_service.lo: io_service.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_asiolink_la-io_service.lo -MD -MP -MF $(DEPDIR)/libkea_asiolink_la-io_service.Tpo -c -o libkea_asiolink_la-io_service.lo `test -f 'io_service.cc' || echo '$(srcdir)/'`io_service.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_asiolink_la-io_service.Tpo $(DEPDIR)/libkea_asiolink_la-io_service.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_service.cc' object='libkea_asiolink_la-io_service.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_asiolink_la-io_service.lo `test -f 'io_service.cc' || echo '$(srcdir)/'`io_service.cc + +libkea_asiolink_la-io_service_signal.lo: io_service_signal.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_asiolink_la-io_service_signal.lo -MD -MP -MF $(DEPDIR)/libkea_asiolink_la-io_service_signal.Tpo -c -o libkea_asiolink_la-io_service_signal.lo `test -f 'io_service_signal.cc' || echo '$(srcdir)/'`io_service_signal.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_asiolink_la-io_service_signal.Tpo $(DEPDIR)/libkea_asiolink_la-io_service_signal.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_service_signal.cc' object='libkea_asiolink_la-io_service_signal.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_asiolink_la-io_service_signal.lo `test -f 'io_service_signal.cc' || echo '$(srcdir)/'`io_service_signal.cc + +libkea_asiolink_la-io_socket.lo: io_socket.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_asiolink_la-io_socket.lo -MD -MP -MF $(DEPDIR)/libkea_asiolink_la-io_socket.Tpo -c -o libkea_asiolink_la-io_socket.lo `test -f 'io_socket.cc' || echo '$(srcdir)/'`io_socket.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_asiolink_la-io_socket.Tpo $(DEPDIR)/libkea_asiolink_la-io_socket.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_socket.cc' object='libkea_asiolink_la-io_socket.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_asiolink_la-io_socket.lo `test -f 'io_socket.cc' || echo '$(srcdir)/'`io_socket.cc + +libkea_asiolink_la-process_spawn.lo: process_spawn.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_asiolink_la-process_spawn.lo -MD -MP -MF $(DEPDIR)/libkea_asiolink_la-process_spawn.Tpo -c -o libkea_asiolink_la-process_spawn.lo `test -f 'process_spawn.cc' || echo '$(srcdir)/'`process_spawn.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_asiolink_la-process_spawn.Tpo $(DEPDIR)/libkea_asiolink_la-process_spawn.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='process_spawn.cc' object='libkea_asiolink_la-process_spawn.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_asiolink_la-process_spawn.lo `test -f 'process_spawn.cc' || echo '$(srcdir)/'`process_spawn.cc + +libkea_asiolink_la-unix_domain_socket.lo: unix_domain_socket.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_asiolink_la-unix_domain_socket.lo -MD -MP -MF $(DEPDIR)/libkea_asiolink_la-unix_domain_socket.Tpo -c -o libkea_asiolink_la-unix_domain_socket.lo `test -f 'unix_domain_socket.cc' || echo '$(srcdir)/'`unix_domain_socket.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_asiolink_la-unix_domain_socket.Tpo $(DEPDIR)/libkea_asiolink_la-unix_domain_socket.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='unix_domain_socket.cc' object='libkea_asiolink_la-unix_domain_socket.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_asiolink_la-unix_domain_socket.lo `test -f 'unix_domain_socket.cc' || echo '$(srcdir)/'`unix_domain_socket.cc + +libkea_asiolink_la-botan_boost_tls.lo: botan_boost_tls.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_asiolink_la-botan_boost_tls.lo -MD -MP -MF $(DEPDIR)/libkea_asiolink_la-botan_boost_tls.Tpo -c -o libkea_asiolink_la-botan_boost_tls.lo `test -f 'botan_boost_tls.cc' || echo '$(srcdir)/'`botan_boost_tls.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_asiolink_la-botan_boost_tls.Tpo $(DEPDIR)/libkea_asiolink_la-botan_boost_tls.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='botan_boost_tls.cc' object='libkea_asiolink_la-botan_boost_tls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_asiolink_la-botan_boost_tls.lo `test -f 'botan_boost_tls.cc' || echo '$(srcdir)/'`botan_boost_tls.cc + +libkea_asiolink_la-botan_tls.lo: botan_tls.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_asiolink_la-botan_tls.lo -MD -MP -MF $(DEPDIR)/libkea_asiolink_la-botan_tls.Tpo -c -o libkea_asiolink_la-botan_tls.lo `test -f 'botan_tls.cc' || echo '$(srcdir)/'`botan_tls.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_asiolink_la-botan_tls.Tpo $(DEPDIR)/libkea_asiolink_la-botan_tls.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='botan_tls.cc' object='libkea_asiolink_la-botan_tls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_asiolink_la-botan_tls.lo `test -f 'botan_tls.cc' || echo '$(srcdir)/'`botan_tls.cc + +libkea_asiolink_la-openssl_tls.lo: openssl_tls.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_asiolink_la-openssl_tls.lo -MD -MP -MF $(DEPDIR)/libkea_asiolink_la-openssl_tls.Tpo -c -o libkea_asiolink_la-openssl_tls.lo `test -f 'openssl_tls.cc' || echo '$(srcdir)/'`openssl_tls.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_asiolink_la-openssl_tls.Tpo $(DEPDIR)/libkea_asiolink_la-openssl_tls.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='openssl_tls.cc' object='libkea_asiolink_la-openssl_tls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_asiolink_la_CPPFLAGS) $(CPPFLAGS) $(libkea_asiolink_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_asiolink_la-openssl_tls.lo `test -f 'openssl_tls.cc' || echo '$(srcdir)/'`openssl_tls.cc + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-libkea_asiolink_includeHEADERS: $(libkea_asiolink_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(libkea_asiolink_include_HEADERS)'; test -n "$(libkea_asiolink_includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libkea_asiolink_includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libkea_asiolink_includedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libkea_asiolink_includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(libkea_asiolink_includedir)" || exit $$?; \ + done + +uninstall-libkea_asiolink_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(libkea_asiolink_include_HEADERS)'; test -n "$(libkea_asiolink_includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(libkea_asiolink_includedir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(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-recursive + +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-recursive + +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 + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libkea_asiolink_includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +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) + +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-recursive + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/libkea_asiolink_la-addr_utilities.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-botan_boost_tls.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-botan_tls.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-common_tls.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-interval_timer.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-io_address.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-io_endpoint.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-io_service.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-io_service_signal.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-io_socket.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-openssl_tls.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-process_spawn.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-unix_domain_socket.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-libkea_asiolink_includeHEADERS + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/libkea_asiolink_la-addr_utilities.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-botan_boost_tls.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-botan_tls.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-common_tls.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-interval_timer.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-io_address.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-io_endpoint.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-io_service.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-io_service_signal.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-io_socket.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-openssl_tls.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-process_spawn.Plo + -rm -f ./$(DEPDIR)/libkea_asiolink_la-unix_domain_socket.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES \ + uninstall-libkea_asiolink_includeHEADERS + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool 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-libLTLIBRARIES \ + install-libkea_asiolink_includeHEADERS install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-libLTLIBRARIES \ + uninstall-libkea_asiolink_includeHEADERS + +.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/README b/src/lib/asiolink/README new file mode 100644 index 0000000..83f66b8 --- /dev/null +++ b/src/lib/asiolink/README @@ -0,0 +1,28 @@ +The asiolink library is intended to provide an abstraction layer between +Kea modules and the socket I/O subsystem we are using (currently, the +headers-only version of ASIO included in Boost). This has several benefits, +including: + + - Simple interface + + - Back-end flexibility: It would be relatively easy to switch to any + other asynchronous I/O system. + + - Cleaner compilation: The ASIO headers include code which can + generate warnings in some compilers due to unused parameters and + such. Including ASIO header files throughout the Kea tree would + require us to relax the strictness of our error checking. Including + them in only one place allows us to relax strictness here, while + leaving it in place elsewhere. + +Some of the classes defined here--for example, IOSocket, IOEndpoint, +and IOAddress--are to be used by Kea modules as wrappers around +ASIO-specific classes. + + +Logging +------- + +At this point, nothing is logged by this low-level library. We may +revisit that in the future, if we find suitable messages to log, but +right now there are also no loggers initialized or called. diff --git a/src/lib/asiolink/addr_utilities.cc b/src/lib/asiolink/addr_utilities.cc new file mode 100644 index 0000000..04b44dc --- /dev/null +++ b/src/lib/asiolink/addr_utilities.cc @@ -0,0 +1,430 @@ +// Copyright (C) 2012-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/addr_utilities.h> +#include <exceptions/exceptions.h> + +#include <vector> +#include <limits> +#include <string.h> + +using namespace isc; +using namespace isc::asiolink; + +namespace { + +/// @brief mask used for first/last address calculation in a IPv4 prefix +/// +/// Using a static mask is faster than calculating it dynamically every time. +const uint32_t bitMask4[] = { 0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff, + 0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff, + 0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff, + 0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff, + 0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff, + 0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff, + 0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f, + 0x0000000f, 0x00000007, 0x00000003, 0x00000001, + 0x00000000 }; + +/// @brief mask used for first/last address calculation in a IPv6 prefix +const uint8_t bitMask6[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; + +/// @brief mask used for IPv6 prefix calculation +const uint8_t revMask6[]= { 0xff, 0x7f, 0x3f, 0x1f, 0xf, 0x7, 0x3, 0x1 }; + + +/// @brief calculates the first IPv6 address in a IPv6 prefix +/// +/// Note: This is a private function. Do not use it directly. +/// Please use firstAddrInPrefix() instead. +/// +/// @param prefix IPv6 prefix +/// @param len prefix length +IOAddress firstAddrInPrefix6(const IOAddress& prefix, uint8_t len) { + if (len > 128) { + isc_throw(isc::BadValue, + "Too large netmask. 0..128 is allowed in IPv6"); + } + + // First we copy the whole address as 16 bytes. + // We don't check that it is a valid IPv6 address and thus has + // the required length because it is already checked by + // the calling function. + uint8_t packed[V6ADDRESS_LEN]; + memcpy(packed, &prefix.toBytes()[0], V6ADDRESS_LEN); + + // If the length is divisible by 8, it is simple. We just zero out the host + // part. Otherwise we need to handle the byte that has to be partially + // zeroed. + if (len % 8 != 0) { + + // Get the appropriate mask. It has relevant bits (those that should + // stay) set and irrelevant (those that should be wiped) cleared. + uint8_t mask = bitMask6[len % 8]; + + // Let's leave only whatever the mask says should not be cleared. + packed[len / 8] = packed[len / 8] & mask; + + // Since we have just dealt with this byte, let's move the prefix length + // to the beginning of the next byte (len is expressed in bits). + len = (len / 8 + 1) * 8; + } + + // Clear out the remaining bits. + for (int i = len / 8; i < sizeof(packed); ++i) { + packed[i] = 0x0; + } + + // Finally, let's wrap this into nice and easy IOAddress object. + return (IOAddress::fromBytes(AF_INET6, packed)); +} + +/// @brief calculates the first IPv4 address in a IPv4 prefix +/// +/// Note: This is a private function. Do not use it directly. +/// Please use firstAddrInPrefix() instead. +/// +/// @param prefix IPv4 prefix +/// @param len netmask length (0-32) +IOAddress firstAddrInPrefix4(const IOAddress& prefix, uint8_t len) { + if (len > 32) { + isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4"); + } + + // We don't check that it is a valid IPv4 address and thus has + // a required length of 4 bytes because it has been already + // checked by the calling function. + uint32_t addr = prefix.toUint32(); + return (IOAddress(addr & (~bitMask4[len]))); +} + +/// @brief calculates the last IPv4 address in a IPv4 prefix +/// +/// Note: This is a private function. Do not use it directly. +/// Please use firstAddrInPrefix() instead. +/// +/// @param prefix IPv4 prefix that we calculate first address for +/// @param len netmask length (0-32) +IOAddress lastAddrInPrefix4(const IOAddress& prefix, uint8_t len) { + if (len > 32) { + isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4"); + } + + uint32_t addr = prefix.toUint32(); + return (IOAddress(addr | bitMask4[len])); +} + +/// @brief calculates the last IPv6 address in a IPv6 prefix +/// +/// Note: This is a private function. Do not use it directly. +/// Please use lastAddrInPrefix() instead. +/// +/// @param prefix IPv6 prefix that we calculate first address for +/// @param len netmask length (0-128) +IOAddress lastAddrInPrefix6(const IOAddress& prefix, uint8_t len) { + if (len > 128) { + isc_throw(isc::BadValue, + "Too large netmask. 0..128 is allowed in IPv6"); + } + + // First we copy the whole address as 16 bytes. + uint8_t packed[V6ADDRESS_LEN]; + memcpy(packed, &prefix.toBytes()[0], 16); + + // if the length is divisible by 8, it is simple. We just fill the host part + // with ones. Otherwise we need to handle the byte that has to be partially + // zeroed. + if (len % 8 != 0) { + // Get the appropriate mask. It has relevant bits (those that should + // stay) set and irrelevant (those that should be set to 1) cleared. + uint8_t mask = bitMask6[len % 8]; + + // Let's set those irrelevant bits with 1. It would be perhaps + // easier to not use negation here and invert bitMask6 content. However, + // with this approach, we can use the same mask in first and last + // address calculations. + packed[len / 8] = packed[len / 8] | ~mask; + + // Since we have just dealt with this byte, let's move the prefix length + // to the beginning of the next byte (len is expressed in bits). + len = (len / 8 + 1) * 8; + } + + // Finally set remaining bits to 1. + for (int i = len / 8; i < sizeof(packed); ++i) { + packed[i] = 0xff; + } + + // Finally, let's wrap this into nice and easy IOAddress object. + return (IOAddress::fromBytes(AF_INET6, packed)); +} + +}; // end of anonymous namespace + +namespace isc { +namespace asiolink { + +IOAddress firstAddrInPrefix(const IOAddress& prefix, uint8_t len) { + if (prefix.isV4()) { + return (firstAddrInPrefix4(prefix, len)); + + } else { + return (firstAddrInPrefix6(prefix, len)); + + } +} + +IOAddress lastAddrInPrefix(const IOAddress& prefix, uint8_t len) { + if (prefix.isV4()) { + return (lastAddrInPrefix4(prefix, len)); + + } else { + return (lastAddrInPrefix6(prefix, len)); + + } +} + +IOAddress getNetmask4(uint8_t len) { + if (len > 32) { + isc_throw(BadValue, "Invalid netmask size " + << static_cast<unsigned>(len) << ", allowed range is 0..32"); + } + uint32_t x = ~bitMask4[len]; + + return (IOAddress(x)); +} + +uint64_t +addrsInRange(const IOAddress& min, const IOAddress& max) { + if (min.getFamily() != max.getFamily()) { + isc_throw(BadValue, "Both addresses have to be the same family"); + } + + if (max < min) { + isc_throw(BadValue, min.toText() << " must not be greater than " + << max.toText()); + } + + if (min.isV4()) { + // Let's explicitly cast last_ and first_ (IOAddress). This conversion is + // automatic, but let's explicitly cast it show that we moved to integer + // domain and addresses are now substractable. + uint64_t max_numeric = static_cast<uint64_t>(max.toUint32()); + uint64_t min_numeric = static_cast<uint64_t>(min.toUint32()); + + // We can simply subtract the values. We need to increase the result + // by one, as both min and max are included in the range. So even if + // min == max, there's one address. + return (max_numeric - min_numeric + 1); + } else { + + // Calculating the difference in v6 is more involved. Let's subtract + // one from the other. By subtracting min from max, we move the + // [a, b] range to the [0, (b-a)] range. We don't care about the beginning + // of the new range (it's always zero). The upper bound now specifies + // the number of addresses minus one. + IOAddress count = IOAddress::subtract(max, min); + + // There's one very special case. Someone is trying to check how many + // IPv6 addresses are in IPv6 address space. He called this method + // with ::, ffff:ffff:ffff:fffff:ffff:ffff:ffff:ffff. The diff is also + // all 1s. Had we increased it by one, the address would flip to all 0s. + // This will not happen in a real world. Apparently, unit-tests are + // sometimes nastier then a real world. + static IOAddress max6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); + if (count == max6) { + return (std::numeric_limits<uint64_t>::max()); + } + + // Increase it by one (a..a range still contains one address, even though + // a subtracted from a is zero). + count = IOAddress::increase(count); + + // We don't have uint128, so for anything greater than 2^64, we'll just + // assume numeric_limits<uint64_t>::max. Let's do it the manual way. + const std::vector<uint8_t>& bin(count.toBytes()); + + // If any of the most significant 64 bits is set, we have more than + // 2^64 addresses and can't represent it even on uint64_t. + for (int i = 0 ; i < 8; i++) { + if (bin[i]) { + return (std::numeric_limits<uint64_t>::max()); + } + } + + // Ok, we're good. The pool is sanely sized. It may be huge, but at least + // that's something we can represent on uint64_t. + uint64_t numeric = 0; + for (int i = 8; i < 16; i++) { + numeric <<= 8; + numeric += bin[i]; + } + + return (numeric); + } +} + +int +prefixLengthFromRange(const IOAddress& min, const IOAddress& max) { + if (min.getFamily() != max.getFamily()) { + isc_throw(BadValue, "Both addresses have to be the same family"); + } + + if (max < min) { + isc_throw(BadValue, min.toText() << " must not be greater than " + << max.toText()); + } + + if (min.isV4()) { + // Get addresses as integers + uint32_t max_numeric = max.toUint32(); + uint32_t min_numeric = min.toUint32(); + + // Get the exclusive or which must be one of the bit masks + // and the min must be at the beginning of the prefix + // so it does not contribute to trailing ones. + uint32_t xor_numeric = max_numeric ^ min_numeric; + if ((min_numeric & ~xor_numeric) != min_numeric) { + return (-1); + } + for (uint8_t prefix_len = 0; prefix_len <= 32; ++prefix_len) { + if (xor_numeric == bitMask4[prefix_len]) { + // Got it: the wanted value is also the index + return (static_cast<int>(prefix_len)); + } + } + + // If it was not found the range is not from a prefix / prefix_len + return (-1); + } else { + // Get addresses as 16 bytes + uint8_t min_packed[V6ADDRESS_LEN]; + memcpy(min_packed, &min.toBytes()[0], 16); + uint8_t max_packed[V6ADDRESS_LEN]; + memcpy(max_packed, &max.toBytes()[0], 16); + + // Scan the exclusive or of addresses to find a difference + int candidate = 128; + bool zeroes = true; + for (uint8_t i = 0; i < 16; ++i) { + uint8_t xor_byte = min_packed[i] ^ max_packed[i]; + // The min must be at the beginning of the prefix + // so it does not contribute to trailing ones. + if ((min_packed[i] & ~xor_byte) != min_packed[i]) { + return (-1); + } + if (zeroes) { + // Skipping zero bits searching for one bits + if (xor_byte == 0) { + continue; + } + // Found a one bit: note the fact + zeroes = false; + // Compare the exclusive or to masks + for (uint8_t j = 0; j < 8; ++j) { + if (xor_byte == revMask6[j]) { + // Got it the prefix length: note it + candidate = static_cast<int>((i * 8) + j); + } + } + if (candidate == 128) { + // Not found? The range is not from a prefix / prefix_len + return (-1); + } + } else { + // Checking that trailing bits are on bits + if (xor_byte == 0xff) { + continue; + } + // Not all ones is bad + return (-1); + } + } + return (candidate); + } +} + +uint64_t prefixesInRange(const uint8_t pool_len, const uint8_t delegated_len) { + if (delegated_len < pool_len) { + return (0); + } + + uint64_t count = delegated_len - pool_len; + + if (count == 0) { + // If we want to delegate /64 out of /64 pool, we have only + // one prefix. + return (1); + } else if (count >= 64) { + // If the difference is greater than or equal 64, e.g. we want to + // delegate /96 out of /16 pool, the number is bigger than we can + // express, so we'll stick with maximum value of uint64_t. + return (std::numeric_limits<uint64_t>::max()); + } else { + // Now count specifies the exponent (e.g. if the difference between the + // delegated and pool length is 4, we have 16 prefixes), so we need + // to calculate 2^(count - 1) + return ((static_cast<uint64_t>(2)) << (count - 1)); + } +} + +IOAddress offsetAddress(const IOAddress& addr, uint64_t offset) { + // There is nothing to do if the offset is 0. + if (offset == 0) { + return (addr); + } + + // If this is an IPv4 address, then we utilize the conversion to uint32_t. + if (addr.isV4()) { + auto addr_uint32 = static_cast<uint64_t>(addr.toUint32()); + // If the result would exceed the maximum possible IPv4 address, let's return + // the maximum IPv4 address. + if (static_cast<uint64_t>(std::numeric_limits<uint32_t>::max() - addr_uint32) < offset) { + return (IOAddress(std::numeric_limits<uint32_t>::max())); + } + return (IOAddress(static_cast<uint32_t>(addr_uint32 + offset))); + } + + // This is IPv6 address. Let's first convert the offset value to network + // byte order and store within the vector. + std::vector<uint8_t> offset_bytes(8); + for (int offset_idx = offset_bytes.size() - 1; offset_idx >= 0; --offset_idx) { + offset_bytes[offset_idx] = static_cast<uint8_t>(offset & 0x00000000000000ff); + offset = offset >> 8; + } + + // Convert the IPv6 address to vector. + auto addr_bytes = addr.toBytes(); + + // Sum up the bytes. + + uint16_t carry = 0; + for (int i = offset_bytes.size() - 1; (i >= 0) || (carry > 0); --i) { + // Sum the bytes of the address, offset and the carry. + uint16_t sum = static_cast<uint16_t>(addr_bytes[i+8]) + carry; + + // Protect against the case when we went beyond the offset vector and + // we have only carry to add. + if (i >= 0 ) { + sum += static_cast<uint16_t>(offset_bytes[i]); + } + + // Update the address byte. + addr_bytes[i+8] = sum % 256; + + // Calculate the carry value. + carry = sum / 256; + } + + // Reconstruct IPv6 address from the vector. + return (IOAddress::fromBytes(AF_INET6, &addr_bytes[0])); +} + + +}; +}; diff --git a/src/lib/asiolink/addr_utilities.h b/src/lib/asiolink/addr_utilities.h new file mode 100644 index 0000000..bd78dee --- /dev/null +++ b/src/lib/asiolink/addr_utilities.h @@ -0,0 +1,98 @@ +// Copyright (C) 2012-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/. + +#ifndef ADDR_UTILITIES_H +#define ADDR_UTILITIES_H + +#include <asiolink/io_address.h> + +namespace isc { +namespace asiolink { + +/// This code is based on similar code from the Dibbler project. I, Tomasz Mrugalski, +/// as a sole creator of that code hereby release it under BSD license for the benefit +/// of the Kea project. + +/// @brief returns a first address in a given prefix +/// +/// Example: For 2001:db8:1\::deaf:beef and length /120 the function will return +/// 2001:db8:1\::dead:be00. See also @ref lastAddrInPrefix. +/// +/// @param prefix and address that belongs to a prefix +/// @param len prefix length +/// +/// @return first address from a prefix +IOAddress firstAddrInPrefix(const IOAddress& prefix, uint8_t len); + +/// @brief returns a last address in a given prefix +/// +/// Example: For 2001:db8:1\::deaf:beef and length /112 the function will return +/// 2001:db8:1\::dead:ffff. See also @ref firstAddrInPrefix. +/// +/// @param prefix and address that belongs to a prefix +/// @param len prefix length +/// +/// @return first address from a prefix +IOAddress lastAddrInPrefix(const IOAddress& prefix, uint8_t len); + +/// @brief Generates an IPv4 netmask of specified length +/// @throw BadValue if len is greater than 32 +/// @return netmask +IOAddress getNetmask4(uint8_t len); + + +/// @brief Returns number of available addresses in the specified range (min - max). +/// +/// Note that for min equal max case, the number of addresses is one. +/// +/// @throw BadValue if min and max are not of the same family (both must be +/// either IPv4 or IPv6) or if max < min. +/// +/// @param min the first address in range +/// @param max the last address in range +/// @return number of addresses in range +uint64_t addrsInRange(const IOAddress& min, const IOAddress& max); + +/// @brief Returns prefix length from the specified range (min - max). +/// +/// This can be considered as log2(addrsInRange) +/// +/// @throw BadValue if min and max do not define a prefix. +/// +/// @param min the first address in range +/// @param max the last address in range +/// @return the prefix length or -1 if the range is not from a prefix +int prefixLengthFromRange(const IOAddress& min, const IOAddress& max); + +/// @brief Returns number of available IPv6 prefixes in the specified prefix. +/// +/// Note that if the answer is bigger than uint64_t can hold, it will return +/// the max value of uint64_t type. +/// +/// Example: prefixesInRange(48, 64) returns 65536, as there are /48 pool +/// can be split into 65536 prefixes, each /64 large. +/// +/// @param pool_len length of the pool in bits +/// @param delegated_len length of the prefixes to be delegated from the pool +/// @return number of prefixes in range +uint64_t prefixesInRange(const uint8_t pool_len, const uint8_t delegated_len); + +/// @brief Finds the address increased by offset. +/// +/// Adds offset to the IPv4 or iPv6 address and finds the resulting address. +/// Note that the current limitation is the maximum value of the offset, +/// i.e. max uint64_t. If the sum of the IPv4 address and the offset exceeds +/// the maximum value of uint32_t type, the 255.255.255.255 is returned. +/// +/// @param addr input address +/// @param offset distance of the returned address from the input address. +/// @return address being offset greater than the input address +IOAddress offsetAddress(const IOAddress& addr, uint64_t offset); + +}; +}; + +#endif // ADDR_UTILITIES_H diff --git a/src/lib/asiolink/asio_wrapper.h b/src/lib/asiolink/asio_wrapper.h new file mode 100644 index 0000000..1cedd09 --- /dev/null +++ b/src/lib/asiolink/asio_wrapper.h @@ -0,0 +1,78 @@ +// 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/. +#ifndef ASIO_WRAPPER_H +#define ASIO_WRAPPER_H 1 + +/// !!! IMPORTANT THIS IS A HACK FOR BOOST HEADERS ONLY BUILDING !!!! +/// +/// As of #5215 (Kea 1.3) The default build configuration is to link with +/// Boost's system library (boost_system) rather than build with Boost's +/// headers only. Linking with the boost_system eliminates the issue as +/// detailed below. This file exists solely for the purpose of allowing +/// people to attempt to build headers only. ISC DOES NOT RECOMMEND +/// building Kea with Boost headers only. +/// +/// This file must be included anywhere one would normally have included +/// boost/asio.hpp. Until the issue described below is resolved in some +/// other fashion, (or we abandon support for headers only building) +/// asio.hpp MUST NOT be included other than through this file. +/// +/// The optimizer as of gcc 5.2.0, may not reliably ensure a single value +/// returned by boost::system::system_category() within a translation unit +/// when building the header only version of the boost error handling. +/// See Trac #4243 for more details. For now we turn off optimization for +/// header only builds the under the suspect GCC versions. +/// +/// The issue arises from in-lining the above function, which returns a +/// reference to a local static variable, system_category_const. This leads +/// to situations where a construct such as the following: +/// +/// {{{ +/// if (ec == boost::asio::error::would_block +/// || ec == boost::asio::error::try_again) +/// return false; +/// }}} +/// +/// which involve implicit conversion of enumerates to error_code instances +/// to not evaluate correctly. During the implicit conversion the error_code +/// instances may be assigned differing values error_code:m_cat. This +/// causes two instances of error_code which should have been equal to +/// to not be equal. +/// +/// The problem disappears if either error handling code is not built header +/// only as this results in a single definition of system_category() supplied +/// by libboost_system; or the error handling code is not optimized. +/// +/// We're doing the test here, rather than in configure to guard against the +/// user supplying the header only flag via environment variables. +/// +/// We opened bugs with GNU and BOOST: +/// +/// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69789 +/// https://svn.boost.org/trac/boost/ticket/11989 +/// +/// @todo Version 6.0 will need to be tested. +/// +/// As of 2016-08-19, the version 5.4.0 from Ubuntu 16.04 is affected. Updated +/// the check to cover everything that is not 6.0, hoping that 6.0 solves the +/// problem. + +#define GNU_CC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) + +#if (defined(__GNUC__) && \ + ((GNU_CC_VERSION >= 50200) && (GNU_CC_VERSION < 60000)) \ + && defined(BOOST_ERROR_CODE_HEADER_ONLY)) +#pragma GCC push_options +#pragma GCC optimize ("O0") +#include <boost/asio.hpp> +#pragma GCC pop_options +#else +#include <boost/asio.hpp> +#endif + +#endif // ASIO_WRAPPER_H diff --git a/src/lib/asiolink/asiolink.dox b/src/lib/asiolink/asiolink.dox new file mode 100644 index 0000000..8a74b8b --- /dev/null +++ b/src/lib/asiolink/asiolink.dox @@ -0,0 +1,103 @@ +// Copyright (C) 2020-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/. + +/** + @page libasiolink libkea-asiolink - Kea Boost ASIO Library + +@section asiolinkUtilities Boost ASIO Utilities + +The asiolink library (libkea-asiolink) encapsulates Boost ASIO tools: + + - addr utilities: prefix (IOAddress and length pair) tools. + + - dummy I/O callback. + + - interval timer. + + - I/O acceptor: asynchronous server ASIO socket (base class). + + - I/O address: ASIO IP address. + + - I/O ASIO socket (derived from I/O socket). + + - I/O endpoint: ASIO IP endpoint (abstraction of a socket address). + + - I/O error: @c isc::asiolink::IOError exception declaration. + + - I/O service: ASIO I/O service (named I/O context in recent versions). + + - I/O socket: ASIO I/O socket base class. + + - TCP acceptor: TCP derivation of I/O acceptor. + + - TCP endpoint: TCP derivation of I/O endpoint. + + - TCP socket: TCP derivation of I/O socket. + + - TLS acceptor: TLS derivation of TCP acceptor. + + - TLS socket: TLS derivation of I/O socket embedding a TCP socket. + + - UDP endpoint: UDP derivation of I/O endpoint. + + - UDP socket: UDP derivation of I/O socket. + + - Unix domain socket: Unix socket (AF_LOCAL) derivation of I/O socket. + + - Unix domain acceptor: Unix socket (AF_LOCAL) derivation of I/O acceptor. + + - Unix domain endpoint: Unix socket (AF_LOCAL) derivation of I/O endpoint. + +These tools in general use the error code (vs throwing exception) overload +and for asynchronous methods / functions a callback taking the error code +as its first argument. + +@section asiolinkTLSSupport TLS ASIO support + +The TLS support is an extension of the TCP one: + + - the TLS acceptor is derived from the TCP acceptor. + + - the TLS socket embeds a TCP socket. + +Basic socket operations as accept, connect, close, etc, are performed on +the parent or embedded TCP socket. + +TLS adds a TLS context (TLS context objects encapsulate the TLS setup e.g. +certificates) and two new operations: + + - TLS handshake which must be called after accept or connect before + any read or write. + + - TLS shutdown which is an optional orderly close prepare. It must not + be called on any kind of errors and after a TLS shutdown the TLS stream + can not be used: the only valid operation on it is to close it. + +@note TLS shows a stream of sequenced records interface but there is +no direct mapping between high level TLS operations and TCP I/O, +e.g. a TLS read can involve a TCP write and the opposite. + +@note TLS introduces a new error code "stream_truncated" which is +the TLS short read. + +To debug or extend the TLS support two tools are available: + + - client and server samples for both OpenSSL and Botan. + + - TLS unit tests (tls_unittest.cc file). + +@section asiolinkMTConsiderations Multi-Threading Consideration for Boost ASIO Utilities + +By default Boost ASIO utilities are not thread safe even if Boost ASIO tools +themselves are. When there is no state and the encapsulation is direct +the thread safety property is preserved. Exceptions to the by default +no thread safe are: + + - I/O address (direct encapsulation) is thread safe. + + - interval timer setup and cancel methods are thread safe. + +*/ diff --git a/src/lib/asiolink/asiolink.h b/src/lib/asiolink/asiolink.h new file mode 100644 index 0000000..dfe31a2 --- /dev/null +++ b/src/lib/asiolink/asiolink.h @@ -0,0 +1,67 @@ +// Copyright (C) 2010-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/. + +#ifndef ASIOLINK_H +#define ASIOLINK_H 1 + +// IMPORTANT NOTE: only very few ASIO headers files can be included in +// this file. In particular, asio.hpp should never be included here. +// See the description of the namespace below. + +#include <asiolink/io_service.h> +#include <asiolink/interval_timer.h> + +#include <asiolink/io_address.h> +#include <asiolink/io_endpoint.h> +#include <asiolink/io_socket.h> +#include <asiolink/io_error.h> + +/// \namespace asiolink +/// \brief A wrapper interface for the ASIO library. +/// +/// The \c asiolink namespace is used to define a set of wrapper interfaces +/// for the ASIO library. +/// +/// Kea uses the Boost version of ASIO in its header-only mode, +/// i.e., does not require a separate library object to be linked, and thus +/// lowers the bar for introduction. +/// +/// But the advantage comes with its own costs: since the header-only version +/// includes more definitions in public header files, it tends to trigger +/// more compiler warnings for our own sources, and, depending on the +/// compiler options, may make the build fail. +/// +/// We also found it may be tricky to use ASIO and standard C++ libraries +/// in a single translation unit, i.e., a .cc file: depending on the order +/// of including header files, ASIO may or may not work on some platforms. +/// +/// This wrapper interface is intended to centralize these +/// problematic issues in a single sub module. Other Kea modules should +/// simply include \c asiolink.h and use the wrapper API instead of +/// including ASIO header files and using ASIO-specific classes directly. +/// +/// This wrapper may be used for other IO libraries if and when we want to +/// switch, but generality for that purpose is not the primary goal of +/// this module. The resulting interfaces are thus straightforward mapping +/// to the ASIO counterparts. +/// +/// One obvious drawback of this approach is performance overhead +/// due to the additional layer. We should eventually evaluate the cost +/// of the wrapper abstraction in benchmark tests. Another drawback is +/// that the wrapper interfaces don't provide all features of ASIO +/// (at least for the moment). We should also re-evaluate the +/// maintenance overhead of providing necessary wrappers as we develop +/// more. +/// +/// On the other hand, we may be able to exploit the wrapper approach to +/// simplify the interfaces (by limiting the usage) and unify performance +/// optimization points. +/// +/// As for optimization, we may want to provide a custom allocator for +/// the placeholder of callback handlers: +/// http://think-async.com/Asio/asio-1.3.1/doc/asio/reference/asio_handler_allocate.html + +#endif // ASIOLINK_H diff --git a/src/lib/asiolink/botan_boost_tls.cc b/src/lib/asiolink/botan_boost_tls.cc new file mode 100644 index 0000000..12db353 --- /dev/null +++ b/src/lib/asiolink/botan_boost_tls.cc @@ -0,0 +1,339 @@ +// Copyright (C) 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> + +/// @file botan_boost_tls.cc Botan boost ASIO implementation of the TLS API. + +#if defined(WITH_BOTAN) && defined(WITH_BOTAN_BOOST) + +#include <asiolink/asio_wrapper.h> +#include <asiolink/crypto_tls.h> + +#include <botan/auto_rng.h> +#include <botan/certstor_flatfile.h> +#include <botan/data_src.h> +#include <botan/pem.h> +#include <botan/pkcs8.h> + +using namespace isc::cryptolink; + +namespace isc { +namespace asiolink { + +// Classes of Kea certificate stores. +using KeaCertificateStorePath = Botan::Certificate_Store_In_Memory; +using KeaCertificateStoreFile = Botan::Flatfile_Certificate_Store; + +// Class of Kea credential managers. +class KeaCredentialsManager : public Botan::Credentials_Manager { +public: + // Constructor. + KeaCredentialsManager() : store_(), use_stores_(true), certs_(), key_() { + } + + // Destructor. + virtual ~KeaCredentialsManager() { + } + + // CA certificate stores. + // nullptr means do not require or check peer certificate. + std::vector<Botan::Certificate_Store*> + trusted_certificate_authorities(const std::string&, + const std::string&) override { + std::vector<Botan::Certificate_Store*> result; + if (use_stores_ && store_) { + result.push_back(store_.get()); + } + return (result); + } + + // Certificate chain. + std::vector<Botan::X509_Certificate> + cert_chain(const std::vector<std::string>&, + const std::string&, + const std::string&) override { + return (certs_); + } + + // Private key. + Botan::Private_Key* + private_key_for(const Botan::X509_Certificate&, + const std::string&, + const std::string&) override { + return (key_.get()); + } + + // Set the store from a path. + void setStorePath(const std::string& path) { + store_.reset(new KeaCertificateStorePath(path)); + } + + // Set the store from a file. + void setStoreFile(const std::string& file) { + store_.reset(new KeaCertificateStoreFile(file)); + } + + // Get the use of CA certificate stores flag. + bool getUseStores() const { + return (use_stores_); + } + + // Set the use of CA certificate stores flag. + void setUseStores(bool use_stores) { + use_stores_ = use_stores; + } + + // Set the certificate chain. + void setCertChain(const std::string& file) { + Botan::DataSource_Stream source(file); + certs_.clear(); + while (!source.end_of_data()) { + std::string label; + std::vector<uint8_t> cert; + try { + cert = unlock(Botan::PEM_Code::decode(source, label)); + if ((label != "CERTIFICATE") && + (label != "X509 CERTIFICATE") && + (label != "TRUSTED CERTIFICATE")) { + isc_throw(LibraryError, "Expected a certificate, got '" + << label << "'"); + } + certs_.push_back(Botan::X509_Certificate(cert)); + } catch (const std::exception& ex) { + if (certs_.empty()) { + throw; + } + // Got one certificate so skipping garbage. + continue; + } + } + if (certs_.empty()) { + isc_throw(LibraryError, "Found no certificate?"); + } + } + + // Set the private key. + void setPrivateKey(const std::string& file, + Botan::RandomNumberGenerator& rng, + bool& is_rsa) { + key_.reset(Botan::PKCS8::load_key(file, rng)); + if (!key_) { + isc_throw(Unexpected, + "Botan::PKCS8::load_key failed but not threw?"); + } + is_rsa = (key_->algo_name() == "RSA"); + } + + // Pointer to the CA certificate store. + std::unique_ptr<Botan::Certificate_Store> store_; + + // Use the CA certificate store flag. + bool use_stores_; + + // The certificate chain. + std::vector<Botan::X509_Certificate> certs_; + + // Pointer to the private key. + std::unique_ptr<Botan::Private_Key> key_; +}; + +// Class of Kea policy. +// Use Strict_Policy? +class KeaPolicy : public Botan::TLS::Default_Policy { +public: + // Constructor. + KeaPolicy() : prefer_rsa_(true) { + } + + // Destructor. + virtual ~KeaPolicy() { + } + + // Allowed signature methods in preference order. + std::vector<std::string> allowed_signature_methods() const override { + if (prefer_rsa_) { + return (AllowedSignatureMethodsRSA); + } else { + return (AllowedSignatureMethodsECDSA); + } + } + + // Disable OSCP. + bool require_cert_revocation_info() const override { + return false; + } + + // Set the RSA preferred flag. + void setPrefRSA(bool prefer_rsa) { + prefer_rsa_ = prefer_rsa; + } + + // Prefer RSA preferred flag. + bool prefer_rsa_; + + // Allowed signature methods which prefers RSA. + static const std::vector<std::string> AllowedSignatureMethodsRSA; + + // Allowed signature methods which prefers ECDSA. + static const std::vector<std::string> AllowedSignatureMethodsECDSA; +}; + + +// Kea session manager. +using KeaSessionManager = Botan::TLS::Session_Manager_Noop; + +// Allowed signature methods which prefers RSA. +const std::vector<std::string> +KeaPolicy::AllowedSignatureMethodsRSA = { "RSA", "DSA", "ECDSA" }; + +// Allowed signature methods which prefers ECDSA. +const std::vector<std::string> +KeaPolicy::AllowedSignatureMethodsECDSA = { "ECDSA", "RSA", "DSA" }; + +// Class of Botan TLS context implementations. +class TlsContextImpl { +public: + // Constructor. + TlsContextImpl() : cred_mgr_(), rng_(), sess_mgr_(), policy_() { + } + + // Destructor. + virtual ~TlsContextImpl() { + } + + // Get the peer certificate requirement mode. + virtual bool getCertRequired() const { + return (cred_mgr_.getUseStores()); + } + + // Set the peer certificate requirement mode. + // + // With Botan this means to provide or not the CA certificate stores. + virtual void setCertRequired(bool cert_required) { + cred_mgr_.setUseStores(cert_required); + } + + // Load the trust anchor aka certificate authority (path). + virtual void loadCaPath(const std::string& ca_path) { + try { + cred_mgr_.setStorePath(ca_path); + } catch (const std::exception& ex) { + isc_throw(LibraryError, ex.what()); + } + } + + // Load the trust anchor aka certificate authority (file). + virtual void loadCaFile(const std::string& ca_file) { + try { + cred_mgr_.setStoreFile(ca_file); + } catch (const std::exception& ex) { + isc_throw(LibraryError, ex.what()); + } + } + + /// @brief Load the certificate file. + virtual void loadCertFile(const std::string& cert_file) { + try { + cred_mgr_.setCertChain(cert_file); + } catch (const std::exception& ex) { + isc_throw(LibraryError, ex.what()); + } + } + + /// @brief Load the private key file. + /// + /// As a side effect set the preference for RSA in the policy. + virtual void loadKeyFile(const std::string& key_file) { + try { + bool is_rsa = true; + cred_mgr_.setPrivateKey(key_file, rng_, is_rsa); + policy_.setPrefRSA(is_rsa); + } catch (const std::exception& ex) { + isc_throw(LibraryError, ex.what()); + } + } + + // Build the context if not yet done. + virtual void build() { + if (context_) { + return; + } + context_.reset(new Botan::TLS::Context(cred_mgr_, + rng_, + sess_mgr_, + policy_)); + } + + virtual Botan::TLS::Context& get() { + return (*context_); + } + + // Credentials Manager. + KeaCredentialsManager cred_mgr_; + + // Random Number Generator. + Botan::AutoSeeded_RNG rng_; + + // Session Manager. + KeaSessionManager sess_mgr_; + + KeaPolicy policy_; + + std::unique_ptr<Botan::TLS::Context> context_; +}; + +TlsContext::~TlsContext() { +} + +TlsContext::TlsContext(TlsRole role) + : TlsContextBase(role), impl_(new TlsContextImpl()) { +} + +Botan::TLS::Context& +TlsContext::getContext() { + impl_->build(); + return (impl_->get()); +} + +void +TlsContext::setCertRequired(bool cert_required) { + if (!cert_required && (getRole() == TlsRole::CLIENT)) { + isc_throw(BadValue, + "'cert-required' parameter must be true for a TLS client"); + } + impl_->setCertRequired(cert_required); +} + +bool +TlsContext::getCertRequired() const { + return (impl_->getCertRequired()); +} + +void +TlsContext::loadCaFile(const std::string& ca_file) { + impl_->loadCaFile(ca_file); +} + +void +TlsContext::loadCaPath(const std::string& ca_path) { + impl_->loadCaPath(ca_path); +} + +void +TlsContext::loadCertFile(const std::string& cert_file) { + impl_->loadCertFile(cert_file); +} + +void +TlsContext::loadKeyFile(const std::string& key_file) { + impl_->loadKeyFile(key_file); +} + +} // namespace asiolink +} // namespace isc + +#endif // WITH_BOTAN && WITH_BOTAN_BOOST diff --git a/src/lib/asiolink/botan_boost_tls.h b/src/lib/asiolink/botan_boost_tls.h new file mode 100644 index 0000000..9037ebc --- /dev/null +++ b/src/lib/asiolink/botan_boost_tls.h @@ -0,0 +1,207 @@ +// Copyright (C) 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/. + +// Do not include this header directly: use crypto_tls.h instead. + +#ifndef BOTAN_BOOST_TLS_H +#define BOTAN_BOOST_TLS_H + +/// @file botan_boost_tls.h Botan boost ASIO implementation of the TLS API. + +#if defined(WITH_BOTAN) && defined(WITH_BOTAN_BOOST) + +#include <asiolink/asio_wrapper.h> +#include <asiolink/io_asio_socket.h> +#include <asiolink/io_service.h> +#include <asiolink/common_tls.h> +#include <exceptions/exceptions.h> + +#include <asiolink/botan_boost_wrapper.h> +#include <botan/asio_stream.h> + +namespace isc { +namespace asiolink { + +/// @brief Translate TLS role into implementation. +inline Botan::TLS::Connection_Side roleToImpl(TlsRole role) { + if (role == TlsRole::SERVER) { + return (Botan::TLS::Connection_Side::SERVER); + } else { + return (Botan::TLS::Connection_Side::CLIENT); + } +} + +/// @brief Forward declaration of Botan TLS context. +class TlsContextImpl; + +/// @brief Botan boost ASIO TLS context. +class TlsContext : public TlsContextBase { +public: + + /// @brief Destructor. + /// + /// @note The destructor can't be defined here because a unique + /// pointer to an incomplete type is used. + virtual ~TlsContext(); + + /// @brief Create a fresh context. + /// + /// @param role The TLS role client or server. + explicit TlsContext(TlsRole role); + + /// @brief Return the underlying context. + Botan::TLS::Context& getContext(); + + /// @brief Get the peer certificate requirement mode. + /// + /// @return True if peer certificates are required, false if they + /// are optional. + virtual bool getCertRequired() const; + +protected: + /// @brief Set the peer certificate requirement mode. + /// + /// @param cert_required True if peer certificates are required, + /// false if they are optional. + virtual void setCertRequired(bool cert_required); + + /// @brief Load the trust anchor aka certification authority. + /// + /// @param ca_file The certificate file name. + virtual void loadCaFile(const std::string& ca_file); + + /// @brief Load the trust anchor aka certification authority. + /// + /// @param ca_path The certificate directory name. + virtual void loadCaPath(const std::string& ca_path); + + /// @brief Load the certificate file. + /// + /// @param cert_file The certificate file name. + virtual void loadCertFile(const std::string& cert_file); + + /// @brief Load the private key from a file. + /// + /// @param key_file The private key file name. + virtual void loadKeyFile(const std::string& key_file); + + /// @brief Botan TLS context. + std::unique_ptr<TlsContextImpl> impl_; + + /// @brief Allow access to protected methods by the base class. + friend class TlsContextBase; +}; + +/// @brief The type of underlying TLS streams. +typedef Botan::TLS::Stream<boost::asio::ip::tcp::socket> TlsStreamImpl; + +/// @brief TlsStreamBase constructor. +/// +/// @tparam Callback The type of callbacks. +/// @tparam TlsStreamImpl The type of underlying TLS streams. +/// @param service I/O Service object used to manage the stream. +/// @param context Pointer to the TLS context. +/// @note The caller must not provide a null pointer to the TLS context. +template <typename Callback, typename TlsStreamImpl> +TlsStreamBase<Callback, TlsStreamImpl>:: +TlsStreamBase(IOService& service, TlsContextPtr context) + : TlsStreamImpl(service.get_io_service(), context->getContext()), + role_(context->getRole()) { +} + +/// @brief Botan boost ASIO TLS stream. +/// +/// @tparam callback The callback. +template <typename Callback> +class TlsStream : public TlsStreamBase<Callback, TlsStreamImpl> +{ +public: + + /// @brief Type of the base. + typedef TlsStreamBase<Callback, TlsStreamImpl> Base; + + /// @brief Constructor. + /// + /// @param service I/O Service object used to manage the stream. + /// @param context Pointer to the TLS context. + /// @note The caller must not provide a null pointer to the TLS context. + TlsStream(IOService& service, TlsContextPtr context) + : Base(service, context) { + } + + /// @brief Destructor. + virtual ~TlsStream() { } + + /// @brief TLS Handshake. + /// + /// @param callback Callback object. + virtual void handshake(Callback& callback) { + Base::async_handshake(roleToImpl(Base::getRole()), callback); + } + + /// @brief TLS shutdown. + /// + /// @param callback Callback object. + virtual void shutdown(Callback& callback) { + Base::async_shutdown(callback); + } + + /// @brief Clear the TLS object. + /// + /// @note The idea to reuse a TCP connection for a fresh TLS is at + /// least arguable. Currently it does nothing so the socket is + /// **not** reusable. + virtual void clear() { + } + + /// @brief Return the commonName part of the subjectName of + /// the peer certificate. + /// + /// First commonName when there are more than one, in UTF-8. + /// RFC 3280 provides as a commonName example "Susan Housley", + /// to idea to give access to this come from the Role Based + /// Access Control experiment. + /// + /// @return The commonName part of the subjectName or the empty string. + virtual std::string getSubject() { + const std::vector<Botan::X509_Certificate>& cert_chain = + Base::native_handle()->peer_cert_chain(); + if (cert_chain.empty()) { + return (""); + } + const Botan::X509_DN& subject = cert_chain[0].subject_dn(); + return (subject.get_first_attribute("CommonName")); + } + + /// @brief Return the commonName part of the issuerName of + /// the peer certificate. + /// + /// First commonName when there are more than one, in UTF-8. + /// The issuerName is the subjectName of the signing certificate + /// (the issue in PKIX terms). The idea is to encode a group as + /// members of an intermediate certification authority. + /// + /// @return The commonName part of the issuerName or the empty string. + virtual std::string getIssuer() { + const std::vector<Botan::X509_Certificate>& cert_chain = + Base::native_handle()->peer_cert_chain(); + if (cert_chain.empty()) { + return (""); + } + const Botan::X509_DN& issuer = cert_chain[0].issuer_dn(); + return (issuer.get_first_attribute("CommonName")); + } +}; + +// Stream truncated error code. +const int STREAM_TRUNCATED = Botan::TLS::StreamError::StreamTruncated; + +} // namespace asiolink +} // namespace isc + +#endif // WITH_BOTAN && WITH_BOTAN_BOOST + +#endif // BOTAN_BOOST_TLS_H diff --git a/src/lib/asiolink/botan_boost_wrapper.h b/src/lib/asiolink/botan_boost_wrapper.h new file mode 100644 index 0000000..e244bbb --- /dev/null +++ b/src/lib/asiolink/botan_boost_wrapper.h @@ -0,0 +1,32 @@ +// Copyright (C) 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/. + +// Do not include this header directly: use crypto_tls.h instead. + +#ifndef BOTAN_BOOST_WRAPPER_H +#define BOTAN_BOOST_WRAPPER_H + +/// @file botan_boost_wrapper.h Botan boost ASIO wrapper. + +#if defined(WITH_BOTAN) && defined(WITH_BOTAN_BOOST) + +/// The error classes do not define virtual destructors. +/// This workaround is taken from the boost header. + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#endif + +#include <botan/asio_error.h> + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif // WITH_BOTAN && WITH_BOTAN_BOOST + +#endif // BOTAN_BOOST_WRAPPER_H diff --git a/src/lib/asiolink/botan_tls.cc b/src/lib/asiolink/botan_tls.cc new file mode 100644 index 0000000..3cd1818 --- /dev/null +++ b/src/lib/asiolink/botan_tls.cc @@ -0,0 +1,60 @@ +// Copyright (C) 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/. + +/// @file botan_tls.cc Botan fake implementation of the TLS API. + +#include <config.h> + +#if defined(WITH_BOTAN) && !defined(WITH_BOTAN_BOOST) + +#include <asiolink/asio_wrapper.h> +#include <asiolink/crypto_tls.h> + +namespace isc { +namespace asiolink { + +TlsContext::TlsContext(TlsRole role) + : TlsContextBase(role), cert_required_(true) { +} + +void +TlsContext::setCertRequired(bool cert_required) { + if (!cert_required && (getRole() == TlsRole::CLIENT)) { + isc_throw(BadValue, + "'cert-required' parameter must be true for a TLS client"); + } + cert_required_ = cert_required; +} + +bool +TlsContext::getCertRequired() const { + return (cert_required_); +} + +void +TlsContext::loadCaFile(const std::string&) { + isc_throw(NotImplemented, "Botan TLS is not yet supported"); +} + +void +TlsContext::loadCaPath(const std::string&) { + isc_throw(NotImplemented, "loadCaPath is not implemented by Botan"); +} + +void +TlsContext::loadCertFile(const std::string&) { + isc_throw(NotImplemented, "Botan TLS is not yet supported"); +} + +void +TlsContext::loadKeyFile(const std::string&) { + isc_throw(NotImplemented, "Botan TLS is not yet supported"); +} + +} // namespace asiolink +} // namespace isc + +#endif // WITH_BOTAN && !WITH_BOTAN_BOOST diff --git a/src/lib/asiolink/botan_tls.h b/src/lib/asiolink/botan_tls.h new file mode 100644 index 0000000..1735cc7 --- /dev/null +++ b/src/lib/asiolink/botan_tls.h @@ -0,0 +1,168 @@ +// Copyright (C) 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/. + +// Do not include this header directly: use crypto_tls.h instead. + +#ifndef BOTAN_TLS_H +#define BOTAN_TLS_H + +/// @file botan_tls.h Botan fake implementation of the TLS API. + +#if defined(WITH_BOTAN) && !defined(WITH_BOTAN_BOOST) + +#include <asiolink/asio_wrapper.h> +#include <asiolink/io_asio_socket.h> +#include <asiolink/io_service.h> +#include <asiolink/common_tls.h> + +#include <exceptions/exceptions.h> + +namespace isc { +namespace asiolink { + +/// @brief Botan TLS context. +class TlsContext : public TlsContextBase { +public: + + /// @brief Destructor. + virtual ~TlsContext() { } + + /// @brief Create a fresh context. + /// + /// @param role The TLS role client or server. + explicit TlsContext(TlsRole role); + + /// @brief Get the peer certificate requirement mode. + /// + /// @return True if peer certificates are required, false if they + /// are optional. + virtual bool getCertRequired() const; + +protected: + /// @brief Set the peer certificate requirement mode. + /// + /// @param cert_required True if peer certificates are required, + /// false if they are optional. + virtual void setCertRequired(bool cert_required); + + /// @brief Load the trust anchor aka certification authority. + /// + /// @param ca_file The certificate file name. + /// @throw isc::cryptolink::LibraryError on various errors as + /// file not found, bad format, etc. + virtual void loadCaFile(const std::string& ca_file); + + /// @brief Load the trust anchor aka certification authority. + /// + /// @param ca_path The certificate directory name. + /// @throw isc::cryptolink::LibraryError on various errors as + /// file not found, bad format, etc. + virtual void loadCaPath(const std::string& ca_path); + + /// @brief Load the certificate file. + /// + /// @param cert_file The certificate file name. + /// @throw isc::cryptolink::LibraryError on various errors as + /// file not found, bad format, etc. + virtual void loadCertFile(const std::string& cert_file); + + /// @brief Load the private key from a file. + /// + /// @param key_file The private key file name. + /// @throw isc::cryptolink::LibraryError on various errors as + /// file not found, bad format, etc. + virtual void loadKeyFile(const std::string& key_file); + + /// @brief Cached cert_required value. + bool cert_required_; + + /// @brief Allow access to protected methods by the base class. + friend class TlsContextBase; +}; + +/// @brief The type of Botan TLS streams (in fact pure TCP streams). +typedef boost::asio::ip::tcp::socket TlsStreamImpl; + +/// @brief TlsStreamBase constructor. +/// +/// @tparam Callback The type of callbacks. +/// @tparam TlsStreamImpl The type of underlying TLS streams. +/// @param service I/O Service object used to manage the stream. +/// @param context Pointer to the TLS context. +/// @note The caller must not provide a null pointer to the TLS context. +template <typename Callback, typename TlsStreamImpl> +TlsStreamBase<Callback, TlsStreamImpl>:: +TlsStreamBase(IOService& service, TlsContextPtr context) + : TlsStreamImpl(service.get_io_service()), role_(context->getRole()) { +} + +/// @brief Botan fake TLS stream. +/// +/// @tparam callback The callback. +template <typename Callback> +class TlsStream : public TlsStreamBase<Callback, TlsStreamImpl> { +public: + + /// @brief Type of the base. + typedef TlsStreamBase<Callback, TlsStreamImpl> Base; + + /// @brief Constructor. + /// + /// @param service I/O Service object used to manage the stream. + /// @param context Pointer to the TLS context. + /// @note The caller must not provide a null pointer to the TLS context. + TlsStream(IOService& service, TlsContextPtr context) + : Base(service, context) { + } + + /// @brief Destructor. + virtual ~TlsStream() { } + + /// @brief TLS Handshake. + virtual void handshake(Callback&) { + isc_throw(NotImplemented, "Botan TLS is not yet supported"); + } + + /// @brief TLS shutdown. + virtual void shutdown(Callback&) { + isc_throw(NotImplemented, "Botan TLS is not yet supported"); + } + + /// @brief Return the commonName part of the subjectName of + /// the peer certificate. + /// + /// First commonName when there are more than one, in UTF-8. + /// RFC 3280 provides as a commonName example "Susan Housley", + /// to idea to give access to this come from the Role Based + /// Access Control experiment. + /// + /// + /// @return The commonName part of the subjectName or the empty string. + std::string getSubject() { + return (""); + } + + /// @brief Return the commonName part of the issuerName of + /// the peer certificate. + /// + /// First commonName when there are more than one, in UTF-8. + /// The issuerName is the subjectName of the signing certificate + /// (the issue in PKIX terms). The idea is to encode a group as + /// members of an intermediate certification authority. + /// + /// + /// @return The commonName part of the issuerName or the empty string. + std::string getIssuer() { + return (""); + } +}; + +} // namespace asiolink +} // namespace isc + +#endif // WITH_BOTAN && !WITH_BOTAN_BOOST + +#endif // BOTAN_TLS_H diff --git a/src/lib/asiolink/common_tls.cc b/src/lib/asiolink/common_tls.cc new file mode 100644 index 0000000..35ca637 --- /dev/null +++ b/src/lib/asiolink/common_tls.cc @@ -0,0 +1,65 @@ +// 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/. + +/// @file common_tls.cc Common part of implementations of the TLS API. + +#include <config.h> + +#include <asiolink/asio_wrapper.h> +#include <asiolink/crypto_tls.h> +#include <util/file_utilities.h> + +using namespace isc::cryptolink; +using namespace isc::util; + +namespace isc { +namespace asiolink { + +void +TlsContextBase::configure(TlsContextPtr& context, + TlsRole role, + const std::string& ca_file, + const std::string& cert_file, + const std::string& key_file, + bool cert_required) { + try { + context.reset(new TlsContext(role)); + context->setCertRequired(cert_required); + if (file::isDir(ca_file)) { + try { + context->loadCaPath(ca_file); + } catch (const std::exception& ex) { + isc_throw(isc::BadValue, "load of CA directory '" + << ca_file << "' failed: " << ex.what()); + } + } else { + try { + context->loadCaFile(ca_file); + } catch (const std::exception& ex) { + isc_throw(isc::BadValue, "load of CA file '" + << ca_file << "' failed: " << ex.what()); + } + } + try { + context->loadCertFile(cert_file); + } catch (const std::exception& ex) { + isc_throw(isc::BadValue, "load of cert file '" + << cert_file << "' failed: " << ex.what()); + } + try { + context->loadKeyFile(key_file); + } catch (const std::exception& ex) { + isc_throw(isc::BadValue, "load of private key file '" + << key_file << "' failed: " << ex.what()); + } + } catch (...) { + context.reset(); + throw; + } +} + +} // namespace asiolink +} // namespace isc diff --git a/src/lib/asiolink/common_tls.h b/src/lib/asiolink/common_tls.h new file mode 100644 index 0000000..b119760 --- /dev/null +++ b/src/lib/asiolink/common_tls.h @@ -0,0 +1,183 @@ +// Copyright (C) 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/. + +// Do not include this header directly: use crypto_tls.h instead. + +#ifndef COMMON_TLS_H +#define COMMON_TLS_H + +/// @file common_tls.h Common TLS API. + +// Verify that this file was not directly included. +#ifndef CRYPTO_TLS_H +#error crypto_tls.h must be included in place of common_tls.h +#endif + +#include <cryptolink/cryptolink.h> + +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> + +#include <netinet/in.h> +#include <sys/socket.h> + +namespace isc { +namespace asiolink { + +/// @brief Client and server roles. +enum TlsRole { CLIENT, SERVER }; + +/// @brief Forward declaration of backend TLS context. +class TlsContext; + +/// @brief The type of shared pointers to TlsContext objects. +typedef boost::shared_ptr<TlsContext> TlsContextPtr; + +/// @brief TLS context base class. +class TlsContextBase : private boost::noncopyable { +public: + /// @brief Destructor. + virtual ~TlsContextBase() { } + + /// @brief Create a fresh context. + /// + /// @param role The TLS role client or server. + explicit TlsContextBase(TlsRole role) : role_(role) { } + + /// @brief Returns the role. + TlsRole getRole() const { + return (role_); + } + + /// @note No need for a role set method. + + /// @brief Configure. + /// + /// @param context The TLS context to configure. + /// @param role The TLS role client or server. + /// @param ca_file The certificate file or directory name. + /// @param cert_file The certificate file name. + /// @param key_file The private key file name. + /// @param cert_required True if peer certificates are required, + /// false if they are optional. This is a server specific parameter. + /// @throw isc::BadValue on error. + static void configure(TlsContextPtr& context, + TlsRole role, + const std::string& ca_file, + const std::string& cert_file, + const std::string& key_file, + bool cert_required = true); + + /// @brief Get the peer certificate requirement mode. + /// + /// @return True if peer certificates are required, false if they + /// are optional. + virtual bool getCertRequired() const = 0; + +protected: + /// @brief Set the peer certificate requirement mode. + /// + /// @param cert_required True if peer certificates are required, + /// false if they are optional. + /// @throw isc::BadValue when cert_required is set to false for a client. + virtual void setCertRequired(bool cert_required) = 0; + + /// @brief Load the trust anchor aka certification authority. + /// + /// @param ca_file The certificate file name. + /// @throw isc::cryptolink::LibraryError on various errors as + /// file not found, bad format, etc. + virtual void loadCaFile(const std::string& ca_file) = 0; + + /// @brief Load the trust anchor aka certification authority. + /// + /// @param ca_path The certificate directory name. + /// @throw isc::cryptolink::LibraryError on various errors as + /// file not found, bad format, etc. + virtual void loadCaPath(const std::string& ca_path) = 0; + + /// @brief Load the certificate file. + /// + /// @param cert_file The certificate file name. + /// @throw isc::cryptolink::LibraryError on various errors as + /// file not found, bad format, etc. + virtual void loadCertFile(const std::string& cert_file) = 0; + + /// @brief Load the private key from a file. + /// + /// @param key_file The private key file name. + /// @throw isc::cryptolink::LibraryError on various errors as + /// file not found, bad format, etc. + virtual void loadKeyFile(const std::string& key_file) = 0; + +public: + /// @brief The role i.e. client or server. + TlsRole role_; +}; + +/// @brief TLS stream base class. +/// +/// @tparam Callback The type of callbacks. +/// @tparam TlsStreamImpl The type of underlying TLS streams. +template <typename Callback, typename TlsStreamImpl> +class TlsStreamBase : public TlsStreamImpl { +public: + + /// @brief Constructor. + /// + /// @param service I/O Service object used to manage the stream. + /// @param context Pointer to the TLS context. + /// @note The caller must not provide a null pointer to the TLS context. + TlsStreamBase(IOService& service, TlsContextPtr context); + + /// @brief Destructor. + virtual ~TlsStreamBase() { } + + /// @brief Returns the role. + TlsRole getRole() const { + return (role_); + } + + /// @brief TLS Handshake. + /// + /// @param callback Callback object. + virtual void handshake(Callback& callback) = 0; + + /// @brief TLS shutdown. + /// + /// @param callback Callback object. + virtual void shutdown(Callback& callback) = 0; + + /// @brief Return the commonName part of the subjectName of + /// the peer certificate. + /// + /// First commonName when there are more than one, in UTF-8. + /// RFC 3280 provides as a commonName example "Susan Housley", + /// to idea to give access to this come from the Role Based + /// Access Control experiment. + /// + /// @return The commonName part of the subjectName or the empty string. + virtual std::string getSubject() = 0; + + /// @brief Return the commonName part of the issuerName of + /// the peer certificate. + /// + /// First commonName when there are more than one, in UTF-8. + /// The issuerName is the subjectName of the signing certificate + /// (the issue in PKIX terms). The idea is to encode a group as + /// members of an intermediate certification authority. + /// + /// @return The commonName part of the issuerName or the empty string. + virtual std::string getIssuer() = 0; + + /// @brief The role i.e. client or server. + TlsRole role_; +}; + +} // namespace asiolink +} // namespace isc + +#endif // COMMON_TLS_H diff --git a/src/lib/asiolink/crypto_tls.h b/src/lib/asiolink/crypto_tls.h new file mode 100644 index 0000000..c3e899f --- /dev/null +++ b/src/lib/asiolink/crypto_tls.h @@ -0,0 +1,27 @@ +// Copyright (C) 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/. + +#ifndef CRYPTO_TLS_H +#define CRYPTO_TLS_H + +/// @file crypto_tls.h TLS API. + +// Verify that config.h was included. +#ifndef CONFIG_H_WAS_INCLUDED +#error config.h must be included before crypto_tls.h +#endif + +// Include different versions. +#include <asiolink/botan_boost_tls.h> +#include <asiolink/botan_tls.h> +#include <asiolink/openssl_tls.h> + +// Verify that one version matched. +#ifndef COMMON_TLS_H +#error no TLS backend was found +#endif + +#endif // CRYPTO_TLS_H diff --git a/src/lib/asiolink/dummy_io_cb.h b/src/lib/asiolink/dummy_io_cb.h new file mode 100644 index 0000000..795fad9 --- /dev/null +++ b/src/lib/asiolink/dummy_io_cb.h @@ -0,0 +1,65 @@ +// 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/. + +#ifndef DUMMY_IO_CB_H +#define DUMMY_IO_CB_H + +#include <iostream> + +#include <exceptions/exceptions.h> + +#include <boost/asio/error.hpp> + +namespace isc { +namespace asiolink { + +/// \brief Asynchronous I/O Completion Callback +/// +/// The two socket classes (UDPSocket and TCPSocket) require that the I/O +/// completion callback function have an operator() method with the appropriate +/// signature. The classes are templates, any class with that method and +/// signature can be passed as the callback object - there is no need for a +/// base class defining the interface. However, some users of the socket +/// classes do not use the asynchronous I/O operations, yet have to supply a +/// template parameter. This is the reason for this class - it is the dummy +/// template parameter. + +class DummyIOCallback { +public: + + /// \brief Asynchronous I/O callback method + /// + /// Should never be called, as this class is a convenience class provided + /// for instances where a socket is required but it is known that no + /// asynchronous operations will be carried out. + void operator()(boost::system::error_code) { + // If the function is called, there is a serious logic error in + // the program (this class should not be used as the callback + // class). As the asiolink module is too low-level for logging + // errors, throw an exception. + isc_throw(isc::Unexpected, + "DummyIOCallback::operator() must not be called"); + } + + /// \brief Asynchronous I/O callback method + /// + /// Should never be called, as this class is a convenience class provided + /// for instances where a socket is required but it is known that no + /// asynchronous operations will be carried out. + void operator()(boost::system::error_code, size_t) { + // If the function is called, there is a serious logic error in + // the program (this class should not be used as the callback + // class). As the asiolink module is too low-level for logging + // errors, throw an exception. + isc_throw(isc::Unexpected, + "DummyIOCallback::operator() must not be called"); + } +}; + +} // namespace asiolink +} // namespace isc + +#endif // DUMMY_IO_CB_H diff --git a/src/lib/asiolink/interval_timer.cc b/src/lib/asiolink/interval_timer.cc new file mode 100644 index 0000000..2cda9b0 --- /dev/null +++ b/src/lib/asiolink/interval_timer.cc @@ -0,0 +1,201 @@ +// 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/. + +#include <config.h> +#include <asiolink/asio_wrapper.h> +#include <asiolink/interval_timer.h> +#include <asiolink/io_service.h> + +#include <boost/enable_shared_from_this.hpp> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> + +#include <exceptions/exceptions.h> + +#include <atomic> +#include <functional> +#include <mutex> + +using namespace std; +namespace ph = std::placeholders; + +namespace isc { +namespace asiolink { + +/// This class holds a call back function of asynchronous operations. +/// To ensure the object is alive while an asynchronous operation refers +/// to it, we use shared_ptr and enable_shared_from_this. +/// The object will be destructed in case IntervalTimer has been destructed +/// and no asynchronous operation refers to it. +/// Please follow the link to get an example: +/// http://think-async.com/asio/asio-1.4.8/doc/asio/tutorial/tutdaytime3.html#asio.tutorial.tutdaytime3.the_tcp_connection_class +class IntervalTimerImpl : + public boost::enable_shared_from_this<IntervalTimerImpl>, + public boost::noncopyable { +public: + + /// @brief Constructor. + /// + /// @param io_service The IO service used to handle events. + IntervalTimerImpl(IOService& io_service); + + /// @brief Destructor. + ~IntervalTimerImpl(); + + /// @brief Setup function to register callback and start timer. + /// + /// @param cbfunc The callback function registered on timer. + /// @param interval The interval used to start the timer. + /// @param interval_mode The interval mode used by the timer. + void setup(const IntervalTimer::Callback& cbfunc, const long interval, + const IntervalTimer::Mode& interval_mode + = IntervalTimer::REPEATING); + + /// @brief Callback function which calls the registerd callback. + /// + /// @param error The error code retrieved from the timer. + void callback(const boost::system::error_code& error); + + /// @brief Cancel timer. + void cancel() { + lock_guard<mutex> lk (mutex_); + timer_.cancel(); + interval_ = 0; + } + + /// @brief Get the timer interval. + /// + /// @return The timer interval. + long getInterval() const { return (interval_); } + +private: + + /// @brief Update function to update timer_ when it expires. + /// + /// Should be called in a thread safe context. + void update(); + + /// @brief The callback function to call when timer_ expires. + IntervalTimer::Callback cbfunc_; + + /// @brief The interval in milliseconds. + std::atomic<long> interval_; + + /// @brief The asio timer. + boost::asio::deadline_timer timer_; + + /// @brief Controls how the timer behaves after expiration. + IntervalTimer::Mode mode_; + + /// @brief Mutex to protect the internal state. + std::mutex mutex_; + + /// @brief Invalid interval value. + /// + /// @ref interval_ will be set to this value in destructor in order to + /// detect use-after-free type of bugs. + static const long INVALIDATED_INTERVAL = -1; +}; + +IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) : + interval_(0), timer_(io_service.get_io_service()), + mode_(IntervalTimer::REPEATING) { +} + +IntervalTimerImpl::~IntervalTimerImpl() { + interval_ = INVALIDATED_INTERVAL; +} + +void +IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc, + const long interval, + const IntervalTimer::Mode& mode) { + // Interval should not be less than 0. + if (interval < 0) { + isc_throw(isc::BadValue, "Interval should not be less than or " + "equal to 0"); + } + // Call back function should not be empty. + if (!cbfunc) { + isc_throw(isc::InvalidParameter, "Callback function is empty"); + } + + lock_guard<mutex> lk(mutex_); + cbfunc_ = cbfunc; + interval_ = interval; + mode_ = mode; + + // Set initial expire time. + // At this point the timer is not running yet and will not expire. + // After calling IOService::run(), the timer will expire. + update(); +} + +void +IntervalTimerImpl::update() { + try { + // Update expire time to (current time + interval_). + timer_.expires_from_now(boost::posix_time::millisec(long(interval_))); + // Reset timer. + // Pass a function bound with a shared_ptr to this. + timer_.async_wait(std::bind(&IntervalTimerImpl::callback, + shared_from_this(), + ph::_1)); //error + } catch (const boost::system::system_error& e) { + isc_throw(isc::Unexpected, "Failed to update timer: " << e.what()); + } catch (const boost::bad_weak_ptr&) { + // Can't happen. It means a severe internal bug. + } +} + +void +IntervalTimerImpl::callback(const boost::system::error_code& ec) { + if (interval_ == INVALIDATED_INTERVAL) { + isc_throw(isc::BadValue, "Interval internal state"); + } + if (interval_ == 0 || ec) { + // timer has been canceled. Do nothing. + } else { + { + lock_guard<mutex> lk(mutex_); + // If we should repeat, set next expire time. + if (mode_ == IntervalTimer::REPEATING) { + update(); + } + } + + // Invoke the call back function. + cbfunc_(); + } +} + +IntervalTimer::IntervalTimer(IOService& io_service) : + impl_(new IntervalTimerImpl(io_service)) { +} + +IntervalTimer::~IntervalTimer() { + // Cancel the timer to make sure cbfunc_() will not be called any more. + cancel(); +} + +void +IntervalTimer::setup(const Callback& cbfunc, const long interval, + const IntervalTimer::Mode& mode) { + return (impl_->setup(cbfunc, interval, mode)); +} + +void +IntervalTimer::cancel() { + impl_->cancel(); +} + +long +IntervalTimer::getInterval() const { + return (impl_->getInterval()); +} + +} // namespace asiolink +} // namespace isc diff --git a/src/lib/asiolink/interval_timer.h b/src/lib/asiolink/interval_timer.h new file mode 100644 index 0000000..5dc8b71 --- /dev/null +++ b/src/lib/asiolink/interval_timer.h @@ -0,0 +1,142 @@ +// 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/. + +#ifndef ASIOLINK_INTERVAL_TIMER_H +#define ASIOLINK_INTERVAL_TIMER_H 1 + +#include <boost/shared_ptr.hpp> +#include <functional> + +#include <asiolink/io_service.h> + +namespace isc { +namespace asiolink { + +class IntervalTimerImpl; + +/// \brief The \c IntervalTimer class is a wrapper for the ASIO +/// \c boost::asio::deadline_timer class. +/// +/// This class is implemented to use \c boost::asio::deadline_timer as interval +/// timer. +/// +/// \c setup() sets a timer to expire on (now + interval), a call back +/// function, and an interval mode. +/// +/// \c IntervalTimerImpl::callback() is called by the timer when it expires. +/// +/// The function calls the call back function set by \c setup() and if the +/// the interval mode indicates a repeating interval, will reschedule the +/// timer to expire in (now + interval) milliseconds. +/// +/// The type of call back function is \c void(void). +/// +/// The call back function will not be called if the instance of this class is +/// destroyed before the timer is expired. +/// +/// Sample code: +/// \code +/// void function_to_call_back() { +/// // this function will be called periodically +/// } +/// int interval_in_milliseconds = 1000; +/// IOService io_service; +/// +/// IntervalTimer intervalTimer(io_service); +/// intervalTimer.setup(function_to_call_back, interval_in_milliseconds); +/// io_service.run(); +/// \endcode +class IntervalTimer { +public: + /// \name The type of timer callback function + typedef std::function<void()> Callback; + + /// \brief Defines possible timer modes used to setup a timer. + /// - REPEATING - Timer will reschedule itself after each expiration + /// - ONE_SHOT - Timer will expire after one interval and not reschedule. + enum Mode + { + REPEATING, + ONE_SHOT + }; + + /// + /// \name Constructors and Destructor + /// + /// Note: The copy constructor and the assignment operator are + /// intentionally defined as private, making this class non-copyable. + //@{ +private: + IntervalTimer(const IntervalTimer& source); + IntervalTimer& operator=(const IntervalTimer& source); +public: + /// \brief The constructor with \c IOService. + /// + /// This constructor may throw a standard exception if + /// memory allocation fails inside the method. + /// This constructor may also throw \c boost::system::system_error. + /// + /// \param io_service A reference to an instance of IOService + IntervalTimer(IOService& io_service); + + /// \brief The destructor. + /// + /// This destructor never throws an exception. + /// + /// On the destruction of this class the timer will be canceled + /// inside \c boost::asio::deadline_timer. + ~IntervalTimer(); + //@} + + /// \brief Register timer callback function and interval. + /// + /// This function sets callback function and interval in milliseconds. + /// Timer will actually start after calling \c IOService::run(). + /// + /// \param cbfunc A reference to a function \c void(void) to call back + /// when the timer is expired (should not be an empty functor) + /// \param interval Interval in milliseconds (greater than 0) + /// \param mode Determines if the timer will automatically reschedule after + /// each expiration (the default) or behave as a one-shot which will run + /// for a single interval and not reschedule. + /// + /// Note: IntervalTimer will not pass \c boost::system::error_code to + /// call back function. In case the timer is canceled, the function + /// will not be called. + /// + /// \throw isc::InvalidParameter cbfunc is empty + /// \throw isc::BadValue interval is less than or equal to 0 + /// \throw isc::Unexpected internal runtime error + void setup(const Callback& cbfunc, const long interval, + const Mode& mode = REPEATING); + + /// Cancel the timer. + /// + /// If the timer has been set up, this method cancels any asynchronous + /// events waiting on the timer and stops the timer itself. + /// If the timer has already been canceled, this method effectively does + /// nothing. + /// + /// This method never throws an exception. + void cancel(); + + /// Return the timer interval. + /// + /// This method returns the timer interval in milliseconds if it's running; + /// if the timer has been canceled it returns 0. + /// + /// This method never throws an exception. + long getInterval() const; + +private: + boost::shared_ptr<IntervalTimerImpl> impl_; +}; + +typedef boost::shared_ptr<isc::asiolink::IntervalTimer> IntervalTimerPtr; + +} // namespace asiolink +} // namespace isc +#endif // ASIOLINK_INTERVAL_TIMER_H diff --git a/src/lib/asiolink/io_acceptor.h b/src/lib/asiolink/io_acceptor.h new file mode 100644 index 0000000..eab0d83 --- /dev/null +++ b/src/lib/asiolink/io_acceptor.h @@ -0,0 +1,136 @@ +// Copyright (C) 2017-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/. + +#ifndef IO_ACCEPTOR_H +#define IO_ACCEPTOR_H + +#ifndef BOOST_ASIO_HPP +#error "asio.hpp must be included before including this, see asiolink.h as to why" +#endif + +#include <asiolink/io_service.h> +#include <asiolink/io_socket.h> + +namespace isc { +namespace asiolink { + +/// @brief Base class for acceptor services in Kea. +/// +/// This is a wrapper class for ASIO acceptor service. Classes implementing +/// services for specific protocol types should derive from this class. +/// +/// Acceptor is an IO object which accepts incoming connections into a socket +/// object. This socket is then used for data transmission from the client +/// to server and back. The acceptor is continued to be used to accept new +/// connections while the accepted connection is active. +/// +/// @tparam ProtocolType ASIO protocol type, e.g. stream_protocol +/// @tparam CallbackType Callback function type which should have the following +/// signature: @c void(const boost::system::error_code&). +template<typename ProtocolType, typename CallbackType> +class IOAcceptor : public IOSocket { +public: + + /// @brief Constructor. + /// + /// @param io_service Reference to the IO service. + explicit IOAcceptor(IOService& io_service) + : IOSocket(), + acceptor_(new typename ProtocolType::acceptor(io_service.get_io_service())) { + } + + /// @brief Destructor. + virtual ~IOAcceptor() { } + + /// @brief Returns file descriptor of the underlying socket. + virtual int getNative() const { +#if BOOST_VERSION < 106600 + return (acceptor_->native()); +#else + return (acceptor_->native_handle()); +#endif + } + + /// @brief Opens acceptor socket given the endpoint. + /// + /// @param endpoint Reference to the endpoint object defining local + /// acceptor endpoint. + /// + /// @tparam EndpointType Endpoint type. + template<typename EndpointType> + void open(const EndpointType& endpoint) { + acceptor_->open(endpoint.getASIOEndpoint().protocol()); + } + + /// @brief Binds socket to an endpoint. + /// + /// @param endpoint Reference to the endpoint object defining local + /// acceptor endpoint. + /// + /// @tparam EndpointType Endpoint type. + template<typename EndpointType> + void bind(const EndpointType& endpoint) { + acceptor_->bind(endpoint.getASIOEndpoint()); + } + + /// @brief Sets socket option. + /// + /// @param socket_option Reference to the object encapsulating an option to + /// be set for the socket. + /// @tparam SettableSocketOption Type of the object encapsulating socket option + /// being set. + template<typename SettableSocketOption> + void setOption(const SettableSocketOption& socket_option) { + acceptor_->set_option(socket_option); + } + + /// @brief Starts listening new connections. + void listen() { + acceptor_->listen(); + } + + /// @brief Checks if the acceptor is open. + /// + /// @return true if acceptor is open. + bool isOpen() const { + return (acceptor_->is_open()); + } + + /// @brief Closes the acceptor. + void close() const { + acceptor_->close(); + } + +protected: + + /// @brief Asynchronously accept new connection. + /// + /// This method accepts new connection into the specified socket. When the + /// new connection arrives or an error occurs the specified callback + /// function is invoked. + /// + /// @param socket Socket into which connection should be accepted. + /// @param callback Callback function to be invoked when the new connection + /// arrives. + /// @tparam SocketType Socket type, e.g. @ref UnixDomainSocket. It must + /// implement @c getASIOSocket method. + template<typename SocketType> + void asyncAcceptInternal(const SocketType& socket, + const CallbackType& callback) { + acceptor_->async_accept(socket.getASIOSocket(), callback); + } + + + /// @brief Underlying ASIO acceptor implementation. + boost::shared_ptr<typename ProtocolType::acceptor> acceptor_; + +}; + + +} // end of namespace asiolink +} // end of isc + +#endif // IO_ACCEPTOR_H diff --git a/src/lib/asiolink/io_address.cc b/src/lib/asiolink/io_address.cc new file mode 100644 index 0000000..7f7134d --- /dev/null +++ b/src/lib/asiolink/io_address.cc @@ -0,0 +1,189 @@ +// Copyright (C) 2010-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/asio_wrapper.h> +#include <asiolink/io_address.h> +#include <asiolink/io_error.h> +#include <exceptions/exceptions.h> + +#include <boost/static_assert.hpp> +// moved to container_hash on recent boost versions (backward compatible) +#include <boost/functional/hash.hpp> + +#include <unistd.h> // for some IPC/network system calls +#include <stdint.h> +#include <sys/socket.h> +#include <netinet/in.h> + +using namespace boost::asio; +using boost::asio::ip::udp; +using boost::asio::ip::tcp; + +using namespace std; + +namespace isc { +namespace asiolink { + +// XXX: we cannot simply construct the address in the initialization list, +// because we'd like to throw our own exception on failure. +IOAddress::IOAddress(const std::string& address_str) { + boost::system::error_code err; + asio_address_ = ip::address::from_string(address_str, err); + if (err) { + isc_throw(IOError, "Failed to convert string to address '" + << address_str << "': " << err.message()); + } +} + +IOAddress::IOAddress(const boost::asio::ip::address& asio_address) : + asio_address_(asio_address) +{} + +IOAddress::IOAddress(uint32_t v4address): + asio_address_(boost::asio::ip::address_v4(v4address)) { + +} + +string +IOAddress::toText() const { + return (asio_address_.to_string()); +} + +IOAddress +IOAddress::fromBytes(short family, const uint8_t* data) { + if (data == NULL) { + isc_throw(BadValue, "NULL pointer received."); + } else + if ( (family != AF_INET) && (family != AF_INET6) ) { + isc_throw(BadValue, "Invalid family type. Only AF_INET and AF_INET6" + << "are supported"); + } + + BOOST_STATIC_ASSERT(INET6_ADDRSTRLEN >= INET_ADDRSTRLEN); + char addr_str[INET6_ADDRSTRLEN]; + inet_ntop(family, data, addr_str, INET6_ADDRSTRLEN); + return IOAddress(string(addr_str)); +} + +std::vector<uint8_t> +IOAddress::toBytes() const { + if (asio_address_.is_v4()) { + const boost::asio::ip::address_v4::bytes_type bytes4 = + asio_address_.to_v4().to_bytes(); + return (std::vector<uint8_t>(bytes4.begin(), bytes4.end())); + } + + // Not V4 address, so must be a V6 address (else we could never construct + // this object). + const boost::asio::ip::address_v6::bytes_type bytes6 = + asio_address_.to_v6().to_bytes(); + return (std::vector<uint8_t>(bytes6.begin(), bytes6.end())); +} + +short +IOAddress::getFamily() const { + if (asio_address_.is_v4()) { + return (AF_INET); + } else { + return (AF_INET6); + } +} + +bool +IOAddress::isV6LinkLocal() const { + if (!asio_address_.is_v6()) { + return (false); + } + return (asio_address_.to_v6().is_link_local()); +} + +bool +IOAddress::isV6Multicast() const { + if (!asio_address_.is_v6()) { + return (false); + } + return (asio_address_.to_v6().is_multicast()); +} + +uint32_t +IOAddress::toUint32() const { + if (asio_address_.is_v4()) { + return (asio_address_.to_v4().to_ulong()); + } else { + isc_throw(BadValue, "Can't convert " << toText() + << " address to IPv4."); + } +} + +std::ostream& +operator<<(std::ostream& os, const IOAddress& address) { + os << address.toText(); + return (os); +} + +IOAddress +IOAddress::subtract(const IOAddress& a, const IOAddress& b) { + if (a.getFamily() != b.getFamily()) { + isc_throw(BadValue, "Both addresses have to be the same family"); + } + if (a.isV4()) { + // Subtracting v4 is easy. We have a conversion function to uint32_t. + return (IOAddress(a.toUint32() - b.toUint32())); + } else { + // v6 is more involved. + + // Let's extract the raw data first. + vector<uint8_t> a_vec = a.toBytes(); + vector<uint8_t> b_vec = b.toBytes(); + + // ... and prepare the result + vector<uint8_t> result(V6ADDRESS_LEN,0); + + // Carry is a boolean, but to avoid its frequent casting, let's + // use uint8_t. Also, some would prefer to call it borrow, but I prefer + // carry. Somewhat relevant discussion here: + // http://en.wikipedia.org/wiki/Carry_flag#Carry_flag_vs._Borrow_flag + uint8_t carry = 0; + + // Now perform subtraction with borrow. + for (int i = a_vec.size() - 1; i >= 0; --i) { + result[i] = a_vec[i] - b_vec[i] - carry; + carry = (a_vec[i] < b_vec[i] + carry); + } + + return (fromBytes(AF_INET6, &result[0])); + } +} + +IOAddress +IOAddress::increase(const IOAddress& addr) { + std::vector<uint8_t> packed(addr.toBytes()); + + // Start increasing the least significant byte + for (int i = packed.size() - 1; i >= 0; --i) { + // if we haven't overflowed (0xff -> 0x0), than we are done + if (++packed[i] != 0) { + break; + } + } + + return (IOAddress::fromBytes(addr.getFamily(), &packed[0])); +} + +size_t +hash_value(const IOAddress& address) { + if (address.isV4()) { + boost::hash<uint32_t> hasher; + return (hasher(address.toUint32())); + } else { + boost::hash<std::vector<uint8_t> > hasher; + return (hasher(address.toBytes())); + } +} + +} // namespace asiolink +} // namespace isc diff --git a/src/lib/asiolink/io_address.h b/src/lib/asiolink/io_address.h new file mode 100644 index 0000000..fb1d67b --- /dev/null +++ b/src/lib/asiolink/io_address.h @@ -0,0 +1,318 @@ +// Copyright (C) 2010-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/. + +#ifndef IO_ADDRESS_H +#define IO_ADDRESS_H 1 + +// IMPORTANT NOTE: only very few ASIO headers files can be included in +// this file. In particular, asio.hpp should never be included here. +// See the description of the namespace below. +#include <unistd.h> // for some network system calls +#include <stdint.h> // for uint32_t +#include <boost/asio/ip/address.hpp> + +#include <functional> +#include <string> +#include <vector> + +#include <exceptions/exceptions.h> + +namespace isc { +namespace asiolink { + + /// Defines length of IPv6 address (in binary format). + static constexpr size_t V6ADDRESS_LEN = 16; + + /// Defines length of IPv4 address (in binary format). + static constexpr size_t V4ADDRESS_LEN = 4; + + /// @brief Maximum size of an IPv4 address represented as a text string. 12 + /// digits plus 3 full stops (dots). + static constexpr size_t V4ADDRESS_TEXT_MAX_LEN = 15u; + + /// @brief Maximum size of an IPv6 address represented as a text string. 32 + /// hexadecimal characters written in 8 groups of four, plus 7 colon + /// separators. + static constexpr size_t V6ADDRESS_TEXT_MAX_LEN = 39u; + +/// \brief The \c IOAddress class represents an IP addresses (version +/// agnostic) +/// +/// This class is a wrapper for the ASIO \c ip::address class. +class IOAddress { +public: + /// + /// \name Constructors and Destructor + /// + /// This class is copyable. We use default versions of copy constructor + /// and the assignment operator. + /// We use the default destructor. + //@{ + /// \brief Constructor from string. + /// + /// This constructor converts a textual representation of IPv4 and IPv6 + /// addresses into an IOAddress object. + /// If \c address_str is not a valid representation of any type of + /// address, an exception of class \c IOError will be thrown. + /// This constructor allocates memory for the object, and if that fails + /// a corresponding standard exception will be thrown. + /// + /// \param address_str Textual representation of address. + IOAddress(const std::string& address_str); + + /// \brief Constructor from an ASIO \c ip::address object. + /// + /// This constructor is intended to be used within the wrapper + /// implementation; user applications of the wrapper API won't use it. + /// + /// This constructor never throws an exception. + /// + /// \param asio_address The ASIO \c ip::address to be converted. + IOAddress(const boost::asio::ip::address& asio_address); + //@} + + /// @brief Constructor for ip::address_v4 object. + /// + /// This constructor is intended to be used when constructing + /// IPv4 address out of uint32_t type. Passed value must be in + /// network byte order + /// + /// @param v4address IPv4 address represented by uint32_t + IOAddress(uint32_t v4address); + + /// \brief Convert the address to a string. + /// + /// This method is basically expected to be exception free, but + /// generating the string will involve resource allocation, + /// and if it fails the corresponding standard exception will be thrown. + /// + /// \return A string representation of the address. + std::string toText() const; + + /// \brief Returns the address family + /// + /// \return AF_INET for IPv4 or AF_INET6 for IPv6. + short getFamily() const; + + /// \brief Convenience function to check for an IPv4 address + /// + /// \return true if the address is a V4 address + bool isV4() const { + return (asio_address_.is_v4()); + } + + /// \brief Convenience function to check if it is an IPv4 zero address. + /// + /// \return true if the address is the zero IPv4 address. + bool isV4Zero() const { + return (equals(IPV4_ZERO_ADDRESS())); + } + + /// \brief Convenience function to check if it is an IPv4 broadcast + /// address. + /// + /// \return true if the address is the broadcast IPv4 address. + bool isV4Bcast() const { + return (equals(IPV4_BCAST_ADDRESS())); + } + + /// \brief Convenience function to check for an IPv6 address + /// + /// \return true if the address is a V6 address + bool isV6() const { + return (asio_address_.is_v6()); + } + + /// \brief Convenience function to check if it is an IPv4 zero address. + /// + /// \return true if the address is the zero IPv4 address. + bool isV6Zero() const { + return (equals(IPV6_ZERO_ADDRESS())); + } + + /// \brief checks whether and address is IPv6 and is link-local + /// + /// \return true if the address is IPv6 link-local, false otherwise + bool isV6LinkLocal() const; + + /// \brief checks whether and address is IPv6 and is multicast + /// + /// \return true if the address is IPv6 multicast, false otherwise + bool isV6Multicast() const; + + /// \brief Creates an address from over wire data. + /// + /// \param family AF_INET for IPv4 or AF_INET6 for IPv6. + /// \param data pointer to first char of data + /// + /// \return Created IOAddress object + static IOAddress fromBytes(short family, const uint8_t* data); + + /// \brief Return address as set of bytes + /// + /// \return Contents of the address as a set of bytes in network-byte + /// order. + std::vector<uint8_t> toBytes() const; + + /// \brief Compare addresses for equality + /// + /// \param other Address to compare against. + /// + /// \return true if addresses are equal, false if not. + bool equals(const IOAddress& other) const { + return (asio_address_ == other.asio_address_); + } + + /// \brief Compare addresses for equality + /// + /// \param other Address to compare against. + /// + /// \return true if addresses are equal, false if not. + bool operator==(const IOAddress& other) const { + return equals(other); + } + + /// \brief Compare addresses for inequality + /// + /// \param other Address to compare against. + /// + /// \return false if addresses are equal, true if not. + bool nequals(const IOAddress& other) const { + return (!equals(other)); + } + + /// \brief Checks if one address is smaller than the other + /// + /// \param other Address to compare against. + bool operator<(const IOAddress& other) const { + return (asio_address_ < other.asio_address_); + } + + /// \brief Checks if one address is smaller or equal than the other + /// + /// \param other Address to compare against. + bool operator<=(const IOAddress& other) const { + return (asio_address_ <= other.asio_address_); + } + + /// \brief Compare addresses for inequality + /// + /// \param other Address to compare against. + /// + /// \return false if addresses are equal, true if not. + bool operator!=(const IOAddress& other) const { + return (nequals(other)); + } + + /// @brief Subtracts one address from another (a - b) + /// + /// Treats addresses as integers and subtracts them. For example: + /// @code + /// 192.0.2.5 - 192.0.2.0 = 0.0.0.5 + /// fe80::abcd - fe80:: = ::abcd + /// @endcode + /// + /// It is possible to subtract greater from lesser address, e.g. + /// 192.168.56.10 - 192.168.67.20, but please do understand that + /// the address space is a finite field in mathematical sense, so + /// you may end up with a result that is greater then any of the + /// addresses you specified. Also, subtraction is not commutative, + /// so a - b != b - a. + /// + /// This operation is essential for calculating the number of + /// leases in a pool, where we need to calculate (max - min). + /// @throw BadValue if addresses are of different family + /// @param a address to be subtracted from + /// @param b address to be subtracted + /// @return IOAddress object that represents the difference + static IOAddress subtract(const IOAddress& a, const IOAddress& b); + + /// @brief Returns an address increased by one + /// + /// This method works for both IPv4 and IPv6 addresses. For example, + /// increase 192.0.2.255 will become 192.0.3.0. + /// + /// Address space is a finite field in the mathematical sense, so keep + /// in mind that the address space "loops". 255.255.255.255 increased + /// by one gives 0.0.0.0. The same is true for maximum value of IPv6 + /// (all 1's) looping to ::. + /// + /// @todo Determine if we have a use-case for increasing the address + /// by more than one. Increase by one is used in AllocEngine. This method + /// could take extra parameter that specifies the value by which the + /// address should be increased. + /// + /// @param addr address to be increased + /// @return address increased by one + static IOAddress + increase(const IOAddress& addr); + + /// \brief Converts IPv4 address to uint32_t + /// + /// Will throw BadValue exception if that is not IPv4 + /// address. + /// + /// \return uint32_t that represents IPv4 address in + /// network byte order + uint32_t toUint32() const; + + /// @name Methods returning @c IOAddress objects encapsulating typical addresses. + /// + //@{ + /// @brief Returns an address set to all zeros. + static const IOAddress& IPV4_ZERO_ADDRESS() { + static IOAddress address(0); + return (address); + } + + /// @brief Returns a "255.255.255.255" broadcast address. + static const IOAddress& IPV4_BCAST_ADDRESS() { + static IOAddress address(0xFFFFFFFF); + return (address); + } + + /// @brief Returns an IPv6 zero address. + static const IOAddress& IPV6_ZERO_ADDRESS() { + static IOAddress address("::"); + return (address); + } + + //@} + +private: + boost::asio::ip::address asio_address_; +}; + +/// \brief Insert the IOAddress as a string into stream. +/// +/// This method converts the \c address into a string and inserts it +/// into the output stream \c os. +/// +/// This function overloads the global operator<< to behave as described +/// in ostream::operator<< but applied to \c IOAddress objects. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param address The \c IOAddress object output by the operation. +/// \return A reference to the same \c std::ostream object referenced by +/// parameter \c os after the insertion operation. +std::ostream& +operator<<(std::ostream& os, const IOAddress& address); + +/// \brief Hash the IOAddress. +/// +/// This method allows boost multi-index hashed indexes on IOAddresses. +/// It follows the requirement with equality: if two addresses are equal +/// their hashes are equal, if two addresses are not equal their hashes +/// are almost surely not equal. +/// +/// \param address A \c IOAddress to hash. +/// \return The hash of the IOAddress. +size_t hash_value(const IOAddress& address); + +} // namespace asiolink +} // namespace isc +#endif // IO_ADDRESS_H diff --git a/src/lib/asiolink/io_asio_socket.h b/src/lib/asiolink/io_asio_socket.h new file mode 100644 index 0000000..29ca97f --- /dev/null +++ b/src/lib/asiolink/io_asio_socket.h @@ -0,0 +1,381 @@ +// Copyright (C) 2010-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/. + +#ifndef IO_ASIO_SOCKET_H +#define IO_ASIO_SOCKET_H 1 + +// IMPORTANT NOTE: only very few ASIO headers files can be included in +// this file. In particular, asio.hpp should never be included here. +// See the description of the namespace below. +#include <config.h> + +#include <unistd.h> // for some network system calls + +#include <functional> +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> + +#include <asiolink/io_error.h> +#include <asiolink/io_socket.h> + +// We want to use coroutine.hpp from the system's boost headers if possible. +// However, very old Boost versions (provided by RHEL 7 or CentOS 7) didn't have +// this header. So we can resort to our bundled version, but only if necessary. +#ifndef HAVE_BOOST_ASIO_COROUTINE_HPP +#include <ext/coroutine/coroutine.hpp> +#else +#include <boost/asio/coroutine.hpp> +#endif + +namespace isc { +namespace asiolink { + +/// \brief Socket not open +/// +/// Thrown on an attempt to do read/write to a socket that is not open. +class SocketNotOpen : public IOError { +public: + SocketNotOpen(const char* file, size_t line, const char* what) : + IOError(file, line, what) {} +}; + +/// \brief Error setting socket options +/// +/// Thrown if attempt to change socket options fails. +class SocketSetError : public IOError { +public: + SocketSetError(const char* file, size_t line, const char* what) : + IOError(file, line, what) {} +}; + +/// \brief Buffer overflow +/// +/// Thrown if an attempt is made to receive into an area beyond the end of +/// the receive data buffer. +class BufferOverflow : public IOError { +public: + BufferOverflow(const char* file, size_t line, const char* what) : + IOError(file, line, what) {} +}; + +/// Forward declaration of an IOEndpoint +class IOEndpoint; + + +/// \brief I/O Socket with asynchronous operations +/// +/// This class is a wrapper for the ASIO socket classes such as +/// \c ip::tcp::socket and \c ip::udp::socket. +/// +/// This is the basic IOSocket with additional operations - open, send, receive +/// and close. Depending on how the asiolink code develops, it may be a +/// temporary class: its main use is to add the template parameter needed for +/// the derived classes UDPSocket and TCPSocket but without changing the +/// signature of the more basic IOSocket class. +/// +/// We may revisit this decision when we generalize the wrapper and more +/// modules use it. Also, at that point we may define a separate (visible) +/// derived class for testing purposes rather than providing factory methods +/// (i.e., getDummy variants below). +/// +/// \param C Template parameter identifying type of the callback object. + +template <typename C> +class IOAsioSocket : public IOSocket { + + /// + /// \name Constructors and Destructor + /// + /// Note: The copy constructor and the assignment operator are + /// intentionally defined as private, making this class non-copyable. + //@{ +private: + IOAsioSocket(const IOAsioSocket<C>& source); + IOAsioSocket& operator=(const IOAsioSocket<C>& source); +protected: + /// \brief The default constructor. + /// + /// This is intentionally defined as \c protected as this base class + /// should never be instantiated (except as part of a derived class). + IOAsioSocket() {} +public: + /// The destructor. + virtual ~IOAsioSocket() {} + //@} + + /// \brief Return the "native" representation of the socket. + /// + /// In practice, this is the file descriptor of the socket for UNIX-like + /// systems so the current implementation simply uses \c int as the type of + /// the return value. We may have to need revisit this decision later. + /// + /// In general, the application should avoid using this method; it + /// essentially discloses an implementation specific "handle" that can + /// change the internal state of the socket (consider what would happen if + /// the application closes it, for example). But we sometimes need to + /// perform very low-level operations that requires the native + /// representation. Passing the file descriptor to a different process is + /// one example. This method is provided as a necessary evil for such + /// limited purposes. + /// + /// This method never throws an exception. + /// + /// \return The native representation of the socket. This is the socket + /// file descriptor for UNIX-like systems. + virtual int getNative() const = 0; + + /// \brief Return the transport protocol of the socket. + /// + /// Currently, it returns \c IPPROTO_UDP for UDP sockets, and + /// \c IPPROTO_TCP for TCP sockets. + /// + /// This method never throws an exception. + /// + /// \return \c IPPROTO_UDP for UDP sockets, \c IPPROTO_TCP for TCP sockets + virtual int getProtocol() const = 0; + + /// \brief Is Open() synchronous? + /// + /// On a TCP socket, an "open" operation is a call to the socket's "open()" + /// method followed by a connection to the remote system: it is an + /// asynchronous operation. On a UDP socket, it is just a call to "open()" + /// and completes synchronously. + /// + /// For TCP, signalling of the completion of the operation is done by + /// by calling the callback function in the normal way. This could be done + /// for UDP (by posting en event on the event queue); however, that will + /// incur additional overhead in the most common case. So we give the + /// caller the choice for calling this open() method synchronously or + /// asynchronously. + /// + /// Owing to the way that the stackless coroutines are implemented, we need + /// to know _before_ executing the "open" function whether or not it is + /// asynchronous. So this method is called to provide that information. + /// + /// (The reason there is a need to know is because the call to open() passes + /// in the state of the coroutine at the time the call is made. On an + /// asynchronous I/O, we need to set the state to point to the statement + /// after the call to open() _before_ we pass the coroutine to the open() + /// call. Unfortunately, the macros that set the state of the coroutine + /// also yield control - which we don't want to do if the open is + /// synchronous. Hence we need to know before we make the call to open() + /// whether that call will complete asynchronously.) + virtual bool isOpenSynchronous() const = 0; + + /// \brief Open AsioSocket + /// + /// Opens the socket for asynchronous I/O. The open will complete + /// synchronously on UCP or asynchronously on TCP (in which case a callback + /// will be queued). + /// + /// \param endpoint Pointer to the endpoint object. This is ignored for + /// a UDP socket (the target is specified in the send call), but + /// should be of type TCPEndpoint for a TCP connection. + /// \param callback I/O Completion callback, called when the operation has + /// completed, but only if the operation was asynchronous. (It is + /// ignored on a UDP socket.) + virtual void open(const IOEndpoint* endpoint, C& callback) = 0; + + /// \brief Send Asynchronously + /// + /// This corresponds to async_send_to() for UDP sockets and async_send() + /// for TCP. In both cases an endpoint argument is supplied indicating the + /// target of the send - this is ignored for TCP. + /// + /// \param data Data to send + /// \param length Length of data to send + /// \param endpoint Target of the send + /// \param callback Callback object. + virtual void asyncSend(const void* data, size_t length, + const IOEndpoint* endpoint, C& callback) = 0; + + /// \brief Receive Asynchronously + /// + /// This corresponds to async_receive_from() for UDP sockets and + /// async_receive() for TCP. In both cases, an endpoint argument is + /// supplied to receive the source of the communication. For TCP it will + /// be filled in with details of the connection. + /// + /// \param data Buffer to receive incoming message + /// \param length Length of the data buffer + /// \param offset Offset into buffer where data is to be put. Although the + /// offset could be implied by adjusting "data" and "length" + /// appropriately, using this argument allows data to be specified as + /// "const void*" - the overhead of converting it to a pointer to a + /// set of bytes is hidden away here. + /// \param endpoint Source of the communication + /// \param callback Callback object + virtual void asyncReceive(void* data, size_t length, size_t offset, + IOEndpoint* endpoint, C& callback) = 0; + + /// \brief Processes received data + /// + /// In the IOFetch code, data is received into a staging buffer before being + /// copied into the target buffer. (This is because (a) we don't know how + /// much data we will be receiving, so don't know how to size the output + /// buffer and (b) TCP data is preceded by a two-byte count field that needs + /// to be discarded before being returned to the user.) + /// + /// An additional consideration is that TCP data is not received in one + /// I/O - it may take a number of I/Os - each receiving any non-zero number + /// of bytes - to read the entire message. + /// + /// So the IOFetch code has to loop until it determines that all the data + /// has been read. This is where this method comes in. It has several + /// functions: + /// + /// - It checks if the received data is complete. + /// - If data is not complete, decides if the next set of data is to go into + /// the start of the staging buffer or at some offset into it. (This + /// simplifies the case we could have in a TCP receive where the two-byte + /// count field is received in one-byte chunks: we put off interpreting + /// the count until we have all of it. The alternative - copying the + /// data to the output buffer and interpreting the count from there - + /// would require moving the data in the output buffer by two bytes before + /// returning it to the caller.) + /// - Copies data from the staging buffer into the output buffer. + /// + /// This functionality mainly applies to TCP receives. For UDP, all the + /// data is received in one I/O, so this just copies the data into the + /// output buffer. + /// + /// \param staging Pointer to the start of the staging buffer. + /// \param length Amount of data in the staging buffer. + /// \param cumulative Amount of data received before the staging buffer is + /// processed (this includes the TCP count field if appropriate). + /// The value should be set to zero before the receive loop is + /// entered, and it will be updated by this method as required. + /// \param offset Offset into the staging buffer where the next read should + /// put the received data. It should be set to zero before the first + /// call and may be updated by this method. + /// \param expected Expected amount of data to be received. This is + /// really the TCP count field and is set to that value when enough + /// of a TCP message is received. It should be initialized to -1 + /// before the first read is executed. + /// \param outbuff Output buffer. Data in the staging buffer may be copied + /// to this output buffer in the call. + /// + /// \return true if the receive is complete, false if another receive is + /// needed. This is always true for UDP, but for TCP involves + /// checking the amount of data received so far against the amount + /// expected (as indicated by the two-byte count field). If this + /// method returns false, another read should be queued and data + /// should be read into the staging buffer at offset given by the + /// "offset" parameter. + virtual bool processReceivedData(const void* staging, size_t length, + size_t& cumulative, size_t& offset, + size_t& expected, + isc::util::OutputBufferPtr& outbuff) = 0; + + /// \brief Cancel I/O On AsioSocket + virtual void cancel() = 0; + + /// \brief Close socket + virtual void close() = 0; +}; + + +/// \brief The \c DummyAsioSocket class is a concrete derived class of +/// \c IOAsioSocket that is not associated with any real socket. +/// +/// This main purpose of this class is tests, where it may be desirable to +/// instantiate an \c IOAsioSocket object without involving system resource +/// allocation such as real network sockets. +/// +/// \param C Template parameter identifying type of the callback object. + +template <typename C> +class DummyAsioSocket : public IOAsioSocket<C> { +private: + DummyAsioSocket(const DummyAsioSocket<C>& source); + DummyAsioSocket& operator=(const DummyAsioSocket<C>& source); +public: + /// \brief Constructor from the protocol number. + /// + /// The protocol must validly identify a standard network protocol. + /// For example, to specify TCP \c protocol must be \c IPPROTO_TCP. + /// + /// \param protocol The network protocol number for the socket. + DummyAsioSocket(const int protocol) : protocol_(protocol) {} + + /// \brief A dummy derived method of \c IOAsioSocket::getNative(). + /// + /// \return Always returns -1 as the object is not associated with a real + /// (native) socket. + virtual int getNative() const { return (-1); } + + /// \brief A dummy derived method of \c IOAsioSocket::getProtocol(). + /// + /// \return Protocol socket was created with + virtual int getProtocol() const { return (protocol_); } + + + /// \brief Is socket opening synchronous? + /// + /// \return true - it is for this class. + bool isOpenSynchronous() const { + return true; + } + + /// \brief Open AsioSocket + /// + /// A call that is a no-op on UDP sockets, this opens a connection to the + /// system identified by the given endpoint. + /// The endpoint and callback are unused. + /// + /// \return false indicating that the operation completed synchronously. + virtual bool open(const IOEndpoint*, C&) { + return (false); + } + + /// \brief Send Asynchronously + /// + /// Must be supplied as it is abstract in the base class. + /// This is unused. + virtual void asyncSend(const void*, size_t, const IOEndpoint*, C&) { + } + + /// \brief Receive Asynchronously + /// + /// Must be supplied as it is abstract in the base class. + /// The parameters are unused. + virtual void asyncReceive(void*, size_t, size_t, IOEndpoint*, C&) { + } + + /// \brief Checks if the data received is complete. + /// + /// The parameters are unused. + /// \return Always true + virtual bool receiveComplete(const void*, size_t, size_t&, size_t&, + size_t&, isc::util::OutputBufferPtr&) + { + return (true); + } + + + /// \brief Cancel I/O On AsioSocket + /// + /// Must be supplied as it is abstract in the base class. + virtual void cancel() { + } + + /// \brief Close socket + /// + /// Must be supplied as it is abstract in the base class. + virtual void close() { + } + +private: + const int protocol_; +}; + +} // namespace asiolink +} // namespace isc + +#endif // IO_ASIO_SOCKET_H diff --git a/src/lib/asiolink/io_endpoint.cc b/src/lib/asiolink/io_endpoint.cc new file mode 100644 index 0000000..d236aa8 --- /dev/null +++ b/src/lib/asiolink/io_endpoint.cc @@ -0,0 +1,68 @@ +// 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/io_error.h> +#include <asiolink/io_endpoint.h> +#include <asiolink/tcp_endpoint.h> +#include <asiolink/udp_endpoint.h> + +#include <boost/lexical_cast.hpp> + +#include <cassert> +#include <unistd.h> // for some IPC/network system calls +#include <sys/socket.h> +#include <netinet/in.h> + +using namespace std; + +namespace isc { +namespace asiolink { + +const IOEndpoint* +IOEndpoint::create(const int protocol, const IOAddress& address, + const unsigned short port) +{ + if (protocol == IPPROTO_UDP) { + return (new UDPEndpoint(address, port)); + } else if (protocol == IPPROTO_TCP) { + return (new TCPEndpoint(address, port)); + } + isc_throw(IOError, + "IOEndpoint creation attempt for unsupported protocol: " << + protocol); +} + +bool +IOEndpoint::operator==(const IOEndpoint& other) const { + return (getProtocol() == other.getProtocol() && + getPort() == other.getPort() && + getFamily() == other.getFamily() && + getAddress() == other.getAddress()); +} + +bool +IOEndpoint::operator!=(const IOEndpoint& other) const { + return (!operator==(other)); +} + +ostream& +operator<<(ostream& os, const IOEndpoint& endpoint) { + if (endpoint.getFamily() == AF_INET6) { + os << "[" << endpoint.getAddress() << "]"; + } else { + // In practice this should be AF_INET, but it's not guaranteed by + // the interface. We'll use the result of textual address + // representation opaquely. + os << endpoint.getAddress(); + } + os << ":" << boost::lexical_cast<string>(endpoint.getPort()); + return (os); +} +} // namespace asiolink +} // namespace isc diff --git a/src/lib/asiolink/io_endpoint.h b/src/lib/asiolink/io_endpoint.h new file mode 100644 index 0000000..8421a30 --- /dev/null +++ b/src/lib/asiolink/io_endpoint.h @@ -0,0 +1,183 @@ +// Copyright (C) 2010-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/. + +#ifndef IO_ENDPOINT_H +#define IO_ENDPOINT_H 1 + +// IMPORTANT NOTE: only very few ASIO headers files can be included in +// this file. In particular, asio.hpp should never be included here. +// See the description of the namespace below. + +#include <functional> +#include <string> + +#include <exceptions/exceptions.h> +#include <asiolink/io_address.h> + +# include <ostream> + +#include <unistd.h> // for some network system calls + +#include <sys/socket.h> // for sockaddr + +namespace isc { +namespace asiolink { + +/// \brief The \c IOEndpoint class is an abstract base class to represent +/// a communication endpoint. +/// +/// This class is a wrapper for the ASIO endpoint classes such as +/// \c ip::tcp::endpoint and \c ip::udp::endpoint. +/// +/// Derived class implementations are completely hidden within the +/// implementation. User applications only get access to concrete +/// \c IOEndpoint objects via the abstract interfaces. +class IOEndpoint { + /// + /// \name Constructors and Destructor + /// + /// Note: The copy constructor and the assignment operator are + /// intentionally defined as private, making this class non-copyable. + //@{ +private: + IOEndpoint(const IOEndpoint& source); + IOEndpoint& operator=(const IOEndpoint& source); +protected: + /// \brief The default constructor. + /// + /// This is intentionally defined as \c protected as this base class + /// should never be instantiated (except as part of a derived class). + IOEndpoint() {} +public: + /// The destructor. + virtual ~IOEndpoint() {} + //@} + + /// \brief Returns the address of the endpoint. + /// + /// This method returns an IOAddress object corresponding to \c this + /// endpoint. + /// + /// Note that the return value is a real object, not a reference or + /// a pointer. + /// + /// This is aligned with the interface of the ASIO counterpart: + /// the \c address() method of \c ip::xxx::endpoint classes returns + /// an \c ip::address object. + /// + /// This also means handling the address of an endpoint using this method + /// can be expensive. If the address information is necessary in a + /// performance sensitive context and there's a more efficient interface + /// for that purpose, it's probably better to avoid using this method. + /// + /// This method never throws an exception. + /// + /// \return A copy of \c IOAddress object corresponding to the endpoint. + virtual IOAddress getAddress() const = 0; + + /// \brief Returns the port of the endpoint. + virtual uint16_t getPort() const = 0; + + /// \brief Returns the protocol number of the endpoint (TCP, UDP...) + virtual short getProtocol() const = 0; + + /// \brief Returns the address family of the endpoint. + virtual short getFamily() const = 0; + + /// \brief Returns the address of the endpoint in the form of sockaddr + /// structure. + /// + /// The actual instance referenced by the returned value of this method + /// is of per address family structure: For IPv4 (AF_INET), it's + /// \c sockaddr_in; for IPv6 (AF_INET6), it's \c sockaddr_in6. + /// The corresponding port and address members of the underlying structure + /// will be set in the network byte order. + /// + /// This method is "redundant" in that all information to construct the + /// \c sockaddr is available via the other "get" methods. + /// It is still defined for performance sensitive applications that need + /// to get the address information, such as for address based access + /// control at a high throughput. Internally it is implemented with + /// minimum overhead such as data copy (this is another reason why this + /// method returns a reference). + /// + /// As a tradeoff, this method is more fragile; it assumes that the + /// underlying ASIO implementation stores the address information in + /// the form of \c sockaddr and it can be accessed in an efficient way. + /// This is the case as of this writing, but if the underlying + /// implementation changes this method may become much slower or its + /// interface may have to be changed, too. + /// + /// It is therefore discouraged for normal applications to use this + /// method. Unless the application is very performance sensitive, it + /// should use the other "get" method to retrieve specific information + /// of the endpoint. + /// + /// The returned reference is only valid while the corresponding + /// \c IOEndpoint is valid. Once it's destructed the reference will + /// become invalid. + /// + /// \exception None + /// \return Reference to a \c sockaddr structure corresponding to the + /// endpoint. + virtual const struct sockaddr& getSockAddr() const = 0; + + bool operator==(const IOEndpoint& other) const; + bool operator!=(const IOEndpoint& other) const; + + /// \brief A polymorphic factory of endpoint from address and port. + /// + /// This method creates a new instance of (a derived class of) + /// \c IOEndpoint object that identifies the pair of given address + /// and port. + /// The appropriate derived class is chosen based on the specified + /// transport protocol. If the \c protocol doesn't specify a protocol + /// supported in this implementation, an exception of class \c IOError + /// will be thrown. + /// + /// Memory for the created object will be dynamically allocated. It's + /// the caller's responsibility to \c delete it later. + /// If resource allocation for the new object fails, a corresponding + /// standard exception will be thrown. + /// + /// \param protocol The transport protocol used for the endpoint. + /// Currently, only \c IPPROTO_UDP and \c IPPROTO_TCP can be specified. + /// \param address The (IP) address of the endpoint. + /// \param port The transport port number of the endpoint + /// \return A pointer to a newly created \c IOEndpoint object. + static const IOEndpoint* create(const int protocol, + const IOAddress& address, + const unsigned short port); +}; + +/// \brief Insert the \c IOEndpoint as a string into stream. +/// +/// This method converts \c endpoint into a string and inserts it into the +/// output stream \c os. +/// +/// This method converts the address and port of the endpoint in the textual +/// format that other Kea modules would use in logging, i.e., +/// - For IPv6 address: [<address>]:port (e.g., [2001:db8::5300]:53) +/// - For IPv4 address: <address>:port (e.g., 192.0.2.53:5300) +/// +/// If it's neither IPv6 nor IPv4, it converts the endpoint into text in the +/// same format as that for IPv4, although in practice such a case is not +/// really expected. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param endpoint A reference to an \c IOEndpoint object output by the +/// operation. +/// \return A reference to the same \c std::ostream object referenced by +/// parameter \c os after the insertion operation. +std::ostream& operator<<(std::ostream& os, const IOEndpoint& endpoint); +} // namespace asiolink +} // namespace isc +#endif // IO_ENDPOINT_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/asiolink/io_error.h b/src/lib/asiolink/io_error.h new file mode 100644 index 0000000..692070c --- /dev/null +++ b/src/lib/asiolink/io_error.h @@ -0,0 +1,29 @@ +// 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/. + + +#ifndef IO_ERROR_H +#define IO_ERROR_H + +#include <exceptions/exceptions.h> + +namespace isc { +namespace asiolink { + +/// \brief An exception that is thrown if an error occurs within the IO +/// module. This is mainly intended to be a wrapper exception class for +/// ASIO specific exceptions. +class IOError : public isc::Exception { +public: + IOError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + + +} // namespace asiolink +} // namespace isc + +#endif // IO_ERROR_H diff --git a/src/lib/asiolink/io_service.cc b/src/lib/asiolink/io_service.cc new file mode 100644 index 0000000..aad5dc7 --- /dev/null +++ b/src/lib/asiolink/io_service.cc @@ -0,0 +1,182 @@ +// 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/. + +#include <config.h> +#include <asiolink/asio_wrapper.h> +#include <asiolink/io_service.h> + +#include <unistd.h> // for some IPC/network system calls +#include <netinet/in.h> +#include <boost/shared_ptr.hpp> +#include <sys/socket.h> + +namespace isc { +namespace asiolink { + +namespace { +// A trivial wrapper for std::function. SunStudio doesn't seem to be capable +// of handling a std::function object if directly passed to +// io_service::post(). +class CallbackWrapper { +public: + + /// \brief Constructor + CallbackWrapper(const std::function<void()>& callback) : + callback_(callback) {} + + /// \brief Function operator + void operator()() { + callback_(); + } + +private: + + /// \brief The callback function + std::function<void()> callback_; +}; +} + +class IOServiceImpl { +private: + IOServiceImpl(const IOService& source); + IOServiceImpl& operator=(const IOService& source); +public: + /// \brief The constructor + IOServiceImpl() : + io_service_(), + work_(new boost::asio::io_service::work(io_service_)) { + }; + + /// \brief The destructor. + ~IOServiceImpl() {}; + //@} + + /// \brief Start the underlying event loop. + /// + /// This method does not return control to the caller until + /// the \c stop() method is called via some handler. + void run() { + io_service_.run(); + }; + + /// \brief Run the underlying event loop for a single event. + /// + /// This method return control to the caller as soon as the + /// first handler has completed. (If no handlers are ready when + /// it is run, it will block until one is.) + void run_one() { + io_service_.run_one(); + }; + + /// \brief Run the underlying event loop for a ready events. + /// + /// This method executes handlers for all ready events and returns. + /// It will return immediately if there are no ready events. + void poll() { + io_service_.poll(); + }; + + /// \brief Stop the underlying event loop. + /// + /// This will return the control to the caller of the \c run() method. + void stop() { + io_service_.stop(); + } + + /// \brief Indicates if the IOService has been stopped. + /// + /// \return true if the IOService has been stopped, false otherwise. + bool stopped() const { + return (io_service_.stopped()); + } + + /// \brief Restarts the IOService in preparation for a subsequent \c run() invocation. + void restart() { + io_service_.reset(); + } + + /// \brief Removes IO service work object to let it finish running + /// when all handlers have been invoked. + void stopWork() { + work_.reset(); + } + + /// \brief Return the native \c io_service object used in this wrapper. + /// + /// This is a short term work around to support other Kea modules + /// that share the same \c io_service with the authoritative server. + /// It will eventually be removed once the wrapper interface is + /// generalized. + boost::asio::io_service& get_io_service() { + return (io_service_); + } + + /// \brief Post a callback on the IO service + /// + /// \param callback The callback to be run on the IO service. + void post(const std::function<void ()>& callback) { + const CallbackWrapper wrapper(callback); + io_service_.post(wrapper); + } + +private: + boost::asio::io_service io_service_; + boost::shared_ptr<boost::asio::io_service::work> work_; +}; + +IOService::IOService() : io_impl_(new IOServiceImpl()) { +} + +IOService::~IOService() { +} + +void +IOService::run() { + io_impl_->run(); +} + +void +IOService::run_one() { + io_impl_->run_one(); +} + +void +IOService::poll() { + io_impl_->poll(); +} + +void +IOService::stop() { + io_impl_->stop(); +} + +bool +IOService::stopped() const { + return (io_impl_->stopped()); +} + +void +IOService::restart() { + io_impl_->restart(); +} + +void +IOService::stopWork() { + io_impl_->stopWork(); +} + +boost::asio::io_service& +IOService::get_io_service() { + return (io_impl_->get_io_service()); +} + +void +IOService::post(const std::function<void ()>& callback) { + return (io_impl_->post(callback)); +} + +} // namespace asiolink +} // namespace isc diff --git a/src/lib/asiolink/io_service.h b/src/lib/asiolink/io_service.h new file mode 100644 index 0000000..4bcedf1 --- /dev/null +++ b/src/lib/asiolink/io_service.h @@ -0,0 +1,114 @@ +// 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/. + +#ifndef ASIOLINK_IO_SERVICE_H +#define ASIOLINK_IO_SERVICE_H + +#include <boost/version.hpp> +#include <boost/shared_ptr.hpp> +#include <functional> + +namespace boost { +namespace asio { +#if BOOST_VERSION < 106600 + class io_service; +#else + class io_context; + typedef io_context io_service; +#endif +} +} + +namespace isc { +namespace asiolink { + +class IOServiceImpl; + +/// \brief The \c IOService class is a wrapper for the ASIO \c io_service +/// class. +/// +class IOService { + /// + /// \name Constructors and Destructor + /// + /// Note: The copy constructor and the assignment operator are + /// intentionally defined as private, making this class non-copyable. + //@{ +private: + IOService(const IOService& source); + IOService& operator=(const IOService& source); +public: + /// \brief The constructor + IOService(); + /// \brief The destructor. + ~IOService(); + //@} + + /// \brief Start the underlying event loop. + /// + /// This method does not return control to the caller until + /// the \c stop() method is called via some handler. + void run(); + + /// \brief Run the underlying event loop for a single event. + /// + /// This method return control to the caller as soon as the + /// first handler has completed. (If no handlers are ready when + /// it is run, it will block until one is.) + void run_one(); + + /// \brief Run the underlying event loop for a ready events. + /// + /// This method executes handlers for all ready events and returns. + /// It will return immediately if there are no ready events. + void poll(); + + /// \brief Stop the underlying event loop. + /// + /// This will return the control to the caller of the \c run() method. + void stop(); + + /// \brief Indicates if the IOService has been stopped. + /// + /// \return true if the IOService has been stopped, false otherwise. + bool stopped() const; + + /// \brief Restarts the IOService in preparation for a subsequent \c run() invocation. + void restart(); + + /// \brief Removes IO service work object to let it finish running + /// when all handlers have been invoked. + void stopWork(); + + /// \brief Return the native \c io_service object used in this wrapper. + /// + /// This is a short term work around to support other Kea modules + /// that share the same \c io_service with the authoritative server. + /// It will eventually be removed once the wrapper interface is + /// generalized. + boost::asio::io_service& get_io_service(); + + /// \brief Post a callback to the end of the queue. + /// + /// Requests the callback be called sometime later. It is not guaranteed + /// by the underlying asio, but it can reasonably be expected the callback + /// is put to the end of the callback queue. It is not called from within + /// this function. + /// + /// It may be used to implement "background" work, for example (doing stuff + /// by small bits that are called from time to time). + void post(const std::function<void ()>& callback); + +private: + boost::shared_ptr<IOServiceImpl> io_impl_; +}; + +/// @brief Defines a smart pointer to an IOService instance. +typedef boost::shared_ptr<IOService> IOServicePtr; + +} // namespace asiolink +} // namespace isc +#endif // ASIOLINK_IO_SERVICE_H diff --git a/src/lib/asiolink/io_service_signal.cc b/src/lib/asiolink/io_service_signal.cc new file mode 100644 index 0000000..ea10103 --- /dev/null +++ b/src/lib/asiolink/io_service_signal.cc @@ -0,0 +1,129 @@ +// Copyright (C) 2020-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/io_service_signal.h> +#include <exceptions/exceptions.h> + +#include <boost/enable_shared_from_this.hpp> +#include <boost/noncopyable.hpp> +#include <boost/asio/signal_set.hpp> +#include <functional> + +namespace ph = std::placeholders; + +namespace isc { +namespace asiolink { + +/// @brief Implementation class of IOSignalSet. +class IOSignalSetImpl : public boost::enable_shared_from_this<IOSignalSetImpl>, + public boost::noncopyable { +public: + /// @brief Constructor. + /// + /// @param io_service the process IO service. + /// @param handler the signal handler. + IOSignalSetImpl(IOServicePtr io_service, IOSignalHandler handler); + + /// @brief Destructor. + ~IOSignalSetImpl() = default; + + /// @brief Install the callback on the IO service queue. + void install(); + + /// @brief Add a signal to the ASIO signal set. + /// + /// @param signum the signal number. + void add(int signum); + + /// @brief Remove a signal from the ASIO signal set. + /// + /// @param signum the signal number. + void remove(int signum); + +private: + /// @brief Extends the lifetime of IOService to avoid heap-use-after-free. + IOServicePtr io_service_; + + /// @brief the ASIO signal set. + boost::asio::signal_set signal_set_; + + /// @brief the signal handler. + IOSignalHandler handler_; + + /// @brief the callback (called on cancel or received signal). + /// + /// The callback is installed on the IO service queue and calls + /// the handler if the operation was not aborted. + void callback(const boost::system::error_code& ec, int signum); +}; + +IOSignalSetImpl::IOSignalSetImpl(IOServicePtr io_service, + IOSignalHandler handler) + : io_service_(io_service), + signal_set_(io_service_->get_io_service()), + handler_(handler) { +} + +void +IOSignalSetImpl::callback(const boost::system::error_code& ec, int signum) { + if (ec && ec.value() == boost::asio::error::operation_aborted) { + return; + } + install(); + if (!ec && (signum > 0)) { + try { + handler_(signum); + } catch (const std::exception& ex) { + } + } +} + +void +IOSignalSetImpl::install() { + signal_set_.async_wait(std::bind(&IOSignalSetImpl::callback, + shared_from_this(), ph::_1, ph::_2)); +} + +void +IOSignalSetImpl::add(int signum) { + try { + signal_set_.add(signum); + } catch (const boost::system::system_error& ex) { + isc_throw(isc::Unexpected, + "Failed to add signal " << signum << ": " << ex.what()); + } +} + +void +IOSignalSetImpl::remove(int signum) { + try { + signal_set_.remove(signum); + } catch (const boost::system::system_error& ex) { + isc_throw(isc::Unexpected, + "Failed to remove signal " << signum << ": " << ex.what()); + } +} + +IOSignalSet::IOSignalSet(IOServicePtr io_service, IOSignalHandler handler) : + impl_(new IOSignalSetImpl(io_service, handler)) { + // It can throw but the error is fatal... + impl_->install(); +} + +void +IOSignalSet::add(int signum) { + impl_->add(signum); +} + +void +IOSignalSet::remove(int signum) { + impl_->remove(signum); +} + +} // namespace asiolink +} // namespace isc diff --git a/src/lib/asiolink/io_service_signal.h b/src/lib/asiolink/io_service_signal.h new file mode 100644 index 0000000..b078ce4 --- /dev/null +++ b/src/lib/asiolink/io_service_signal.h @@ -0,0 +1,60 @@ +// Copyright (C) 2020-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/. + +#ifndef IO_SERVICE_SIGNAL_H +#define IO_SERVICE_SIGNAL_H + +#include <asiolink/io_service.h> + +#include <boost/shared_ptr.hpp> + +namespace isc { +namespace asiolink { + +/// @brief Defines a handler function for an IOSignal. +typedef std::function<void(int signum)> IOSignalHandler; + +class IOSignalSetImpl; + +/// @brief Implements an asynchronous "signal" for IOService driven processing +/// +/// This class allows a OS signal such as SIGHUP to propagated to an IOService +/// as a ready event with a callback using boost ASIO. +class IOSignalSet { +public: + /// @brief Constructor. + /// + /// @param io_service IOService to which to send the signal. + /// @param handler Handler to call when a signal is received. + IOSignalSet(asiolink::IOServicePtr io_service, IOSignalHandler handler); + + /// @brief Destructor. + ~IOSignalSet() = default; + + /// @brief Add a signal to the list of signals to handle. + /// + /// @param signum Signal number. + /// @throw Unexpected on error. + void add(int signum); + + /// @brief Remove a signal from the list of signals to handle. + /// + /// @param signum Signal number. + /// @throw Unexpected on error. + void remove(int signum); + +private: + /// @brief Pointer to the implementation. + boost::shared_ptr<IOSignalSetImpl> impl_; +}; + +/// @brief Defines a pointer to an IOSignalSet. +typedef boost::shared_ptr<IOSignalSet> IOSignalSetPtr; + +} // namespace asiolink +} // namespace isc + +#endif // IO_SERVICE_SIGNAL_H diff --git a/src/lib/asiolink/io_socket.cc b/src/lib/asiolink/io_socket.cc new file mode 100644 index 0000000..f7792a9 --- /dev/null +++ b/src/lib/asiolink/io_socket.cc @@ -0,0 +1,57 @@ +// Copyright (C) 2010-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> + +namespace isc { +namespace asiolink { + +/// \brief The \c DummySocket class is a concrete derived class of +/// \c IOSocket that is not associated with any real socket. +/// +/// This main purpose of this class is tests, where it may be desirable to +/// instantiate an \c IOSocket object without involving system resource +/// allocation such as real network sockets. +class DummySocket : public IOSocket { +private: + DummySocket(const DummySocket& source); + DummySocket& operator=(const DummySocket& source); +public: + /// \brief Constructor from the protocol number. + /// + /// The protocol must validly identify a standard network protocol. + /// For example, to specify TCP \c protocol must be \c IPPROTO_TCP. + /// + /// \param protocol The network protocol number for the socket. + DummySocket(const int protocol) : protocol_(protocol) {} + + /// \brief A dummy derived method of \c IOSocket::getNative(). + /// + /// This version of method always returns -1 as the object is not + /// associated with a real (native) socket. + virtual int getNative() const { return (-1); } + + virtual int getProtocol() const { return (protocol_); } +private: + const int protocol_; +}; + +IOSocket& +IOSocket::getDummyUDPSocket() { + static DummySocket socket(IPPROTO_UDP); + return (socket); +} + +IOSocket& +IOSocket::getDummyTCPSocket() { + static DummySocket socket(IPPROTO_TCP); + return (socket); +} + +} // namespace asiolink +} // namespace isc diff --git a/src/lib/asiolink/io_socket.h b/src/lib/asiolink/io_socket.h new file mode 100644 index 0000000..9c9cee1 --- /dev/null +++ b/src/lib/asiolink/io_socket.h @@ -0,0 +1,128 @@ +// Copyright (C) 2010-2017 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/. + +#ifndef IO_SOCKET_H +#define IO_SOCKET_H 1 + +// IMPORTANT NOTE: only very few ASIO headers files can be included in +// this file. In particular, asio.hpp should never be included here. +// See the description of the namespace below. +#include <unistd.h> // for some network system calls + +#include <functional> +#include <string> + +#include <exceptions/exceptions.h> + +namespace isc { +namespace asiolink { + +/// \brief The \c IOSocket class is an abstract base class to represent +/// various types of network sockets. +/// +/// This class is a wrapper for the ASIO socket classes such as +/// \c ip::tcp::socket and \c ip::udp::socket. +/// +/// Derived class implementations are completely hidden within the +/// implementation. User applications only get access to concrete +/// \c IOSocket objects via the abstract interfaces. +/// +/// We may revisit this decision when we generalize the wrapper and more +/// modules use it. Also, at that point we may define a separate (visible) +/// derived class for testing purposes rather than providing factory methods +/// (i.e., getDummy variants below). +class IOSocket { +public: + + /// @name Types of objects encapsulating socket options. + //@{ + + /// @brief Represents SO_REUSEADDR socket option. + typedef boost::asio::socket_base::reuse_address ReuseAddress; + + //@} + + /// + /// \name Constructors and Destructor + /// + /// Note: The copy constructor and the assignment operator are + /// intentionally defined as private, making this class non-copyable. + //@{ +private: + IOSocket(const IOSocket& source); + IOSocket& operator=(const IOSocket& source); +protected: + /// \brief The default constructor. + /// + /// This is intentionally defined as \c protected as this base class + /// should never be instantiated (except as part of a derived class). + IOSocket() {} +public: + /// The destructor. + virtual ~IOSocket() {} + //@} + + /// \brief Return the "native" representation of the socket. + /// + /// In practice, this is the file descriptor of the socket for + /// UNIX-like systems so the current implementation simply uses + /// \c int as the type of the return value. + /// We may have to need revisit this decision later. + /// + /// In general, the application should avoid using this method; + /// it essentially discloses an implementation specific "handle" that + /// can change the internal state of the socket (consider the + /// application closes it, for example). + /// But we sometimes need to perform very low-level operations that + /// requires the native representation. Passing the file descriptor + /// to a different process is one example. + /// This method is provided as a necessary evil for such limited purposes. + /// + /// This method never throws an exception. + /// + /// \return The native representation of the socket. This is the socket + /// file descriptor for UNIX-like systems. + virtual int getNative() const = 0; + + /// \brief Return the transport protocol of the socket. + /// + /// Currently, it returns \c IPPROTO_UDP for UDP sockets, and + /// \c IPPROTO_TCP for TCP sockets. + /// + /// This method never throws an exception. + /// + /// \return IPPROTO_UDP for UDP sockets + /// \return IPPROTO_TCP for TCP sockets + virtual int getProtocol() const = 0; + + /// \brief Return a non-usable "dummy" UDP socket for testing. + /// + /// This is a class method that returns a "mock" of UDP socket. + /// This is not associated with any actual socket, and its only + /// responsibility is to return \c IPPROTO_UDP from \c getProtocol(). + /// The only feasible usage of this socket is for testing so that + /// the test code can prepare some "UDP data" even without opening any + /// actual socket. + /// + /// This method never throws an exception. + /// + /// \return A reference to an \c IOSocket object whose \c getProtocol() + /// returns \c IPPROTO_UDP. + static IOSocket& getDummyUDPSocket(); + + /// \brief Return a non-usable "dummy" TCP socket for testing. + /// + /// See \c getDummyUDPSocket(). This method is its TCP version. + /// + /// \return A reference to an \c IOSocket object whose \c getProtocol() + /// returns \c IPPROTO_TCP. + static IOSocket& getDummyTCPSocket(); +}; + +} // namespace asiolink +} // namespace isc + +#endif // IO_SOCKET_H diff --git a/src/lib/asiolink/openssl_tls.cc b/src/lib/asiolink/openssl_tls.cc new file mode 100644 index 0000000..270d96a --- /dev/null +++ b/src/lib/asiolink/openssl_tls.cc @@ -0,0 +1,143 @@ +// 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/. + +/// @file openssl_tls.cc OpenSSL implementation of the TLS API. + +#include <config.h> + +#ifdef WITH_OPENSSL + +#include <asiolink/asio_wrapper.h> +#include <asiolink/crypto_tls.h> + +#include <sys/stat.h> + +#include <openssl/opensslv.h> + +using namespace boost::asio; +using namespace boost::asio::ssl; +using namespace boost::system; +using namespace isc::cryptolink; + +namespace isc { +namespace asiolink { + +// Enforce TLS 1.2 when the generic TLS method is not available (i.e. +// the boost version is older than 1.64.0). +TlsContext::TlsContext(TlsRole role) + : TlsContextBase(role), cert_required_(true), +#ifdef HAVE_GENERIC_TLS_METHOD + context_(context::method::tls) +#else +#ifdef HAVE_TLS_1_2_METHOD + context_(context::method::tlsv12) +#else + context_(context::method::tlsv1) +#endif +#endif +{ + // Not leave the verify mode to OpenSSL default. + setCertRequired(true); +} + +boost::asio::ssl::context& +TlsContext::getContext() { + return (context_); +} + +::SSL_CTX* +TlsContext::getNativeContext() { + return (context_.native_handle()); +} + +void +TlsContext::setCertRequired(bool cert_required) { + if (!cert_required && (getRole() == TlsRole::CLIENT)) { + isc_throw(BadValue, + "'cert-required' parameter must be true for a TLS client"); + } + cert_required_ = cert_required; + error_code ec; + int mode = verify_peer | verify_fail_if_no_peer_cert; + if (!cert_required_) { + mode = verify_none; + } + context_.set_verify_mode(mode, ec); + if (ec) { + isc_throw(LibraryError, getErrMsg(ec)); + } +} + +bool +TlsContext::getCertRequired() const { + return (cert_required_); +} + +void +TlsContext::loadCaFile(const std::string& ca_file) { + error_code ec; + context_.load_verify_file(ca_file, ec); + if (ec) { + isc_throw(LibraryError, getErrMsg(ec)); + } +} + +void +TlsContext::loadCaPath(const std::string& ca_path) { + error_code ec; + context_.add_verify_path(ca_path, ec); + if (ec) { + isc_throw(LibraryError, getErrMsg(ec)); + } +} + +void +TlsContext::loadCertFile(const std::string& cert_file) { + error_code ec; + context_.use_certificate_chain_file(cert_file, ec); + if (ec) { + isc_throw(LibraryError, getErrMsg(ec)); + } +} + +void +TlsContext::loadKeyFile(const std::string& key_file) { + error_code ec; + context_.use_private_key_file(key_file, context::file_format::pem, ec); + if (ec) { + isc_throw(LibraryError, getErrMsg(ec)); + } +} + +std::string +TlsContext::getErrMsg(error_code ec) { + std::string msg = ec.message(); +#ifdef ERR_SYSTEM_ERROR + // The SSL category message() method uses ERR_reason_error_string() + // which since OpenSSL 3.0 returns NULL on system errors in order + // to avoid a memory leak with the strerror_r() buffer. + // This code recovers the user-friendly message from the error code + // value i.e. the OpenSSL error. Layout of OpenSSL errors is detailed + // in the OpenSSL err.h header. + unsigned long err = static_cast<unsigned long>(ec.value()); + if ((msg == "asio.ssl error") && (ERR_SYSTEM_ERROR(err))) { + char buf[1024]; +#ifndef __USE_GNU + if (strerror_r(err & ERR_SYSTEM_MASK, &buf[0], sizeof(buf)) == 0) { + msg = buf; + } +#else + msg = strerror_r(err & ERR_SYSTEM_MASK, &buf[0], sizeof(buf)); +#endif + } +#endif + return (msg); +} + +} // namespace asiolink +} // namespace isc + +#endif // WITH_OPENSSL diff --git a/src/lib/asiolink/openssl_tls.h b/src/lib/asiolink/openssl_tls.h new file mode 100644 index 0000000..e5a5a21 --- /dev/null +++ b/src/lib/asiolink/openssl_tls.h @@ -0,0 +1,242 @@ +// 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/. + +// Do not include this header directly: use crypto_tls.h instead. + +#ifndef OPENSSL_TLS_H +#define OPENSSL_TLS_H + +/// @file openssl_tls.h OpenSSL implementation of the TLS API. + +#ifdef WITH_OPENSSL + +#include <asiolink/asio_wrapper.h> +#include <asiolink/io_asio_socket.h> +#include <asiolink/io_service.h> +#include <asiolink/common_tls.h> + +#include <boost/asio/ssl.hpp> + +namespace isc { +namespace asiolink { + +/// @brief Translate TLS role into implementation. +inline boost::asio::ssl::stream_base::handshake_type roleToImpl(TlsRole role) { + if (role == TlsRole::SERVER) { + return (boost::asio::ssl::stream_base::server); + } else { + return (boost::asio::ssl::stream_base::client); + } +} + +/// @brief OpenSSL TLS context. +class TlsContext : public TlsContextBase { +public: + + /// @brief Destructor. + virtual ~TlsContext() { } + + /// @brief Create a fresh context. + /// + /// @param role The TLS role client or server. + explicit TlsContext(TlsRole role); + + /// @brief Return a reference to the underlying context. + boost::asio::ssl::context& getContext(); + + /// @brief Return the pointer to the SSL_CTX object. + /// + /// Currently used only for tests. Please note that since OpenSSL 1.1 + /// The SSL_CTX type is not fully publicly defined. + ::SSL_CTX* getNativeContext(); + + /// @brief Get the peer certificate requirement mode. + /// + /// @return True if peer certificates are required, false if they + /// are optional. + virtual bool getCertRequired() const; + + /// @brief Get the error message. + /// + /// @note Wrapper against OpenSSL 3.x not returning error messages + /// from system errors. + /// + /// @param ec The Boost error code. + /// @return The error message. + static std::string getErrMsg(boost::system::error_code ec); + +protected: + /// @brief Set the peer certificate requirement mode. + /// + /// @param cert_required True if peer certificates are required, + /// false if they are optional. + virtual void setCertRequired(bool cert_required); + + /// @brief Load the trust anchor aka certification authority. + /// + /// @param ca_file The certificate file name. + virtual void loadCaFile(const std::string& ca_file); + + /// @brief Load the trust anchor aka certification authority. + /// + /// @param ca_path The certificate directory name. + virtual void loadCaPath(const std::string& ca_path); + + /// @brief Load the certificate file. + /// + /// @param cert_file The certificate file name. + virtual void loadCertFile(const std::string& cert_file); + + /// @brief Load the private key from a file. + /// + /// @param key_file The private key file name. + virtual void loadKeyFile(const std::string& key_file); + + /// @brief Cached cert_required value. + bool cert_required_; + + /// @brief Boost ASIO SSL object. + boost::asio::ssl::context context_; + + /// @brief Allow access to protected methods by the base class. + friend class TlsContextBase; +}; + +/// @brief The type of underlying TLS streams. +typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> TlsStreamImpl; + +/// @brief TlsStreamBase constructor. +/// +/// @tparam Callback The type of callbacks. +/// @tparam TlsStreamImpl The type of underlying TLS streams. +/// @param service I/O Service object used to manage the stream. +/// @param context Pointer to the TLS context. +/// @note The caller must not provide a null pointer to the TLS context. +template <typename Callback, typename TlsStreamImpl> +TlsStreamBase<Callback, TlsStreamImpl>:: +TlsStreamBase(IOService& service, TlsContextPtr context) + : TlsStreamImpl(service.get_io_service(), context->getContext()), + role_(context->getRole()) { +} + +/// @brief OpenSSL TLS stream. +/// +/// @tparam callback The callback. +template <typename Callback> +class TlsStream : public TlsStreamBase<Callback, TlsStreamImpl> { +public: + + /// @brief Type of the base. + typedef TlsStreamBase<Callback, TlsStreamImpl> Base; + + /// @brief Constructor. + /// + /// @param service I/O Service object used to manage the stream. + /// @param context Pointer to the TLS context. + /// @note The caller must not provide a null pointer to the TLS context. + TlsStream(IOService& service, TlsContextPtr context) + : Base(service, context) { + } + + /// @brief Destructor. + virtual ~TlsStream() { } + + /// @brief TLS Handshake. + /// + /// @param callback Callback object. + virtual void handshake(Callback& callback) { + Base::async_handshake(roleToImpl(Base::getRole()), callback); + } + + /// @brief TLS shutdown. + /// + /// @param callback Callback object. + virtual void shutdown(Callback& callback) { + Base::async_shutdown(callback); + } + + /// @brief Return the commonName part of the subjectName of + /// the peer certificate. + /// + /// First commonName when there are more than one, in UTF-8. + /// RFC 3280 provides as a commonName example "Susan Housley", + /// to idea to give access to this come from the Role Based + /// Access Control experiment. + /// + /// + /// @return The commonName part of the subjectName or the empty string. + virtual std::string getSubject() { + ::X509* cert = ::SSL_get_peer_certificate(this->native_handle()); + if (!cert) { + return (""); + } + ::X509_NAME *name = ::X509_get_subject_name(cert); + int loc = ::X509_NAME_get_index_by_NID(name, NID_commonName, -1); + ::X509_NAME_ENTRY* ne = ::X509_NAME_get_entry(name, loc); + if (!ne) { + ::X509_free(cert); + return (""); + } + unsigned char* buf = 0; + int len = ::ASN1_STRING_to_UTF8(&buf, ::X509_NAME_ENTRY_get_data(ne)); + if (len < 0) { + ::X509_free(cert); + return (""); + } + std::string ret(reinterpret_cast<char*>(buf), static_cast<size_t>(len)); + ::OPENSSL_free(buf); + ::X509_free(cert); + return (ret); + } + + /// @brief Return the commonName part of the issuerName of + /// the peer certificate. + /// + /// First commonName when there are more than one, in UTF-8. + /// The issuerName is the subjectName of the signing certificate + /// (the issue in PKIX terms). The idea is to encode a group as + /// members of an intermediate certification authority. + /// + /// + /// @return The commonName part of the issuerName or the empty string. + virtual std::string getIssuer() { + ::X509* cert = ::SSL_get_peer_certificate(this->native_handle()); + if (!cert) { + return (""); + } + ::X509_NAME *name = ::X509_get_issuer_name(cert); + int loc = ::X509_NAME_get_index_by_NID(name, NID_commonName, -1); + ::X509_NAME_ENTRY* ne = ::X509_NAME_get_entry(name, loc); + if (!ne) { + ::X509_free(cert); + return (""); + } + unsigned char* buf = 0; + int len = ::ASN1_STRING_to_UTF8(&buf, ::X509_NAME_ENTRY_get_data(ne)); + if (len < 0) { + ::X509_free(cert); + return (""); + } + std::string ret(reinterpret_cast<char*>(buf), static_cast<size_t>(len)); + ::OPENSSL_free(buf); + ::X509_free(cert); + return (ret); + } +}; + +// Stream truncated error code. +#ifdef HAVE_STREAM_TRUNCATED_ERROR +const int STREAM_TRUNCATED = boost::asio::ssl::error::stream_truncated; +#else +const int STREAM_TRUNCATED = ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ); +#endif + +} // namespace asiolink +} // namespace isc + +#endif // WITH_OPENSSL + +#endif // OPENSSL_TLS_H diff --git a/src/lib/asiolink/process_spawn.cc b/src/lib/asiolink/process_spawn.cc new file mode 100644 index 0000000..9eb1b06 --- /dev/null +++ b/src/lib/asiolink/process_spawn.cc @@ -0,0 +1,448 @@ +// 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/. + +#include <config.h> + +#include <asiolink/io_service_signal.h> +#include <asiolink/process_spawn.h> +#include <exceptions/exceptions.h> +#include <cstring> +#include <functional> +#include <map> +#include <mutex> +#include <signal.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <boost/make_shared.hpp> + +using namespace std; +namespace ph = std::placeholders; + +namespace isc { +namespace asiolink { + +/// @brief Type for process state +struct ProcessState { + + /// @brief Constructor + ProcessState() : running_(true), status_(0) { + } + + /// @brief true until the exit status is collected + bool running_; + + /// @brief 0 or the exit status + int status_; +}; + +/// @brief Defines a pointer to a ProcessState. +typedef boost::shared_ptr<ProcessState> ProcessStatePtr; + +/// @brief ProcessStates container which stores a ProcessState for each process +/// identified by PID. +typedef std::map<pid_t, ProcessStatePtr> ProcessStates; + +class ProcessSpawnImpl; + +/// @brief ProcessCollection container which stores all ProcessStates for each +/// instance of @ref ProcessSpawnImpl. +typedef std::map<const ProcessSpawnImpl*, ProcessStates> ProcessCollection; + +/// @brief Implementation of the @c ProcessSpawn class. +/// +/// This pimpl idiom is used by the @c ProcessSpawn in this case to +/// avoid exposing the internals of the implementation, such as +/// custom handling of a SIGCHLD signal, and the conversion of the +/// arguments of the executable from the STL container to the array. +/// +/// This class is made noncopyable so that we don't have attempts +/// to make multiple copies of an object. This avoid problems +/// with multiple copies of objects for a single global resource +/// such as the SIGCHLD signal handler. In addition making it +/// noncopyable keeps the static check code from flagging the +/// lack of a copy constructor as an issue. +class ProcessSpawnImpl : boost::noncopyable { +public: + + /// @brief Constructor. + /// + /// @param io_service The IOService which handles signal handlers. + /// @param executable A full path to the program to be executed. + /// @param args Arguments for the program to be executed. + /// @param vars Environment variables for the program to be executed. + ProcessSpawnImpl(IOServicePtr io_service, + const std::string& executable, + const ProcessArgs& args, + const ProcessEnvVars& vars); + + /// @brief Destructor. + ~ProcessSpawnImpl(); + + /// @brief Returns full command line, including arguments, for the process. + std::string getCommandLine() const; + + /// @brief Spawn the new process. + /// + /// This method forks the current process and executes the specified + /// binary with arguments within the child process. + /// + /// The child process will return EXIT_FAILURE if the method was unable + /// to start the executable, e.g. as a result of insufficient permissions + /// or when the executable does not exist. If the process ends successfully + /// the EXIT_SUCCESS is returned. + /// + /// @param dismiss The flag which indicated if the process status can be + /// disregarded. + /// @return PID of the spawned process. + /// @throw ProcessSpawnError if forking a current process failed. + pid_t spawn(bool dismiss); + + /// @brief Checks if the process is still running. + /// + /// @param pid ID of the child processes for which state should be checked. + /// @return true if the child process is running, false otherwise. + bool isRunning(const pid_t pid) const; + + /// @brief Checks if any of the spawned processes is still running. + /// + /// @return true if at least one child process is still running. + bool isAnyRunning() const; + + /// @brief Returns exit status of the process. + /// + /// If the process is still running, the previous status is returned + /// or 0, if the process is being ran for the first time. + /// + /// @param pid ID of the child process for which exit status should be + /// returned. + /// @return Exit code of the process. + int getExitStatus(const pid_t pid) const; + + /// @brief Removes the status of the process with a specified PID. + /// + /// This method removes the status of the process with a specified PID. + /// If the process is still running, the status is not removed and the + /// exception is thrown. + /// + /// @param pid A process pid. + void clearState(const pid_t pid); + +private: + + /// @brief Initializer class for the SIGCHLD signal handler. + /// + /// This is a singleton class used to initialize the SIGCHLD signal handler + /// only on the first call of @ref initIOSignalSet which happens on each + /// call of @ref ProcessSpawn::spawn. + class IOSignalSetInitializer { + private: + + /// @brief Constructor + /// + /// @param io_service The IOService which handles signal handlers. + IOSignalSetInitializer(IOServicePtr io_service) { + if (!io_service) { + isc_throw(ProcessSpawnError, "NULL IOService instance"); + } + io_signal_set_ = boost::make_shared<IOSignalSet>(io_service, + std::bind(&ProcessSpawnImpl::waitForProcess, ph::_1)); + io_signal_set_->add(SIGCHLD); + } + + /// @brief Destructor + ~IOSignalSetInitializer() { + io_signal_set_->remove(SIGCHLD); + } + + public: + + /// @brief Initialize the SIGCHLD signal handler. + /// + /// It creates the single instance of @ref IOSignalSetInitializer. + /// + /// @param io_service The IOService which handles signal handlers. + static void initIOSignalSet(IOServicePtr io_service); + + private: + + /// @brief ASIO signal set. + IOSignalSetPtr io_signal_set_; + }; + + /// @brief Copies the argument specified as a C++ string to the new + /// C string. + /// + /// This method is used to convert arguments specified as an STL container + /// holding @c std::string objects to an array of C strings, used by the + /// @c execve function in the @c ProcessSpawnImpl::spawn. It allocates a + /// new C string and copies the contents of the @c src to it. + /// The data is stored in an internal container so that the caller of the + /// function can be exception safe. + /// + /// @param src A source string. + /// + /// @return Allocated C string holding the data from @c src. + char* allocateInternal(const std::string& src); + + /// @brief Signal handler for SIGCHLD. + /// + /// This handler waits for the child process to finish and retrieves + /// its exit code into the @c status_ member. + /// + /// @return true if the processed signal was SIGCHLD or false if it + /// was a different signal. + static bool waitForProcess(int signum); + + /// @brief A map holding the status codes of executed processes. + static ProcessCollection process_collection_; + + /// @brief Path to an executable. + std::string executable_; + + /// @brief An array holding arguments for the executable. + boost::shared_ptr<char*[]> args_; + + /// @brief An array holding environment variables for the executable. + boost::shared_ptr<char*[]> vars_; + + /// @brief Typedef for CString pointer. + typedef boost::shared_ptr<char[]> CStringPtr; + + /// @brief An storage container for all allocated C strings. + std::vector<CStringPtr> storage_; + + /// @brief Flag to indicate if process status must be stored. + bool store_; + + /// @brief Mutex to protect internal state. + static std::mutex mutex_; + + /// @brief The IOService which handles IO operations. + IOServicePtr io_service_; +}; + +ProcessCollection ProcessSpawnImpl::process_collection_; +std::mutex ProcessSpawnImpl::mutex_; + +void ProcessSpawnImpl::IOSignalSetInitializer::initIOSignalSet(IOServicePtr io_service) { + static IOSignalSetInitializer init(io_service); +} + +ProcessSpawnImpl::ProcessSpawnImpl(IOServicePtr io_service, + const std::string& executable, + const ProcessArgs& args, + const ProcessEnvVars& vars) + : executable_(executable), args_(new char*[args.size() + 2]), + vars_(new char*[vars.size() + 1]), store_(false), io_service_(io_service) { + + struct stat st; + + if (stat(executable_.c_str(), &st)) { + isc_throw(ProcessSpawnError, "File not found: " << executable_); + } + + if (!(st.st_mode & S_IEXEC)) { + isc_throw(ProcessSpawnError, "File not executable: " << executable_); + } + + // Conversion of the arguments to the C-style array we start by setting + // all pointers within an array to NULL to indicate that they haven't + // been allocated yet. + memset(args_.get(), 0, (args.size() + 2) * sizeof(char*)); + memset(vars_.get(), 0, (vars.size() + 1) * sizeof(char*)); + // By convention, the first argument points to an executable name. + args_[0] = allocateInternal(executable_); + // Copy arguments to the array. + for (int i = 1; i <= args.size(); ++i) { + args_[i] = allocateInternal(args[i - 1]); + } + // Copy environment variables to the array. + for (int i = 0; i < vars.size(); ++i) { + vars_[i] = allocateInternal(vars[i]); + } +} + +ProcessSpawnImpl::~ProcessSpawnImpl() { + if (store_) { + lock_guard<std::mutex> lk(mutex_); + process_collection_.erase(this); + } +} + +std::string +ProcessSpawnImpl::getCommandLine() const { + std::ostringstream s; + s << executable_; + // Start with index 1, because the first argument duplicates the + // path to the executable. Note, that even if there are no parameters + // the minimum size of the table is 2. + int i = 1; + while (args_[i] != NULL) { + s << " " << args_[i]; + ++i; + } + return (s.str()); +} + +pid_t +ProcessSpawnImpl::spawn(bool dismiss) { + lock_guard<std::mutex> lk(mutex_); + ProcessSpawnImpl::IOSignalSetInitializer::initIOSignalSet(io_service_); + // Create the child + pid_t pid = fork(); + if (pid < 0) { + isc_throw(ProcessSpawnError, "unable to fork current process"); + + } else if (pid == 0) { + // Reset masked signals for the child process. + sigset_t sset; + sigemptyset(&sset); + pthread_sigmask(SIG_SETMASK, &sset, 0); + // Run the executable. + execve(executable_.c_str(), args_.get(), vars_.get()); + // We may end up here if the execve failed, e.g. as a result + // of issue with permissions or invalid executable name. + _exit(EXIT_FAILURE); + } + + // We're in the parent process. + if (!dismiss) { + store_ = true; + process_collection_[this].insert(std::pair<pid_t, ProcessStatePtr>(pid, ProcessStatePtr(new ProcessState()))); + } + return (pid); +} + +bool +ProcessSpawnImpl::isRunning(const pid_t pid) const { + lock_guard<std::mutex> lk(mutex_); + ProcessStates::const_iterator proc; + if (process_collection_.find(this) == process_collection_.end() || + (proc = process_collection_[this].find(pid)) == process_collection_[this].end()) { + isc_throw(BadValue, "the process with the pid '" << pid + << "' hasn't been spawned and it status cannot be" + " returned"); + } + return (proc->second->running_); +} + +bool +ProcessSpawnImpl::isAnyRunning() const { + lock_guard<std::mutex> lk(mutex_); + if (process_collection_.find(this) != process_collection_.end()) { + for (auto const& proc : process_collection_[this]) { + if (proc.second->running_) { + return (true); + } + } + } + return (false); +} + +int +ProcessSpawnImpl::getExitStatus(const pid_t pid) const { + lock_guard<std::mutex> lk(mutex_); + ProcessStates::const_iterator proc; + if (process_collection_.find(this) == process_collection_.end() || + (proc = process_collection_[this].find(pid)) == process_collection_[this].end()) { + isc_throw(InvalidOperation, "the process with the pid '" << pid + << "' hasn't been spawned and it status cannot be" + " returned"); + } + return (WEXITSTATUS(proc->second->status_)); +} + +char* +ProcessSpawnImpl::allocateInternal(const std::string& src) { + const size_t src_len = src.length(); + storage_.push_back(CStringPtr(new char[src_len + 1])); + // Allocate the C-string with one byte more for the null termination. + char* dest = storage_[storage_.size() - 1].get(); + // copy doesn't append the null at the end. + src.copy(dest, src_len); + // Append null on our own. + dest[src_len] = '\0'; + return (dest); +} + +bool +ProcessSpawnImpl::waitForProcess(int) { + lock_guard<std::mutex> lk(mutex_); + for (;;) { + int status = 0; + pid_t pid = waitpid(-1, &status, WNOHANG); + if (pid <= 0) { + break; + } + for (auto const& instance : process_collection_) { + auto const& proc = instance.second.find(pid); + /// Check that the terminating process was started + /// by our instance of ProcessSpawn + if (proc != instance.second.end()) { + // In this order please + proc->second->status_ = status; + proc->second->running_ = false; + } + } + } + return (true); +} + +void +ProcessSpawnImpl::clearState(const pid_t pid) { + if (isRunning(pid)) { + isc_throw(InvalidOperation, "unable to remove the status for the" + "process (pid: " << pid << ") which is still running"); + } + lock_guard<std::mutex> lk(mutex_); + if (process_collection_.find(this) != process_collection_.end()) { + process_collection_[this].erase(pid); + } +} + +ProcessSpawn::ProcessSpawn(IOServicePtr io_service, + const std::string& executable, + const ProcessArgs& args, + const ProcessEnvVars& vars) + : impl_(new ProcessSpawnImpl(io_service, executable, args, vars)) { +} + +std::string +ProcessSpawn::getCommandLine() const { + return (impl_->getCommandLine()); +} + +pid_t +ProcessSpawn::spawn(bool dismiss) { + return (impl_->spawn(dismiss)); +} + +bool +ProcessSpawn::isRunning(const pid_t pid) const { + return (impl_->isRunning(pid)); +} + +bool +ProcessSpawn::isAnyRunning() const { + return (impl_->isAnyRunning()); +} + +int +ProcessSpawn::getExitStatus(const pid_t pid) const { + return (impl_->getExitStatus(pid)); +} + +void +ProcessSpawn::clearState(const pid_t pid) { + return (impl_->clearState(pid)); +} + +} // namespace asiolink +} // namespace isc diff --git a/src/lib/asiolink/process_spawn.h b/src/lib/asiolink/process_spawn.h new file mode 100644 index 0000000..ca72ce8 --- /dev/null +++ b/src/lib/asiolink/process_spawn.h @@ -0,0 +1,149 @@ +// 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/. + +#ifndef PROCESS_SPAWN_H +#define PROCESS_SPAWN_H + +#include <asiolink/io_service.h> +#include <exceptions/exceptions.h> +#include <boost/noncopyable.hpp> +#include <string> +#include <sys/types.h> +#include <vector> +#include <boost/shared_ptr.hpp> + +namespace isc { +namespace asiolink { + +/// @brief Exception thrown when error occurs during spawning a process. +class ProcessSpawnError : public Exception { +public: + ProcessSpawnError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Forward declaration to the implementation of the @c ProcessSpawn +/// class. +class ProcessSpawnImpl; + +/// @brief Pointer to a ProcessSpawnImpl class. +typedef boost::shared_ptr<ProcessSpawnImpl> ProcessSpawnImplPtr; + +/// @brief Type of the container holding arguments of the executable +/// being run as a background process. +typedef std::vector<std::string> ProcessArgs; + +/// @brief Type of the container holding environment variables of the executable +/// being run as a background process. +typedef std::vector<std::string> ProcessEnvVars; + +/// @brief Utility class for spawning new processes. +/// +/// This class is used to spawn new process by Kea. It forks the current +/// process and then uses the @c execve function to execute the specified +/// binary with parameters. The @c ProcessSpawn installs the handler for +/// the SIGCHLD signal, which is executed when the child process ends. +/// The handler checks the exit code returned by the process and records +/// it. The exit code can be retrieved by the caller using the +/// @c ProcessSpawn::getExitStatus method. +/// +/// This class is made noncopyable so that we don't have attempts +/// to make multiple copies of an object. This avoid problems +/// with multiple copies of objects for a single global resource +/// such as the SIGCHLD signal handler. In addition making it +/// noncopyable keeps the static check code from flagging the +/// lack of a copy constructor as an issue. +/// +/// @note The ProcessSpawn uses full path for the program to execute. +class ProcessSpawn : boost::noncopyable { +public: + + /// @brief Constructor. + /// + /// @param io_service The IOService which handles signal handlers. + /// @param executable A full path to the program to be executed. + /// @param args Arguments for the program to be executed. + /// @param vars Environment variables for the program to be executed. + ProcessSpawn(isc::asiolink::IOServicePtr io_service, + const std::string& executable, + const ProcessArgs& args = ProcessArgs(), + const ProcessEnvVars& vars = ProcessEnvVars()); + + /// @brief Destructor. + ~ProcessSpawn() = default; + + /// @brief Returns full command line, including arguments, for the process. + std::string getCommandLine() const; + + /// @brief Spawn the new process. + /// + /// This method forks the current process and executes the specified + /// binary with arguments within the child process. + /// + /// The child process will return EXIT_FAILURE if the method was unable + /// to start the executable, e.g. as a result of insufficient permissions + /// or when the executable does not exist. If the process ends successfully + /// the EXIT_SUCCESS is returned. + /// + /// @param dismiss The flag which indicated if the process status can be + /// disregarded. + /// @throw ProcessSpawnError if forking a current process failed. + pid_t spawn(bool dismiss = false); + + /// @brief Checks if the process is still running. + /// + /// Note that only a negative (false) result is reliable as the child + /// process can exit between the time its state is checked and this + /// function returns. + /// + /// @param pid ID of the child processes for which state should be checked. + /// + /// @return true if the child process is running, false otherwise. + bool isRunning(const pid_t pid) const; + + /// @brief Checks if any of the spawned processes is still running. + /// + /// @return true if at least one child process is still running. + bool isAnyRunning() const; + + /// @brief Returns exit status of the process. + /// + /// If the process is still running, the previous status is returned + /// or 0, if the process is being ran for the first time. + /// + /// @note @c ProcessSpawn::isRunning should be called and have returned + /// false before using @c ProcessSpawn::getExitStatus. + /// + /// @param pid ID of the child process for which exit status should be + /// returned. + /// + /// @return Exit code of the process. + int getExitStatus(const pid_t pid) const; + + /// @brief Removes the status of the process with a specified PID. + /// + /// This method removes the status of the process with a specified PID. + /// If the process is still running, the status is not removed and the + /// exception is thrown. + /// + /// Note @c ProcessSpawn::isRunning must be called and have returned + /// false before using clearState(). And of course + /// @c ProcessSpawn::getExitStatus should be called first, if there is + /// some interest in the status. + /// + /// @param pid A process pid. + void clearState(const pid_t pid); + +private: + + /// @brief A smart pointer to the implementation of this class. + ProcessSpawnImplPtr impl_; +}; + +} // namespace asiolink +} // namespace isc + +#endif // PROCESS_SPAWN_H diff --git a/src/lib/asiolink/tcp_acceptor.h b/src/lib/asiolink/tcp_acceptor.h new file mode 100644 index 0000000..4fab1dd --- /dev/null +++ b/src/lib/asiolink/tcp_acceptor.h @@ -0,0 +1,69 @@ +// Copyright (C) 2016-2017 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/. + +#ifndef TCP_ACCEPTOR_H +#define TCP_ACCEPTOR_H + +#ifndef BOOST_ASIO_HPP +#error "asio.hpp must be included before including this, see asiolink.h as to why" +#endif + +#include <asiolink/io_acceptor.h> +#include <asiolink/io_service.h> +#include <asiolink/io_socket.h> +#include <asiolink/tcp_endpoint.h> +#include <asiolink/tcp_socket.h> +#include <boost/shared_ptr.hpp> +#include <netinet/in.h> + +namespace isc { +namespace asiolink { + +/// @brief Provides a service for accepting new TCP connections. +/// +/// Internally it uses @c boost::asio::ip::tcp::acceptor class to implement +/// the acceptor service. +/// +/// @tparam C Acceptor callback type. +template<typename C> +class TCPAcceptor : public IOAcceptor<boost::asio::ip::tcp, C> { +public: + + /// @brief Constructor. + /// + /// @param io_service IO service. + explicit TCPAcceptor(IOService& io_service) + : IOAcceptor<boost::asio::ip::tcp, C>(io_service) { + } + + /// @brief Returns protocol of the socket. + /// + /// @return IPPROTO_TCP. + virtual int getProtocol() const final { + return (IPPROTO_TCP); + } + + /// @brief Asynchronously accept new connection. + /// + /// This method accepts new connection into the specified socket. When the + /// new connection arrives or an error occurs the specified callback function + /// is invoked. + /// + /// @param socket Socket into which connection should be accepted. + /// @param callback Callback function to be invoked when the new connection + /// arrives. + /// @tparam SocketCallback Type of the callback for the @ref TCPSocket. + template<typename SocketCallback> + void asyncAccept(const TCPSocket<SocketCallback>& socket, C& callback) { + IOAcceptor<boost::asio::ip::tcp, C>::asyncAcceptInternal(socket, callback); + } +}; + + +} // namespace asiolink +} // namespace isc + +#endif diff --git a/src/lib/asiolink/tcp_endpoint.h b/src/lib/asiolink/tcp_endpoint.h new file mode 100644 index 0000000..7cbb73b --- /dev/null +++ b/src/lib/asiolink/tcp_endpoint.h @@ -0,0 +1,115 @@ +// 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/. + +#ifndef TCP_ENDPOINT_H +#define TCP_ENDPOINT_H 1 + +#ifndef BOOST_ASIO_HPP +#error "asio.hpp must be included before including this, see asiolink.h as to why" +#endif + +#include <asiolink/io_endpoint.h> + +namespace isc { +namespace asiolink { + +/// \brief The \c TCPEndpoint class is a concrete derived class of +/// \c IOEndpoint that represents an endpoint of a TCP packet. +/// +/// Other notes about \c TCPEndpoint applies to this class, too. +class TCPEndpoint : public IOEndpoint { +public: + /// + /// \name Constructors and Destructor. + /// + //@{ + + /// \brief Default Constructor + /// + /// Creates an internal endpoint. This is expected to be set by some + /// external call. + TCPEndpoint() : + asio_endpoint_placeholder_(new boost::asio::ip::tcp::endpoint()), + asio_endpoint_(*asio_endpoint_placeholder_) + {} + + /// \brief Constructor from a pair of address and port. + /// + /// \param address The IP address of the endpoint. + /// \param port The TCP port number of the endpoint. + TCPEndpoint(const IOAddress& address, const unsigned short port) : + asio_endpoint_placeholder_( + new boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address.toText()), + port)), + asio_endpoint_(*asio_endpoint_placeholder_) + {} + + /// \brief Constructor from an ASIO TCP endpoint. + /// + /// This constructor is designed to be an efficient wrapper for the + /// corresponding ASIO class, \c tcp::endpoint. + /// + /// \param asio_endpoint The ASIO representation of the TCP endpoint. + TCPEndpoint(boost::asio::ip::tcp::endpoint& asio_endpoint) : + asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint) + {} + + /// \brief Constructor from an ASIO TCP endpoint. + /// + /// This constructor is designed to be an efficient wrapper for the + /// corresponding ASIO class, \c tcp::endpoint. + /// + /// \param asio_endpoint The ASIO representation of the TCP endpoint. + TCPEndpoint(const boost::asio::ip::tcp::endpoint& asio_endpoint) : + asio_endpoint_placeholder_(new boost::asio::ip::tcp::endpoint(asio_endpoint)), + asio_endpoint_(*asio_endpoint_placeholder_) + {} + + /// \brief The destructor. + virtual ~TCPEndpoint() { delete asio_endpoint_placeholder_; } + //@} + + virtual IOAddress getAddress() const { + return (asio_endpoint_.address()); + } + + virtual const struct sockaddr& getSockAddr() const { + return (*asio_endpoint_.data()); + } + + virtual uint16_t getPort() const { + return (asio_endpoint_.port()); + } + + virtual short getProtocol() const { + return (asio_endpoint_.protocol().protocol()); + } + + virtual short getFamily() const { + return (asio_endpoint_.protocol().family()); + } + + // This is not part of the exposed IOEndpoint API but allows + // direct access to the ASIO implementation of the endpoint + inline const boost::asio::ip::tcp::endpoint& getASIOEndpoint() const { + return (asio_endpoint_); + } + inline boost::asio::ip::tcp::endpoint& getASIOEndpoint() { + return (asio_endpoint_); + } + +private: + boost::asio::ip::tcp::endpoint* asio_endpoint_placeholder_; + boost::asio::ip::tcp::endpoint& asio_endpoint_; +}; + +} // namespace asiolink +} // namespace isc +#endif // TCP_ENDPOINT_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/asiolink/tcp_socket.h b/src/lib/asiolink/tcp_socket.h new file mode 100644 index 0000000..c8ea454 --- /dev/null +++ b/src/lib/asiolink/tcp_socket.h @@ -0,0 +1,503 @@ +// 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/. + +#ifndef TCP_SOCKET_H +#define TCP_SOCKET_H 1 + +#ifndef BOOST_ASIO_HPP +#error "asio.hpp must be included before including this, see asiolink.h as to why" +#endif + +#include <netinet/in.h> +#include <sys/socket.h> +#include <unistd.h> // for some IPC/network system calls + +#include <algorithm> +#include <cstddef> + +#include <boost/numeric/conversion/cast.hpp> + +#include <util/buffer.h> +#include <util/io_utilities.h> + +#include <asiolink/io_asio_socket.h> +#include <asiolink/io_endpoint.h> +#include <asiolink/io_service.h> +#include <asiolink/tcp_endpoint.h> + +#include <exceptions/isc_assert.h> + +namespace isc { +namespace asiolink { + +/// \brief Buffer Too Large +/// +/// Thrown on an attempt to send a buffer > 64k +class BufferTooLarge : public IOError { +public: + BufferTooLarge(const char* file, size_t line, const char* what) : + IOError(file, line, what) {} +}; + +/// \brief The \c TCPSocket class is a concrete derived class of \c IOAsioSocket +/// that represents a TCP socket. +/// +/// \param C Callback type +template <typename C> +class TCPSocket : public IOAsioSocket<C> { +private: + /// \brief Class is non-copyable + TCPSocket(const TCPSocket&); + TCPSocket& operator=(const TCPSocket&); + +public: + + /// \brief Constructor from an ASIO TCP socket. + /// + /// \param socket The ASIO representation of the TCP socket. It is assumed + /// that the caller will open and close the socket, so these + /// operations are a no-op for that socket. + TCPSocket(boost::asio::ip::tcp::socket& socket); + + /// \brief Constructor + /// + /// Used when the TCPSocket is being asked to manage its own internal + /// socket. In this case, the open() and close() methods are used. + /// + /// \param service I/O Service object used to manage the socket. + TCPSocket(IOService& service); + + /// \brief Destructor + virtual ~TCPSocket(); + + /// \brief Return file descriptor of underlying socket + virtual int getNative() const { +#if BOOST_VERSION < 106600 + return (socket_.native()); +#else + return (socket_.native_handle()); +#endif + } + + /// \brief Return protocol of socket + virtual int getProtocol() const { + return (IPPROTO_TCP); + } + + /// \brief Is "open()" synchronous? + /// + /// Indicates that the opening of a TCP socket is asynchronous. + virtual bool isOpenSynchronous() const { + return (false); + } + + /// \brief Checks if the connection is usable. + /// + /// The connection is usable if the socket is open and the peer has not + /// closed its connection. + /// + /// \return true if the connection is usable. + bool isUsable() const { + // If the socket is open it doesn't mean that it is still usable. The connection + // could have been closed on the other end. We have to check if we can still + // use this socket. + if (socket_.is_open()) { + // Remember the current non blocking setting. + const bool non_blocking_orig = socket_.non_blocking(); + // Set the socket to non blocking mode. We're going to test if the socket + // returns would_block status on the attempt to read from it. + socket_.non_blocking(true); + + boost::system::error_code ec; + char data[2]; + + // Use receive with message peek flag to avoid removing the data awaiting + // to be read. + socket_.receive(boost::asio::buffer(data, sizeof(data)), + boost::asio::socket_base::message_peek, + ec); + + // Revert the original non_blocking flag on the socket. + socket_.non_blocking(non_blocking_orig); + + // If the connection is alive we'd typically get would_block status code. + // If there are any data that haven't been read we may also get success + // status. We're guessing that try_again may also be returned by some + // implementations in some situations. Any other error code indicates a + // problem with the connection so we assume that the connection has been + // closed. + return (!ec || (ec.value() == boost::asio::error::try_again) || + (ec.value() == boost::asio::error::would_block)); + } + + return (false); + } + + /// \brief Open Socket + /// + /// Opens the TCP socket. This is an asynchronous operation, completion of + /// which will be signalled via a call to the callback function. + /// + /// \param endpoint Endpoint to which the socket will connect. + /// \param callback Callback object. + virtual void open(const IOEndpoint* endpoint, C& callback); + + /// \brief Send Asynchronously + /// + /// Calls the underlying socket's async_send() method to send a packet of + /// data asynchronously to the remote endpoint. The callback will be called + /// on completion. + /// + /// \param data Data to send + /// \param length Length of data to send + /// \param endpoint Target of the send. (Unused for a TCP socket because + /// that was determined when the connection was opened.) + /// \param callback Callback object. + /// \throw BufferTooLarge on attempt to send a buffer larger than 64kB. + virtual void asyncSend(const void* data, size_t length, + const IOEndpoint* endpoint, C& callback); + + /// \brief Send Asynchronously without count. + /// + /// This variant of the method sends data over the TCP socket without + /// preceding the data with a data count. Eventually, we should migrate + /// the virtual method to not insert the count but there are existing + /// classes using the count. Once this migration is done, the existing + /// virtual method should be replaced by this method. + /// + /// \param data Data to send + /// \param length Length of data to send + /// \param callback Callback object. + /// \throw BufferTooLarge on attempt to send a buffer larger than 64kB. + void asyncSend(const void* data, size_t length, C& callback); + + /// \brief Receive Asynchronously + /// + /// Calls the underlying socket's async_receive() method to read a packet + /// of data from a remote endpoint. Arrival of the data is signalled via a + /// call to the callback function. + /// + /// \param data Buffer to receive incoming message + /// \param length Length of the data buffer + /// \param offset Offset into buffer where data is to be put + /// \param endpoint Source of the communication + /// \param callback Callback object + virtual void asyncReceive(void* data, size_t length, size_t offset, + IOEndpoint* endpoint, C& callback); + + /// \brief Process received data packet + /// + /// See the description of IOAsioSocket::receiveComplete for a complete + /// description of this method. + /// + /// \param staging Pointer to the start of the staging buffer. + /// \param length Amount of data in the staging buffer. + /// \param cumulative Amount of data received before the staging buffer is + /// processed. + /// \param offset Unused. + /// \param expected unused. + /// \param outbuff Output buffer. Data in the staging buffer is be copied + /// to this output buffer in the call. + /// + /// \return Always true + virtual bool processReceivedData(const void* staging, size_t length, + size_t& cumulative, size_t& offset, + size_t& expected, + isc::util::OutputBufferPtr& outbuff); + + /// \brief Cancel I/O On Socket + virtual void cancel(); + + /// \brief Close socket + virtual void close(); + + /// \brief Returns reference to the underlying ASIO socket. + /// + /// \return Reference to underlying ASIO socket. + virtual boost::asio::ip::tcp::socket& getASIOSocket() const { + return (socket_); + } + +private: + /// Two variables to hold the socket - a socket and a pointer to it. This + /// handles the case where a socket is passed to the TCPSocket on + /// construction, or where it is asked to manage its own socket. + + /// Pointer to own socket + std::unique_ptr<boost::asio::ip::tcp::socket> socket_ptr_; + + /// Socket + boost::asio::ip::tcp::socket& socket_; + + /// @todo Remove temporary buffer + /// The current implementation copies the buffer passed to asyncSend() into + /// a temporary buffer and precedes it with a two-byte count field. As + /// ASIO should really be just about sending and receiving data, the TCP + /// code should not do this. If the protocol using this requires a two-byte + /// count, it should add it before calling this code. (This may be best + /// achieved by altering isc::dns::buffer to have pairs of methods: + /// getLength()/getTCPLength(), getData()/getTCPData(), with the getTCPXxx() + /// methods taking into account a two-byte count field.) + /// + /// The option of sending the data in two operations, the count followed by + /// the data was discounted as that would lead to two callbacks which would + /// cause problems with the stackless coroutine code. + + /// Send buffer + isc::util::OutputBufferPtr send_buffer_; +}; + +// Constructor - caller manages socket + +template <typename C> +TCPSocket<C>::TCPSocket(boost::asio::ip::tcp::socket& socket) : + socket_ptr_(), socket_(socket), send_buffer_() +{ +} + +// Constructor - create socket on the fly + +template <typename C> +TCPSocket<C>::TCPSocket(IOService& service) : + socket_ptr_(new boost::asio::ip::tcp::socket(service.get_io_service())), + socket_(*socket_ptr_) +{ +} + +// Destructor. + +template <typename C> +TCPSocket<C>::~TCPSocket() +{ +} + +// Open the socket. + +template <typename C> void +TCPSocket<C>::open(const IOEndpoint* endpoint, C& callback) { + // If socket is open on this end but has been closed by the peer, + // we need to reconnect. + if (socket_.is_open() && !isUsable()) { + close(); + } + // Ignore opens on already-open socket. Don't throw a failure because + // of uncertainties as to what precedes when using asynchronous I/O. + // Also allows us a treat a passed-in socket as a self-managed socket. + if (!socket_.is_open()) { + if (endpoint->getFamily() == AF_INET) { + socket_.open(boost::asio::ip::tcp::v4()); + } else { + socket_.open(boost::asio::ip::tcp::v6()); + } + + // Set options on the socket: + + // Reuse address - allow the socket to bind to a port even if the port + // is in the TIMED_WAIT state. + socket_.set_option(boost::asio::socket_base::reuse_address(true)); + } + + // Upconvert to a TCPEndpoint. We need to do this because although + // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it does not + // contain a method for getting at the underlying endpoint type - that is in + /// the derived class and the two classes differ on return type. + isc_throw_assert(endpoint->getProtocol() == IPPROTO_TCP); + const TCPEndpoint* tcp_endpoint = + static_cast<const TCPEndpoint*>(endpoint); + + // Connect to the remote endpoint. On success, the handler will be + // called (with one argument - the length argument will default to + // zero). + socket_.async_connect(tcp_endpoint->getASIOEndpoint(), callback); +} + +// Send a message. Should never do this if the socket is not open, so throw +// an exception if this is the case. + +template <typename C> void +TCPSocket<C>::asyncSend(const void* data, size_t length, C& callback) +{ + if (socket_.is_open()) { + + try { + send_buffer_.reset(new isc::util::OutputBuffer(length)); + send_buffer_->writeData(data, length); + + // Send the data. + socket_.async_send(boost::asio::buffer(send_buffer_->getData(), + send_buffer_->getLength()), + callback); + } catch (const boost::numeric::bad_numeric_cast&) { + isc_throw(BufferTooLarge, + "attempt to send buffer larger than 64kB"); + } + + } else { + isc_throw(SocketNotOpen, + "attempt to send on a TCP socket that is not open"); + } +} + +template <typename C> void +TCPSocket<C>::asyncSend(const void* data, size_t length, + const IOEndpoint*, C& callback) +{ + if (socket_.is_open()) { + + /// Need to copy the data into a temporary buffer and precede it with + /// a two-byte count field. + /// @todo arrange for the buffer passed to be preceded by the count + try { + /// Ensure it fits into 16 bits + uint16_t count = boost::numeric_cast<uint16_t>(length); + + /// Copy data into a buffer preceded by the count field. + send_buffer_.reset(new isc::util::OutputBuffer(length + 2)); + send_buffer_->writeUint16(count); + send_buffer_->writeData(data, length); + + /// ... and send it + socket_.async_send(boost::asio::buffer(send_buffer_->getData(), + send_buffer_->getLength()), callback); + } catch (const boost::numeric::bad_numeric_cast&) { + isc_throw(BufferTooLarge, + "attempt to send buffer larger than 64kB"); + } + + } else { + isc_throw(SocketNotOpen, + "attempt to send on a TCP socket that is not open"); + } +} + +// Receive a message. Note that the "offset" argument is used as an index +// into the buffer in order to decide where to put the data. It is up to the +// caller to initialize the data to zero +template <typename C> void +TCPSocket<C>::asyncReceive(void* data, size_t length, size_t offset, + IOEndpoint* endpoint, C& callback) +{ + if (socket_.is_open()) { + // Upconvert to a TCPEndpoint. We need to do this because although + // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it + // does not contain a method for getting at the underlying endpoint + // type - that is in the derived class and the two classes differ on + // return type. + isc_throw_assert(endpoint->getProtocol() == IPPROTO_TCP); + TCPEndpoint* tcp_endpoint = static_cast<TCPEndpoint*>(endpoint); + + // Write the endpoint details from the communications link. Ideally + // we should make IOEndpoint assignable, but this runs in to all sorts + // of problems concerning the management of the underlying Boost + // endpoint (e.g. if it is not self-managed, is the copied one + // self-managed?) The most pragmatic solution is to let Boost take care + // of everything and copy details of the underlying endpoint. + tcp_endpoint->getASIOEndpoint() = socket_.remote_endpoint(); + + // Ensure we can write into the buffer and if so, set the pointer to + // where the data will be written. + if (offset >= length) { + isc_throw(BufferOverflow, "attempt to read into area beyond end of " + "TCP receive buffer"); + } + void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset); + + // ... and kick off the read. + socket_.async_receive(boost::asio::buffer(buffer_start, length - offset), callback); + + } else { + isc_throw(SocketNotOpen, + "attempt to receive from a TCP socket that is not open"); + } +} + +// Is the receive complete? + +template <typename C> bool +TCPSocket<C>::processReceivedData(const void* staging, size_t length, + size_t& cumulative, size_t& offset, + size_t& expected, + isc::util::OutputBufferPtr& outbuff) +{ + // Point to the data in the staging buffer and note how much there is. + const uint8_t* data = static_cast<const uint8_t*>(staging); + size_t data_length = length; + + // Is the number is "expected" valid? It won't be unless we have received + // at least two bytes of data in total for this set of receives. + if (cumulative < 2) { + + // "expected" is not valid. Did this read give us enough data to + // work it out? + cumulative += length; + if (cumulative < 2) { + + // Nope, still not valid. This must have been the first packet and + // was only one byte long. Tell the fetch code to read the next + // packet into the staging buffer beyond the data that is already + // there so that the next time we are called we have a complete + // TCP count. + offset = cumulative; + return (false); + } + + // Have enough data to interpret the packet count, so do so now. + expected = isc::util::readUint16(data, cumulative); + + // We have two bytes less of data to process. Point to the start of the + // data and adjust the packet size. Note that at this point, + // "cumulative" is the true amount of data in the staging buffer, not + // "length". + data += 2; + data_length = cumulative - 2; + } else { + + // Update total amount of data received. + cumulative += length; + } + + // Regardless of anything else, the next read goes into the start of the + // staging buffer. + offset = 0; + + // Work out how much data we still have to put in the output buffer. (This + // could be zero if we have just interpreted the TCP count and that was + // set to zero.) + if (expected >= outbuff->getLength()) { + + // Still need data in the output packet. Copy what we can from the + // staging buffer to the output buffer. + size_t copy_amount = std::min(expected - outbuff->getLength(), data_length); + outbuff->writeData(data, copy_amount); + } + + // We can now say if we have all the data. + return (expected == outbuff->getLength()); +} + +// Cancel I/O on the socket. No-op if the socket is not open. + +template <typename C> void +TCPSocket<C>::cancel() { + if (socket_.is_open()) { + socket_.cancel(); + } +} + +// Close the socket down. Can only do this if the socket is open and we are +// managing it ourself. + +template <typename C> void +TCPSocket<C>::close() { + if (socket_.is_open() && socket_ptr_) { + socket_.close(); + } +} + +} // namespace asiolink +} // namespace isc + +#endif // TCP_SOCKET_H diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am new file mode 100644 index 0000000..a402e92 --- /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 += 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..beb4cf4 --- /dev/null +++ b/src/lib/asiolink/tests/Makefile.in @@ -0,0 +1,1260 @@ +# 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_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_sysrepo.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 \ + 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-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_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_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_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_SYSREPO = @HAVE_SYSREPO@ +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@ +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_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +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@ 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_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-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_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_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..a856437 --- /dev/null +++ b/src/lib/asiolink/tests/addr_utilities_unittest.cc @@ -0,0 +1,386 @@ +// Copyright (C) 2012-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/addr_utilities.h> +#include <exceptions/exceptions.h> + +#include <gtest/gtest.h> + +#include <vector> + +#include <stdint.h> +#include <stdlib.h> + +using namespace std; +using namespace isc::asiolink; + +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(std::numeric_limits<uint64_t>::max(), + prefixesInRange(64, 128)); + + // Let's go overboard again. How many IPv6 addresses are there? + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + 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..79888d2 --- /dev/null +++ b/src/lib/asiolink/tests/io_address_unittest.cc @@ -0,0 +1,341 @@ +// 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/. + +#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> + +using namespace isc::asiolink; + +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..42b4e4a --- /dev/null +++ b/src/lib/asiolink/tests/io_service_signal_unittests.cc @@ -0,0 +1,279 @@ +// Copyright (C) 2014-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 <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() {} + + /// @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_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..51aed64 --- /dev/null +++ b/src/lib/asiolink/tests/process_spawn_unittest.cc @@ -0,0 +1,347 @@ +// 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/. + +#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); + } + + /// @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..59a3b3c --- /dev/null +++ b/src/lib/asiolink/tests/tls_unittest.cc @@ -0,0 +1,2693 @@ +// 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/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)"); + // 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()); +} + +} diff --git a/src/lib/asiolink/testutils/Makefile.am b/src/lib/asiolink/testutils/Makefile.am new file mode 100644 index 0000000..cb06448 --- /dev/null +++ b/src/lib/asiolink/testutils/Makefile.am @@ -0,0 +1,93 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) +TEST_CA_DIR = $(abs_srcdir)/ca +AM_CPPFLAGS += -DTEST_CA_DIR=\"$(TEST_CA_DIR)\" + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +EXTRA_DIST = +EXTRA_DIST += ca/00af7a28.0 +EXTRA_DIST += ca/0c7eedb9.0 +EXTRA_DIST += ca/28f5a777.0 +EXTRA_DIST += ca/2eefa08b.0 +EXTRA_DIST += ca/71336a4d.0 +EXTRA_DIST += ca/7a5b785e.0 +EXTRA_DIST += ca/ad950210.0 +EXTRA_DIST += ca/doc.txt +EXTRA_DIST += ca/ext-addr-conf.cnf +EXTRA_DIST += ca/ext-conf.cnf +EXTRA_DIST += ca/kea-ca.crt +EXTRA_DIST += ca/kea-ca.key +EXTRA_DIST += ca/kea-client.crt +EXTRA_DIST += ca/kea-client.csr +EXTRA_DIST += ca/kea-client.key +EXTRA_DIST += ca/kea-client.p12 +EXTRA_DIST += ca/kea-other.crt +EXTRA_DIST += ca/kea-other.key +EXTRA_DIST += ca/kea-self.crt +EXTRA_DIST += ca/kea-self.key +EXTRA_DIST += ca/kea-server-addr.crt +EXTRA_DIST += ca/kea-server-addr.csr +EXTRA_DIST += ca/kea-server-raw.crt +EXTRA_DIST += ca/kea-server-raw.csr +EXTRA_DIST += ca/kea-server.crt +EXTRA_DIST += ca/kea-server.csr +EXTRA_DIST += ca/kea-server.key +EXTRA_DIST += ca/server-addr-conf.cnf +EXTRA_DIST += ca/server-conf.cnf + +CLEANFILES = *.gcno *.gcda + +if HAVE_GTEST + +noinst_LTLIBRARIES = libasiolinktest.la + +libasiolinktest_la_SOURCES = test_server_unix_socket.cc test_server_unix_socket.h +libasiolinktest_la_SOURCES += timed_signal.cc timed_signal.h +libasiolinktest_la_SOURCES += test_tls.cc test_tls.h + +libasiolinktest_la_CXXFLAGS = $(AM_CXXFLAGS) +libasiolinktest_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +libasiolinktest_la_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) + +libasiolinktest_la_LIBADD = $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libasiolinktest_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libasiolinktest_la_LIBADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +if HAVE_OPENSSL +# Boost ASIO SSL sample server and client for C++11. +# https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/example/cpp11/ssl/ +# openssl_sample_server <port> +# openssl_sample_server <address> <port> + +noinst_PROGRAMS = openssl_sample_client openssl_sample_server + +openssl_sample_client_SOURCES = openssl_sample_client.cc +openssl_sample_client_CPPFLAGS = $(AM_CPPFLAGS) +openssl_sample_client_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) +openssl_sample_client_LDADD = $(BOOST_LIBS) $(CRYPTO_LIBS) + +openssl_sample_server_SOURCES = openssl_sample_server.cc +openssl_sample_server_CPPFLAGS = $(AM_CPPFLAGS) +openssl_sample_server_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) +openssl_sample_server_LDADD = $(BOOST_LIBS) $(CRYPTO_LIBS) +endif + +if HAVE_BOTAN_BOOST +# Same samples ported to Botan boost ASIO. + +noinst_PROGRAMS = botan_boost_sample_client botan_boost_sample_server + +botan_boost_sample_client_SOURCES = botan_boost_sample_client.cc +botan_boost_sample_client_CPPFLAGS = $(AM_CPPFLAGS) +botan_boost_sample_client_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) +botan_boost_sample_client_LDADD = $(BOOST_LIBS) $(CRYPTO_LIBS) + +botan_boost_sample_server_SOURCES = botan_boost_sample_server.cc +botan_boost_sample_server_CPPFLAGS = $(AM_CPPFLAGS) +botan_boost_sample_server_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) +botan_boost_sample_server_LDADD = $(BOOST_LIBS) $(CRYPTO_LIBS) +endif +endif diff --git a/src/lib/asiolink/testutils/Makefile.in b/src/lib/asiolink/testutils/Makefile.in new file mode 100644 index 0000000..c6b3cd4 --- /dev/null +++ b/src/lib/asiolink/testutils/Makefile.in @@ -0,0 +1,1052 @@ +# 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@ +@HAVE_BOTAN_BOOST_FALSE@@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@noinst_PROGRAMS = openssl_sample_client$(EXEEXT) \ +@HAVE_BOTAN_BOOST_FALSE@@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@ openssl_sample_server$(EXEEXT) +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@noinst_PROGRAMS = botan_boost_sample_client$(EXEEXT) \ +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@ botan_boost_sample_server$(EXEEXT) +subdir = src/lib/asiolink/testutils +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_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_sysrepo.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 = +CONFIG_CLEAN_VPATH_FILES = +PROGRAMS = $(noinst_PROGRAMS) +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +@HAVE_GTEST_TRUE@libasiolinktest_la_DEPENDENCIES = $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am__libasiolinktest_la_SOURCES_DIST = test_server_unix_socket.cc \ + test_server_unix_socket.h timed_signal.cc timed_signal.h \ + test_tls.cc test_tls.h +@HAVE_GTEST_TRUE@am_libasiolinktest_la_OBJECTS = libasiolinktest_la-test_server_unix_socket.lo \ +@HAVE_GTEST_TRUE@ libasiolinktest_la-timed_signal.lo \ +@HAVE_GTEST_TRUE@ libasiolinktest_la-test_tls.lo +libasiolinktest_la_OBJECTS = $(am_libasiolinktest_la_OBJECTS) +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 = +libasiolinktest_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(libasiolinktest_la_CXXFLAGS) $(CXXFLAGS) \ + $(libasiolinktest_la_LDFLAGS) $(LDFLAGS) -o $@ +@HAVE_GTEST_TRUE@am_libasiolinktest_la_rpath = +am__botan_boost_sample_client_SOURCES_DIST = \ + botan_boost_sample_client.cc +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@am_botan_boost_sample_client_OBJECTS = botan_boost_sample_client-botan_boost_sample_client.$(OBJEXT) +botan_boost_sample_client_OBJECTS = \ + $(am_botan_boost_sample_client_OBJECTS) +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@botan_boost_sample_client_DEPENDENCIES = \ +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) \ +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) +botan_boost_sample_client_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) \ + $(botan_boost_sample_client_LDFLAGS) $(LDFLAGS) -o $@ +am__botan_boost_sample_server_SOURCES_DIST = \ + botan_boost_sample_server.cc +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@am_botan_boost_sample_server_OBJECTS = botan_boost_sample_server-botan_boost_sample_server.$(OBJEXT) +botan_boost_sample_server_OBJECTS = \ + $(am_botan_boost_sample_server_OBJECTS) +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@botan_boost_sample_server_DEPENDENCIES = \ +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) \ +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) +botan_boost_sample_server_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) \ + $(botan_boost_sample_server_LDFLAGS) $(LDFLAGS) -o $@ +am__openssl_sample_client_SOURCES_DIST = openssl_sample_client.cc +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@am_openssl_sample_client_OBJECTS = openssl_sample_client-openssl_sample_client.$(OBJEXT) +openssl_sample_client_OBJECTS = $(am_openssl_sample_client_OBJECTS) +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@openssl_sample_client_DEPENDENCIES = \ +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@ $(am__DEPENDENCIES_1) \ +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@ $(am__DEPENDENCIES_1) +openssl_sample_client_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(openssl_sample_client_LDFLAGS) \ + $(LDFLAGS) -o $@ +am__openssl_sample_server_SOURCES_DIST = openssl_sample_server.cc +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@am_openssl_sample_server_OBJECTS = openssl_sample_server-openssl_sample_server.$(OBJEXT) +openssl_sample_server_OBJECTS = $(am_openssl_sample_server_OBJECTS) +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@openssl_sample_server_DEPENDENCIES = \ +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@ $(am__DEPENDENCIES_1) \ +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@ $(am__DEPENDENCIES_1) +openssl_sample_server_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(openssl_sample_server_LDFLAGS) \ + $(LDFLAGS) -o $@ +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)/botan_boost_sample_client-botan_boost_sample_client.Po \ + ./$(DEPDIR)/botan_boost_sample_server-botan_boost_sample_server.Po \ + ./$(DEPDIR)/libasiolinktest_la-test_server_unix_socket.Plo \ + ./$(DEPDIR)/libasiolinktest_la-test_tls.Plo \ + ./$(DEPDIR)/libasiolinktest_la-timed_signal.Plo \ + ./$(DEPDIR)/openssl_sample_client-openssl_sample_client.Po \ + ./$(DEPDIR)/openssl_sample_server-openssl_sample_server.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 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libasiolinktest_la_SOURCES) \ + $(botan_boost_sample_client_SOURCES) \ + $(botan_boost_sample_server_SOURCES) \ + $(openssl_sample_client_SOURCES) \ + $(openssl_sample_server_SOURCES) +DIST_SOURCES = $(am__libasiolinktest_la_SOURCES_DIST) \ + $(am__botan_boost_sample_client_SOURCES_DIST) \ + $(am__botan_boost_sample_server_SOURCES_DIST) \ + $(am__openssl_sample_client_SOURCES_DIST) \ + $(am__openssl_sample_server_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +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 +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +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_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_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_SYSREPO = @HAVE_SYSREPO@ +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@ +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_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +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@ +SUBDIRS = . +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \ + $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) \ + -DTEST_CA_DIR=\"$(TEST_CA_DIR)\" +TEST_CA_DIR = $(abs_srcdir)/ca +AM_CXXFLAGS = $(KEA_CXXFLAGS) +EXTRA_DIST = ca/00af7a28.0 ca/0c7eedb9.0 ca/28f5a777.0 ca/2eefa08b.0 \ + ca/71336a4d.0 ca/7a5b785e.0 ca/ad950210.0 ca/doc.txt \ + ca/ext-addr-conf.cnf ca/ext-conf.cnf ca/kea-ca.crt \ + ca/kea-ca.key ca/kea-client.crt ca/kea-client.csr \ + ca/kea-client.key ca/kea-client.p12 ca/kea-other.crt \ + ca/kea-other.key ca/kea-self.crt ca/kea-self.key \ + ca/kea-server-addr.crt ca/kea-server-addr.csr \ + ca/kea-server-raw.crt ca/kea-server-raw.csr ca/kea-server.crt \ + ca/kea-server.csr ca/kea-server.key ca/server-addr-conf.cnf \ + ca/server-conf.cnf +CLEANFILES = *.gcno *.gcda +@HAVE_GTEST_TRUE@noinst_LTLIBRARIES = libasiolinktest.la +@HAVE_GTEST_TRUE@libasiolinktest_la_SOURCES = \ +@HAVE_GTEST_TRUE@ test_server_unix_socket.cc \ +@HAVE_GTEST_TRUE@ test_server_unix_socket.h timed_signal.cc \ +@HAVE_GTEST_TRUE@ timed_signal.h test_tls.cc test_tls.h +@HAVE_GTEST_TRUE@libasiolinktest_la_CXXFLAGS = $(AM_CXXFLAGS) +@HAVE_GTEST_TRUE@libasiolinktest_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +@HAVE_GTEST_TRUE@libasiolinktest_la_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) +@HAVE_GTEST_TRUE@libasiolinktest_la_LIBADD = $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(BOOST_LIBS) $(CRYPTO_LIBS) +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@openssl_sample_client_SOURCES = openssl_sample_client.cc +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@openssl_sample_client_CPPFLAGS = $(AM_CPPFLAGS) +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@openssl_sample_client_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@openssl_sample_client_LDADD = $(BOOST_LIBS) $(CRYPTO_LIBS) +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@openssl_sample_server_SOURCES = openssl_sample_server.cc +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@openssl_sample_server_CPPFLAGS = $(AM_CPPFLAGS) +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@openssl_sample_server_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) +@HAVE_GTEST_TRUE@@HAVE_OPENSSL_TRUE@openssl_sample_server_LDADD = $(BOOST_LIBS) $(CRYPTO_LIBS) +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@botan_boost_sample_client_SOURCES = botan_boost_sample_client.cc +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@botan_boost_sample_client_CPPFLAGS = $(AM_CPPFLAGS) +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@botan_boost_sample_client_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@botan_boost_sample_client_LDADD = $(BOOST_LIBS) $(CRYPTO_LIBS) +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@botan_boost_sample_server_SOURCES = botan_boost_sample_server.cc +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@botan_boost_sample_server_CPPFLAGS = $(AM_CPPFLAGS) +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@botan_boost_sample_server_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) +@HAVE_BOTAN_BOOST_TRUE@@HAVE_GTEST_TRUE@botan_boost_sample_server_LDADD = $(BOOST_LIBS) $(CRYPTO_LIBS) +all: all-recursive + +.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/testutils/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/asiolink/testutils/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): + +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 + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libasiolinktest.la: $(libasiolinktest_la_OBJECTS) $(libasiolinktest_la_DEPENDENCIES) $(EXTRA_libasiolinktest_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libasiolinktest_la_LINK) $(am_libasiolinktest_la_rpath) $(libasiolinktest_la_OBJECTS) $(libasiolinktest_la_LIBADD) $(LIBS) + +botan_boost_sample_client$(EXEEXT): $(botan_boost_sample_client_OBJECTS) $(botan_boost_sample_client_DEPENDENCIES) $(EXTRA_botan_boost_sample_client_DEPENDENCIES) + @rm -f botan_boost_sample_client$(EXEEXT) + $(AM_V_CXXLD)$(botan_boost_sample_client_LINK) $(botan_boost_sample_client_OBJECTS) $(botan_boost_sample_client_LDADD) $(LIBS) + +botan_boost_sample_server$(EXEEXT): $(botan_boost_sample_server_OBJECTS) $(botan_boost_sample_server_DEPENDENCIES) $(EXTRA_botan_boost_sample_server_DEPENDENCIES) + @rm -f botan_boost_sample_server$(EXEEXT) + $(AM_V_CXXLD)$(botan_boost_sample_server_LINK) $(botan_boost_sample_server_OBJECTS) $(botan_boost_sample_server_LDADD) $(LIBS) + +openssl_sample_client$(EXEEXT): $(openssl_sample_client_OBJECTS) $(openssl_sample_client_DEPENDENCIES) $(EXTRA_openssl_sample_client_DEPENDENCIES) + @rm -f openssl_sample_client$(EXEEXT) + $(AM_V_CXXLD)$(openssl_sample_client_LINK) $(openssl_sample_client_OBJECTS) $(openssl_sample_client_LDADD) $(LIBS) + +openssl_sample_server$(EXEEXT): $(openssl_sample_server_OBJECTS) $(openssl_sample_server_DEPENDENCIES) $(EXTRA_openssl_sample_server_DEPENDENCIES) + @rm -f openssl_sample_server$(EXEEXT) + $(AM_V_CXXLD)$(openssl_sample_server_LINK) $(openssl_sample_server_OBJECTS) $(openssl_sample_server_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/botan_boost_sample_client-botan_boost_sample_client.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/botan_boost_sample_server-botan_boost_sample_server.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libasiolinktest_la-test_server_unix_socket.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libasiolinktest_la-test_tls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libasiolinktest_la-timed_signal.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl_sample_client-openssl_sample_client.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl_sample_server-openssl_sample_server.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 $@ $< + +libasiolinktest_la-test_server_unix_socket.lo: test_server_unix_socket.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libasiolinktest_la_CPPFLAGS) $(CPPFLAGS) $(libasiolinktest_la_CXXFLAGS) $(CXXFLAGS) -MT libasiolinktest_la-test_server_unix_socket.lo -MD -MP -MF $(DEPDIR)/libasiolinktest_la-test_server_unix_socket.Tpo -c -o libasiolinktest_la-test_server_unix_socket.lo `test -f 'test_server_unix_socket.cc' || echo '$(srcdir)/'`test_server_unix_socket.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libasiolinktest_la-test_server_unix_socket.Tpo $(DEPDIR)/libasiolinktest_la-test_server_unix_socket.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='test_server_unix_socket.cc' object='libasiolinktest_la-test_server_unix_socket.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libasiolinktest_la_CPPFLAGS) $(CPPFLAGS) $(libasiolinktest_la_CXXFLAGS) $(CXXFLAGS) -c -o libasiolinktest_la-test_server_unix_socket.lo `test -f 'test_server_unix_socket.cc' || echo '$(srcdir)/'`test_server_unix_socket.cc + +libasiolinktest_la-timed_signal.lo: timed_signal.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libasiolinktest_la_CPPFLAGS) $(CPPFLAGS) $(libasiolinktest_la_CXXFLAGS) $(CXXFLAGS) -MT libasiolinktest_la-timed_signal.lo -MD -MP -MF $(DEPDIR)/libasiolinktest_la-timed_signal.Tpo -c -o libasiolinktest_la-timed_signal.lo `test -f 'timed_signal.cc' || echo '$(srcdir)/'`timed_signal.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libasiolinktest_la-timed_signal.Tpo $(DEPDIR)/libasiolinktest_la-timed_signal.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='timed_signal.cc' object='libasiolinktest_la-timed_signal.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libasiolinktest_la_CPPFLAGS) $(CPPFLAGS) $(libasiolinktest_la_CXXFLAGS) $(CXXFLAGS) -c -o libasiolinktest_la-timed_signal.lo `test -f 'timed_signal.cc' || echo '$(srcdir)/'`timed_signal.cc + +libasiolinktest_la-test_tls.lo: test_tls.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libasiolinktest_la_CPPFLAGS) $(CPPFLAGS) $(libasiolinktest_la_CXXFLAGS) $(CXXFLAGS) -MT libasiolinktest_la-test_tls.lo -MD -MP -MF $(DEPDIR)/libasiolinktest_la-test_tls.Tpo -c -o libasiolinktest_la-test_tls.lo `test -f 'test_tls.cc' || echo '$(srcdir)/'`test_tls.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libasiolinktest_la-test_tls.Tpo $(DEPDIR)/libasiolinktest_la-test_tls.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='test_tls.cc' object='libasiolinktest_la-test_tls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libasiolinktest_la_CPPFLAGS) $(CPPFLAGS) $(libasiolinktest_la_CXXFLAGS) $(CXXFLAGS) -c -o libasiolinktest_la-test_tls.lo `test -f 'test_tls.cc' || echo '$(srcdir)/'`test_tls.cc + +botan_boost_sample_client-botan_boost_sample_client.o: botan_boost_sample_client.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(botan_boost_sample_client_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT botan_boost_sample_client-botan_boost_sample_client.o -MD -MP -MF $(DEPDIR)/botan_boost_sample_client-botan_boost_sample_client.Tpo -c -o botan_boost_sample_client-botan_boost_sample_client.o `test -f 'botan_boost_sample_client.cc' || echo '$(srcdir)/'`botan_boost_sample_client.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/botan_boost_sample_client-botan_boost_sample_client.Tpo $(DEPDIR)/botan_boost_sample_client-botan_boost_sample_client.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='botan_boost_sample_client.cc' object='botan_boost_sample_client-botan_boost_sample_client.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) $(botan_boost_sample_client_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o botan_boost_sample_client-botan_boost_sample_client.o `test -f 'botan_boost_sample_client.cc' || echo '$(srcdir)/'`botan_boost_sample_client.cc + +botan_boost_sample_client-botan_boost_sample_client.obj: botan_boost_sample_client.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(botan_boost_sample_client_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT botan_boost_sample_client-botan_boost_sample_client.obj -MD -MP -MF $(DEPDIR)/botan_boost_sample_client-botan_boost_sample_client.Tpo -c -o botan_boost_sample_client-botan_boost_sample_client.obj `if test -f 'botan_boost_sample_client.cc'; then $(CYGPATH_W) 'botan_boost_sample_client.cc'; else $(CYGPATH_W) '$(srcdir)/botan_boost_sample_client.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/botan_boost_sample_client-botan_boost_sample_client.Tpo $(DEPDIR)/botan_boost_sample_client-botan_boost_sample_client.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='botan_boost_sample_client.cc' object='botan_boost_sample_client-botan_boost_sample_client.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) $(botan_boost_sample_client_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o botan_boost_sample_client-botan_boost_sample_client.obj `if test -f 'botan_boost_sample_client.cc'; then $(CYGPATH_W) 'botan_boost_sample_client.cc'; else $(CYGPATH_W) '$(srcdir)/botan_boost_sample_client.cc'; fi` + +botan_boost_sample_server-botan_boost_sample_server.o: botan_boost_sample_server.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(botan_boost_sample_server_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT botan_boost_sample_server-botan_boost_sample_server.o -MD -MP -MF $(DEPDIR)/botan_boost_sample_server-botan_boost_sample_server.Tpo -c -o botan_boost_sample_server-botan_boost_sample_server.o `test -f 'botan_boost_sample_server.cc' || echo '$(srcdir)/'`botan_boost_sample_server.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/botan_boost_sample_server-botan_boost_sample_server.Tpo $(DEPDIR)/botan_boost_sample_server-botan_boost_sample_server.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='botan_boost_sample_server.cc' object='botan_boost_sample_server-botan_boost_sample_server.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) $(botan_boost_sample_server_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o botan_boost_sample_server-botan_boost_sample_server.o `test -f 'botan_boost_sample_server.cc' || echo '$(srcdir)/'`botan_boost_sample_server.cc + +botan_boost_sample_server-botan_boost_sample_server.obj: botan_boost_sample_server.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(botan_boost_sample_server_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT botan_boost_sample_server-botan_boost_sample_server.obj -MD -MP -MF $(DEPDIR)/botan_boost_sample_server-botan_boost_sample_server.Tpo -c -o botan_boost_sample_server-botan_boost_sample_server.obj `if test -f 'botan_boost_sample_server.cc'; then $(CYGPATH_W) 'botan_boost_sample_server.cc'; else $(CYGPATH_W) '$(srcdir)/botan_boost_sample_server.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/botan_boost_sample_server-botan_boost_sample_server.Tpo $(DEPDIR)/botan_boost_sample_server-botan_boost_sample_server.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='botan_boost_sample_server.cc' object='botan_boost_sample_server-botan_boost_sample_server.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) $(botan_boost_sample_server_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o botan_boost_sample_server-botan_boost_sample_server.obj `if test -f 'botan_boost_sample_server.cc'; then $(CYGPATH_W) 'botan_boost_sample_server.cc'; else $(CYGPATH_W) '$(srcdir)/botan_boost_sample_server.cc'; fi` + +openssl_sample_client-openssl_sample_client.o: openssl_sample_client.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(openssl_sample_client_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT openssl_sample_client-openssl_sample_client.o -MD -MP -MF $(DEPDIR)/openssl_sample_client-openssl_sample_client.Tpo -c -o openssl_sample_client-openssl_sample_client.o `test -f 'openssl_sample_client.cc' || echo '$(srcdir)/'`openssl_sample_client.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openssl_sample_client-openssl_sample_client.Tpo $(DEPDIR)/openssl_sample_client-openssl_sample_client.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='openssl_sample_client.cc' object='openssl_sample_client-openssl_sample_client.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) $(openssl_sample_client_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o openssl_sample_client-openssl_sample_client.o `test -f 'openssl_sample_client.cc' || echo '$(srcdir)/'`openssl_sample_client.cc + +openssl_sample_client-openssl_sample_client.obj: openssl_sample_client.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(openssl_sample_client_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT openssl_sample_client-openssl_sample_client.obj -MD -MP -MF $(DEPDIR)/openssl_sample_client-openssl_sample_client.Tpo -c -o openssl_sample_client-openssl_sample_client.obj `if test -f 'openssl_sample_client.cc'; then $(CYGPATH_W) 'openssl_sample_client.cc'; else $(CYGPATH_W) '$(srcdir)/openssl_sample_client.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openssl_sample_client-openssl_sample_client.Tpo $(DEPDIR)/openssl_sample_client-openssl_sample_client.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='openssl_sample_client.cc' object='openssl_sample_client-openssl_sample_client.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) $(openssl_sample_client_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o openssl_sample_client-openssl_sample_client.obj `if test -f 'openssl_sample_client.cc'; then $(CYGPATH_W) 'openssl_sample_client.cc'; else $(CYGPATH_W) '$(srcdir)/openssl_sample_client.cc'; fi` + +openssl_sample_server-openssl_sample_server.o: openssl_sample_server.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(openssl_sample_server_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT openssl_sample_server-openssl_sample_server.o -MD -MP -MF $(DEPDIR)/openssl_sample_server-openssl_sample_server.Tpo -c -o openssl_sample_server-openssl_sample_server.o `test -f 'openssl_sample_server.cc' || echo '$(srcdir)/'`openssl_sample_server.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openssl_sample_server-openssl_sample_server.Tpo $(DEPDIR)/openssl_sample_server-openssl_sample_server.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='openssl_sample_server.cc' object='openssl_sample_server-openssl_sample_server.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) $(openssl_sample_server_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o openssl_sample_server-openssl_sample_server.o `test -f 'openssl_sample_server.cc' || echo '$(srcdir)/'`openssl_sample_server.cc + +openssl_sample_server-openssl_sample_server.obj: openssl_sample_server.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(openssl_sample_server_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT openssl_sample_server-openssl_sample_server.obj -MD -MP -MF $(DEPDIR)/openssl_sample_server-openssl_sample_server.Tpo -c -o openssl_sample_server-openssl_sample_server.obj `if test -f 'openssl_sample_server.cc'; then $(CYGPATH_W) 'openssl_sample_server.cc'; else $(CYGPATH_W) '$(srcdir)/openssl_sample_server.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openssl_sample_server-openssl_sample_server.Tpo $(DEPDIR)/openssl_sample_server-openssl_sample_server.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='openssl_sample_server.cc' object='openssl_sample_server-openssl_sample_server.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) $(openssl_sample_server_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o openssl_sample_server-openssl_sample_server.obj `if test -f 'openssl_sample_server.cc'; then $(CYGPATH_W) 'openssl_sample_server.cc'; else $(CYGPATH_W) '$(srcdir)/openssl_sample_server.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(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-recursive + +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-recursive + +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 + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +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) + +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-recursive + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/botan_boost_sample_client-botan_boost_sample_client.Po + -rm -f ./$(DEPDIR)/botan_boost_sample_server-botan_boost_sample_server.Po + -rm -f ./$(DEPDIR)/libasiolinktest_la-test_server_unix_socket.Plo + -rm -f ./$(DEPDIR)/libasiolinktest_la-test_tls.Plo + -rm -f ./$(DEPDIR)/libasiolinktest_la-timed_signal.Plo + -rm -f ./$(DEPDIR)/openssl_sample_client-openssl_sample_client.Po + -rm -f ./$(DEPDIR)/openssl_sample_server-openssl_sample_server.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/botan_boost_sample_client-botan_boost_sample_client.Po + -rm -f ./$(DEPDIR)/botan_boost_sample_server-botan_boost_sample_server.Po + -rm -f ./$(DEPDIR)/libasiolinktest_la-test_server_unix_socket.Plo + -rm -f ./$(DEPDIR)/libasiolinktest_la-test_tls.Plo + -rm -f ./$(DEPDIR)/libasiolinktest_la-timed_signal.Plo + -rm -f ./$(DEPDIR)/openssl_sample_client-openssl_sample_client.Po + -rm -f ./$(DEPDIR)/openssl_sample_server-openssl_sample_server.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic clean-libtool \ + clean-noinstLTLIBRARIES 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 installdirs-am 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/testutils/botan_boost_sample_client.cc b/src/lib/asiolink/testutils/botan_boost_sample_client.cc new file mode 100644 index 0000000..8049e96 --- /dev/null +++ b/src/lib/asiolink/testutils/botan_boost_sample_client.cc @@ -0,0 +1,229 @@ +// +// client.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <config.h> + +#include <cstdlib> +#include <cstring> +#include <functional> +#include <iostream> +#include <boost/asio.hpp> + +#include <asiolink/botan_boost_wrapper.h> +#include <botan/asio_stream.h> +#include <botan/certstor_flatfile.h> +#include <botan/pkcs8.h> +#include <botan/auto_rng.h> + +inline std::string CA_(const std::string& filename) { + return (std::string(TEST_CA_DIR) + "/" + filename); +} + +using boost::asio::ip::tcp; + +enum { max_length = 1024 }; + +using Client_Certificate_Store = Botan::Flatfile_Certificate_Store; + +class Client_Credentials_Manager : public Botan::Credentials_Manager +{ +public: + explicit Client_Credentials_Manager(Botan::RandomNumberGenerator& rng) + : stores_(), certs_(), + store_(new Client_Certificate_Store(CA_("kea-ca.crt"))), + cert_(Botan::X509_Certificate(CA_("kea-client.crt"))), + key_(Botan::PKCS8::load_key(CA_("kea-client.key"), rng)) + { + stores_.push_back(store_.get()); + certs_.push_back(cert_); + } + + virtual ~Client_Credentials_Manager() + { + } + + std::vector<Botan::Certificate_Store*> + trusted_certificate_authorities(const std::string&, + const std::string&) override + { + return stores_; + } + + std::vector<Botan::X509_Certificate> + cert_chain(const std::vector<std::string>&, + const std::string&, + const std::string&) override + { + return certs_; + } + + Botan::Private_Key* + private_key_for(const Botan::X509_Certificate&, + const std::string&, + const std::string&) override + { + return key_.get(); + } + + std::vector<Botan::Certificate_Store*> stores_; + std::vector<Botan::X509_Certificate> certs_; + std::shared_ptr<Botan::Certificate_Store> store_; + Botan::X509_Certificate cert_; + std::unique_ptr<Botan::Private_Key> key_; +}; + +using Client_Session_Manager = Botan::TLS::Session_Manager_Noop; + +class Client_Policy : public Botan::TLS::Default_Policy { +public: + virtual ~Client_Policy() + { + } + + std::vector<std::string> allowed_signature_methods() const override + { + return { "RSA", "ECDSA", "IMPLICIT" }; + } + + bool require_cert_revocation_info() const override + { + return false; + } +}; + +class client +{ +public: + client(boost::asio::io_service& io_context, + Botan::TLS::Context& context, + const tcp::endpoint& endpoint) + : socket_(io_context, context) + { + connect(endpoint); + } + +private: + void connect(const tcp::endpoint& endpoint) + { + socket_.lowest_layer().async_connect(endpoint, + [this](const boost::system::error_code& error) + { + if (!error) + { + handshake(); + } + else + { + std::cout << "Connect failed: " << error.message() << "\n"; + } + }); + } + + void handshake() + { + socket_.async_handshake(Botan::TLS::Connection_Side::CLIENT, + [this](const boost::system::error_code& error) + { + if (!error) + { + // Print the certificate's subject name. + const std::vector<Botan::X509_Certificate>& cert_chain = + socket_.native_handle()->peer_cert_chain(); + for (auto const& cert : cert_chain) { + const Botan::X509_DN& subject = cert.subject_dn(); + std::cout << "Verified " << subject.to_string() << "\n"; + } + + send_request(); + } + else + { + std::cout << "Handshake failed: " << error.message() << "\n"; + } + }); + } + + void send_request() + { + std::cout << "Enter message: "; + std::cin.getline(request_, max_length); + size_t request_length = std::strlen(request_); + + boost::asio::async_write(socket_, + boost::asio::buffer(request_, request_length), + [this](const boost::system::error_code& error, std::size_t length) + { + if (!error) + { + receive_response(length); + } + else + { + std::cout << "Write failed: " << error.message() << "\n"; + } + }); + } + + void receive_response(std::size_t length) + { + boost::asio::async_read(socket_, + boost::asio::buffer(reply_, length), + [this](const boost::system::error_code& error, std::size_t length) + { + if (!error) + { + std::cout << "Reply: "; + std::cout.write(reply_, length); + std::cout << "\n"; + } + else + { + std::cout << "Read failed: " << error.message() << "\n"; + } + }); + } + + Botan::TLS::Stream<tcp::socket> socket_; + char request_[max_length]; + char reply_[max_length]; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: client <addr> <port>\n"; + return 1; + } + + boost::asio::io_service io_context; + + using namespace std; // For atoi. + tcp::endpoint endpoint( + boost::asio::ip::address::from_string(argv[1]), atoi(argv[2])); + Botan::AutoSeeded_RNG rng; + Client_Credentials_Manager creds_mgr(rng); + Client_Session_Manager sess_mgr; + Client_Policy policy; + Botan::TLS::Context ctx(creds_mgr, rng, sess_mgr, policy); + + client c(io_context, ctx, endpoint); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/lib/asiolink/testutils/botan_boost_sample_server.cc b/src/lib/asiolink/testutils/botan_boost_sample_server.cc new file mode 100644 index 0000000..86400ad --- /dev/null +++ b/src/lib/asiolink/testutils/botan_boost_sample_server.cc @@ -0,0 +1,220 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <config.h> + +#include <cstdlib> +#include <functional> +#include <iostream> +#include <boost/asio.hpp> + +#include <asiolink/botan_boost_wrapper.h> +#include <botan/asio_stream.h> +#include <botan/certstor_flatfile.h> +#include <botan/pkcs8.h> +#include <botan/auto_rng.h> + +inline std::string CA_(const std::string& filename) { + return (std::string(TEST_CA_DIR) + "/" + filename); +} + +using boost::asio::ip::tcp; + +using Server_Certificate_Store = Botan::Flatfile_Certificate_Store; + +class Server_Credentials_Manager : public Botan::Credentials_Manager +{ +public: + explicit Server_Credentials_Manager(Botan::RandomNumberGenerator& rng) + : stores_(), certs_(), + store_(new Server_Certificate_Store(CA_("kea-ca.crt"))), + cert_(Botan::X509_Certificate(CA_("kea-server.crt"))), + key_(Botan::PKCS8::load_key(CA_("kea-server.key"), rng)) + { + stores_.push_back(store_.get()); + certs_.push_back(cert_); + } + + virtual ~Server_Credentials_Manager() + { + } + + std::vector<Botan::Certificate_Store*> + trusted_certificate_authorities(const std::string&, + const std::string&) override + { + return stores_; + } + + std::vector<Botan::X509_Certificate> + cert_chain(const std::vector<std::string>&, + const std::string&, + const std::string&) override + { + return certs_; + } + + Botan::Private_Key* + private_key_for(const Botan::X509_Certificate&, + const std::string&, + const std::string&) override + { + return key_.get(); + } + + std::vector<Botan::Certificate_Store*> stores_; + std::vector<Botan::X509_Certificate> certs_; + std::shared_ptr<Botan::Certificate_Store> store_; + Botan::X509_Certificate cert_; + std::unique_ptr<Botan::Private_Key> key_; +}; + +using Server_Session_Manager = Botan::TLS::Session_Manager_Noop; + +class Server_Policy : public Botan::TLS::Default_Policy { +public: + virtual ~Server_Policy() + { + } + + std::vector<std::string> allowed_signature_methods() const override + { + return { "RSA", "ECDSA", "IMPLICIT" }; + } + + bool require_cert_revocation_info() const override + { + return false; + } +}; + +class session : public std::enable_shared_from_this<session> +{ +public: + session(tcp::socket socket, Botan::TLS::Context& ctx) + : socket_(std::move(socket), ctx) + { + } + + void start() + { + do_handshake(); + } + +private: + void do_handshake() + { + auto self(shared_from_this()); + socket_.async_handshake(Botan::TLS::Connection_Side::SERVER, + [this, self](const boost::system::error_code& error) + { + if (!error) + { + do_read(); + } + else + { + std::cerr << "handshake failed with " << error.message() << "\n"; + } + }); + } + + void do_read() + { + auto self(shared_from_this()); + socket_.async_read_some(boost::asio::buffer(data_), + [this, self](const boost::system::error_code& ec, std::size_t length) + { + if (!ec) + { + do_write(length); + } + }); + } + + void do_write(std::size_t length) + { + auto self(shared_from_this()); + boost::asio::async_write(socket_, boost::asio::buffer(data_, length), + [this, self](const boost::system::error_code& ec, + std::size_t /*length*/) + { + if (!ec) + { + do_read(); + } + }); + } + + Botan::TLS::Stream<tcp::socket> socket_; + char data_[1024]; +}; + +class server +{ +public: + server(boost::asio::io_service& io_context, + unsigned short port, + Botan::Credentials_Manager& creds_mgr, + Botan::RandomNumberGenerator& rng, + Botan::TLS::Session_Manager& sess_mgr, + Botan::TLS::Policy& policy) + : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)), + context_(creds_mgr, rng, sess_mgr, policy) + { + do_accept(); + } + +private: + void do_accept() + { + acceptor_.async_accept( + [this](const boost::system::error_code& error, tcp::socket socket) + { + if (!error) + { + std::make_shared<session>(std::move(socket), context_)->start(); + } + + do_accept(); + }); + } + + tcp::acceptor acceptor_; + Botan::TLS::Context context_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: server <port>\n"; + return 1; + } + + boost::asio::io_service io_context; + + Botan::AutoSeeded_RNG rng; + Server_Credentials_Manager creds_mgr(rng); + Server_Session_Manager sess_mgr; + Server_Policy policy; + server s(io_context, std::atoi(argv[1]), creds_mgr, rng, sess_mgr, policy); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/lib/asiolink/testutils/ca/00af7a28.0 b/src/lib/asiolink/testutils/ca/00af7a28.0 new file mode 100644 index 0000000..5d7534d --- /dev/null +++ b/src/lib/asiolink/testutils/ca/00af7a28.0 @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDHDCCAgSgAwIBAgIUe1AyLcAeSfKwCZNZLFTRkWMyOJQwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxETAPBgNVBAoM +CElTQyBJbmMuMREwDwYDVQQDDAhrZWEtc2VsZjAeFw0yMTAzMDIxNDQ3MDdaFw0z +MTAyMjgxNDQ3MDdaMEgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRl +MREwDwYDVQQKDAhJU0MgSW5jLjERMA8GA1UEAwwIa2VhLXNlbGYwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAoEENWQ6tl6aaRMn+yaNUKTBIIWpVoy5+ +uGsBdZW++fEvw4xmleGD+bwyHZFEsHPos/v7zWUNFaX2aWD0H+Hk4l2WTFigWO3u +tPoXDzDOjfQmglKG+R08p3giURrJzUKWwe/RRJBs7qXdcD9yNXVOb2JWp4Cxk1iP +j7zTS/LGsFr7F4/k2nlH3EuqvB3GBEXHa/sA55xigMyvqVnVb4rNh+PjGL8l5SZz +SnrbdoIEtKw/LVbBCAVrQsgcADNqjR7ILbqeIqg1Td11QvQzB7f/U5dQoQPzq3j4 +ow1zOiaSokZE7UcUCUNfjRv5E2lW+mmyM7nkgyE9LqUJ/3udIh1vAgMBAAEwDQYJ +KoZIhvcNAQELBQADggEBAHWFX55xUt1Opqtji+I2XvBrcexleSAME+irKwExe+tY +laFEWb1eWyzFHiuOSuNLjcXt1PkUYZ0lYUg17cDj5urpAy+F07uCRQWTXBY8W53H +IppYl4KjN3w4e5DSyDfiTv99MT8xVKJk+rVu75lQ0kgg68fZR6yK82SLjBQmjV2A +OcSqHNHtnBU5RcdlZ+E05M1Vo1jHzxHpybkgNxjvmUgBRc9ieLbgSFRZji0nNmhA +TSZ0DjRce6eyDI+OoEFJL0wXMl0ZOijeuCJr4C45h3TyreU2COC1GaoIeNwmGSIb +mw0j+XR4rKHcgkUQ7L2DfwOjGFG7IeT+k0QdyeM2NU4= +-----END CERTIFICATE----- diff --git a/src/lib/asiolink/testutils/ca/0c7eedb9.0 b/src/lib/asiolink/testutils/ca/0c7eedb9.0 new file mode 100644 index 0000000..3476032 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/0c7eedb9.0 @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID+DCCAeCgAwIBAgIBFDANBgkqhkiG9w0BAQsFADAwMQswCQYDVQQGEwJVUzEQ +MA4GA1UECgwHSVNDIEluYzEPMA0GA1UEAwwGa2VhLWNhMB4XDTIxMDMwMjE1MDEy +N1oXDTMxMDIyODE1MDEyN1owNTELMAkGA1UEBhMCVVMxETAPBgNVBAoMCElTQyBJ +bmMuMRMwEQYDVQQDDAprZWEtc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAveRRgIN0S8oeBXVaIEnsG1DKuDzKKqLoLdBQNfoZrKzDLIMNzlab +xu20h82Y/OU02EdEzar98OstzglIWimKFVI0Omi0AuinUkv9640tjoO0g0oyCiWF +pJLJ8WOF4j7vmZUWuSS3VthlB+MLWlOZ5zACyPyWPo4Z2noHaYjfiQxBH8r5GJtQ +iJGapgWRbeyI+m837bjimpz6V1AGebHvf+zd1Lj+zDOczp38PqIGUbmAvfKCj+IL +MS46wYjjHTvCG5WSCG/Skker2HAJM2cNcEPmQqAOpAkmFQ2G46bXB4rBXh9dNZB5 +2U9QkyPFHKrnNn400B/xBGNKoyTSYbLQEwIDAQABoxgwFjAUBgNVHREEDTALggls +b2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggIBAKYtC4/KKZnTktvWankLnlVact5K +L0bJT4qCDg/0gj0pj3rofqyOEoGIjZssQtAG/wmJNF6gNisX/1F23BdEdPAsOJQv +KuRwr4zL3uj2Mkz585Or/iz633LnD8Ibv8KQsKLnJ/UnJikeH5UgxqcU9kA7ymAE +pzilP23p3bINvyBMwWZUzT3CsYB7PrcRzx3ScZhbhYaN0f8lq83nspXr8U3FyH5U +NkrgpuqIE9dFPiaY4CsjNIISpYANcVeWwyPKMk/uty3KbzbmDr7ssm1u1MyJjeVP +jE/Dhq+WTbDGMfqR3gyXBWq7b1ROA7tk9kAMQg91PLAELSB6lRmzfxzrH/wYk6E/ +0gHgpznpDcA68uW/54eX8phJQQp7Ak7csElXjqXDJ1AWA8VVjRXHerOkq0cUWply +YsJQCkx3jKdLDFfjtKZWVOjc9rGCnph4BfUej/Lt7z7tTr/Yh+oAR+UyowRzdZM/ +RSsui8vVbvKU+bRlyB5qmNR8cSI5oEA+kAs5DXK2bh5v1SGSxVjwKuwwLeu8eCr3 +HUYQMxKi7Y15+BqjbrOZCEfHE4WORkKze1dh9U/UU9h+LVd+TB7jprZc3ZOvuqYP +Bb+ponHJJaRvHUKD/jL8kHQ7KX79wXNVkrevGcPe8qE1X/xu4ChK5PuDzq2HQPLs +USYWw/aARNwslhV6 +-----END CERTIFICATE----- diff --git a/src/lib/asiolink/testutils/ca/28f5a777.0 b/src/lib/asiolink/testutils/ca/28f5a777.0 new file mode 100644 index 0000000..bdcc9bd --- /dev/null +++ b/src/lib/asiolink/testutils/ca/28f5a777.0 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID4DCCAcigAwIBAgIBFDANBgkqhkiG9w0BAQsFADAzMQswCQYDVQQGEwJVUzER +MA8GA1UECgwISVNDIEluYy4xETAPBgNVBAMMCG90aGVyLWNhMB4XDTIxMDMwMjE0 +NTI0OFoXDTMxMDIyODE0NTI0OFowNDELMAkGA1UEBhMCVVMxETAPBgNVBAoMCElT +QyBJbmMuMRIwEAYDVQQDDAlrZWEtb3RoZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDAoEENWQ6tl6aaRMn+yaNUKTBIIWpVoy5+uGsBdZW++fEvw4xm +leGD+bwyHZFEsHPos/v7zWUNFaX2aWD0H+Hk4l2WTFigWO3utPoXDzDOjfQmglKG ++R08p3giURrJzUKWwe/RRJBs7qXdcD9yNXVOb2JWp4Cxk1iPj7zTS/LGsFr7F4/k +2nlH3EuqvB3GBEXHa/sA55xigMyvqVnVb4rNh+PjGL8l5SZzSnrbdoIEtKw/LVbB +CAVrQsgcADNqjR7ILbqeIqg1Td11QvQzB7f/U5dQoQPzq3j4ow1zOiaSokZE7UcU +CUNfjRv5E2lW+mmyM7nkgyE9LqUJ/3udIh1vAgMBAAEwDQYJKoZIhvcNAQELBQAD +ggIBAMYcxVfoCIn+NPlsoRB2m5vAOuJTuBNigf8Fm0HYougE2W+p50+5USx2BCM8 +M1Cet+8X0dktHbRdDL5aZrRbYnz/OENBD4tKuWMQoP/qzafRiKSkDckxYM6AR4T+ +fzPgLjUde2NE1cDeRlJUmereRXiD2qefEFH55StLl8YnnciAMGTRjwBuLiReF+qE +noaD8ZIKZ5pBMzoxyOe+39tLJkzhESdZ8gJZRXGm+ickAlP96w8z8TlQiWHG3Caw +kM7SZSyVYdyfiF32J6A7hwlG3qud83GcunfrjOurWBe1lv51pb/OFGe6wlRD/pcS +UcKZ07KXXYMXV40O6A5Dv0yJB8ocKhOkfU5MvotAAm2GL2ZXizfmEAz23X9I8830 +B5ggVxgp/bO/exC1sBJjUgF4qVPByE1MdDDWYvPKT8cYg5j8pD9rDn7WGVAmgCk9 +59lEI0HBP33ulBRoxrOQ7kV3pUlV8oP3wG/joz8PwSNAbbtQuUnAmjElONPyTrMN +2Yqah89SqH9ygzz/UomdrKYuoTu/QEfLLtBcyBLKHrRT8ODvsp2kY9RpveCctsAR +2gmnYixj7GDdp5c6zTich1+QkVvFtrl3Zu+AWRekFAn92bwwOli14S3LgW2t4iXL +InVUqNg6l6K9d+FdHogvITQLKKMpfIfsCKPqvacpqryyaith +-----END CERTIFICATE----- diff --git a/src/lib/asiolink/testutils/ca/2eefa08b.0 b/src/lib/asiolink/testutils/ca/2eefa08b.0 new file mode 100644 index 0000000..e5762cd --- /dev/null +++ b/src/lib/asiolink/testutils/ca/2eefa08b.0 @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFMzCCAxugAwIBAgIJAJHdRK24tsELMA0GCSqGSIb3DQEBCwUAMDAxCzAJBgNV +BAYTAlVTMRAwDgYDVQQKDAdJU0MgSW5jMQ8wDQYDVQQDDAZrZWEtY2EwHhcNMjEw +MzAyMTQ1OTM3WhcNMzEwMjI4MTQ1OTM3WjAwMQswCQYDVQQGEwJVUzEQMA4GA1UE +CgwHSVNDIEluYzEPMA0GA1UEAwwGa2VhLWNhMIICIjANBgkqhkiG9w0BAQEFAAOC +Ag8AMIICCgKCAgEAvKQ/vJpJnXjZ+/LxZNfPc/QYSChSEQ8qoxh8prBYvPXyDu9O +RHOaDtd5AWusQLCI3iNYMDaJwrazj0g91jPKcxfvFZbnzFHTAZrDnmJwcTw96Ufr +P4b7PyXpUSF1/YfDf+/M3C7Wm9IJ/e704XHln/vFCw2dR/N5VOrXXJRcCd5NOES/ +ICXexe62Mv7OjUQS8u6ovejtaaMkvoV2hGSG2LXdgVOCv0U8ybRs03Xl8BVM4lFY +VO9HjnQ7O9AeGMqebvuyNAyGK9Dv+ERu65M9hB+pW//d+tVv3Dkfou+d5cOXPFXj +f6vIK+2ClxkBH4A5dhsRJ7vPI41mwXA+H0g+MzxJ8Lg0pzJuLher03RZq3pBHvEc +/jekP4u6mPrc+5J84jQ0hFwH4XIpxaKJsUiE/r1nFDiWRV27PgXMQgEbjdotxFX4 +IDBNKPtQNrybxiQHsYoZPdKcEfh8XyVT4NHrcbqN1SNf2ZIfDkm09aeDYXDdINAD ++0yZE+3YMeH4oWPpOIfW4OVzEDyfBGHyo2klTZfI5zdd54Kp4dKkzSlmIPC7Oubd +ZZGoSlZfUlWVcRkqMbUAsZ8H2sdz0l+4k8+VmyiA4EWAiO6SV5xmYSncPQIN5dE2 +PbIxjKosl9JGhajs2gxCqlK+ZA3zgoFHhG1mKGWW7ucMic8Jy4oEq1XsoI0CAwEA +AaNQME4wHQYDVR0OBBYEFA2rYljxKlzKLA/dsiAmRtO876ifMB8GA1UdIwQYMBaA +FA2rYljxKlzKLA/dsiAmRtO876ifMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggIBAGqY1cv913Hj1+FDmD5fhzW6D/SeyL/vh3bCJ6ZJmnHFXxHZbK4lufdi +v3HRJ4iSPnU40ZWVukWE+vKrZOJeBM2ip/cqv8iAiZg2NaQ56AcDgrpOfJcXOJzD +83kZI8W3dF/zk1flJM3rsi5QlwkCaxBvwA+QInejN+oncA90CphumNqblPQp1Ifm +dt+b1BIk6QJFYT0oEXnNj+5EmSu+zJ+fR5bJoZX0YTcP6YAHjdZo2qAHTeM6yX8s +bLnX97IopyPZ/xgG2kdlp2TZZdeysaICOZ16LldE7fp2OD2ifjrAqF9eezwa2ybi +wNhduRUn0Nmuw/Vy3X5l3gUekc3mS9br8ooHy6N+8pnq04gGWK3AAZLY5v7uvzmD +BC6eA0IJAvLyeiuTpBlkHZTFxk7ENaStEMFjvPiLrgquHLmJQzsgKoUtR7TGdEJ+ +DOeLAhuXjpaZ/kefSODmm09BP0d/q3iFU3gp1xGu2svUK0/BC6NQNuTIIap+L/I+ +tKq+SpPpp7laJ7M04TqAlI+EMQ4KFRDbmlWAy5uq/ynEpEJ1FFuyg6Zo+fxracTR +ytP3p/LUEYl1VQbtn9IEcrkzZNEshBglRSJ09u1nLccy3WoX03P0iQiF4oNCEPMg +PdPlvvf1t3FbcEn5AFOsMRW4U7MBPD/gvy0EVuEJ/boydq8qMzyi +-----END CERTIFICATE----- diff --git a/src/lib/asiolink/testutils/ca/71336a4d.0 b/src/lib/asiolink/testutils/ca/71336a4d.0 new file mode 100644 index 0000000..34f3392 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/71336a4d.0 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID4jCCAcqgAwIBAgIBHjANBgkqhkiG9w0BAQsFADAwMQswCQYDVQQGEwJVUzEQ +MA4GA1UECgwHSVNDIEluYzEPMA0GA1UEAwwGa2VhLWNhMB4XDTIxMDMyMjEyNTcw +MFoXDTMxMDMyMDEyNTcwMFowOTELMAkGA1UEBhMCVVMxETAPBgNVBAoMCElTQyBJ +bmMuMRcwFQYDVQQDDA5rZWEtc2VydmVyLXJhdzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAL3kUYCDdEvKHgV1WiBJ7BtQyrg8yiqi6C3QUDX6GayswyyD +Dc5Wm8bttIfNmPzlNNhHRM2q/fDrLc4JSFopihVSNDpotALop1JL/euNLY6DtINK +MgolhaSSyfFjheI+75mVFrkkt1bYZQfjC1pTmecwAsj8lj6OGdp6B2mI34kMQR/K ++RibUIiRmqYFkW3siPpvN+244pqc+ldQBnmx73/s3dS4/swznM6d/D6iBlG5gL3y +go/iCzEuOsGI4x07whuVkghv0pJHq9hwCTNnDXBD5kKgDqQJJhUNhuOm1weKwV4f +XTWQedlPUJMjxRyq5zZ+NNAf8QRjSqMk0mGy0BMCAwEAATANBgkqhkiG9w0BAQsF +AAOCAgEArTCCoN7IKQ1g9PqrCeZe0sFOPmL8tEfg83bdTnOUF1rcaK5b3E/ktuT2 +b4axEOTLo8OdwBFFdGHn7XcXAWEx9mVeEw3J1X4143FfhzwnU5ZfLvgKx3yY22ZO +9WUf0sT35aEH8jS9OzqeaGqkgNufCrmNG5TBXnTG8iFVVKqxdaI9EpoiXjLJwOi1 +5ZO3iB04saPPekVA+u0nngG+sx30hjpNu8EDl9u5f04B0cE3iZSvc4/DN4GDBjIn +eHzAwlP++mDTQ6d9K8h9BRNnqXBwdN+6CbTTB3Mw5DlvHxBSXRf9xIuhWEdiT7kQ +Ac7tTs9qsC+g56j3N526hVegbnhB9SSlO1gNWhKdWoag51TJQP38d7lrD6YhJIVi +57idCeEfvGNcrIMr7hbn6nm8q1nd8waE2dX0FMm3WCf3Nj8Zpsj8JxnQj3jQ/Q38 +bHoHVtAvc7W7tAzMHl5R7UufEqP/42lnes4DECQ5WvN+t9l5gErO4svHfeXNFGbM +nbjVxGeJeiRPGriej8dlD5Ea0WVHOETh77+5p7DdDBir/xLHSbS/QypKnTGixhwB +Zg5z8CHeepVf5Y+xhteOZwJCjxCTwW43aOEHQ0U7gHke2hNtCagwlbmLBITzJMJL +HIFvpHfNTLX1ZRU/z/3OJVEfuMRjah5BJZPGuhuJxR47hP0tLJY= +-----END CERTIFICATE----- diff --git a/src/lib/asiolink/testutils/ca/7a5b785e.0 b/src/lib/asiolink/testutils/ca/7a5b785e.0 new file mode 100644 index 0000000..29ff5e5 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/7a5b785e.0 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID3TCCAcWgAwIBAgIBCjANBgkqhkiG9w0BAQsFADAwMQswCQYDVQQGEwJVUzEQ +MA4GA1UECgwHSVNDIEluYzEPMA0GA1UEAwwGa2VhLWNhMB4XDTIxMDMwMjE1MDAz +M1oXDTMxMDIyODE1MDAzM1owNDELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0lTQyBJ +bmMxEzARBgNVBAMMCmtlYS1jbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDKbsDkElojvFhVt234GQOEVVudEp4s8KYnDQTZpsdeidrP3yY+qWfz +G1k16qMB5jXF7dRhzq4FiPbZMs5cz3BfwZDlxjWMxgixPaCrVphYLGhI8AOne8PE +l47e4Ae3Cl96dWUfQKQmGIzzHfTcJvCxUOCob5zYOCDvtjk48IxdvHi18Ab/hXyG +JKXSuqCsaXBRK7Amn8/jxMgdhds92tNxm0BiAJtsmkQm9QW8ztcoiEEgO4ViDRJS +RKaG9hVRrAe4GPisOjUzerADkPX/pchHIqmrTJ9YKhngOfDdiAZY1lkZc1cbM6zq +qTgTp1MvttSv8JEN6OMhM+bpCbaiWp4DAgMBAAEwDQYJKoZIhvcNAQELBQADggIB +AENl7hCBjAft1uC/XAO/yBkkDrTk6R21+mdJMghJ9ojFP33QvYYv0pDNeCZ/IJEK +G2ML8gFzd2YulF1qzBMuFvESRQyqJMnIWJS8FSEIKEyqj5RMTnVWjFM6V2yGhBA5 +XXAL4CVVNz/NqWV/Ebd1XB1OB/y5uz+ZowpWktHtqCKYhDzDtK600GswMOJ5UsZF +X6JtkvG86nVfuyOIK3NtMXQE/ptAgwa87hVecu7yY/u6PmRwS7YbVBsh9VplnAsQ +bLARtTGCWHL3otZaDi81dghHkHYmv1NmaubgKnFffKxJGLCtyHF0pqS7C0v7lLOo +qOhSd3qaFEU1yWpXCFlyglDnadFQs8pdWIPBngwQC2luF1N7Kppz5zzGF5MHNt+E +LuPlRAwgs8aRRPsySGYKvtCeNYAgjsbec9f0P7lMEGr+AqbZF9qNbbQQkq0dHrMH +goazCek3XtlMAYYUdmkqQ5a44XRQUu4FuTVqzCH8nqhkeHcWTwO9BHayUebxiBk8 +njDwLtHiQ8u9TjVf/35UOdqFSxra+wZJPKYbH++82KG6rbEotGp3jv0uxasgiHVL +qrD3dkQAU8zF7cllsUkRE3Gw4tDaZXkZCawiMfLiGK1FVApXkUnKilASDsaH6i3x +Ui8LM1F9vbtJnzftTa3yi0FR6Gmi5Mc+R42gpE8xCa4y +-----END CERTIFICATE----- diff --git a/src/lib/asiolink/testutils/ca/ad950210.0 b/src/lib/asiolink/testutils/ca/ad950210.0 new file mode 100644 index 0000000..2332046 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/ad950210.0 @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIECjCCAfKgAwIBAgIBHjANBgkqhkiG9w0BAQsFADAwMQswCQYDVQQGEwJVUzEQ +MA4GA1UECgwHSVNDIEluYzEPMA0GA1UEAwwGa2VhLWNhMB4XDTIxMDMwMjE1MDE0 +OVoXDTMxMDIyODE1MDE0OVowOjELMAkGA1UEBhMCVVMxETAPBgNVBAoMCElTQyBJ +bmMuMRgwFgYDVQQDDA9rZWEtc2VydmVyLWFkZHIwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQC95FGAg3RLyh4FdVogSewbUMq4PMoqougt0FA1+hmsrMMs +gw3OVpvG7bSHzZj85TTYR0TNqv3w6y3OCUhaKYoVUjQ6aLQC6KdSS/3rjS2Og7SD +SjIKJYWkksnxY4XiPu+ZlRa5JLdW2GUH4wtaU5nnMALI/JY+jhnaegdpiN+JDEEf +yvkYm1CIkZqmBZFt7Ij6bzftuOKanPpXUAZ5se9/7N3UuP7MM5zOnfw+ogZRuYC9 +8oKP4gsxLjrBiOMdO8IblZIIb9KSR6vYcAkzZw1wQ+ZCoA6kCSYVDYbjptcHisFe +H101kHnZT1CTI8Ucquc2fjTQH/EEY0qjJNJhstATAgMBAAGjJTAjMCEGA1UdEQQa +MBiHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggIBAAaf +GIHwgnSo4zo6cIfpzirVpSqjzOrsAqzSswigZdj7dwx959sgSJzZssDf/TA98iXM +YQEkBao6jPuo8fTlCF0XGCUGAfq/f6Yn1Nhkk0qUdxLrNsEjKPXjISZPaVZllZBR ++mRMKObn0l86vJ/0zGzPRxH2P5CKg9g3sT8zkg1fGIE/SNr8abZV5Cf3spYQ9PF9 +zQ2TdpgaEGGufKR6VAIJH4CVShMfvBF0qFbzMC7R/CTdSvEBXagWclBT7PqcVGlV +rK/NB6rt8W8hLQQE6bRunJmkLrmLKLVjFtPZPq5hm3jE8fnGxfzvThiZHTj+oFGw +KXcbuSvwgYuLKym648V+VDGiDWdpS2dIwQi2JeHTt7Y4P+8dqPfHY7oDy2+67J6o +ElTXvloGVNCedQtpp9gNrtil5avXrU9HCfD9avYlsn89kqYZ3Ht1GBYPyqeSZDCo +a+sffazhYPfqFdH0U7wpq6Gf8/JMSAuQmAR2UAwhjoQatqDqEJ3pAFsI3YcQOZqm +kj3/T0iYkU8YdJkxI2YgVCRRIzTKHkGMVc/iz+C0OJwFeJDuj+dj+EXXtyi3sjhL +oTQT2y01nW2TPrHqlG3/fQyPx1gKXrij+1uOZJpZcgKE7/YBGByRiUdOyRJ0E6h6 +oimhTLT6mC9wteMiRmj68z5tTC1P0H4nuOU7OqwL +-----END CERTIFICATE----- diff --git a/src/lib/asiolink/testutils/ca/doc.txt b/src/lib/asiolink/testutils/ca/doc.txt new file mode 100644 index 0000000..fc88a6f --- /dev/null +++ b/src/lib/asiolink/testutils/ca/doc.txt @@ -0,0 +1,129 @@ +Similar to doc/examples/https/nginx/kea-nginx.conf + password is keatest + Country Name is US + Organization Name is ISC Inc. + Common Name is the key name. + +Some critical details: + - recent versions of OpenSSL requires at least 2038 bit RSA + - certificate version should be 3 (enforced by Botan for leaves), + if openssl creates a version 1 add an extension + - RSA allows a simpler format than PKCS#8 for RSA private keys + but Botan and other algorithms require PKCS#8 + - some tools check the alternate subject name of the server so put + a correct value in it + +Files: + - doc.txt this file + - ext-addr-conf.cnf extension definition file to add an IP address subject + alternative name to the server certificate (IP 127.0.0.1) + - ext-conf.cnf extension definition file to add a subject alternative + name to the server certificate (DNS localhost) + - kea-ca.crt Certification Authority (CA) certificate + - kea-ca.key Certification Authority (CA) private key (password keatest) + - kea-client.crt client certificate + - kea-client.csr client PKCS#10 certificate request + - kea-client.key client private key (not encrypted) + - kea-client.p12 client PKCS#12 archive with the certificate and the private + key (required by curl on macOS or iOS when built with Secure Transport) + - kea-other.crt test client certificate (signed by another CA) + - kea-other.key test client private key (signed by another CA, not encrypted) + - kea-self.crt test client certificate (self-signed) + - kea-self.key test client private key (self-signed, not encrypted) + - kea-server-addr.crt server certificate using the 127.0.0.1 IP address + - kea-server-addr.csr server PKCS#10 certificate request using the + 127.0.0.1 IP address + - kea-server-raw.crt server certificate with no subject alternative name + - kea-server-raw.csr server PKCS#10 certificate request using no + subject alternative name + - kea-server.crt server certificate using the localhost DNS name + - kea-server.csr server PKCS#10 certificate request using the localhost + DNS name + - kea-server.key server private key (all certificates, not encrypted) + - server-addr-conf.cnf OpenSSL configuration file to add an IP address + subject alternative name (127.0.0.1 and ::1) + - server-conf.cnf OpenSSL configuration file to add a DNS subject + alternative name (localhost) + +NOTE: On some systems, the openssl pkcs8 commands require -topk8 parameter. + +Procedure to build CA, server and client files: + +1 - create a CA self signed certificate (password is keatest) + openssl genrsa -aes128 -out kea-ca.key 4096 + openssl req -new -x509 -days 3650 -key kea-ca.key -out kea-ca.crt \ + -extensions v3_ca -config server-conf.cnf + +2 - create a key for the client and convert to PKCS#8 + openssl genrsa -aes128 -out kea-client-aes.key 2048 + openssl pkcs8 -in kea-client-aes.key -out kea-client.key -nocrypt + rm kea-client-aes.key + +3 - create a certificate for the client + openssl req -new -key kea-client.key -out kea-client.csr + openssl x509 -req -days 3650 -in kea-client.csr -CA kea-ca.crt \ + -CAkey kea-ca.key -set_serial 10 -out kea-client.crt \ + -extfile /dev/null -sha256 + +4 - create a PKCS#12 bundle on macOS (password is keatest) + openssl pkcs12 -in kea-client.crt -inkey kea-client.key -export \ + -out kea-client.p12 + +5 - create a key for the server and convert to PKCS#8 (same than 2) + openssl genrsa -aes128 -out kea-server-aes.key 2048 + openssl pkcs8 -in kea-server-aes.key -out kea-server.key -nocrypt + rm kea-server-aes.key + +6 - create a certificate with a subject alternate name set to localhost + for the server + openssl req -new -key kea-server.key -out kea-server.csr \ + -config server-conf.cnf + openssl x509 -req -days 3650 -in kea-server.csr -CA kea-ca.crt \ + -CAkey kea-ca.key -set_serial 20 -out kea-server.crt \ + -extfile ext-conf.cnf -sha256 + +7 - create a certificate with a subject alternate name set to 127.0.0.1 + and ::1 for the server + openssl req -new -key kea-server.key -out kea-server-addr.csr \ + -config server-addr-conf.cnf + openssl x509 -req -days 3650 -in kea-server-addr.csr -CA kea-ca.crt \ + -CAkey kea-ca.key -set_serial 30 -out kea-server-addr.crt \ + -extfile ext-addr-conf.cnf -sha256 + +8 - use c_rehash or openssl rehash to create hashes + openssl rehash . + +Setup the control agent: kea-ctrl-agent.json sample. + +Using curl. +Note the localhost is important: using 127.0.0.1 instead can make the +subjectAltName check to fail. curl is also picky about http vs https. + +to send a command (e.g. list-commands) directly to the control agent +listening at port 8000: + +curl -D - -X POST -H Content-Type:application/json \ + -d '{ "command": "list-commands" }' http://localhost:8000 + +With the CA only (so authenticating the server only): +curl -D - -X POST -H Content-Type:application/json --cacert kea-ca.crt \ + -d '{ "command": "list-commands" }' https://localhost:8443 + +With mutual authentication using OpenSSL: +curl -D - -X POST -H Content-Type:application/json \ + --cacert kea-ca.crt --cert kea-client.crt --key kea-client.key \ + +With the mutual authentication on macOS (when the OpenSSL one fails): +curl -D - -X POST -H Content-Type:application/json \ + --cacert kea-ca.crt --cert kea-client.p12:keatest --cert-type P12 \ + -d '{ "command": "list-commands" }' https://localhost:8443 + +To the control agent: +echo | kea-shell + +With server authentication only: +echo | kea-shell --ca kea-ca.crt --port 8443 --host localhost + +With the mutual authentication: +echo | kea-shell --ca kea-ca.crt --port 8443 --host localhost \ + --cert kea-client.crt --key kea-client.key diff --git a/src/lib/asiolink/testutils/ca/ext-addr-conf.cnf b/src/lib/asiolink/testutils/ca/ext-addr-conf.cnf new file mode 100644 index 0000000..a6b78c1 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/ext-addr-conf.cnf @@ -0,0 +1 @@ +subjectAltName=IP:127.0.0.1,IP:::1 diff --git a/src/lib/asiolink/testutils/ca/ext-conf.cnf b/src/lib/asiolink/testutils/ca/ext-conf.cnf new file mode 100644 index 0000000..aafe5bd --- /dev/null +++ b/src/lib/asiolink/testutils/ca/ext-conf.cnf @@ -0,0 +1 @@ +subjectAltName=DNS:localhost diff --git a/src/lib/asiolink/testutils/ca/kea-ca.crt b/src/lib/asiolink/testutils/ca/kea-ca.crt new file mode 100644 index 0000000..e5762cd --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-ca.crt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFMzCCAxugAwIBAgIJAJHdRK24tsELMA0GCSqGSIb3DQEBCwUAMDAxCzAJBgNV +BAYTAlVTMRAwDgYDVQQKDAdJU0MgSW5jMQ8wDQYDVQQDDAZrZWEtY2EwHhcNMjEw +MzAyMTQ1OTM3WhcNMzEwMjI4MTQ1OTM3WjAwMQswCQYDVQQGEwJVUzEQMA4GA1UE +CgwHSVNDIEluYzEPMA0GA1UEAwwGa2VhLWNhMIICIjANBgkqhkiG9w0BAQEFAAOC +Ag8AMIICCgKCAgEAvKQ/vJpJnXjZ+/LxZNfPc/QYSChSEQ8qoxh8prBYvPXyDu9O +RHOaDtd5AWusQLCI3iNYMDaJwrazj0g91jPKcxfvFZbnzFHTAZrDnmJwcTw96Ufr +P4b7PyXpUSF1/YfDf+/M3C7Wm9IJ/e704XHln/vFCw2dR/N5VOrXXJRcCd5NOES/ +ICXexe62Mv7OjUQS8u6ovejtaaMkvoV2hGSG2LXdgVOCv0U8ybRs03Xl8BVM4lFY +VO9HjnQ7O9AeGMqebvuyNAyGK9Dv+ERu65M9hB+pW//d+tVv3Dkfou+d5cOXPFXj +f6vIK+2ClxkBH4A5dhsRJ7vPI41mwXA+H0g+MzxJ8Lg0pzJuLher03RZq3pBHvEc +/jekP4u6mPrc+5J84jQ0hFwH4XIpxaKJsUiE/r1nFDiWRV27PgXMQgEbjdotxFX4 +IDBNKPtQNrybxiQHsYoZPdKcEfh8XyVT4NHrcbqN1SNf2ZIfDkm09aeDYXDdINAD ++0yZE+3YMeH4oWPpOIfW4OVzEDyfBGHyo2klTZfI5zdd54Kp4dKkzSlmIPC7Oubd +ZZGoSlZfUlWVcRkqMbUAsZ8H2sdz0l+4k8+VmyiA4EWAiO6SV5xmYSncPQIN5dE2 +PbIxjKosl9JGhajs2gxCqlK+ZA3zgoFHhG1mKGWW7ucMic8Jy4oEq1XsoI0CAwEA +AaNQME4wHQYDVR0OBBYEFA2rYljxKlzKLA/dsiAmRtO876ifMB8GA1UdIwQYMBaA +FA2rYljxKlzKLA/dsiAmRtO876ifMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggIBAGqY1cv913Hj1+FDmD5fhzW6D/SeyL/vh3bCJ6ZJmnHFXxHZbK4lufdi +v3HRJ4iSPnU40ZWVukWE+vKrZOJeBM2ip/cqv8iAiZg2NaQ56AcDgrpOfJcXOJzD +83kZI8W3dF/zk1flJM3rsi5QlwkCaxBvwA+QInejN+oncA90CphumNqblPQp1Ifm +dt+b1BIk6QJFYT0oEXnNj+5EmSu+zJ+fR5bJoZX0YTcP6YAHjdZo2qAHTeM6yX8s +bLnX97IopyPZ/xgG2kdlp2TZZdeysaICOZ16LldE7fp2OD2ifjrAqF9eezwa2ybi +wNhduRUn0Nmuw/Vy3X5l3gUekc3mS9br8ooHy6N+8pnq04gGWK3AAZLY5v7uvzmD +BC6eA0IJAvLyeiuTpBlkHZTFxk7ENaStEMFjvPiLrgquHLmJQzsgKoUtR7TGdEJ+ +DOeLAhuXjpaZ/kefSODmm09BP0d/q3iFU3gp1xGu2svUK0/BC6NQNuTIIap+L/I+ +tKq+SpPpp7laJ7M04TqAlI+EMQ4KFRDbmlWAy5uq/ynEpEJ1FFuyg6Zo+fxracTR +ytP3p/LUEYl1VQbtn9IEcrkzZNEshBglRSJ09u1nLccy3WoX03P0iQiF4oNCEPMg +PdPlvvf1t3FbcEn5AFOsMRW4U7MBPD/gvy0EVuEJ/boydq8qMzyi +-----END CERTIFICATE----- diff --git a/src/lib/asiolink/testutils/ca/kea-ca.key b/src/lib/asiolink/testutils/ca/kea-ca.key new file mode 100644 index 0000000..4ac82d3 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-ca.key @@ -0,0 +1,54 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,1E4500430B45CC59A1AFA62E20D0632E + +L1O4pVdZnk9nHSyH4fkoEehRNfhE4xbYt28YMtVctjeOQQWCf6m89k/rtOlSb9c6 +82WMHWiACuWNGxcd3RLZl0dWTPZYE4xk6T3TzTk/GwkDbQRf/6hfzGcRnObVRGYq +kzBq6zXtoqFbq2jAACqCSoRlZgpLOv8hUdUcnto707iT0ebmwbNgPsxCBXjvxOYO +Pvkihpfd7QY5GD8fn14y/y/im/9sqZgpNfhEVeO//Dpo1Nvo6DasU1gTnEoOkRRK +/IBl12N4FxdiAjg16SfDw/M3/uka6ftekdr4PwD616qiUsBdKsuslp9aN82k+5RK +X3iuODmMc/42SUoSskbL5mkuroOZxihwbiKsejcmGOfVygYXuZ9a9tLHLsdKLoWO +1mmTMU4fzNpwXPor4h0yEDaortX2KwBVPnSWOMCJtwreukgt0GHfePfbd08Ojf6M +pyZZ7gVv/q573RSgQL6nipU+4Il6T+cK4Iwdui9WSFahiOKgALuhTX0eY7CmlfcR +hgNqmJhXEuXbEiQONcDA7iEAggdha4W3bm8blCj7QEBpr45fAyDSZxP/dNrIoZWC +BxbrTq+YqzLyhUOOE7THdR5qpCha5Tsoyv8n7K91v77wZjmL1poyqHbXqvWDIJni ++LAPJDd6/Z0lqXLyTV3U9FcE6cAz6kkl5J1aeWFzfWSPtdiSzMPFkaz1MUPPllHF +nyoA1R8PAD1yPj2accSIi8nBMYpOUrwMZcS+MbSW4GsbPEOqkluLgLLas/H9eohp +SdyPsSnNBmWaCAwNHGWRAyRRefeMsrjtlF2AfVMsrCIzUNiSiw0MHsZQV6zlI23i +/xyYxMn3fDmMxqJCJ8FkEHxVx5SeyzbysYmCfBsquKnfzE8JAyjmRQzdqfXHt5H9 +MEctsLiTQ+WPwWMN/6zHjuJMpJFZTfK/y0RUgTUyf02t0C4Bobx30DOx0SM4B7Rx +QQ7uwMlarE8Pg7tCDA0kC2aGCSaHo2u0qssmLVGhNKNkBVKkr7SpS4CM7dcIh+Yk +30Q4UQfCzRbS17RD1LfdUg+SPCeDFoKdh4f4FVoHXrbeEOhPJVeCjPli78nnPuZ0 +kGvndf/v+4DH40Wvt5aZj90mes6q+2Hy4GlgciELEWhMcj2QSiRISNi5UFNYRsSL +RsEhuksONQVrFnRS3n3WvQrZ8X4OLAfatlFewpR9UVvgfWXLuWLy6etDWa056wDa +4OW715YaEedSsF8WrfhRXmU/IDJ19oiQzsQiyeiKoFW3OVRyf2ngb8psUOwLbgA6 +kjcrzt77RsYKlP7TYC2hvycqnvvDhKCe6yQmd6vS1lOdBm8VZWzJCGFfoeucx4i2 +DS5ryWhU9d4VoCxFYEEsNhC8GKkrcATikhLnB8riJgt5PrJenYMBd9EsuwAo3Xaa ++95SeiAdka2XIN2dBDOJ4qAJYKhHyZF/fJpJP/1s3zGsdBN3mkY3C1C3/dYR1fan +7fK9Qx2fcZjeMTkdm91Ito7ui2LQDVjJoTEaZ0LyMh3Gz7hALuDfPeS3Eft3QXMB +Do3Tki68lvtc9DadlDQfTm84WvS4BVyOhQVQqhS2Ttq+ICGrNekPg1zyMUI2N0bo +8ulenrCKStFBqgyWq1aczcLNEDth0GWOFjLdgWUwI2pcN3tuouLHXpfKKARxxdis +Un3Dj5nhg6G2vGhTTTRdxMQeiT0Dr6Q2tD9VUNojVZwJ1c50dgZ6hlhzU5pv+1vU +krBjlx9szF2ikx2pUp8RHDAziKkv17zXDjvEJpE/pvYWHBfBPoQr5NPaPGYnbFIX +qaLYtWOAFlL3BI1XSO/32nYee0+WjnKMr4IOvXJfnaa94S+wU6pJEbTGHP+1aGNS +wsslmcfRDmmeblGd40Bo4ENCc93KxBf3V7g7/JnSUZO39TyfvMnyy1E3JC6fu/A4 +VvnlnFM+6ZjdhkiZ4RJqd2rc2AhA6HhOslJSa0kPRc6UQQqAci+7YHZBc/PELhpD +LpFbBXbqyi1jNQNodhhJtkD8VkvYHOisqzHFTITZp5epK8mjLkBhIW2VUVZ+dDK+ +3kFrKB+CaEvE1OBAlDYeVxMAvT1rmyjT04mqPRnp0G57+5VQQFYrKfVevDddLIt2 +tQphIcgZYAHTU+2otlPAOXqgPJWRoKNTw6Rtc6dELrAOE/kDFqZ4VKRnXRNFmxj3 +NSC8zapuNmkGQTo8CHzJuRI8sfNHjcDrMELHV1Fe8XSoqdovV2X+Xa/fesCaYfrp +6506uFGZSR7SrMdT5MoXGri1IEvGXkGI30UDq5QTEzHiyyYgC7kZFn3E/zREbA0y +/WahS8zICLsEK2ZknSv3q6e9aONokNbYu7PqvQtW5IPGrjdZxuQDtRXEYafiDLKT +c3h9eE8OKk5Si49TRjsYbuR4+BBw9N0R0RIfs5TIDkkGeCu0M4yFPKQVhCN98OAk +h0L+ZhQJZfbDE7QNBuvmRBNcpJYe7JTXl2/p6JjoxeyZTgShk81BiOmMCaWavKB+ +gIqy4X39y+J+AiYMiKy/+B5gtNaZaE9hka7RH2tV5nkiTBilZ6v5N1A4V4Q0PRFT +HZAXgnUwI0HcIRfkqxlF3gXMzhG1+K2wxS9uVn5K0E27xNeswr+ksfLJsyWz+gdT +/ZFgGyErUY6CLmYzmW+WfQox+qd9pd1TMISNuBWXrdoKkX8iFjj8SWyPcZvqMUkx +lo8RVzb/6ugSTcbCQGpf+6H8ZuOe9hZwD9tKBh6XZbC5KtBQ8TtSnrmsk9ufIzn8 +ACrJFTVOG4u/g/xn1j3MY4NIaLA77YSCed+TzOXBPmG+LrJM67n1tMtGWEPoOnGi +6pzJpF5cxsF4i0QoqdYFThqMb6mHtaVPsjjIpdzEXmYyQENLQECERE6lYlz9ZVkS +NsOR3KMOxXZQ+iWmqCptazz0hVVmEBFisg6K6WuQR3BpXcf8N9UP7xUnStlUUaQ7 +G5nf6BZl3AIxZPay/NoM87n4I4lplPaQwyK/ReMztu78OQFyx9mC1BGOHxVtF6hO +W+POZqc7ugCXiY8A08vSv5yt8paWDnU+hHXnEo04Hw0ex2KNOOZeL0Eg+idJTZe0 +/0yl0olct0HUgSyhU3wm0uWiHwulreoa3tNL+a4Xt7k5L2e5XcvAh3T2mgxzDq5q +-----END RSA PRIVATE KEY----- diff --git a/src/lib/asiolink/testutils/ca/kea-client.crt b/src/lib/asiolink/testutils/ca/kea-client.crt new file mode 100644 index 0000000..29ff5e5 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-client.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID3TCCAcWgAwIBAgIBCjANBgkqhkiG9w0BAQsFADAwMQswCQYDVQQGEwJVUzEQ +MA4GA1UECgwHSVNDIEluYzEPMA0GA1UEAwwGa2VhLWNhMB4XDTIxMDMwMjE1MDAz +M1oXDTMxMDIyODE1MDAzM1owNDELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0lTQyBJ +bmMxEzARBgNVBAMMCmtlYS1jbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDKbsDkElojvFhVt234GQOEVVudEp4s8KYnDQTZpsdeidrP3yY+qWfz +G1k16qMB5jXF7dRhzq4FiPbZMs5cz3BfwZDlxjWMxgixPaCrVphYLGhI8AOne8PE +l47e4Ae3Cl96dWUfQKQmGIzzHfTcJvCxUOCob5zYOCDvtjk48IxdvHi18Ab/hXyG +JKXSuqCsaXBRK7Amn8/jxMgdhds92tNxm0BiAJtsmkQm9QW8ztcoiEEgO4ViDRJS +RKaG9hVRrAe4GPisOjUzerADkPX/pchHIqmrTJ9YKhngOfDdiAZY1lkZc1cbM6zq +qTgTp1MvttSv8JEN6OMhM+bpCbaiWp4DAgMBAAEwDQYJKoZIhvcNAQELBQADggIB +AENl7hCBjAft1uC/XAO/yBkkDrTk6R21+mdJMghJ9ojFP33QvYYv0pDNeCZ/IJEK +G2ML8gFzd2YulF1qzBMuFvESRQyqJMnIWJS8FSEIKEyqj5RMTnVWjFM6V2yGhBA5 +XXAL4CVVNz/NqWV/Ebd1XB1OB/y5uz+ZowpWktHtqCKYhDzDtK600GswMOJ5UsZF +X6JtkvG86nVfuyOIK3NtMXQE/ptAgwa87hVecu7yY/u6PmRwS7YbVBsh9VplnAsQ +bLARtTGCWHL3otZaDi81dghHkHYmv1NmaubgKnFffKxJGLCtyHF0pqS7C0v7lLOo +qOhSd3qaFEU1yWpXCFlyglDnadFQs8pdWIPBngwQC2luF1N7Kppz5zzGF5MHNt+E +LuPlRAwgs8aRRPsySGYKvtCeNYAgjsbec9f0P7lMEGr+AqbZF9qNbbQQkq0dHrMH +goazCek3XtlMAYYUdmkqQ5a44XRQUu4FuTVqzCH8nqhkeHcWTwO9BHayUebxiBk8 +njDwLtHiQ8u9TjVf/35UOdqFSxra+wZJPKYbH++82KG6rbEotGp3jv0uxasgiHVL +qrD3dkQAU8zF7cllsUkRE3Gw4tDaZXkZCawiMfLiGK1FVApXkUnKilASDsaH6i3x +Ui8LM1F9vbtJnzftTa3yi0FR6Gmi5Mc+R42gpE8xCa4y +-----END CERTIFICATE----- diff --git a/src/lib/asiolink/testutils/ca/kea-client.csr b/src/lib/asiolink/testutils/ca/kea-client.csr new file mode 100644 index 0000000..e607360 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-client.csr @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICeTCCAWECAQAwNDELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0lTQyBJbmMxEzAR +BgNVBAMMCmtlYS1jbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDKbsDkElojvFhVt234GQOEVVudEp4s8KYnDQTZpsdeidrP3yY+qWfzG1k16qMB +5jXF7dRhzq4FiPbZMs5cz3BfwZDlxjWMxgixPaCrVphYLGhI8AOne8PEl47e4Ae3 +Cl96dWUfQKQmGIzzHfTcJvCxUOCob5zYOCDvtjk48IxdvHi18Ab/hXyGJKXSuqCs +aXBRK7Amn8/jxMgdhds92tNxm0BiAJtsmkQm9QW8ztcoiEEgO4ViDRJSRKaG9hVR +rAe4GPisOjUzerADkPX/pchHIqmrTJ9YKhngOfDdiAZY1lkZc1cbM6zqqTgTp1Mv +ttSv8JEN6OMhM+bpCbaiWp4DAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAs7Ed +zY2f2BN33Jd2/XAe3Vl/Jm7JgLN7GnvwzdoM/KewsTsSo0wrgqBU2r36F+W2+/T6 +rN8C0SseFfaURd3CQc66UcGzp4+FKxWIS9loO4P43t6MjBUQ/RiW3IQUAbkMIL52 +CG1HiyyOp7GNtXb861CCu25t82oXeW7WWvWJxaKeAk/hkr7lrVxCcU7XkVY6sDU0 +t4fP3W31p5ZkLUK4qELiZ3iJZLnf/5xaXgJpVlS3E4DUe8tyl3TjayYxroyRj+TT +D0LWwE65QGygJM2cZrraIvue5kVan4C8XZvO/VvZoakWH/ZkGN8Pis33r8oEfrQL +SyGt7oTSRYob5MTWmA== +-----END CERTIFICATE REQUEST----- diff --git a/src/lib/asiolink/testutils/ca/kea-client.key b/src/lib/asiolink/testutils/ca/kea-client.key new file mode 100644 index 0000000..a8768b3 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDKbsDkElojvFhV +t234GQOEVVudEp4s8KYnDQTZpsdeidrP3yY+qWfzG1k16qMB5jXF7dRhzq4FiPbZ +Ms5cz3BfwZDlxjWMxgixPaCrVphYLGhI8AOne8PEl47e4Ae3Cl96dWUfQKQmGIzz +HfTcJvCxUOCob5zYOCDvtjk48IxdvHi18Ab/hXyGJKXSuqCsaXBRK7Amn8/jxMgd +hds92tNxm0BiAJtsmkQm9QW8ztcoiEEgO4ViDRJSRKaG9hVRrAe4GPisOjUzerAD +kPX/pchHIqmrTJ9YKhngOfDdiAZY1lkZc1cbM6zqqTgTp1MvttSv8JEN6OMhM+bp +CbaiWp4DAgMBAAECggEBAKJP05ILtQLaTenMvfwj8lH1LxPuja1y94ZwRedOdqUy +26O5RS0RICwpTYqRrEolkBA39gbGdXoyq9rTheuc2Hmu9sOF/gH195pF08IOGPD6 +ClQRPpzX+8xxyTijYQw+4PeLkZ1Rc0yoeruk1WSARJWoR7pGY/hqaN5Lue4R0jqF +KoVBrsR6ULsLscgjHP+OmIdFiTamnmjfDHcQTLKFH4inf5T0Q6UZnKq9qxoZ/o9i +AELalOOK1U/v4CvGiQFPHX9V82ZWA5P4D7aSSqDgR7eyCYEaPLRpRq2v9A5IQQuc +nD1wdkshqmSUOj5xL/KlJaIYBIZS8LKOYGZUOD8MMMECgYEA8VcBHU3CZU2zmZTk +sD2SCzw/P93gsA5rcKtQQWMFIyt8CQHcl7zTHReaG3ZAhFU9DY0WSUzTtqpOhYy3 +E9KgugaWi+BMQC1zItadGsA2WF3RXAbSbrZiVGQitFxLmbXJL4QoRMIRaewagKt1 +hsF2kolWg13inEAddiCOXMos8fkCgYEA1rq3PRGI/oYdTvI41HTJcRiYuGeJacT0 +D7lQQqMS3hk6jisPGhwFlIQ0/Ax/vnvpW/eRO4vhpKQE8DkPEGyM6r8JPejcWeDZ +pEs0vfuCVitUsCw0g+z9hVa4slnZ9clkxsY9tHZJWFdTxi9Bpjezo6XN9DTtl3hP +lNSegykMDtsCgYAnnrfxHp3mUZ5FfVsZz9HVBFwB2SQU4xkiUw2G3oGuZ2oidGrJ +gldKNGC5V216DCBMxDe/atxq5YSkihhYKcD3KTO33OfHtW5sbr018g457ZT8PaZ4 +RHraDeJgp7JFlsFjipetygpf0EH9k6hkqggUQHWydUxJiIENroSQmSRNyQKBgF5T +dS0BZ/GPDo7gfrBtgRQKXwQaj1WELEY//I7ZPe+Mm5laNu8cQiNElFXoU7Fkk1VQ +Al9rCjsdxgGUvxZS6PAx7ShiA3IEAPdYBhoywsWBkVk2gfc2AwQw3T+TktiSmI9t +BCwjDgMdkXJszeTrcSFBM6DEI163fhX99IffXymjAoGAf9B0v+NIxRXgMac+1rLN +MSzOOA2yq3tI+Ra8q0D4r4ShfauWll/rlEgx6L0FrAdTYfit8I3dBOqKYe3b/E0r +IKjAX5rh9Es/PxsOo6qJYw9l4P4+xxZKsqqvdMNQ1+21ZC90TnHWdy3bRPh1D0Vj +XDwyByyi4FuaEWhZgNA5+44= +-----END PRIVATE KEY----- diff --git a/src/lib/asiolink/testutils/ca/kea-client.p12 b/src/lib/asiolink/testutils/ca/kea-client.p12 Binary files differnew file mode 100644 index 0000000..baf4420 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-client.p12 diff --git a/src/lib/asiolink/testutils/ca/kea-other.crt b/src/lib/asiolink/testutils/ca/kea-other.crt new file mode 100644 index 0000000..bdcc9bd --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-other.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID4DCCAcigAwIBAgIBFDANBgkqhkiG9w0BAQsFADAzMQswCQYDVQQGEwJVUzER +MA8GA1UECgwISVNDIEluYy4xETAPBgNVBAMMCG90aGVyLWNhMB4XDTIxMDMwMjE0 +NTI0OFoXDTMxMDIyODE0NTI0OFowNDELMAkGA1UEBhMCVVMxETAPBgNVBAoMCElT +QyBJbmMuMRIwEAYDVQQDDAlrZWEtb3RoZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDAoEENWQ6tl6aaRMn+yaNUKTBIIWpVoy5+uGsBdZW++fEvw4xm +leGD+bwyHZFEsHPos/v7zWUNFaX2aWD0H+Hk4l2WTFigWO3utPoXDzDOjfQmglKG ++R08p3giURrJzUKWwe/RRJBs7qXdcD9yNXVOb2JWp4Cxk1iPj7zTS/LGsFr7F4/k +2nlH3EuqvB3GBEXHa/sA55xigMyvqVnVb4rNh+PjGL8l5SZzSnrbdoIEtKw/LVbB +CAVrQsgcADNqjR7ILbqeIqg1Td11QvQzB7f/U5dQoQPzq3j4ow1zOiaSokZE7UcU +CUNfjRv5E2lW+mmyM7nkgyE9LqUJ/3udIh1vAgMBAAEwDQYJKoZIhvcNAQELBQAD +ggIBAMYcxVfoCIn+NPlsoRB2m5vAOuJTuBNigf8Fm0HYougE2W+p50+5USx2BCM8 +M1Cet+8X0dktHbRdDL5aZrRbYnz/OENBD4tKuWMQoP/qzafRiKSkDckxYM6AR4T+ +fzPgLjUde2NE1cDeRlJUmereRXiD2qefEFH55StLl8YnnciAMGTRjwBuLiReF+qE +noaD8ZIKZ5pBMzoxyOe+39tLJkzhESdZ8gJZRXGm+ickAlP96w8z8TlQiWHG3Caw +kM7SZSyVYdyfiF32J6A7hwlG3qud83GcunfrjOurWBe1lv51pb/OFGe6wlRD/pcS +UcKZ07KXXYMXV40O6A5Dv0yJB8ocKhOkfU5MvotAAm2GL2ZXizfmEAz23X9I8830 +B5ggVxgp/bO/exC1sBJjUgF4qVPByE1MdDDWYvPKT8cYg5j8pD9rDn7WGVAmgCk9 +59lEI0HBP33ulBRoxrOQ7kV3pUlV8oP3wG/joz8PwSNAbbtQuUnAmjElONPyTrMN +2Yqah89SqH9ygzz/UomdrKYuoTu/QEfLLtBcyBLKHrRT8ODvsp2kY9RpveCctsAR +2gmnYixj7GDdp5c6zTich1+QkVvFtrl3Zu+AWRekFAn92bwwOli14S3LgW2t4iXL +InVUqNg6l6K9d+FdHogvITQLKKMpfIfsCKPqvacpqryyaith +-----END CERTIFICATE----- diff --git a/src/lib/asiolink/testutils/ca/kea-other.key b/src/lib/asiolink/testutils/ca/kea-other.key new file mode 100644 index 0000000..212dbe6 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-other.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDAoEENWQ6tl6aa +RMn+yaNUKTBIIWpVoy5+uGsBdZW++fEvw4xmleGD+bwyHZFEsHPos/v7zWUNFaX2 +aWD0H+Hk4l2WTFigWO3utPoXDzDOjfQmglKG+R08p3giURrJzUKWwe/RRJBs7qXd +cD9yNXVOb2JWp4Cxk1iPj7zTS/LGsFr7F4/k2nlH3EuqvB3GBEXHa/sA55xigMyv +qVnVb4rNh+PjGL8l5SZzSnrbdoIEtKw/LVbBCAVrQsgcADNqjR7ILbqeIqg1Td11 +QvQzB7f/U5dQoQPzq3j4ow1zOiaSokZE7UcUCUNfjRv5E2lW+mmyM7nkgyE9LqUJ +/3udIh1vAgMBAAECggEAYUpPsPszM7Bt4GswDvUu/loTXcsq1vglirGAsmr+aEf7 +bqF473NyRONFD5bpgWUSFg2aDxMtn884VN3ir0rPIHjIxhnnhY2FF1TnH/B3OUxv +bWfTYQK/ppv7THHkctqucFCh3POhcrOSqOaB1SB1EFmntJbDpG0EhPYXbC1nALy/ +1NstWOBhqcr6xTU2VUnzqSDxa49pJPuSZpEj4G0VdqmrlI/8wjl0mSLdY2VqXMlQ +hSJG2aqRKiKe0kgQvUnHVEaic9YNNC2arxX/zp5c4USioxekrmCkoAKXpqbFswGS +zcFWJQer+nvCkIX9zhr+3bFn4/dkDK4GD49Atw0kKQKBgQDew/iEDKgQ2obQFVW2 +2WheTSuE3sDsBdnod3YRZgIn7Vf7QyaNqzoOec8QgE3oMu2EdKKyLDyN8/3IBCVq +zUIeWJLN3CCt6DNK348hEJc3Fc2hibv/Ea4TQ4rZyNxiYJfV86ndYvKMQSMmHz9l +DKzrjB/x1LaBBuO4qLUzgK7vwwKBgQDdXSwJo9MzCeDvqQtUxNbMXKJOfEqFusaW +/NidbS9MnphImcsQobMfcN/h74r2aH+xGHBk5pPecfU/qK48dzEWncrWppRTfJ9V +eU5VYlnkvI/mHDKJqoEZEycRqlUCRvlu8DGqEOHelW7ZCjcGD4DckRDmZCaeD78O +xKkiyu2M5QKBgBi2GI1dcg9cjnPqyfVcrK05VkiJBVGpXIDjL5/Cdx7Cv23KBy7T +/b65WHT2Jq5JZ/u3jIzDR3xfwpk7jIMKffkrzi0z7BQenAIERrZeRsf/jS4MP2SO +K4dLiM2b8IahPHapbwB2B33zg9iowrmM7Gm8w5ZqCEzL3NsRK/ion79NAoGAYEtw +pbzjWfd5Jyg1Kqn5+qptXJEK5gOq8fGJ1WmywrTW7/Ye9NwyjIHQknteyvQIYCSO +eAYp2wFdu1SIfvsmmn0HyLpsGalDsq3zWodPLYatXl9zyJkoUZ0YSMH8+uGfDhhk +smNnrij5MGcWKofB+bENVfvJJMcayLTaEq2OCtUCgYEAoXRn4p1P3kdtLXYnTAsn +gsDIVEzFAnb4DXuvK3ozA3mUgIwGHQiiwcN+mCHml3hkMnmkDSx4bakbAUuQ+EIz +kS66aKRTvXdIZujGSNLyjN9SQkaJfKDYLW32WmxTowoLh5MyDpaVOMFegVzex5c/ +zeY0qPLtGf+qogMcoftZeR4= +-----END PRIVATE KEY----- diff --git a/src/lib/asiolink/testutils/ca/kea-self.crt b/src/lib/asiolink/testutils/ca/kea-self.crt new file mode 100644 index 0000000..5d7534d --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-self.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDHDCCAgSgAwIBAgIUe1AyLcAeSfKwCZNZLFTRkWMyOJQwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxETAPBgNVBAoM +CElTQyBJbmMuMREwDwYDVQQDDAhrZWEtc2VsZjAeFw0yMTAzMDIxNDQ3MDdaFw0z +MTAyMjgxNDQ3MDdaMEgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRl +MREwDwYDVQQKDAhJU0MgSW5jLjERMA8GA1UEAwwIa2VhLXNlbGYwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAoEENWQ6tl6aaRMn+yaNUKTBIIWpVoy5+ +uGsBdZW++fEvw4xmleGD+bwyHZFEsHPos/v7zWUNFaX2aWD0H+Hk4l2WTFigWO3u +tPoXDzDOjfQmglKG+R08p3giURrJzUKWwe/RRJBs7qXdcD9yNXVOb2JWp4Cxk1iP +j7zTS/LGsFr7F4/k2nlH3EuqvB3GBEXHa/sA55xigMyvqVnVb4rNh+PjGL8l5SZz +SnrbdoIEtKw/LVbBCAVrQsgcADNqjR7ILbqeIqg1Td11QvQzB7f/U5dQoQPzq3j4 +ow1zOiaSokZE7UcUCUNfjRv5E2lW+mmyM7nkgyE9LqUJ/3udIh1vAgMBAAEwDQYJ +KoZIhvcNAQELBQADggEBAHWFX55xUt1Opqtji+I2XvBrcexleSAME+irKwExe+tY +laFEWb1eWyzFHiuOSuNLjcXt1PkUYZ0lYUg17cDj5urpAy+F07uCRQWTXBY8W53H +IppYl4KjN3w4e5DSyDfiTv99MT8xVKJk+rVu75lQ0kgg68fZR6yK82SLjBQmjV2A +OcSqHNHtnBU5RcdlZ+E05M1Vo1jHzxHpybkgNxjvmUgBRc9ieLbgSFRZji0nNmhA +TSZ0DjRce6eyDI+OoEFJL0wXMl0ZOijeuCJr4C45h3TyreU2COC1GaoIeNwmGSIb +mw0j+XR4rKHcgkUQ7L2DfwOjGFG7IeT+k0QdyeM2NU4= +-----END CERTIFICATE----- diff --git a/src/lib/asiolink/testutils/ca/kea-self.key b/src/lib/asiolink/testutils/ca/kea-self.key new file mode 100644 index 0000000..212dbe6 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-self.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDAoEENWQ6tl6aa +RMn+yaNUKTBIIWpVoy5+uGsBdZW++fEvw4xmleGD+bwyHZFEsHPos/v7zWUNFaX2 +aWD0H+Hk4l2WTFigWO3utPoXDzDOjfQmglKG+R08p3giURrJzUKWwe/RRJBs7qXd +cD9yNXVOb2JWp4Cxk1iPj7zTS/LGsFr7F4/k2nlH3EuqvB3GBEXHa/sA55xigMyv +qVnVb4rNh+PjGL8l5SZzSnrbdoIEtKw/LVbBCAVrQsgcADNqjR7ILbqeIqg1Td11 +QvQzB7f/U5dQoQPzq3j4ow1zOiaSokZE7UcUCUNfjRv5E2lW+mmyM7nkgyE9LqUJ +/3udIh1vAgMBAAECggEAYUpPsPszM7Bt4GswDvUu/loTXcsq1vglirGAsmr+aEf7 +bqF473NyRONFD5bpgWUSFg2aDxMtn884VN3ir0rPIHjIxhnnhY2FF1TnH/B3OUxv +bWfTYQK/ppv7THHkctqucFCh3POhcrOSqOaB1SB1EFmntJbDpG0EhPYXbC1nALy/ +1NstWOBhqcr6xTU2VUnzqSDxa49pJPuSZpEj4G0VdqmrlI/8wjl0mSLdY2VqXMlQ +hSJG2aqRKiKe0kgQvUnHVEaic9YNNC2arxX/zp5c4USioxekrmCkoAKXpqbFswGS +zcFWJQer+nvCkIX9zhr+3bFn4/dkDK4GD49Atw0kKQKBgQDew/iEDKgQ2obQFVW2 +2WheTSuE3sDsBdnod3YRZgIn7Vf7QyaNqzoOec8QgE3oMu2EdKKyLDyN8/3IBCVq +zUIeWJLN3CCt6DNK348hEJc3Fc2hibv/Ea4TQ4rZyNxiYJfV86ndYvKMQSMmHz9l +DKzrjB/x1LaBBuO4qLUzgK7vwwKBgQDdXSwJo9MzCeDvqQtUxNbMXKJOfEqFusaW +/NidbS9MnphImcsQobMfcN/h74r2aH+xGHBk5pPecfU/qK48dzEWncrWppRTfJ9V +eU5VYlnkvI/mHDKJqoEZEycRqlUCRvlu8DGqEOHelW7ZCjcGD4DckRDmZCaeD78O +xKkiyu2M5QKBgBi2GI1dcg9cjnPqyfVcrK05VkiJBVGpXIDjL5/Cdx7Cv23KBy7T +/b65WHT2Jq5JZ/u3jIzDR3xfwpk7jIMKffkrzi0z7BQenAIERrZeRsf/jS4MP2SO +K4dLiM2b8IahPHapbwB2B33zg9iowrmM7Gm8w5ZqCEzL3NsRK/ion79NAoGAYEtw +pbzjWfd5Jyg1Kqn5+qptXJEK5gOq8fGJ1WmywrTW7/Ye9NwyjIHQknteyvQIYCSO +eAYp2wFdu1SIfvsmmn0HyLpsGalDsq3zWodPLYatXl9zyJkoUZ0YSMH8+uGfDhhk +smNnrij5MGcWKofB+bENVfvJJMcayLTaEq2OCtUCgYEAoXRn4p1P3kdtLXYnTAsn +gsDIVEzFAnb4DXuvK3ozA3mUgIwGHQiiwcN+mCHml3hkMnmkDSx4bakbAUuQ+EIz +kS66aKRTvXdIZujGSNLyjN9SQkaJfKDYLW32WmxTowoLh5MyDpaVOMFegVzex5c/ +zeY0qPLtGf+qogMcoftZeR4= +-----END PRIVATE KEY----- diff --git a/src/lib/asiolink/testutils/ca/kea-server-addr.crt b/src/lib/asiolink/testutils/ca/kea-server-addr.crt new file mode 100644 index 0000000..2332046 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-server-addr.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIECjCCAfKgAwIBAgIBHjANBgkqhkiG9w0BAQsFADAwMQswCQYDVQQGEwJVUzEQ +MA4GA1UECgwHSVNDIEluYzEPMA0GA1UEAwwGa2VhLWNhMB4XDTIxMDMwMjE1MDE0 +OVoXDTMxMDIyODE1MDE0OVowOjELMAkGA1UEBhMCVVMxETAPBgNVBAoMCElTQyBJ +bmMuMRgwFgYDVQQDDA9rZWEtc2VydmVyLWFkZHIwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQC95FGAg3RLyh4FdVogSewbUMq4PMoqougt0FA1+hmsrMMs +gw3OVpvG7bSHzZj85TTYR0TNqv3w6y3OCUhaKYoVUjQ6aLQC6KdSS/3rjS2Og7SD +SjIKJYWkksnxY4XiPu+ZlRa5JLdW2GUH4wtaU5nnMALI/JY+jhnaegdpiN+JDEEf +yvkYm1CIkZqmBZFt7Ij6bzftuOKanPpXUAZ5se9/7N3UuP7MM5zOnfw+ogZRuYC9 +8oKP4gsxLjrBiOMdO8IblZIIb9KSR6vYcAkzZw1wQ+ZCoA6kCSYVDYbjptcHisFe +H101kHnZT1CTI8Ucquc2fjTQH/EEY0qjJNJhstATAgMBAAGjJTAjMCEGA1UdEQQa +MBiHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggIBAAaf +GIHwgnSo4zo6cIfpzirVpSqjzOrsAqzSswigZdj7dwx959sgSJzZssDf/TA98iXM +YQEkBao6jPuo8fTlCF0XGCUGAfq/f6Yn1Nhkk0qUdxLrNsEjKPXjISZPaVZllZBR ++mRMKObn0l86vJ/0zGzPRxH2P5CKg9g3sT8zkg1fGIE/SNr8abZV5Cf3spYQ9PF9 +zQ2TdpgaEGGufKR6VAIJH4CVShMfvBF0qFbzMC7R/CTdSvEBXagWclBT7PqcVGlV +rK/NB6rt8W8hLQQE6bRunJmkLrmLKLVjFtPZPq5hm3jE8fnGxfzvThiZHTj+oFGw +KXcbuSvwgYuLKym648V+VDGiDWdpS2dIwQi2JeHTt7Y4P+8dqPfHY7oDy2+67J6o +ElTXvloGVNCedQtpp9gNrtil5avXrU9HCfD9avYlsn89kqYZ3Ht1GBYPyqeSZDCo +a+sffazhYPfqFdH0U7wpq6Gf8/JMSAuQmAR2UAwhjoQatqDqEJ3pAFsI3YcQOZqm +kj3/T0iYkU8YdJkxI2YgVCRRIzTKHkGMVc/iz+C0OJwFeJDuj+dj+EXXtyi3sjhL +oTQT2y01nW2TPrHqlG3/fQyPx1gKXrij+1uOZJpZcgKE7/YBGByRiUdOyRJ0E6h6 +oimhTLT6mC9wteMiRmj68z5tTC1P0H4nuOU7OqwL +-----END CERTIFICATE----- diff --git a/src/lib/asiolink/testutils/ca/kea-server-addr.csr b/src/lib/asiolink/testutils/ca/kea-server-addr.csr new file mode 100644 index 0000000..d6ba063 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-server-addr.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICyzCCAbMCAQAwOjELMAkGA1UEBhMCVVMxETAPBgNVBAoMCElTQyBJbmMuMRgw +FgYDVQQDDA9rZWEtc2VydmVyLWFkZHIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQC95FGAg3RLyh4FdVogSewbUMq4PMoqougt0FA1+hmsrMMsgw3OVpvG +7bSHzZj85TTYR0TNqv3w6y3OCUhaKYoVUjQ6aLQC6KdSS/3rjS2Og7SDSjIKJYWk +ksnxY4XiPu+ZlRa5JLdW2GUH4wtaU5nnMALI/JY+jhnaegdpiN+JDEEfyvkYm1CI +kZqmBZFt7Ij6bzftuOKanPpXUAZ5se9/7N3UuP7MM5zOnfw+ogZRuYC98oKP4gsx +LjrBiOMdO8IblZIIb9KSR6vYcAkzZw1wQ+ZCoA6kCSYVDYbjptcHisFeH101kHnZ +T1CTI8Ucquc2fjTQH/EEY0qjJNJhstATAgMBAAGgTDBKBgkqhkiG9w0BCQ4xPTA7 +MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMCEGA1UdEQQaMBiHBH8AAAGHEAAAAAAA +AAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggEBADlAkM7Vt3acIbgx9uz/nzEU +biTUETzQnCU/mJZU+F8nuZtIlH9TAej4oT0J1uBuneGdkgGSm3lONUNxYJ7Uz8dm +wyudv4cpvtacAzPqZNb0aapX3qD9/lUbXfReoOUmt+asdmF2ncmn3l465ercxtUg +zhbU5uQUEk7C7f4OZQ3b08yG+tblFhpO7Xm4JD6nJk9iQ6gB4WBUDSr7mdm7PMmV +T8xesD7lDZVjSdXql9p/6YxJJR3360jycLXeTQbom6gfvsfQcs91yfGHRel2yoDx +ZBcmjfkYK7mwagpB/QCsZDuC4cxZyFM7lV/ukIysviW7WzrtT9mvfTEcTqmPsPU= +-----END CERTIFICATE REQUEST----- diff --git a/src/lib/asiolink/testutils/ca/kea-server-raw.crt b/src/lib/asiolink/testutils/ca/kea-server-raw.crt new file mode 100644 index 0000000..34f3392 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-server-raw.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID4jCCAcqgAwIBAgIBHjANBgkqhkiG9w0BAQsFADAwMQswCQYDVQQGEwJVUzEQ +MA4GA1UECgwHSVNDIEluYzEPMA0GA1UEAwwGa2VhLWNhMB4XDTIxMDMyMjEyNTcw +MFoXDTMxMDMyMDEyNTcwMFowOTELMAkGA1UEBhMCVVMxETAPBgNVBAoMCElTQyBJ +bmMuMRcwFQYDVQQDDA5rZWEtc2VydmVyLXJhdzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAL3kUYCDdEvKHgV1WiBJ7BtQyrg8yiqi6C3QUDX6GayswyyD +Dc5Wm8bttIfNmPzlNNhHRM2q/fDrLc4JSFopihVSNDpotALop1JL/euNLY6DtINK +MgolhaSSyfFjheI+75mVFrkkt1bYZQfjC1pTmecwAsj8lj6OGdp6B2mI34kMQR/K ++RibUIiRmqYFkW3siPpvN+244pqc+ldQBnmx73/s3dS4/swznM6d/D6iBlG5gL3y +go/iCzEuOsGI4x07whuVkghv0pJHq9hwCTNnDXBD5kKgDqQJJhUNhuOm1weKwV4f +XTWQedlPUJMjxRyq5zZ+NNAf8QRjSqMk0mGy0BMCAwEAATANBgkqhkiG9w0BAQsF +AAOCAgEArTCCoN7IKQ1g9PqrCeZe0sFOPmL8tEfg83bdTnOUF1rcaK5b3E/ktuT2 +b4axEOTLo8OdwBFFdGHn7XcXAWEx9mVeEw3J1X4143FfhzwnU5ZfLvgKx3yY22ZO +9WUf0sT35aEH8jS9OzqeaGqkgNufCrmNG5TBXnTG8iFVVKqxdaI9EpoiXjLJwOi1 +5ZO3iB04saPPekVA+u0nngG+sx30hjpNu8EDl9u5f04B0cE3iZSvc4/DN4GDBjIn +eHzAwlP++mDTQ6d9K8h9BRNnqXBwdN+6CbTTB3Mw5DlvHxBSXRf9xIuhWEdiT7kQ +Ac7tTs9qsC+g56j3N526hVegbnhB9SSlO1gNWhKdWoag51TJQP38d7lrD6YhJIVi +57idCeEfvGNcrIMr7hbn6nm8q1nd8waE2dX0FMm3WCf3Nj8Zpsj8JxnQj3jQ/Q38 +bHoHVtAvc7W7tAzMHl5R7UufEqP/42lnes4DECQ5WvN+t9l5gErO4svHfeXNFGbM +nbjVxGeJeiRPGriej8dlD5Ea0WVHOETh77+5p7DdDBir/xLHSbS/QypKnTGixhwB +Zg5z8CHeepVf5Y+xhteOZwJCjxCTwW43aOEHQ0U7gHke2hNtCagwlbmLBITzJMJL +HIFvpHfNTLX1ZRU/z/3OJVEfuMRjah5BJZPGuhuJxR47hP0tLJY= +-----END CERTIFICATE----- diff --git a/src/lib/asiolink/testutils/ca/kea-server-raw.csr b/src/lib/asiolink/testutils/ca/kea-server-raw.csr new file mode 100644 index 0000000..b0ab32d --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-server-raw.csr @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICfjCCAWYCAQAwOTELMAkGA1UEBhMCVVMxETAPBgNVBAoMCElTQyBJbmMuMRcw +FQYDVQQDDA5rZWEtc2VydmVyLXJhdzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAL3kUYCDdEvKHgV1WiBJ7BtQyrg8yiqi6C3QUDX6GayswyyDDc5Wm8bt +tIfNmPzlNNhHRM2q/fDrLc4JSFopihVSNDpotALop1JL/euNLY6DtINKMgolhaSS +yfFjheI+75mVFrkkt1bYZQfjC1pTmecwAsj8lj6OGdp6B2mI34kMQR/K+RibUIiR +mqYFkW3siPpvN+244pqc+ldQBnmx73/s3dS4/swznM6d/D6iBlG5gL3ygo/iCzEu +OsGI4x07whuVkghv0pJHq9hwCTNnDXBD5kKgDqQJJhUNhuOm1weKwV4fXTWQedlP +UJMjxRyq5zZ+NNAf8QRjSqMk0mGy0BMCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IB +AQBsIgOnhSxHeOmtPDNyDqPfr6JHyrPko4+gdK+iazsxWbJmqK/BSGaMIDEq48qI +3mt52d4VQvw19Vsf51WhYZRz4igXGmHaxKf1RMIjnGoDjqzHzsdj2BfIqcToGCep +QT5rdBsECcHBq4zs/wa7F01B5gqAfk7Ytt7Qeu5o4aAzCGvJsZsWlr+7tEcMbMNf +IzMp2FCdV6HJiB157GuVZotRq6bjLrc/YT1G+XG5COUNmWmgXip3deVRhPOxshxn +ofJTZ5ryhImG2nJNRjobaiTeWpWPaXoXLZ0qiRl9ZfydPLktky2k48AvF11Jp8Y1 +auOe48l7d/LyWEmCNACeoyvJ +-----END CERTIFICATE REQUEST----- diff --git a/src/lib/asiolink/testutils/ca/kea-server.crt b/src/lib/asiolink/testutils/ca/kea-server.crt new file mode 100644 index 0000000..3476032 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-server.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID+DCCAeCgAwIBAgIBFDANBgkqhkiG9w0BAQsFADAwMQswCQYDVQQGEwJVUzEQ +MA4GA1UECgwHSVNDIEluYzEPMA0GA1UEAwwGa2VhLWNhMB4XDTIxMDMwMjE1MDEy +N1oXDTMxMDIyODE1MDEyN1owNTELMAkGA1UEBhMCVVMxETAPBgNVBAoMCElTQyBJ +bmMuMRMwEQYDVQQDDAprZWEtc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAveRRgIN0S8oeBXVaIEnsG1DKuDzKKqLoLdBQNfoZrKzDLIMNzlab +xu20h82Y/OU02EdEzar98OstzglIWimKFVI0Omi0AuinUkv9640tjoO0g0oyCiWF +pJLJ8WOF4j7vmZUWuSS3VthlB+MLWlOZ5zACyPyWPo4Z2noHaYjfiQxBH8r5GJtQ +iJGapgWRbeyI+m837bjimpz6V1AGebHvf+zd1Lj+zDOczp38PqIGUbmAvfKCj+IL +MS46wYjjHTvCG5WSCG/Skker2HAJM2cNcEPmQqAOpAkmFQ2G46bXB4rBXh9dNZB5 +2U9QkyPFHKrnNn400B/xBGNKoyTSYbLQEwIDAQABoxgwFjAUBgNVHREEDTALggls +b2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggIBAKYtC4/KKZnTktvWankLnlVact5K +L0bJT4qCDg/0gj0pj3rofqyOEoGIjZssQtAG/wmJNF6gNisX/1F23BdEdPAsOJQv +KuRwr4zL3uj2Mkz585Or/iz633LnD8Ibv8KQsKLnJ/UnJikeH5UgxqcU9kA7ymAE +pzilP23p3bINvyBMwWZUzT3CsYB7PrcRzx3ScZhbhYaN0f8lq83nspXr8U3FyH5U +NkrgpuqIE9dFPiaY4CsjNIISpYANcVeWwyPKMk/uty3KbzbmDr7ssm1u1MyJjeVP +jE/Dhq+WTbDGMfqR3gyXBWq7b1ROA7tk9kAMQg91PLAELSB6lRmzfxzrH/wYk6E/ +0gHgpznpDcA68uW/54eX8phJQQp7Ak7csElXjqXDJ1AWA8VVjRXHerOkq0cUWply +YsJQCkx3jKdLDFfjtKZWVOjc9rGCnph4BfUej/Lt7z7tTr/Yh+oAR+UyowRzdZM/ +RSsui8vVbvKU+bRlyB5qmNR8cSI5oEA+kAs5DXK2bh5v1SGSxVjwKuwwLeu8eCr3 +HUYQMxKi7Y15+BqjbrOZCEfHE4WORkKze1dh9U/UU9h+LVd+TB7jprZc3ZOvuqYP +Bb+ponHJJaRvHUKD/jL8kHQ7KX79wXNVkrevGcPe8qE1X/xu4ChK5PuDzq2HQPLs +USYWw/aARNwslhV6 +-----END CERTIFICATE----- diff --git a/src/lib/asiolink/testutils/ca/kea-server.csr b/src/lib/asiolink/testutils/ca/kea-server.csr new file mode 100644 index 0000000..458b369 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-server.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICuTCCAaECAQAwNTELMAkGA1UEBhMCVVMxETAPBgNVBAoMCElTQyBJbmMuMRMw +EQYDVQQDDAprZWEtc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAveRRgIN0S8oeBXVaIEnsG1DKuDzKKqLoLdBQNfoZrKzDLIMNzlabxu20h82Y +/OU02EdEzar98OstzglIWimKFVI0Omi0AuinUkv9640tjoO0g0oyCiWFpJLJ8WOF +4j7vmZUWuSS3VthlB+MLWlOZ5zACyPyWPo4Z2noHaYjfiQxBH8r5GJtQiJGapgWR +beyI+m837bjimpz6V1AGebHvf+zd1Lj+zDOczp38PqIGUbmAvfKCj+ILMS46wYjj +HTvCG5WSCG/Skker2HAJM2cNcEPmQqAOpAkmFQ2G46bXB4rBXh9dNZB52U9QkyPF +HKrnNn400B/xBGNKoyTSYbLQEwIDAQABoD8wPQYJKoZIhvcNAQkOMTAwLjAJBgNV +HRMEAjAAMAsGA1UdDwQEAwIF4DAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZI +hvcNAQELBQADggEBAECqICoEZb0XeGwoBedtG2Exb4RUeoTAfL24q5a8cOtv0+Mw +i7y9LNihtRqP2kzhoZ7IhzSUZGVuh4BIUywpJHuWfM9b+fe+hxSGdqCeULKS3InK +4RWRh9jr12L7hEKfAG7VtL03/+Lm5DHLr47X6RkeZ5GwP29qqLwJcrK9qeFi26Bs +TrEafPInhF7PgyFjH2YVZVotNaOFMRvwEQwAMtuF7SAqRHr+8VHXP3yi9UjHvxRs +BpbVD6fEWNkLLJhoSqERgjWnsFlU3O+kj9R+iKA+6arxr4d+HS+dyYitFtVJaR6C +0+De9msTbJmn+2mu4zQ09Sdf0pN5lb/I3pgcbLU= +-----END CERTIFICATE REQUEST----- diff --git a/src/lib/asiolink/testutils/ca/kea-server.key b/src/lib/asiolink/testutils/ca/kea-server.key new file mode 100644 index 0000000..7709e16 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/kea-server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC95FGAg3RLyh4F +dVogSewbUMq4PMoqougt0FA1+hmsrMMsgw3OVpvG7bSHzZj85TTYR0TNqv3w6y3O +CUhaKYoVUjQ6aLQC6KdSS/3rjS2Og7SDSjIKJYWkksnxY4XiPu+ZlRa5JLdW2GUH +4wtaU5nnMALI/JY+jhnaegdpiN+JDEEfyvkYm1CIkZqmBZFt7Ij6bzftuOKanPpX +UAZ5se9/7N3UuP7MM5zOnfw+ogZRuYC98oKP4gsxLjrBiOMdO8IblZIIb9KSR6vY +cAkzZw1wQ+ZCoA6kCSYVDYbjptcHisFeH101kHnZT1CTI8Ucquc2fjTQH/EEY0qj +JNJhstATAgMBAAECggEAdhnidsNLOTfjpBFwlFRlfDerXRqxwgK/1H6S5H9AKJzq +Zmy70XEcQYTlmvDMDb2gOENbD28hsQ0T1+j+DtV3A/u0b/9etdBtAEozCqUriE9x +nZYvuQ/NJqYE4xS62BO8gRCwqUWkoWbErzsOfIcyWQ8LLGWsLAvFGJR8t65hGKJq +7/eYAdSd4mJhMv6mpBWR7OdKRarVxin0DucbLj7eXHPKPeu7//9LbxXhRKGHnc+A +8PmCFYo+oslhtLplzVPxzSa2D9xvBkFTK5dbwCRP+J9i4kQGfehBaW4jZpzBXtjL +6idGAU6x9nBSJfWhDuR42HsPn7lcnoKlnErG0inDQQKBgQDzBbo7TsBB8DR003Ix +seKN9Vpul4bUUp+pl4iECdtgvBTUMmKKkeV/I0OjvAzQAzbum6iEwCOjXpl9pxSx +6u4iJRK+/xtxZAT2Ddc3tEaql8kcc+VEjZVZPSIcSSOQw6Sri+K59ZzEbP+fDgFk +REGdiYA4A2ZvBdftmdVsKeuMRQKBgQDICEM881RNAZNwwT3yVU+iEe+16S5qoDjX +mi0K0cDffQjiYbwjdcZhtjKZdieZsjPLQof5mMxl1NeJaao9giHQ/tk55cpc17xl +N50LB9f7XAamOTTUoIjOl2hLxflZ43bf/W2GgX4dMp2FEFC17rxYUPfoO1vSvUfB +74goLyHsdwKBgHSLSZ1Jje/RRwbDpF7qpPBZOo4Qwsst+H23OvO/WmKQsBh3NUSo +5PtMqRJri2VyNTTGl1FaZ3zgUBGvP8B3Hs5nIw9PfhSp16s8RfrjzIPhGMQ5XDi9 +AWNzatlPxeuVt3HBOvDdNdoJP6lCaS5xgVoQZ9n033ncvommnXAqxlhVAoGATBqI +qlnRivK8i7uZu+clQv4b+1PaKwsGVVD9Lg6bmOvTQ333vG4EqgxNuAEyE9Guzvhj +D11I9r1Bu7AN6xTllMRBFTwN/8C8lq3P+/BiBen/RaKiLPte0WrdbWbG9aILCjE7 +SF9gAe/N6mBItM89rUQw7ZQX3VfSQ0DExrUX7QUCgYAAhwKg3c9rMVZKw0w7gR9l +/hVSCdOD0OHeYwfwzeEQbJshJJUrPk0gsEI4pEo0s8u6PhPBNy6t6U2Mw5S+R3/7 +JCC7UH0iY24d1K8mNL1PNYKBnbpDXrCgzO/Ip6TLnXiyy3/Uu1a7CKK2YZLSukp/ +e8iWSpxQT1Zwt3cfL8/EqA== +-----END PRIVATE KEY----- diff --git a/src/lib/asiolink/testutils/ca/server-addr-conf.cnf b/src/lib/asiolink/testutils/ca/server-addr-conf.cnf new file mode 100644 index 0000000..12a34f4 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/server-addr-conf.cnf @@ -0,0 +1,355 @@ +# +# OpenSSL example configuration file. +# This is mostly being used for generation of certificate requests. +# + +# This definition stops the following lines choking if HOME isn't +# defined. +HOME = . +RANDFILE = $ENV::HOME/.rnd + +# Extra OBJECT IDENTIFIER info: +#oid_file = $ENV::HOME/.oid +oid_section = new_oids + +# To use this configuration file with the "-extfile" option of the +# "openssl x509" utility, name here the section containing the +# X.509v3 extensions to use: +# extensions = +# (Alternatively, use a configuration file that has only +# X.509v3 extensions in its main [= default] section.) + +[ new_oids ] + +# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. +# Add a simple OID like this: +# testoid1=1.2.3.4 +# Or use config file substitution like this: +# testoid2=${testoid1}.5.6 + +# Policies used by the TSA examples. +tsa_policy1 = 1.2.3.4.1 +tsa_policy2 = 1.2.3.4.5.6 +tsa_policy3 = 1.2.3.4.5.7 + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = ./demoCA # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +#unique_subject = no # Set to 'no' to allow creation of + # several ctificates with same subject. +new_certs_dir = $dir/newcerts # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crlnumber = $dir/crlnumber # the current crl number + # must be commented out to leave a V1 CRL +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem# The private key +RANDFILE = $dir/private/.rand # private random number file + +x509_extensions = usr_cert # The extentions to add to the cert + +# Comment out the following two lines for the "traditional" +# (and highly broken) format. +name_opt = ca_default # Subject Name options +cert_opt = ca_default # Certificate field options + +# Extension copying option: use with caution. +# copy_extensions = copy + +# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs +# so this is commented out by default to leave a V1 CRL. +# crlnumber must also be commented out to leave a V1 CRL. +# crl_extensions = crl_ext + +default_days = 365 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = sha256 # use SHA-256 by default +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 1024 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extentions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString (PKIX recommendation before 2004) +# utf8only: only UTF8Strings (PKIX recommendation after 2004). +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. +string_mask = utf8only + +req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = AU +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +#stateOrProvinceName_default = Some-State + +localityName = Locality Name (eg, city) + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Internet Widgits Pty Ltd + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = Common Name (e.g. server FQDN or YOUR name) +commonName_max = 64 + +emailAddress = Email Address +emailAddress_max = 64 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 + +unstructuredName = An optional company name + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This is required for TSA certificates. +# extendedKeyUsage = critical,timeStamping + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_name + +[ v3_ca ] + + +# Extensions for a typical CA + + +# PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer + +# This is what PKIX recommends but some broken software chokes on critical +# extensions. +#basicConstraints = critical,CA:true +# So we do this instead. +basicConstraints = CA:true + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +# keyUsage = cRLSign, keyCertSign + +# Some might want this also +# nsCertType = sslCA, emailCA + +# Include email address in subject alt name: another PKIX recommendation +# subjectAltName=email:copy +# Copy issuer details +# issuerAltName=issuer:copy + +# DER hex encoding of an extension: beware experts only! +# obj=DER:02:03 +# Where 'obj' is a standard or added object +# You can even override a supported extension: +# basicConstraints= critical, DER:30:03:01:01:FF + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always + +[ proxy_cert_ext ] +# These extensions should be added when creating a proxy certificate + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This really needs to be in place for it to be a proxy certificate. +proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo + +#################################################################### +[ tsa ] + +default_tsa = tsa_config1 # the default TSA section + +[ tsa_config1 ] + +# These are used by the TSA reply generation only. +dir = ./demoCA # TSA root directory +serial = $dir/tsaserial # The current serial number (mandatory) +crypto_device = builtin # OpenSSL engine to use for signing +signer_cert = $dir/tsacert.pem # The TSA signing certificate + # (optional) +certs = $dir/cacert.pem # Certificate chain to include in reply + # (optional) +signer_key = $dir/private/tsakey.pem # The TSA private key (optional) + +default_policy = tsa_policy1 # Policy if request did not specify it + # (optional) +other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) +digests = md5, sha1 # Acceptable message digests (mandatory) +accuracy = secs:1, millisecs:500, microsecs:100 # (optional) +clock_precision_digits = 0 # number of digits after dot. (optional) +ordering = yes # Is ordering defined for timestamps? + # (optional, default: no) +tsa_name = yes # Must the TSA name be included in the reply? + # (optional, default: no) +ess_cert_id_chain = no # Must the ESS cert id chain be included? + # (optional, default: no) + +[ alt_name ] +IP.1 = 127.0.0.1 +IP.2 = ::1 diff --git a/src/lib/asiolink/testutils/ca/server-conf.cnf b/src/lib/asiolink/testutils/ca/server-conf.cnf new file mode 100644 index 0000000..843b641 --- /dev/null +++ b/src/lib/asiolink/testutils/ca/server-conf.cnf @@ -0,0 +1,354 @@ +# +# OpenSSL example configuration file. +# This is mostly being used for generation of certificate requests. +# + +# This definition stops the following lines choking if HOME isn't +# defined. +HOME = . +RANDFILE = $ENV::HOME/.rnd + +# Extra OBJECT IDENTIFIER info: +#oid_file = $ENV::HOME/.oid +oid_section = new_oids + +# To use this configuration file with the "-extfile" option of the +# "openssl x509" utility, name here the section containing the +# X.509v3 extensions to use: +# extensions = +# (Alternatively, use a configuration file that has only +# X.509v3 extensions in its main [= default] section.) + +[ new_oids ] + +# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. +# Add a simple OID like this: +# testoid1=1.2.3.4 +# Or use config file substitution like this: +# testoid2=${testoid1}.5.6 + +# Policies used by the TSA examples. +tsa_policy1 = 1.2.3.4.1 +tsa_policy2 = 1.2.3.4.5.6 +tsa_policy3 = 1.2.3.4.5.7 + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = ./demoCA # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +#unique_subject = no # Set to 'no' to allow creation of + # several ctificates with same subject. +new_certs_dir = $dir/newcerts # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crlnumber = $dir/crlnumber # the current crl number + # must be commented out to leave a V1 CRL +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem# The private key +RANDFILE = $dir/private/.rand # private random number file + +x509_extensions = usr_cert # The extentions to add to the cert + +# Comment out the following two lines for the "traditional" +# (and highly broken) format. +name_opt = ca_default # Subject Name options +cert_opt = ca_default # Certificate field options + +# Extension copying option: use with caution. +# copy_extensions = copy + +# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs +# so this is commented out by default to leave a V1 CRL. +# crlnumber must also be commented out to leave a V1 CRL. +# crl_extensions = crl_ext + +default_days = 365 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = sha256 # use SHA-256 by default +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 1024 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extentions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString (PKIX recommendation before 2004) +# utf8only: only UTF8Strings (PKIX recommendation after 2004). +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. +string_mask = utf8only + +req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = AU +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +#stateOrProvinceName_default = Some-State + +localityName = Locality Name (eg, city) + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Internet Widgits Pty Ltd + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = Common Name (e.g. server FQDN or YOUR name) +commonName_max = 64 + +emailAddress = Email Address +emailAddress_max = 64 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 + +unstructuredName = An optional company name + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This is required for TSA certificates. +# extendedKeyUsage = critical,timeStamping + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_name + +[ v3_ca ] + + +# Extensions for a typical CA + + +# PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer + +# This is what PKIX recommends but some broken software chokes on critical +# extensions. +#basicConstraints = critical,CA:true +# So we do this instead. +basicConstraints = CA:true + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +# keyUsage = cRLSign, keyCertSign + +# Some might want this also +# nsCertType = sslCA, emailCA + +# Include email address in subject alt name: another PKIX recommendation +# subjectAltName=email:copy +# Copy issuer details +# issuerAltName=issuer:copy + +# DER hex encoding of an extension: beware experts only! +# obj=DER:02:03 +# Where 'obj' is a standard or added object +# You can even override a supported extension: +# basicConstraints= critical, DER:30:03:01:01:FF + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always + +[ proxy_cert_ext ] +# These extensions should be added when creating a proxy certificate + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This really needs to be in place for it to be a proxy certificate. +proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo + +#################################################################### +[ tsa ] + +default_tsa = tsa_config1 # the default TSA section + +[ tsa_config1 ] + +# These are used by the TSA reply generation only. +dir = ./demoCA # TSA root directory +serial = $dir/tsaserial # The current serial number (mandatory) +crypto_device = builtin # OpenSSL engine to use for signing +signer_cert = $dir/tsacert.pem # The TSA signing certificate + # (optional) +certs = $dir/cacert.pem # Certificate chain to include in reply + # (optional) +signer_key = $dir/private/tsakey.pem # The TSA private key (optional) + +default_policy = tsa_policy1 # Policy if request did not specify it + # (optional) +other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) +digests = md5, sha1 # Acceptable message digests (mandatory) +accuracy = secs:1, millisecs:500, microsecs:100 # (optional) +clock_precision_digits = 0 # number of digits after dot. (optional) +ordering = yes # Is ordering defined for timestamps? + # (optional, default: no) +tsa_name = yes # Must the TSA name be included in the reply? + # (optional, default: no) +ess_cert_id_chain = no # Must the ESS cert id chain be included? + # (optional, default: no) + +[ alt_name ] +DNS.1 = localhost diff --git a/src/lib/asiolink/testutils/openssl_sample_client.cc b/src/lib/asiolink/testutils/openssl_sample_client.cc new file mode 100644 index 0000000..b1c7645 --- /dev/null +++ b/src/lib/asiolink/testutils/openssl_sample_client.cc @@ -0,0 +1,186 @@ +// +// client.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <config.h> + +#include <iostream> + +#ifdef HAVE_GENERIC_TLS_METHOD + +#include <cstdlib> +#include <cstring> +#include <functional> +#include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> + +using boost::asio::ip::tcp; +using std::placeholders::_1; +using std::placeholders::_2; + +inline std::string CA_(const std::string& filename) { + return (std::string(TEST_CA_DIR) + "/" + filename); +} + +enum { max_length = 1024 }; + +class client +{ +public: + client(boost::asio::io_service& io_context, + boost::asio::ssl::context& context, + const tcp::endpoint& endpoint) + : socket_(io_context, context) + { + socket_.set_verify_mode(boost::asio::ssl::verify_peer | + boost::asio::ssl::verify_fail_if_no_peer_cert); + socket_.set_verify_callback( + std::bind(&client::verify_certificate, this, _1, _2)); + + connect(endpoint); + } + +private: + bool verify_certificate(bool preverified, + boost::asio::ssl::verify_context& ctx) + { + // The verify callback can be used to check whether the certificate that is + // being presented is valid for the peer. For example, RFC 2818 describes + // the steps involved in doing this for HTTPS. Consult the OpenSSL + // documentation for more details. Note that the callback is called once + // for each certificate in the certificate chain, starting from the root + // certificate authority. + + // In this example we will simply print the certificate's subject name. + char subject_name[256]; + X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); + X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256); + std::cout << "Verifying " << subject_name << "\n"; + + return preverified; + } + + void connect(const tcp::endpoint& endpoint) + { + socket_.lowest_layer().async_connect(endpoint, + [this](const boost::system::error_code& error) + { + if (!error) + { + handshake(); + } + else + { + std::cout << "Connect failed: " << error.message() << "\n"; + } + }); + } + + void handshake() + { + socket_.async_handshake(boost::asio::ssl::stream_base::client, + [this](const boost::system::error_code& error) + { + if (!error) + { + send_request(); + } + else + { + std::cout << "Handshake failed: " << error.message() << "\n"; + } + }); + } + + void send_request() + { + std::cout << "Enter message: "; + std::cin.getline(request_, max_length); + size_t request_length = std::strlen(request_); + + boost::asio::async_write(socket_, + boost::asio::buffer(request_, request_length), + [this](const boost::system::error_code& error, std::size_t length) + { + if (!error) + { + receive_response(length); + } + else + { + std::cout << "Write failed: " << error.message() << "\n"; + } + }); + } + + void receive_response(std::size_t length) + { + boost::asio::async_read(socket_, + boost::asio::buffer(reply_, length), + [this](const boost::system::error_code& error, std::size_t length) + { + if (!error) + { + std::cout << "Reply: "; + std::cout.write(reply_, length); + std::cout << "\n"; + } + else + { + std::cout << "Read failed: " << error.message() << "\n"; + } + }); + } + + boost::asio::ssl::stream<tcp::socket> socket_; + char request_[max_length]; + char reply_[max_length]; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: client <addr> <port>\n"; + return 1; + } + + boost::asio::io_service io_context; + + using namespace std; // For atoi. + tcp::endpoint endpoint( + boost::asio::ip::address::from_string(argv[1]), atoi(argv[2])); + + boost::asio::ssl::context ctx(boost::asio::ssl::context::method::tls); + ctx.load_verify_file(CA_("kea-ca.crt")); + ctx.use_certificate_chain_file(CA_("kea-client.crt")); + ctx.use_private_key_file(CA_("kea-client.key"), + boost::asio::ssl::context::pem); + + client c(io_context, ctx, endpoint); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} +#else // !HAVE_GENERIC_TLS_METHOD + +int main() +{ + std::cerr << "this tool requires recent boost version (>= 1.64)\n"; + return 0; +} +#endif diff --git a/src/lib/asiolink/testutils/openssl_sample_server.cc b/src/lib/asiolink/testutils/openssl_sample_server.cc new file mode 100644 index 0000000..f2f0c67 --- /dev/null +++ b/src/lib/asiolink/testutils/openssl_sample_server.cc @@ -0,0 +1,192 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// Use the cpp03 version because the cpp11 version does not compile with +// some g++ e.g. on Fedora 33. + +#include <config.h> + +#include <iostream> + +#ifdef HAVE_GENERIC_TLS_METHOD + +#include <cstdlib> +#include <boost/bind/bind.hpp> +#include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> + +inline std::string CA_(const std::string& filename) { + return (std::string(TEST_CA_DIR) + "/" + filename); +} + +typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket; + +class session +{ +public: + session(boost::asio::io_service& io_context, + boost::asio::ssl::context& context) + : socket_(io_context, context) + { + } + + ssl_socket::lowest_layer_type& socket() + { + return socket_.lowest_layer(); + } + + void start() + { + socket_.async_handshake(boost::asio::ssl::stream_base::server, + boost::bind(&session::handle_handshake, this, + boost::asio::placeholders::error)); + } + + void handle_handshake(const boost::system::error_code& error) + { + if (!error) + { + socket_.async_read_some(boost::asio::buffer(data_, max_length), + boost::bind(&session::handle_read, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + else + { + std::cerr << "handshake error '" << error.message() << "'\n"; + delete this; + } + } + + void handle_read(const boost::system::error_code& error, + size_t bytes_transferred) + { + if (!error) + { + boost::asio::async_write(socket_, + boost::asio::buffer(data_, bytes_transferred), + boost::bind(&session::handle_write, this, + boost::asio::placeholders::error)); + } + else + { + delete this; + } + } + + void handle_write(const boost::system::error_code& error) + { + if (!error) + { + socket_.async_read_some(boost::asio::buffer(data_, max_length), + boost::bind(&session::handle_read, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + else + { + delete this; + } + } + +private: + ssl_socket socket_; + enum { max_length = 1024 }; + char data_[max_length]; +}; + +class server +{ +public: + server(boost::asio::io_service& io_context, unsigned short port) + : io_context_(io_context), + acceptor_(io_context, + boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), + context_(boost::asio::ssl::context::method::tls) + { + //context_.set_options( + // boost::asio::ssl::context::default_workarounds + // | boost::asio::ssl::context::no_sslv2 + // | boost::asio::ssl::context::single_dh_use); + //context_.set_password_callback(boost::bind(&server::get_password, this)); + context_.set_verify_mode(boost::asio::ssl::verify_peer | + boost::asio::ssl::verify_fail_if_no_peer_cert); + context_.load_verify_file(CA_("kea-ca.crt")); + context_.use_certificate_chain_file(CA_("kea-server.crt")); + context_.use_private_key_file(CA_("kea-server.key"), + boost::asio::ssl::context::pem); + //context_.use_tmp_dh_file("dh2048.pem"); + + start_accept(); + } + + void start_accept() + { + session* new_session = new session(io_context_, context_); + acceptor_.async_accept(new_session->socket(), + boost::bind(&server::handle_accept, this, new_session, + boost::asio::placeholders::error)); + } + + void handle_accept(session* new_session, + const boost::system::error_code& error) + { + if (!error) + { + new_session->start(); + } + else + { + delete new_session; + } + + start_accept(); + } + +private: + boost::asio::io_service& io_context_; + boost::asio::ip::tcp::acceptor acceptor_; + boost::asio::ssl::context context_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: server <port>\n"; + return 1; + } + + boost::asio::io_service io_context; + + using namespace std; // For atoi. + server s(io_context, atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} + +#else // !HAVE_GENERIC_TLS_METHOD + +int main() +{ + std::cerr << "this tool requires recent boost version (>= 1.64)\n"; + return 0; +} +#endif + diff --git a/src/lib/asiolink/testutils/test_server_unix_socket.cc b/src/lib/asiolink/testutils/test_server_unix_socket.cc new file mode 100644 index 0000000..7f7007d --- /dev/null +++ b/src/lib/asiolink/testutils/test_server_unix_socket.cc @@ -0,0 +1,331 @@ +// Copyright (C) 2017-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/asio_wrapper.h> +#include <asiolink/testutils/test_server_unix_socket.h> +#include <boost/enable_shared_from_this.hpp> +#include <boost/shared_ptr.hpp> +#include <functional> +#include <set> +#include <sstream> + +using namespace boost::asio::local; +namespace ph = std::placeholders; + +namespace isc { +namespace asiolink { +namespace test { + +/// @brief ASIO unix domain socket. +typedef stream_protocol::socket UnixSocket; + +/// @brief Pointer to the ASIO unix domain socket. +typedef boost::shared_ptr<UnixSocket> UnixSocketPtr; + +/// @brief Callback function invoked when response is sent from the server. +typedef std::function<void()> SentResponseCallback; + +/// @brief Connection to the server over unix domain socket. +/// +/// It reads the data over the socket, sends responses and closes a socket. +class Connection : public boost::enable_shared_from_this<Connection> { +public: + + /// @brief Constructor. + /// + /// It starts asynchronous read operation. + /// + /// @param unix_socket Pointer to the unix domain socket into which + /// connection has been accepted. + /// @param custom_response Custom response that the server should send. + /// @param sent_response_callback Callback function to be invoked when + /// server sends a response. + Connection(const UnixSocketPtr& unix_socket, + const std::string custom_response, + SentResponseCallback sent_response_callback) + : socket_(unix_socket), custom_response_(custom_response), + sent_response_callback_(sent_response_callback) { + } + + /// @brief Starts asynchronous read from the socket. + void start() { + socket_->async_read_some(boost::asio::buffer(&raw_buf_[0], raw_buf_.size()), + std::bind(&Connection::readHandler, shared_from_this(), + ph::_1, // error + ph::_2)); // bytes_transferred + } + + /// @brief Closes the socket. + void stop() { + try { + socket_->close(); + + } catch (...) { + // ignore errors when closing the socket. + } + } + + /// @brief Handler invoked when data have been received over the socket. + /// + /// This is the handler invoked when the data have been received over the + /// socket. If custom response has been specified, this response is sent + /// back to the client. Otherwise, the handler echoes back the request + /// and prepends the word "received ". Finally, it calls a custom + /// callback function (specified in the constructor) to notify that the + /// response has been sent over the socket. + /// + /// @param bytes_transferred Number of bytes received. + void + readHandler(const boost::system::error_code& ec, + size_t bytes_transferred) { + // This is most likely due to the abort. + if (ec) { + // An error occurred so let's close the socket. + stop(); + return; + } + + if (!custom_response_.empty()) { + boost::asio::write(*socket_, + boost::asio::buffer(custom_response_.c_str(), custom_response_.size())); + + } else { + std::string received(&raw_buf_[0], bytes_transferred); + std::string response("received " + received); + boost::asio::write(*socket_, + boost::asio::buffer(response.c_str(), response.size())); + } + + /// @todo We're taking simplistic approach and send a response right away + /// after receiving data over the socket. Therefore, after responding we + /// do not schedule another read. We could extend this logic slightly to + /// parse the received data and see when we've got enough data before we + /// send a response. However, the current unit tests don't really require + /// that. + + // Invoke callback function to notify that the response has been sent. + sent_response_callback_(); + } + +private: + + /// @brief Pointer to the unix domain socket. + UnixSocketPtr socket_; + + /// @brief Custom response to be sent to the client. + std::string custom_response_; + + /// @brief Receive buffer. + std::array<char, 1024> raw_buf_; + + /// @brief Pointer to the callback function to be invoked when response + /// has been sent. + SentResponseCallback sent_response_callback_; + +}; + +/// @brief Pointer to a Connection object. +typedef boost::shared_ptr<Connection> ConnectionPtr; + +/// @brief Connection pool. +/// +/// Holds all connections established with the server and gracefully +/// terminates these connections. +class ConnectionPool { +public: + + /// @brief Constructor. + /// + /// @param io_service Reference to the IO service. + ConnectionPool(IOService& io_service) + : io_service_(io_service), connections_(), next_socket_(), + response_num_(0) { + } + + /// @brief Destructor. + ~ConnectionPool() { + stopAll(); + } + + /// @brief Creates new unix domain socket and returns it. + /// + /// This convenience method creates a socket which can be used to accept + /// new connections. If such socket already exists, it is returned. + /// + /// @return Pointer to the socket. + UnixSocketPtr getSocket() { + if (!next_socket_) { + next_socket_.reset(new UnixSocket(io_service_.get_io_service())); + } + return (next_socket_); + } + + /// @brief Starts new connection. + /// + /// The socket returned by the @ref ConnectionPool::getSocket is used to + /// create new connection. Then, the @ref next_socket_ is reset, to force + /// the @ref ConnectionPool::getSocket to generate a new socket for a + /// next connection. + /// + /// @param custom_response Custom response to be sent to the client. + void start(const std::string& custom_response) { + ConnectionPtr conn(new Connection(next_socket_, custom_response, [this] { + ++response_num_; + })); + conn->start(); + + connections_.insert(conn); + next_socket_.reset(); + } + + /// @brief Stops the given connection. + /// + /// @param conn Pointer to the connection to be stopped. + void stop(const ConnectionPtr& conn) { + conn->stop(); + connections_.erase(conn); + } + + /// @brief Stops all connections. + void stopAll() { + for (auto conn = connections_.begin(); conn != connections_.end(); + ++conn) { + (*conn)->stop(); + } + connections_.clear(); + } + + /// @brief Returns number of responses sent so far. + size_t getResponseNum() const { + return (response_num_); + } + +private: + + /// @brief Reference to the IO service. + IOService& io_service_; + + /// @brief Container holding established connections. + std::set<ConnectionPtr> connections_; + + /// @brief Holds pointer to the generated socket. + /// + /// This socket will be used by the next connection. + UnixSocketPtr next_socket_; + + /// @brief Holds the number of sent responses. + size_t response_num_; +}; + + +TestServerUnixSocket::TestServerUnixSocket(IOService& io_service, + const std::string& socket_file_path, + const std::string& custom_response) + : io_service_(io_service), + server_endpoint_(socket_file_path), + server_acceptor_(io_service_.get_io_service()), + test_timer_(io_service_), + custom_response_(custom_response), + connection_pool_(new ConnectionPool(io_service)), + stopped_(false), + running_(false) { +} + +TestServerUnixSocket::~TestServerUnixSocket() { + server_acceptor_.close(); +} + +void +TestServerUnixSocket::generateCustomResponse(const uint64_t response_size) { + std::ostringstream s; + s << "{"; + while (s.tellp() < response_size) { + s << "\"param\": \"value\","; + } + s << "}"; + custom_response_ = s.str(); +} + +void +TestServerUnixSocket::startTimer(const long test_timeout) { + test_timer_.setup(std::bind(&TestServerUnixSocket::timeoutHandler, this), + test_timeout, IntervalTimer::ONE_SHOT); +} + +void +TestServerUnixSocket::stopServer() { + test_timer_.cancel(); + server_acceptor_.cancel(); + connection_pool_->stopAll(); +} + +void +TestServerUnixSocket::bindServerSocket(const bool use_thread) { + server_acceptor_.open(); + server_acceptor_.bind(server_endpoint_); + server_acceptor_.listen(); + accept(); + + // When threads are in use, we need to post a handler which will be invoked + // when the thread has already started and the IO service is running. The + // main thread can move forward when it receives this signal from the handler. + if (use_thread) { + io_service_.post(std::bind(&TestServerUnixSocket::signalRunning, + this)); + } +} + +void +TestServerUnixSocket::acceptHandler(const boost::system::error_code& ec) { + if (ec) { + return; + } + + connection_pool_->start(custom_response_); + accept(); +} + +void +TestServerUnixSocket::accept() { + server_acceptor_.async_accept(*(connection_pool_->getSocket()), + std::bind(&TestServerUnixSocket::acceptHandler, this, + ph::_1)); // error +} + +void +TestServerUnixSocket::signalRunning() { + { + std::lock_guard<std::mutex> lock(mutex_); + running_ = true; + } + condvar_.notify_one(); +} + +void +TestServerUnixSocket::waitForRunning() { + std::unique_lock<std::mutex> lock(mutex_); + while (!running_) { + condvar_.wait(lock); + } +} + +void +TestServerUnixSocket::timeoutHandler() { + ADD_FAILURE() << "Timeout occurred while running the test!"; + io_service_.stop(); + stopped_ = true; +} + +size_t +TestServerUnixSocket::getResponseNum() const { + return (connection_pool_->getResponseNum()); +} + +} // end of namespace isc::asiolink::test +} // end of namespace isc::asiolink +} // end of namespace isc diff --git a/src/lib/asiolink/testutils/test_server_unix_socket.h b/src/lib/asiolink/testutils/test_server_unix_socket.h new file mode 100644 index 0000000..c272ee7 --- /dev/null +++ b/src/lib/asiolink/testutils/test_server_unix_socket.h @@ -0,0 +1,171 @@ +// Copyright (C) 2017-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/. + +#ifndef TEST_SERVER_UNIX_SOCKET_H +#define TEST_SERVER_UNIX_SOCKET_H + +#include <config.h> +#include <asiolink/interval_timer.h> +#include <asiolink/io_service.h> +#include <boost/shared_ptr.hpp> +#include <gtest/gtest.h> +#include <list> +#include <stdint.h> +#include <string> +#include <mutex> +#include <condition_variable> + +namespace isc { +namespace asiolink { +namespace test { + +class ConnectionPool; + +/// @brief Provides unix domain socket functionality for unit tests. +/// +/// This class represents a server side socket. It can be used to +/// test client's transmission over the unix domain socket. By default, +/// the server side socket echoes the client's message so the client's +/// message (prefixed with the word "received"). +/// +/// It is also possible to specify a custom response from the server +/// instead of echoing back the request. +/// +/// It is possible to make multiple connections to the server side +/// socket simultaneously. +/// +/// The test should perform IOService::run_one until it finds that +/// the number of responses sent by the server is greater than +/// expected. The number of responses sent so far can be retrieved +/// using @ref TestServerUnixSocket::getResponseNum. +/// +/// This class uses @c shared_from_this() to pass its instance to the +/// @c std::bind function, thus the caller must store shared pointer +/// to this object. +class TestServerUnixSocket { +public: + + /// @brief Constructor. + /// + /// @param io_service IO service. + /// @param socket_file_path Socket file path. + /// @param custom_response Custom response to be sent to the client. + TestServerUnixSocket(IOService& io_service, + const std::string& socket_file_path, + const std::string& custom_response = ""); + + /// @brief Destructor. + /// + /// Closes active connections. + ~TestServerUnixSocket(); + + /// @brief Starts timer for detecting test timeout. + /// + /// @param test_timeout Test timeout in milliseconds. + void startTimer(const long test_timeout); + + /// @brief Cancels all asynchronous operations. + void stopServer(); + + /// @brief Generates response of a given length. + /// + /// Note: The response may be a few bytes larger than requested. + /// + /// @param response_size Desired response size. + void generateCustomResponse(const uint64_t response_size); + + /// @brief Creates and binds server socket. + /// + /// @param use_thread Boolean value indicating if the IO service + /// is running in thread. + void bindServerSocket(const bool use_thread = false); + + /// @brief Server acceptor handler. + /// + /// @param ec Error code. + void acceptHandler(const boost::system::error_code& ec); + + /// @brief Callback function invoke upon test timeout. + /// + /// It stops the IO service and reports test timeout. + void timeoutHandler(); + + /// @brief Return number of responses sent so far to the clients. + size_t getResponseNum() const; + + /// @brief Indicates if the server has been stopped. + bool isStopped() { + return (stopped_); + } + + /// @brief Waits for the server signal that it is running. + /// + /// When the caller starts the service he indicates whether + /// IO service will be running in thread or not. If threads + /// are used the caller has to wait for the IO service to + /// actually run. In such case this function should be invoked + /// which waits for a posted callback to be executed. When this + /// happens it means that IO service is running and the main + /// thread can move forward. + void waitForRunning(); + +private: + + /// @brief Asynchronously accept new connections. + void accept(); + + /// @brief Handler invoked to signal that server is running. + /// + /// This is used only when thread is used to run IO service. + void signalRunning(); + + /// @brief IO service used by the tests. + IOService& io_service_; + + /// @brief Server endpoint. + boost::asio::local::stream_protocol::endpoint server_endpoint_; + /// @brief Server acceptor. + boost::asio::local::stream_protocol::acceptor server_acceptor_; + + /// @brief Asynchronous timer service to detect timeouts. + IntervalTimer test_timer_; + + /// @brief Holds custom response to be sent to the client. + std::string custom_response_; + + /// @brief Pool of connections. + boost::shared_ptr<ConnectionPool> connection_pool_; + + /// @brief Indicates if IO service has been stopped as a result of + /// a timeout. + bool stopped_; + + /// @brief Indicates if the server in a thread is running. + bool running_; + + /// @brief Mutex used by the server. + /// + /// Mutex is used in situations when server's IO service is being run in a + /// thread to synchronize this thread with a main thread using + /// @ref signalRunning and @ref waitForRunning. + std::mutex mutex_; + + /// @brief Conditional variable used by the server. + /// + /// Conditional variable is used in situations when server's IO service is + /// being run in a thread to synchronize this thread with a main thread + /// using @ref signalRunning and @ref waitForRunning. + std::condition_variable condvar_; +}; + +/// @brief Pointer to the @ref TestServerUnixSocket. +typedef boost::shared_ptr<TestServerUnixSocket> TestServerUnixSocketPtr; + +} // end of namespace isc::asiolink::test +} // end of namespace isc::asiolink +} // end of namespace isc + +#endif // TEST_SERVER_UNIX_SOCKET_H diff --git a/src/lib/asiolink/testutils/test_tls.cc b/src/lib/asiolink/testutils/test_tls.cc new file mode 100644 index 0000000..de3a7b8 --- /dev/null +++ b/src/lib/asiolink/testutils/test_tls.cc @@ -0,0 +1,74 @@ +// Copyright (C) 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/testutils/test_tls.h> + +namespace isc { +namespace asiolink { +namespace test { + +/// @brief Configure the TLS server. +void configServer(TlsContextPtr& ctx) { + std::string ca(std::string(TEST_CA_DIR) + "/kea-ca.crt"); + std::string cert(std::string(TEST_CA_DIR) + "/kea-server.crt"); + std::string key(std::string(TEST_CA_DIR) + "/kea-server.key"); + TlsContext::configure(ctx, TlsRole::SERVER, ca, cert, key, true); +} + +/// @brief Configure the TLS server trusting the self-signed client. +void configTrustedSelf(TlsContextPtr& ctx) { + std::string ca(std::string(TEST_CA_DIR) + "/kea-self.crt"); + std::string cert(std::string(TEST_CA_DIR) + "/kea-server.crt"); + std::string key(std::string(TEST_CA_DIR) + "/kea-server.key"); + TlsContext::configure(ctx, TlsRole::SERVER, ca, cert, key, true); +} + +/// @brief Configure the TLS server with no client certificate request. +void configServerNoReq(TlsContextPtr& ctx) { + std::string ca(std::string(TEST_CA_DIR) + "/kea-ca.crt"); + std::string cert(std::string(TEST_CA_DIR) + "/kea-server.crt"); + std::string key(std::string(TEST_CA_DIR) + "/kea-server.key"); + TlsContext::configure(ctx, TlsRole::SERVER, ca, cert, key, false); +} + +/// @brief Configure the TLS server with no subject alternative name. +void configServerRaw(TlsContextPtr& ctx) { + std::string ca(std::string(TEST_CA_DIR) + "/kea-ca.crt"); + std::string cert(std::string(TEST_CA_DIR) + "/kea-server-raw.crt"); + std::string key(std::string(TEST_CA_DIR) + "/kea-server.key"); + TlsContext::configure(ctx, TlsRole::SERVER, ca, cert, key, true); +} + +/// @brief Configure the TLS client. +void configClient(TlsContextPtr& ctx) { + std::string ca(std::string(TEST_CA_DIR) + "/kea-ca.crt"); + std::string cert(std::string(TEST_CA_DIR) + "/kea-client.crt"); + std::string key(std::string(TEST_CA_DIR) + "/kea-client.key"); + TlsContext::configure(ctx, TlsRole::CLIENT, ca, cert, key, true); +} + +/// @brief Configure another TLS client. +void configOther(TlsContextPtr& ctx) { + std::string ca(std::string(TEST_CA_DIR) + "/kea-ca.crt"); + std::string cert(std::string(TEST_CA_DIR) + "/kea-other.crt"); + std::string key(std::string(TEST_CA_DIR) + "/kea-other.key"); + TlsContext::configure(ctx, TlsRole::CLIENT, ca, cert, key, true); +} + +/// @brief Configure self-signed TLS client. +void configSelf(TlsContextPtr& ctx) { + std::string ca(std::string(TEST_CA_DIR) + "/kea-ca.crt"); + std::string cert(std::string(TEST_CA_DIR) + "/kea-self.crt"); + std::string key(std::string(TEST_CA_DIR) + "/kea-self.key"); + TlsContext::configure(ctx, TlsRole::CLIENT, ca, cert, key, true); +} + +} // end of namespace isc::asiolink::test +} // end of namespace isc::asiolink +} // end of namespace isc diff --git a/src/lib/asiolink/testutils/test_tls.h b/src/lib/asiolink/testutils/test_tls.h new file mode 100644 index 0000000..ee27015 --- /dev/null +++ b/src/lib/asiolink/testutils/test_tls.h @@ -0,0 +1,47 @@ +// Copyright (C) 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/. + +#ifndef TEST_TLS_H +#define TEST_TLS_H + +#ifndef CONFIG_H_WAS_INCLUDED +#error config.h must be included before test_tls.h +#endif + +#include <asiolink/crypto_tls.h> + +#include <string> + +namespace isc { +namespace asiolink { +namespace test { + +/// @brief Configure the TLS server. +void configServer(TlsContextPtr& ctx); + +/// @brief Configure trusted self-signed TLS server. +void configTrustedSelf(TlsContextPtr& ctx); + +/// @brief Configure the TLS server with no client certificate request. +void configServerNoReq(TlsContextPtr& ctx); + +/// @brief Configure the TLS server with no subject alternative name. +void configServerRaw(TlsContextPtr& ctx); + +/// @brief Configure the TLS client. +void configClient(TlsContextPtr& ctx); + +/// @brief Configure another TLS client. +void configOther(TlsContextPtr& ctx); + +/// @brief Configure self-signed TLS client. +void configSelf(TlsContextPtr& ctx); + +} // end of namespace isc::asiolink::test +} // end of namespace isc::asiolink +} // end of namespace isc + +#endif // TEST_TLS_H diff --git a/src/lib/asiolink/testutils/timed_signal.cc b/src/lib/asiolink/testutils/timed_signal.cc new file mode 100644 index 0000000..28d4b1b --- /dev/null +++ b/src/lib/asiolink/testutils/timed_signal.cc @@ -0,0 +1,17 @@ +// Copyright (C) 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/testutils/timed_signal.h> + +namespace isc { +namespace asiolink { +namespace test { + +} // end of namespace isc::asiolink::test +} // end of namespace isc::asiolink +} // end of namespace isc diff --git a/src/lib/asiolink/testutils/timed_signal.h b/src/lib/asiolink/testutils/timed_signal.h new file mode 100644 index 0000000..f1cd0c9 --- /dev/null +++ b/src/lib/asiolink/testutils/timed_signal.h @@ -0,0 +1,86 @@ +// 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/. + +#ifndef TIMED_SIGNAL_H +#define TIMED_SIGNAL_H + +#include <config.h> + +#include <asiolink/interval_timer.h> +#include <signal.h> +#include <gtest/gtest.h> + +namespace isc { +namespace asiolink { +namespace test { + +/// @brief Implements a time-delayed signal +/// +/// Given an IOService, a signal number, and a time period, this class will +/// send (raise) the signal to the current process. +class TimedSignal { +public: + /// @brief Constructor + /// + /// @param io_service IOService to run the timer + /// @param signum OS signal value (e.g. SIGHUP, SIGUSR1 ...) + /// @param milliseconds time in milliseconds to wait until the signal is + /// raised. + /// @param mode selects between a one-shot signal or a signal which repeats + /// at "milliseconds" interval. + TimedSignal(asiolink::IOService& io_service, int signum, int milliseconds, + const asiolink::IntervalTimer::Mode& mode = + asiolink::IntervalTimer::ONE_SHOT) + : timer_(new asiolink::IntervalTimer(io_service)) { + timer_->setup(SendSignalCallback(signum), milliseconds, mode); + } + + /// @brief Cancels the given timer. + void cancel() { + if (timer_) { + timer_->cancel(); + } + } + + /// @brief Destructor. + ~TimedSignal() { + cancel(); + } + + /// @brief Callback for the TimeSignal's internal timer. + class SendSignalCallback { + public: + + /// @brief Constructor + /// + /// @param signum OS signal value of the signal to send + SendSignalCallback(int signum) : signum_(signum) { + } + + /// @brief Callback method invoked when the timer expires + /// + /// Calls raise with the given signal which should generate that + /// signal to the given process. + void operator()() { + ASSERT_EQ(0, raise(signum_)); + return; + } + + private: + /// @brief Stores the OS signal value to send. + int signum_; + }; + +private: + /// @brief Timer which controls when the signal is sent. + asiolink::IntervalTimerPtr timer_; +}; + +} // end of namespace isc::asiolink::test +} // end of namespace isc::asiolink +} // end of namespace isc + +#endif // TIMED_SIGNAL_H diff --git a/src/lib/asiolink/tls_acceptor.h b/src/lib/asiolink/tls_acceptor.h new file mode 100644 index 0000000..c575559 --- /dev/null +++ b/src/lib/asiolink/tls_acceptor.h @@ -0,0 +1,62 @@ +// 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/. + +#ifndef TLS_ACCEPTOR_H +#define TLS_ACCEPTOR_H + +#ifndef BOOST_ASIO_HPP +#error "asio.hpp must be included before including this, see asiolink.h as to why" +#endif + +#include <asiolink/io_acceptor.h> +#include <asiolink/io_service.h> +#include <asiolink/io_socket.h> +#include <asiolink/tcp_acceptor.h> +#include <asiolink/tcp_endpoint.h> +#include <asiolink/tcp_socket.h> +#include <asiolink/tls_socket.h> +#include <boost/shared_ptr.hpp> +#include <netinet/in.h> + +namespace isc { +namespace asiolink { + +/// @brief Provides a service for accepting new TLS connections. +/// +/// @tparam C Acceptor callback type. +template<typename C> +class TLSAcceptor : public TCPAcceptor<C> { +public: + + /// @brief Constructor. + /// + /// @param io_service IO service. + explicit TLSAcceptor(IOService& io_service) : TCPAcceptor<C>(io_service) { + } + + /// @brief Destructor. + virtual ~TLSAcceptor() { } + + /// @brief Asynchronously accept new connection. + /// + /// This method accepts new connection into the specified socket. When the + /// new connection arrives or an error occurs the specified callback function + /// is invoked. + /// + /// @param socket Socket into which connection should be accepted. + /// @param callback Callback function to be invoked when the new connection + /// arrives. + /// @tparam SocketCallback Type of the callback for the @ref TLSSocket. + template<typename SocketCallback> + void asyncAccept(const TLSSocket<SocketCallback>& socket, C& callback) { + TCPAcceptor<C>::acceptor_->async_accept(socket.getASIOSocket(), callback); + } +}; + +} // namespace asiolink +} // namespace isc + +#endif diff --git a/src/lib/asiolink/tls_socket.h b/src/lib/asiolink/tls_socket.h new file mode 100644 index 0000000..cdd2f78 --- /dev/null +++ b/src/lib/asiolink/tls_socket.h @@ -0,0 +1,519 @@ +// Copyright (C) 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/. + +#ifndef TLS_SOCKET_H +#define TLS_SOCKET_H + +#ifndef BOOST_ASIO_HPP +#error "asio.hpp must be included before including this, see asiolink.h as to why" +#endif + +#include <asiolink/crypto_tls.h> +#include <asiolink/tcp_socket.h> + +#include <boost/noncopyable.hpp> + +namespace isc { +namespace asiolink { + +/// @brief The @c TLSSocket class is a concrete derived class of @c IOAsioSocket +/// that represents a TLS socket. +/// +/// @tparam C Callback type. +template <typename C> +class TLSSocket : public IOAsioSocket<C>, private boost::noncopyable { +public: + + /// @brief Constructor from a TLS stream. + /// + /// It is assumed that the caller will open and close the stream, + /// so these operations are a no-op for that stream. + /// + /// @param stream The TLS stream. + TLSSocket(TlsStream<C>& stream); + + /// @brief Constructor. + /// + /// Used when the TLSSocket is being asked to manage its own internal + /// socket. In this case, the open() and close() methods are used. + /// + /// @param service I/O Service object used to manage the socket. + /// @param context Pointer to TLS context. + TLSSocket(IOService& service, TlsContextPtr context); + + /// @brief Destructor. + virtual ~TLSSocket() { } + + /// @brief Return file descriptor of underlying socket. + virtual int getNative() const { +#if BOOST_VERSION < 106600 + return (socket_.native()); +#else + return (socket_.native_handle()); +#endif + } + + /// @brief Return protocol of socket. + virtual int getProtocol() const { + return (IPPROTO_TCP); + } + + /// @brief Is "open()" synchronous predicate. + /// + /// Indicates that the opening of a TLS socket is asynchronous. + virtual bool isOpenSynchronous() const { + return (false); + } + + /// @brief Checks if the connection is usable. + /// + /// The connection is usable if the socket is open and the peer has not + /// closed its connection. + /// + /// @return true if the connection is usable. + bool isUsable() const { + // If the socket is open it doesn't mean that it is still + // usable. The connection could have been closed on the other + // end. We have to check if we can still use this socket. + if (socket_.is_open()) { + // Remember the current non blocking setting. + const bool non_blocking_orig = socket_.non_blocking(); + + // Set the socket to non blocking mode. We're going to + // test if the socket returns would_block status on the + // attempt to read from it. + socket_.non_blocking(true); + + // Use receive with message peek flag to avoid removing + // the data awaiting to be read. + char data[2]; + int err = 0; + int cc = recv(getNative(), data, sizeof(data), MSG_PEEK); + if (cc < 0) { + // Error case. + err = errno; + } else if (cc == 0) { + // End of file. + err = -1; + } + + // Revert the original non_blocking flag on the socket. + socket_.non_blocking(non_blocking_orig); + + // If the connection is alive we'd typically get + // would_block status code. If there are any data that + // haven't been read we may also get success status. We're + // guessing that try_again may also be returned by some + // implementations in some situations. Any other error + // code indicates a problem with the connection so we + // assume that the connection has been closed. + return ((err == 0) || (err == EAGAIN) || (err == EWOULDBLOCK)); + } + + return (false); + } + + /// @brief Open Socket. + /// + /// Opens the TLS socket. This is an asynchronous operation, completion of + /// which will be signalled via a call to the callback function. + /// + /// @param endpoint Endpoint to which the socket will connect. + /// @param callback Callback object. + virtual void open(const IOEndpoint* endpoint, C& callback); + + /// @brief Perform Handshake. + /// + /// Perform the TLS handshake. This is an asynchronous operation, + /// completion of which will be signalled via a call to the callback + /// function. + /// + /// @param callback Callback object. + virtual void handshake(C& callback); + + /// @brief Send Asynchronously. + /// + /// Calls the underlying socket's async_send() method to send a + /// packet of data asynchronously to the remote endpoint. The + /// callback will be called on completion. + /// + /// @param data Data to send. + /// @param length Length of data to send. + /// @param endpoint Target of the send. (Unused for a TLS socket because + /// that was determined when the connection was opened.) + /// @param callback Callback object. + /// @throw BufferTooLarge on attempt to send a buffer larger than 64kB. + virtual void asyncSend(const void* data, size_t length, + const IOEndpoint* endpoint, C& callback); + + /// @brief Send Asynchronously without count. + /// + /// This variant of the method sends data over the TLS socket without + /// preceding the data with a data count. Eventually, we should migrate + /// the virtual method to not insert the count but there are existing + /// classes using the count. Once this migration is done, the existing + /// virtual method should be replaced by this method. + /// + /// @param data Data to send. + /// @param length Length of data to send. + /// @param callback Callback object. + /// @throw BufferTooLarge on attempt to send a buffer larger than 64kB. + void asyncSend(const void* data, size_t length, C& callback); + + /// @brief Receive Asynchronously. + /// + /// Calls the underlying socket's async_receive() method to read a packet + /// of data from a remote endpoint. Arrival of the data is signalled via a + /// call to the callback function. + /// + /// @param data Buffer to receive incoming message. + /// @param length Length of the data buffer. + /// @param offset Offset into buffer where data is to be put. + /// @param endpoint Source of the communication. + /// @param callback Callback object. + virtual void asyncReceive(void* data, size_t length, size_t offset, + IOEndpoint* endpoint, C& callback); + + /// @brief Process received data packet. + /// + /// See the description of IOAsioSocket::receiveComplete for a complete + /// description of this method. + /// + /// @param staging Pointer to the start of the staging buffer. + /// @param length Amount of data in the staging buffer. + /// @param cumulative Amount of data received before the staging buffer is + /// processed. + /// @param offset Unused. + /// @param expected unused. + /// @param outbuff Output buffer. Data in the staging buffer is be copied + /// to this output buffer in the call. + /// + /// @return Always true. + virtual bool processReceivedData(const void* staging, size_t length, + size_t& cumulative, size_t& offset, + size_t& expected, + isc::util::OutputBufferPtr& outbuff); + + /// @brief Cancel I/O On Socket. + virtual void cancel(); + + /// @brief Close socket. + virtual void close(); + + /// @brief TLS shutdown. + /// + /// The callback is called on completion i.e. when the peer performs + /// a shutdown or a close. + virtual void shutdown(C& callback); + + /// @brief Returns reference to the underlying ASIO socket. + /// + /// @return Reference to underlying ASIO socket. + virtual typename TlsStream<C>::lowest_layer_type& getASIOSocket() const { + return (socket_); + } + + /// @brief Returns reference to the underlying TLS stream. + /// + /// @return Reference to underlying TLS stream. + virtual TlsStream<C>& getTlsStream() const { + return (stream_); + } + +private: + /// Two variables to hold the stream - a stream and a pointer to it. This + /// handles the case where a stream is passed to the TLSSocket on + /// construction, or where it is asked to manage its own stream. + + /// @brief Pointer to own stream. + std::unique_ptr<TlsStream<C>> stream_ptr_; + + /// @brief TLS stream. + TlsStream<C>& stream_; + + /// @brief Underlying TCP socket. + typename TlsStream<C>::lowest_layer_type& socket_; + + /// @todo Remove temporary buffer + /// The current implementation copies the buffer passed to asyncSend() into + /// a temporary buffer and precedes it with a two-byte count field. As + /// ASIO should really be just about sending and receiving data, the TCP + /// code should not do this. If the protocol using this requires a two-byte + /// count, it should add it before calling this code. (This may be best + /// achieved by altering isc::dns::buffer to have pairs of methods: + /// getLength()/getTCPLength(), getData()/getTCPData(), with the getTCPXxx() + /// methods taking into account a two-byte count field.) + /// + /// The option of sending the data in two operations, the count followed by + /// the data was discounted as that would lead to two callbacks which would + /// cause problems with the stackless coroutine code. + + /// @brief Send buffer. + isc::util::OutputBufferPtr send_buffer_; +}; + +// Constructor - caller manages socket. + +template <typename C> +TLSSocket<C>::TLSSocket(TlsStream<C>& stream) : + stream_ptr_(), stream_(stream), + socket_(stream_.lowest_layer()), send_buffer_() { +} + +// Constructor - create socket on the fly. + +template <typename C> +TLSSocket<C>::TLSSocket(IOService& service, TlsContextPtr context) : + stream_ptr_(new TlsStream<C>(service, context)), + stream_(*stream_ptr_), socket_(stream_.lowest_layer()), send_buffer_() +{ +} + +// Open the socket. + +template <typename C> void +TLSSocket<C>::open(const IOEndpoint* endpoint, C& callback) { + // Ignore opens on already-open socket. Don't throw a failure because + // of uncertainties as to what precedes when using asynchronous I/O. + // Also allows us a treat a passed-in socket as a self-managed socket. + if (!socket_.is_open()) { + if (endpoint->getFamily() == AF_INET) { + socket_.open(boost::asio::ip::tcp::v4()); + } else { + socket_.open(boost::asio::ip::tcp::v6()); + } + + // Set options on the socket: + + // Reuse address - allow the socket to bind to a port even if the port + // is in the TIMED_WAIT state. + socket_.set_option(boost::asio::socket_base::reuse_address(true)); + } + + // Upconvert to a TCPEndpoint. We need to do this because although + // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it does not + // contain a method for getting at the underlying endpoint type - that is in + /// the derived class and the two classes differ on return type. + isc_throw_assert(endpoint->getProtocol() == IPPROTO_TCP); + const TCPEndpoint* tcp_endpoint = + static_cast<const TCPEndpoint*>(endpoint); + + // Connect to the remote endpoint. On success, the handler will be + // called (with one argument - the length argument will default to + // zero). + socket_.async_connect(tcp_endpoint->getASIOEndpoint(), callback); +} + +// Perform the handshake. + +template <typename C> void +TLSSocket<C>::handshake(C& callback) { + if (!socket_.is_open()) { + isc_throw(SocketNotOpen, "attempt to perform handshake on " + "a TLS socket that is not open"); + } + stream_.handshake(callback); +} + +// Send a message. Should never do this if the socket is not open, so throw +// an exception if this is the case. + +template <typename C> void +TLSSocket<C>::asyncSend(const void* data, size_t length, C& callback) +{ + if (!socket_.is_open()) { + isc_throw(SocketNotOpen, + "attempt to send on a TLS socket that is not open"); + } + + try { + send_buffer_.reset(new isc::util::OutputBuffer(length)); + send_buffer_->writeData(data, length); + + // Send the data. + boost::asio::async_write(stream_, + boost::asio::buffer(send_buffer_->getData(), + send_buffer_->getLength()), + callback); + } catch (const boost::numeric::bad_numeric_cast&) { + isc_throw(BufferTooLarge, + "attempt to send buffer larger than 64kB"); + } +} + +template <typename C> void +TLSSocket<C>::asyncSend(const void* data, size_t length, + const IOEndpoint*, C& callback) +{ + if (!socket_.is_open()) { + isc_throw(SocketNotOpen, + "attempt to send on a TLS socket that is not open"); + } + + /// Need to copy the data into a temporary buffer and precede it with + /// a two-byte count field. + /// @todo arrange for the buffer passed to be preceded by the count + try { + // Ensure it fits into 16 bits + uint16_t count = boost::numeric_cast<uint16_t>(length); + + // Copy data into a buffer preceded by the count field. + send_buffer_.reset(new isc::util::OutputBuffer(length + 2)); + send_buffer_->writeUint16(count); + send_buffer_->writeData(data, length); + + // ... and send it + boost::asio::async_write(stream_, + boost::asio::buffer(send_buffer_->getData(), + send_buffer_->getLength()), + callback); + } catch (const boost::numeric::bad_numeric_cast&) { + isc_throw(BufferTooLarge, + "attempt to send buffer larger than 64kB"); + } +} + +// Receive a message. Note that the "offset" argument is used as an index +// into the buffer in order to decide where to put the data. It is up to the +// caller to initialize the data to zero +template <typename C> void +TLSSocket<C>::asyncReceive(void* data, size_t length, size_t offset, + IOEndpoint* endpoint, C& callback) +{ + if (!socket_.is_open()) { + isc_throw(SocketNotOpen, + "attempt to receive from a TLS socket that is not open"); + } + + // Upconvert to a TCPEndpoint. We need to do this because although + // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it + // does not contain a method for getting at the underlying endpoint + // type - that is in the derived class and the two classes differ on + // return type. + isc_throw_assert(endpoint->getProtocol() == IPPROTO_TCP); + TCPEndpoint* tcp_endpoint = static_cast<TCPEndpoint*>(endpoint); + + // Write the endpoint details from the communications link. Ideally + // we should make IOEndpoint assignable, but this runs in to all sorts + // of problems concerning the management of the underlying Boost + // endpoint (e.g. if it is not self-managed, is the copied one + // self-managed?) The most pragmatic solution is to let Boost take care + // of everything and copy details of the underlying endpoint. + tcp_endpoint->getASIOEndpoint() = socket_.remote_endpoint(); + + // Ensure we can write into the buffer and if so, set the pointer to + // where the data will be written. + if (offset >= length) { + isc_throw(BufferOverflow, "attempt to read into area beyond end of " + "TCP receive buffer"); + } + void* buffer_start = + static_cast<void*>(static_cast<uint8_t*>(data) + offset); + + // ... and kick off the read. + stream_.async_read_some(boost::asio::buffer(buffer_start, length - offset), + callback); +} + +// Is the receive complete? + +template <typename C> bool +TLSSocket<C>::processReceivedData(const void* staging, size_t length, + size_t& cumulative, size_t& offset, + size_t& expected, + isc::util::OutputBufferPtr& outbuff) +{ + // Point to the data in the staging buffer and note how much there is. + const uint8_t* data = static_cast<const uint8_t*>(staging); + size_t data_length = length; + + // Is the number is "expected" valid? It won't be unless we have received + // at least two bytes of data in total for this set of receives. + if (cumulative < 2) { + + // "expected" is not valid. Did this read give us enough data to + // work it out? + cumulative += length; + if (cumulative < 2) { + + // Nope, still not valid. This must have been the first packet and + // was only one byte long. Tell the fetch code to read the next + // packet into the staging buffer beyond the data that is already + // there so that the next time we are called we have a complete + // TCP count. + offset = cumulative; + return (false); + } + + // Have enough data to interpret the packet count, so do so now. + expected = isc::util::readUint16(data, cumulative); + + // We have two bytes less of data to process. Point to the start of the + // data and adjust the packet size. Note that at this point, + // "cumulative" is the true amount of data in the staging buffer, not + // "length". + data += 2; + data_length = cumulative - 2; + } else { + + // Update total amount of data received. + cumulative += length; + } + + // Regardless of anything else, the next read goes into the start of the + // staging buffer. + offset = 0; + + // Work out how much data we still have to put in the output buffer. (This + // could be zero if we have just interpreted the TCP count and that was + // set to zero.) + if (expected >= outbuff->getLength()) { + + // Still need data in the output packet. Copy what we can from the + // staging buffer to the output buffer. + size_t copy_amount = std::min(expected - outbuff->getLength(), + data_length); + outbuff->writeData(data, copy_amount); + } + + // We can now say if we have all the data. + return (expected == outbuff->getLength()); +} + +// Cancel I/O on the socket. No-op if the socket is not open. + +template <typename C> void +TLSSocket<C>::cancel() { + if (socket_.is_open()) { + socket_.cancel(); + } +} + +// TLS shutdown. Can be used for orderly close. + +template <typename C> void +TLSSocket<C>::shutdown(C& callback) { + if (!socket_.is_open()) { + isc_throw(SocketNotOpen, "attempt to perform shutdown on " + "a TLS socket that is not open"); + } + stream_.shutdown(callback); +} + +// Close the socket down. Can only do this if the socket is open and we are +// managing it ourself. + +template <typename C> void +TLSSocket<C>::close() { + if (socket_.is_open() && stream_ptr_) { + socket_.close(); + } +} + +} // namespace asiolink +} // namespace isc + +#endif // TLS_SOCKET_H diff --git a/src/lib/asiolink/udp_endpoint.h b/src/lib/asiolink/udp_endpoint.h new file mode 100644 index 0000000..894032b --- /dev/null +++ b/src/lib/asiolink/udp_endpoint.h @@ -0,0 +1,115 @@ +// 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/. + +#ifndef UDP_ENDPOINT_H +#define UDP_ENDPOINT_H 1 + +#ifndef BOOST_ASIO_HPP +#error "asio.hpp must be included before including this, see asiolink.h as to why" +#endif + +#include <asiolink/io_endpoint.h> + +namespace isc { +namespace asiolink { + +/// \brief The \c UDPEndpoint class is a concrete derived class of +/// \c IOEndpoint that represents an endpoint of a UDP packet. +/// +/// Other notes about \c TCPEndpoint applies to this class, too. +class UDPEndpoint : public IOEndpoint { +public: + /// + /// \name Constructors and Destructor. + /// + //@{ + + /// \brief Default Constructor + /// + /// Creates an internal endpoint. This is expected to be set by some + /// external call. + UDPEndpoint() : + asio_endpoint_placeholder_(new boost::asio::ip::udp::endpoint()), + asio_endpoint_(*asio_endpoint_placeholder_) + {} + + /// \brief Constructor from a pair of address and port. + /// + /// \param address The IP address of the endpoint. + /// \param port The UDP port number of the endpoint. + UDPEndpoint(const IOAddress& address, const unsigned short port) : + asio_endpoint_placeholder_( + new boost::asio::ip::udp::endpoint(boost::asio::ip::address::from_string(address.toText()), + port)), + asio_endpoint_(*asio_endpoint_placeholder_) + {} + + /// \brief Constructor from an ASIO UDP endpoint. + /// + /// This constructor is designed to be an efficient wrapper for the + /// corresponding ASIO class, \c udp::endpoint. + /// + /// \param asio_endpoint The ASIO representation of the UDP endpoint. + UDPEndpoint(boost::asio::ip::udp::endpoint& asio_endpoint) : + asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint) + {} + + /// \brief Constructor from an ASIO UDP endpoint. + /// + /// This constructor is designed to be an efficient wrapper for the + /// corresponding ASIO class, \c udp::endpoint. + /// + /// \param asio_endpoint The ASIO representation of the TCP endpoint. + UDPEndpoint(const boost::asio::ip::udp::endpoint& asio_endpoint) : + asio_endpoint_placeholder_(new boost::asio::ip::udp::endpoint(asio_endpoint)), + asio_endpoint_(*asio_endpoint_placeholder_) + {} + + /// \brief The destructor. + virtual ~UDPEndpoint() { delete asio_endpoint_placeholder_; } + //@} + + virtual IOAddress getAddress() const { + return (asio_endpoint_.address()); + } + + virtual const struct sockaddr& getSockAddr() const { + return (*asio_endpoint_.data()); + } + + virtual uint16_t getPort() const { + return (asio_endpoint_.port()); + } + + virtual short getProtocol() const { + return (asio_endpoint_.protocol().protocol()); + } + + virtual short getFamily() const { + return (asio_endpoint_.protocol().family()); + } + + // This is not part of the exposed IOEndpoint API but allows + // direct access to the ASIO implementation of the endpoint + inline const boost::asio::ip::udp::endpoint& getASIOEndpoint() const { + return (asio_endpoint_); + } + inline boost::asio::ip::udp::endpoint& getASIOEndpoint() { + return (asio_endpoint_); + } + +private: + boost::asio::ip::udp::endpoint* asio_endpoint_placeholder_; + boost::asio::ip::udp::endpoint& asio_endpoint_; +}; + +} // namespace asiolink +} // namespace isc +#endif // UDP_ENDPOINT_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/asiolink/udp_socket.h b/src/lib/asiolink/udp_socket.h new file mode 100644 index 0000000..fea8def --- /dev/null +++ b/src/lib/asiolink/udp_socket.h @@ -0,0 +1,323 @@ +// 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/. + +#ifndef UDP_SOCKET_H +#define UDP_SOCKET_H 1 + +#ifndef BOOST_ASIO_HPP +#error "asio.hpp must be included before including this, see asiolink.h as to why" +#endif + +#include <netinet/in.h> +#include <sys/socket.h> +#include <unistd.h> // for some IPC/network system calls + +#include <cstddef> + +#include <asiolink/io_asio_socket.h> +#include <asiolink/io_endpoint.h> +#include <asiolink/io_service.h> +#include <asiolink/udp_endpoint.h> + +#include <exceptions/isc_assert.h> + +namespace isc { +namespace asiolink { + +/// \brief The \c UDPSocket class is a concrete derived class of \c IOAsioSocket +/// that represents a UDP socket. +/// +/// \param C Callback type +template <typename C> +class UDPSocket : public IOAsioSocket<C> { +private: + /// \brief Class is non-copyable + UDPSocket(const UDPSocket&); + UDPSocket& operator=(const UDPSocket&); + +public: + enum { + MIN_SIZE = 4096 // Minimum send and receive size + }; + + /// \brief Constructor from an ASIO UDP socket. + /// + /// \param socket The ASIO representation of the UDP socket. It is assumed + /// that the caller will open and close the socket, so these + /// operations are a no-op for that socket. + UDPSocket(boost::asio::ip::udp::socket& socket); + + /// \brief Constructor + /// + /// Used when the UDPSocket is being asked to manage its own internal + /// socket. In this case, the open() and close() methods are used. + /// + /// \param service I/O Service object used to manage the socket. + UDPSocket(IOService& service); + + /// \brief Destructor + virtual ~UDPSocket(); + + /// \brief Return file descriptor of underlying socket + virtual int getNative() const { +#if BOOST_VERSION < 106600 + return (socket_.native()); +#else + return (socket_.native_handle()); +#endif + } + + /// \brief Return protocol of socket + virtual int getProtocol() const { + return (IPPROTO_UDP); + } + + /// \brief Is "open()" synchronous? + /// + /// Indicates that the opening of a UDP socket is synchronous. + virtual bool isOpenSynchronous() const { + return true; + } + + /// \brief Open Socket + /// + /// Opens the UDP socket. This is a synchronous operation. + /// + /// \param endpoint Endpoint to which the socket will send data. This is + /// used to determine the address family that should be used for the + /// underlying socket. + /// \param callback Unused as the operation is synchronous. + virtual void open(const IOEndpoint* endpoint, C& callback); + + /// \brief Send Asynchronously + /// + /// Calls the underlying socket's async_send_to() method to send a packet of + /// data asynchronously to the remote endpoint. The callback will be called + /// on completion. + /// + /// \param data Data to send + /// \param length Length of data to send + /// \param endpoint Target of the send + /// \param callback Callback object. + virtual void asyncSend(const void* data, size_t length, + const IOEndpoint* endpoint, C& callback); + + /// \brief Receive Asynchronously + /// + /// Calls the underlying socket's async_receive_from() method to read a + /// packet of data from a remote endpoint. Arrival of the data is signalled + /// via a call to the callback function. + /// + /// \param data Buffer to receive incoming message + /// \param length Length of the data buffer + /// \param offset Offset into buffer where data is to be put + /// \param endpoint Source of the communication + /// \param callback Callback object + virtual void asyncReceive(void* data, size_t length, size_t offset, + IOEndpoint* endpoint, C& callback); + + /// \brief Process received data + /// + /// See the description of IOAsioSocket::receiveComplete for a complete + /// description of this method. + /// + /// \param staging Pointer to the start of the staging buffer. + /// \param length Amount of data in the staging buffer. + /// \param cumulative Amount of data received before the staging buffer is + /// processed. + /// \param offset Unused. + /// \param expected unused. + /// \param outbuff Output buffer. Data in the staging buffer is be copied + /// to this output buffer in the call. + /// + /// \return Always true + virtual bool processReceivedData(const void* staging, size_t length, + size_t& cumulative, size_t& offset, + size_t& expected, + isc::util::OutputBufferPtr& outbuff); + + /// \brief Cancel I/O On Socket + virtual void cancel(); + + /// \brief Close socket + virtual void close(); + + +private: + // Two variables to hold the socket - a socket and a pointer to it. This + // handles the case where a socket is passed to the UDPSocket on + // construction, or where it is asked to manage its own socket. + + /// Pointer to own socket + std::unique_ptr<boost::asio::ip::udp::socket> socket_ptr_; + + // Socket + boost::asio::ip::udp::socket& socket_; + + // True when socket is open + bool isopen_; +}; + +// Constructor - caller manages socket + +template <typename C> +UDPSocket<C>::UDPSocket(boost::asio::ip::udp::socket& socket) : + socket_ptr_(), socket_(socket), isopen_(true) +{ +} + +// Constructor - create socket on the fly + +template <typename C> +UDPSocket<C>::UDPSocket(IOService& service) : + socket_ptr_(new boost::asio::ip::udp::socket(service.get_io_service())), + socket_(*socket_ptr_), isopen_(false) +{ +} + +// Destructor. + +template <typename C> +UDPSocket<C>::~UDPSocket() +{ +} + +// Open the socket. + +template <typename C> void +UDPSocket<C>::open(const IOEndpoint* endpoint, C&) { + + // Ignore opens on already-open socket. (Don't throw a failure because + // of uncertainties as to what precedes when using asynchronous I/O.) + // It also allows us a treat a passed-in socket in exactly the same way as + // a self-managed socket (in that we can call the open() and close() methods + // of this class). + if (!isopen_) { + if (endpoint->getFamily() == AF_INET) { + socket_.open(boost::asio::ip::udp::v4()); + } else { + socket_.open(boost::asio::ip::udp::v6()); + } + isopen_ = true; + + // Ensure it can send and receive at least 4K buffers. + boost::asio::ip::udp::socket::send_buffer_size snd_size; + socket_.get_option(snd_size); + if (snd_size.value() < MIN_SIZE) { + snd_size = MIN_SIZE; + socket_.set_option(snd_size); + } + + boost::asio::ip::udp::socket::receive_buffer_size rcv_size; + socket_.get_option(rcv_size); + if (rcv_size.value() < MIN_SIZE) { + rcv_size = MIN_SIZE; + socket_.set_option(rcv_size); + } + } +} + +// Send a message. Should never do this if the socket is not open, so throw +// an exception if this is the case. + +template <typename C> void +UDPSocket<C>::asyncSend(const void* data, size_t length, + const IOEndpoint* endpoint, C& callback) +{ + if (isopen_) { + + // Upconvert to a UDPEndpoint. We need to do this because although + // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it + // does not contain a method for getting at the underlying endpoint + // type - that is in the derived class and the two classes differ on + // return type. + isc_throw_assert(endpoint->getProtocol() == IPPROTO_UDP); + const UDPEndpoint* udp_endpoint = + static_cast<const UDPEndpoint*>(endpoint); + + // ... and send the message. + socket_.async_send_to(boost::asio::buffer(data, length), + udp_endpoint->getASIOEndpoint(), callback); + } else { + isc_throw(SocketNotOpen, + "attempt to send on a UDP socket that is not open"); + } +} + +// Receive a message. Should never do this if the socket is not open, so throw +// an exception if this is the case. + +template <typename C> void +UDPSocket<C>::asyncReceive(void* data, size_t length, size_t offset, + IOEndpoint* endpoint, C& callback) +{ + if (isopen_) { + + // Upconvert the endpoint again. + isc_throw_assert(endpoint->getProtocol() == IPPROTO_UDP); + UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint); + + // Ensure we can write into the buffer + if (offset >= length) { + isc_throw(BufferOverflow, "attempt to read into area beyond end of " + "UDP receive buffer"); + } + void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset); + + // Issue the read + socket_.async_receive_from(boost::asio::buffer(buffer_start, length - offset), + udp_endpoint->getASIOEndpoint(), callback); + } else { + isc_throw(SocketNotOpen, + "attempt to receive from a UDP socket that is not open"); + } +} + +// Receive complete. Just copy the data across to the output buffer and +// update arguments as appropriate. + +template <typename C> bool +UDPSocket<C>::processReceivedData(const void* staging, size_t length, + size_t& cumulative, size_t& offset, + size_t& expected, + isc::util::OutputBufferPtr& outbuff) +{ + // Set return values to what we should expect. + cumulative = length; + expected = length; + offset = 0; + + // Copy data across + outbuff->writeData(staging, length); + + // ... and mark that we have everything. + return (true); +} + +// Cancel I/O on the socket. No-op if the socket is not open. + +template <typename C> void +UDPSocket<C>::cancel() { + if (isopen_) { + socket_.cancel(); + } +} + +// Close the socket down. Can only do this if the socket is open and we are +// managing it ourself. + +template <typename C> void +UDPSocket<C>::close() { + if (isopen_ && socket_ptr_) { + socket_.close(); + isopen_ = false; + } +} + +} // namespace asiolink +} // namespace isc + +#endif // UDP_SOCKET_H diff --git a/src/lib/asiolink/unix_domain_socket.cc b/src/lib/asiolink/unix_domain_socket.cc new file mode 100644 index 0000000..ca9e15c --- /dev/null +++ b/src/lib/asiolink/unix_domain_socket.cc @@ -0,0 +1,371 @@ +// Copyright (C) 2017-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/asio_wrapper.h> +#include <asiolink/unix_domain_socket.h> +#include <boost/enable_shared_from_this.hpp> +#include <functional> +#include <iostream> + +using namespace boost::asio::local; +namespace ph = std::placeholders; + +namespace isc { +namespace asiolink { + +/// @brief Implementation of the unix domain socket. +class UnixDomainSocketImpl : public boost::enable_shared_from_this<UnixDomainSocketImpl> { +public: + + /// @brief Constructor. + /// + /// @param io_service IO service to be used by the socket class. + UnixDomainSocketImpl(IOService& io_service) + : socket_(io_service.get_io_service()) { + } + + /// @brief Destructor. + /// + /// Closes the socket. + ~UnixDomainSocketImpl() { + close(); + } + + /// @brief Asynchronously connects to an endpoint. + /// + /// This method schedules asynchronous connect and installs the + /// @ref UnixDomainSocketImpl::connectHandler as a callback. + /// + /// @param endpoint Reference to an endpoint to connect to. + /// @param handler User supplied handler to be invoked when the connection + /// is established or when error is signalled. + void asyncConnect(const stream_protocol::endpoint& endpoint, + const UnixDomainSocket::ConnectHandler& handler); + + /// @brief Local handler invoked as a result of asynchronous connection. + /// + /// This is a wrapper around the user supplied callback. It ignores + /// EINPROGRESS errors which are observed on some operating systems as + /// a result of trying to connect asynchronously. This error code doesn't + /// necessarily indicate a problem and the subsequent attempts to read + /// and write to the socket will succeed. Therefore, the handler simply + /// overrides this error code with success status. The user supplied + /// handler doesn't need to deal with the EINPROGRESS error codes. + /// + /// @param remote_handler User supplied callback. + /// @param ec Error code returned as a result of connection. + void connectHandler(const UnixDomainSocket::ConnectHandler& remote_handler, + const boost::system::error_code& ec); + + /// @brief Asynchronously sends data over the socket. + /// + /// This method schedules an asynchronous send and installs the + /// @ref UnixDomainSocketImpl::sendHandler as a callback. + /// + /// @param data Pointer to data to be sent. + /// @param length Number of bytes to be sent. + /// @param handler Callback to be invoked when data have been sent or an + /// sending error is signalled. + void asyncSend(const void* data, const size_t length, + const UnixDomainSocket::Handler& handler); + + /// @brief Asynchronously sends the data over the socket. + /// + /// This method is called by the @ref asyncSend and the @ref sendHandler + /// if the asynchronous send has to be repeated as a result of receiving + /// EAGAIN or EWOULDBLOCK. + /// + /// @param buffer Buffers holding the data to be sent. + /// @param handler User supplied callback to be invoked when data have + /// been sent or sending error is signalled. + void doSend(const boost::asio::const_buffers_1& buffer, + const UnixDomainSocket::Handler& handler); + + + /// @brief Local handler invoked as a result of asynchronous send. + /// + /// This handler is invoked as a result of asynchronous send. It is a + /// wrapper callback around the user supplied callback. It handles + /// EWOULDBLOCK and EAGAIN errors by retrying an asynchronous send. + /// These errors are often returned on some operating systems, even + /// though one would expect that asynchronous operation would not + /// return such errors. Because these errors are handled by the + /// wrapper callback, the user supplied callback never receives + /// these errors. + /// + /// @param remote_handler User supplied callback. + /// @param buffer Buffers holding the data to be sent. + /// @param ec Error code returned as a result of sending the data. + /// @param length Length of the data sent. + void sendHandler(const UnixDomainSocket::Handler& remote_handler, + const boost::asio::const_buffers_1& buffer, + const boost::system::error_code& ec, + size_t length); + + /// @brief Asynchronously receive data over the socket. + /// + /// This method schedules asynchronous receive and installs the + /// @ref UnixDomainSocketImpl::receiveHandler is a callback. + /// + /// @param data Pointer to a buffer into which the data should be read. + /// @param length Length of the buffer. + /// @param handler User supplied callback invoked when data have been + /// received or an error is signalled. + void asyncReceive(void* data, const size_t length, + const UnixDomainSocket::Handler& handler); + + /// @brief Asynchronously receives the data over the socket. + /// + /// This method is called @ref asyncReceive and @ref receiveHandler when + /// EWOULDBLOCK or EAGAIN is returned. + /// + /// @param buffer A buffer into which the data should be received. + /// @param handler User supplied callback invoked when data have been + /// received on an error is signalled. + void doReceive(const boost::asio::mutable_buffers_1& buffer, + const UnixDomainSocket::Handler& handler); + + /// @brief Local handler invoked as a result of asynchronous receive. + /// + /// This handler is invoked as a result of asynchronous receive. It is a + /// wrapper callback around the user supplied callback. It handles + /// EWOULDBLOCK and EAGAIN by retrying to asynchronously receive the + /// data. These errors are often returned on some operating systems, even + /// though one would expect that asynchronous operation would not + /// return such errors. Because these errors are handled by the + /// wrapper callback, the user supplied callback never receives + /// these errors. + /// + /// @param remote_handler User supplied callback. + /// @param buffer Buffer into which the data are received. + /// @param ec Error code returned as a result of asynchronous receive. + /// @param length Size of the received data. + void receiveHandler(const UnixDomainSocket::Handler& remote_handler, + const boost::asio::mutable_buffers_1& buffer, + const boost::system::error_code& ec, + size_t length); + + /// @brief Disables read and write operations on the socket. + void shutdown(); + + /// @brief Cancels asynchronous operations on the socket. + void cancel(); + + /// @brief Closes the socket. + void close(); + + /// @brief Instance of the boost asio unix domain socket. + stream_protocol::socket socket_; +}; + +void +UnixDomainSocketImpl::asyncConnect(const stream_protocol::endpoint& endpoint, + const UnixDomainSocket::ConnectHandler& handler) { + auto local_handler = std::bind(&UnixDomainSocketImpl::connectHandler, + shared_from_this(), + handler, ph::_1); + socket_.async_connect(endpoint, local_handler); +} + +void +UnixDomainSocketImpl::connectHandler(const UnixDomainSocket::ConnectHandler& remote_handler, + const boost::system::error_code& ec) { + // It was observed on Debian and Fedora that asynchronous connect may result + // in EINPROGRESS error. This doesn't really indicate a problem with a + // connection. If we continue transmitting data over the socket it will + // succeed. So we suppress this error and return 'success' to the user's + // handler. + if (ec.value() == boost::asio::error::in_progress) { + remote_handler(boost::system::error_code()); + } else { + remote_handler(ec); + } +} + +void +UnixDomainSocketImpl::asyncSend(const void* data, const size_t length, + const UnixDomainSocket::Handler& handler) { + doSend(boost::asio::buffer(data, length), handler); +} + +void +UnixDomainSocketImpl::doSend(const boost::asio::const_buffers_1& buffer, + const UnixDomainSocket::Handler& handler) { + auto local_handler = std::bind(&UnixDomainSocketImpl::sendHandler, + shared_from_this(), + handler, buffer, ph::_1, ph::_2); + socket_.async_send(buffer, local_handler); +} + +void +UnixDomainSocketImpl::sendHandler(const UnixDomainSocket::Handler& remote_handler, + const boost::asio::const_buffers_1& buffer, + const boost::system::error_code& ec, + size_t length) { + // The asynchronous send may return EWOULDBLOCK or EAGAIN on some + // operating systems. In this case, we simply retry hoping that it + // will succeed next time. The user's callback never sees these + // errors. + if ((ec.value() == boost::asio::error::would_block) || + (ec.value() == boost::asio::error::try_again)) { + doSend(buffer, remote_handler); + + } else { + remote_handler(ec, length); + } +} + +void +UnixDomainSocketImpl::asyncReceive(void* data, const size_t length, + const UnixDomainSocket::Handler& handler) { + doReceive(boost::asio::buffer(data, length), handler); +} + +void +UnixDomainSocketImpl::doReceive(const boost::asio::mutable_buffers_1& buffer, + const UnixDomainSocket::Handler& handler) { + auto local_handler = std::bind(&UnixDomainSocketImpl::receiveHandler, + shared_from_this(), + handler, buffer, ph::_1, ph::_2); + socket_.async_receive(buffer, 0, local_handler); +} + +void +UnixDomainSocketImpl::receiveHandler(const UnixDomainSocket::Handler& remote_handler, + const boost::asio::mutable_buffers_1& buffer, + const boost::system::error_code& ec, + size_t length) { + // The asynchronous receive may return EWOULDBLOCK or EAGAIN on some + // operating systems. In this case, we simply retry hoping that it + // will succeed next time. The user's callback never sees these + // errors. + if ((ec.value() == boost::asio::error::would_block) || + (ec.value() == boost::asio::error::try_again)) { + doReceive(buffer, remote_handler); + + } else { + remote_handler(ec, length); + } +} + +void +UnixDomainSocketImpl::shutdown() { + boost::system::error_code ec; + static_cast<void>(socket_.shutdown(stream_protocol::socket::shutdown_both, ec)); + if (ec) { + isc_throw(UnixDomainSocketError, ec.message()); + } +} + +void +UnixDomainSocketImpl::cancel() { + boost::system::error_code ec; + static_cast<void>(socket_.cancel(ec)); + if (ec) { + isc_throw(UnixDomainSocketError, ec.message()); + } +} + +void +UnixDomainSocketImpl::close() { + boost::system::error_code ec; + static_cast<void>(socket_.close(ec)); + if (ec) { + isc_throw(UnixDomainSocketError, ec.message()); + } +} + +UnixDomainSocket::UnixDomainSocket(IOService& io_service) + : impl_(new UnixDomainSocketImpl(io_service)) { +} + +int +UnixDomainSocket::getNative() const { +#if BOOST_VERSION < 106600 + return (impl_->socket_.native()); +#else + return (impl_->socket_.native_handle()); +#endif +} + +int +UnixDomainSocket::getProtocol() const { + return (0); +} + +void +UnixDomainSocket::connect(const std::string& path) { + boost::system::error_code ec; + impl_->socket_.connect(stream_protocol::endpoint(path.c_str()), ec); + if (ec) { + isc_throw(UnixDomainSocketError, ec.message()); + } +} + +void +UnixDomainSocket::asyncConnect(const std::string& path, const ConnectHandler& handler) { + impl_->asyncConnect(stream_protocol::endpoint(path.c_str()), handler); +} + +size_t +UnixDomainSocket::write(const void* data, size_t length) { + boost::system::error_code ec; + size_t res = boost::asio::write(impl_->socket_, + boost::asio::buffer(data, length), + boost::asio::transfer_all(), + ec); + if (ec) { + isc_throw(UnixDomainSocketError, ec.message()); + } + return (res); +} + +void +UnixDomainSocket::asyncSend(const void* data, const size_t length, + const Handler& handler) { + impl_->asyncSend(data, length, handler); +} + +size_t +UnixDomainSocket::receive(void* data, size_t length) { + boost::system::error_code ec; + size_t res = impl_->socket_.receive(boost::asio::buffer(data, length), 0, ec); + if (ec) { + isc_throw(UnixDomainSocketError, ec.message()); + } + return (res); +} + +void +UnixDomainSocket::asyncReceive(void* data, const size_t length, + const Handler& handler) { + impl_->asyncReceive(data, length, handler); +} + +void +UnixDomainSocket::shutdown() { + impl_->shutdown(); +} + +void +UnixDomainSocket::cancel() { + impl_->cancel(); +} + +void +UnixDomainSocket::close() { + impl_->close(); +} + +boost::asio::local::stream_protocol::socket& +UnixDomainSocket::getASIOSocket() const { + return (impl_->socket_); +} + +} // end of namespace asiolink +} // end of namespace isc diff --git a/src/lib/asiolink/unix_domain_socket.h b/src/lib/asiolink/unix_domain_socket.h new file mode 100644 index 0000000..cd02f41 --- /dev/null +++ b/src/lib/asiolink/unix_domain_socket.h @@ -0,0 +1,137 @@ +// Copyright (C) 2017 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/. + +#ifndef UNIX_DOMAIN_SOCKET_H +#define UNIX_DOMAIN_SOCKET_H + +#include <asiolink/io_service.h> +#include <asiolink/io_socket.h> +#include <boost/shared_ptr.hpp> +#include <functional> +#include <string> + +namespace isc { +namespace asiolink { + +/// @brief Exception thrown upon socket error. +class UnixDomainSocketError : public Exception { +public: + UnixDomainSocketError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +class UnixDomainSocketImpl; + +/// @brief Represents unix domain socket implemented in terms +/// of boost asio. +class UnixDomainSocket : public IOSocket { +public: + + /// @brief Callback type used in call to @ref UnixDomainSocket::asyncConnect. + typedef std::function<void(const boost::system::error_code&)> ConnectHandler; + + /// @brief Callback type used in calls to @ref UnixDomainSocket::asyncSend + /// and @ref UnixDomainSocket::asyncReceive. + typedef std::function<void(const boost::system::error_code&, size_t)> Handler; + + /// @brief Constructor. + /// + /// @param io_service Reference to IOService to be used by this + /// class. + explicit UnixDomainSocket(IOService& io_service); + + /// @brief Returns native socket representation. + virtual int getNative() const; + + /// @brief Always returns 0. + virtual int getProtocol() const; + + /// @brief Connects the socket to the specified endpoint. + /// + /// @param path Path to the unix socket to which we should connect. + /// + /// @throw UnixDomainSocketError if error occurs. + void connect(const std::string& path); + + /// @brief Asynchronously connects the socket to the specified endpoint. + /// + /// Always returns immediately. + /// + /// @param path Path to the unix socket to which we should connect. + /// @param handler Callback to be invoked when connection is established or + /// a connection error is signalled. + void asyncConnect(const std::string& path, const ConnectHandler& handler); + + /// @brief Writes specified amount of data to a socket. + /// + /// @param data Pointer to data to be written. + /// @param length Number of bytes to be written. + /// + /// @return Number of bytes written. + /// @throw UnixDomainSocketError if error occurs. + size_t write(const void* data, size_t length); + + /// @brief Asynchronously sends data over the socket. + /// + /// Always returns immediately. + /// + /// @param data Pointer to data to be sent. + /// @param length Number of bytes to be sent. + /// @param handler Callback to be invoked when data have been sent or + /// sending error is signalled. + void asyncSend(const void* data, const size_t length, const Handler& handler); + + /// @brief Receives data from a socket. + /// + /// @param [out] data Pointer to a location into which the read data should + /// be stored. + /// @param length Length of the buffer. + /// + /// @return Number of bytes read. + /// @throw UnixDomainSocketError if error occurs. + size_t receive(void* data, size_t length); + + /// @brief Asynchronously receives data over the socket. + /// + /// Always returns immediately. + /// @param [out] data Pointer to a location into which the read data should + /// be stored. + /// @param length Length of the buffer. + /// @param handler Callback to be invoked when data have been received or an + /// error is signalled. + void asyncReceive(void* data, const size_t length, const Handler& handler); + + /// @brief Disables read and write operations on the socket. + /// + /// @throw UnixDomainSocketError if an error occurs during shutdown. + void shutdown(); + + /// @brief Cancels scheduled asynchronous operations on the socket. + /// + /// @throw UnixDomainSocketError if an error occurs during cancel operation. + void cancel(); + + /// @brief Closes the socket. + /// + /// @throw UnixDomainSocketError if an error occurs during closure. + void close(); + + /// @brief Returns reference to the underlying ASIO socket. + /// + /// @return Reference to underlying ASIO socket. + virtual boost::asio::local::stream_protocol::socket& getASIOSocket() const; + +private: + + /// @brief Pointer to the implementation of this class. + boost::shared_ptr<UnixDomainSocketImpl> impl_; + +}; + +} // end of namespace isc::asiolink +} // end of namespace isc + +#endif // UNIX_DOMAIN_SOCKET_H diff --git a/src/lib/asiolink/unix_domain_socket_acceptor.h b/src/lib/asiolink/unix_domain_socket_acceptor.h new file mode 100644 index 0000000..8aa11ca --- /dev/null +++ b/src/lib/asiolink/unix_domain_socket_acceptor.h @@ -0,0 +1,65 @@ +// Copyright (C) 2017 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/. + +#ifndef UNIX_DOMAIN_SOCKET_ACCEPTOR_H +#define UNIX_DOMAIN_SOCKET_ACCEPTOR_H + +#ifndef BOOST_ASIO_HPP +#error "asio.hpp must be included before including this, see asiolink.h as to why" +#endif + +#include <asiolink/io_acceptor.h> +#include <asiolink/unix_domain_socket.h> +#include <functional> + +namespace isc { +namespace asiolink { + +/// @brief Implements acceptor service for @ref UnixDomainSocket. +/// +/// This class is used to accept new incoming connections over unix domain +/// sockets. +class UnixDomainSocketAcceptor : public IOAcceptor<boost::asio::local::stream_protocol, + std::function<void(const boost::system::error_code&)> > { +public: + + /// @brief Callback type used in call to @ref UnixDomainSocketAcceptor::asyncAccept. + typedef std::function<void(const boost::system::error_code&)> AcceptHandler; + + /// @brief Constructor. + /// + /// @param io_service Reference to the IO service. + explicit UnixDomainSocketAcceptor(IOService& io_service) + : IOAcceptor<boost::asio::local::stream_protocol, + std::function<void(const boost::system::error_code&)> >(io_service) { + } + + /// @brief Returns the transport protocol of the socket. + /// + /// @return AF_LOCAL. + virtual int getProtocol() const final { + return (AF_LOCAL); + } + + /// @brief Asynchronously accept new connection. + /// + /// This method accepts new connection into the specified socket. When the + /// new connection arrives or an error occurs the specified callback function + /// is invoked. + /// + /// @param socket Socket into which connection should be accepted. + /// @param callback Callback function to be invoked when the new connection + /// arrives. + /// @tparam SocketType + void asyncAccept(const UnixDomainSocket& socket, const AcceptHandler& callback) { + asyncAcceptInternal(socket, callback); + } +}; + +} // end of namespace isc::asiolink +} // end of namespace isc + +#endif // UNIX_DOMAIN_SOCKET_ACCEPTOR_H diff --git a/src/lib/asiolink/unix_domain_socket_endpoint.h b/src/lib/asiolink/unix_domain_socket_endpoint.h new file mode 100644 index 0000000..9378a83 --- /dev/null +++ b/src/lib/asiolink/unix_domain_socket_endpoint.h @@ -0,0 +1,50 @@ +// Copyright (C) 2017 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/. + +#ifndef UNIX_DOMAIN_SOCKET_ENDPOINT_H +#define UNIX_DOMAIN_SOCKET_ENDPOINT_H + +#ifndef BOOST_ASIO_HPP +#error "asio.hpp must be included before including this, see asiolink.h as to why" +#endif + +#include <string> + +namespace isc { +namespace asiolink { + +/// @brief Endpoint for @ref UnixDomainSocket. +/// +/// This is a simple class encapsulating ASIO unix domain socket endpoint. +/// It is used to represent endpoints taking part in communication via +/// unix domain sockets. +class UnixDomainSocketEndpoint { +public: + + /// @brief Constructor. + /// + /// @param endpoint_path Path to the socket descriptor. + explicit UnixDomainSocketEndpoint(const std::string& endpoint_path) + : endpoint_(endpoint_path) { + } + + /// @brief Returns underlying ASIO endpoint. + const boost::asio::local::stream_protocol::endpoint& + getASIOEndpoint() const { + return (endpoint_); + } + +private: + + /// @brief Underlying ASIO endpoint. + boost::asio::local::stream_protocol::endpoint endpoint_; + +}; + +} // end of namespace isc::asiolink +} // end of namespace isc + +#endif // UNIX_DOMAIN_SOCKET_ENDPOINT_H |