summaryrefslogtreecommitdiffstats
path: root/src/lib/asiolink
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib/asiolink/Makefile.am98
-rw-r--r--src/lib/asiolink/Makefile.in1138
-rw-r--r--src/lib/asiolink/README28
-rw-r--r--src/lib/asiolink/addr_utilities.cc430
-rw-r--r--src/lib/asiolink/addr_utilities.h98
-rw-r--r--src/lib/asiolink/asio_wrapper.h78
-rw-r--r--src/lib/asiolink/asiolink.dox103
-rw-r--r--src/lib/asiolink/asiolink.h67
-rw-r--r--src/lib/asiolink/botan_boost_tls.cc339
-rw-r--r--src/lib/asiolink/botan_boost_tls.h207
-rw-r--r--src/lib/asiolink/botan_boost_wrapper.h32
-rw-r--r--src/lib/asiolink/botan_tls.cc60
-rw-r--r--src/lib/asiolink/botan_tls.h168
-rw-r--r--src/lib/asiolink/common_tls.cc65
-rw-r--r--src/lib/asiolink/common_tls.h183
-rw-r--r--src/lib/asiolink/crypto_tls.h27
-rw-r--r--src/lib/asiolink/dummy_io_cb.h65
-rw-r--r--src/lib/asiolink/interval_timer.cc201
-rw-r--r--src/lib/asiolink/interval_timer.h142
-rw-r--r--src/lib/asiolink/io_acceptor.h136
-rw-r--r--src/lib/asiolink/io_address.cc189
-rw-r--r--src/lib/asiolink/io_address.h318
-rw-r--r--src/lib/asiolink/io_asio_socket.h381
-rw-r--r--src/lib/asiolink/io_endpoint.cc68
-rw-r--r--src/lib/asiolink/io_endpoint.h183
-rw-r--r--src/lib/asiolink/io_error.h29
-rw-r--r--src/lib/asiolink/io_service.cc182
-rw-r--r--src/lib/asiolink/io_service.h114
-rw-r--r--src/lib/asiolink/io_service_signal.cc129
-rw-r--r--src/lib/asiolink/io_service_signal.h60
-rw-r--r--src/lib/asiolink/io_socket.cc57
-rw-r--r--src/lib/asiolink/io_socket.h128
-rw-r--r--src/lib/asiolink/openssl_tls.cc143
-rw-r--r--src/lib/asiolink/openssl_tls.h242
-rw-r--r--src/lib/asiolink/process_spawn.cc448
-rw-r--r--src/lib/asiolink/process_spawn.h149
-rw-r--r--src/lib/asiolink/tcp_acceptor.h69
-rw-r--r--src/lib/asiolink/tcp_endpoint.h115
-rw-r--r--src/lib/asiolink/tcp_socket.h503
-rw-r--r--src/lib/asiolink/tests/Makefile.am75
-rw-r--r--src/lib/asiolink/tests/Makefile.in1260
-rw-r--r--src/lib/asiolink/tests/addr_utilities_unittest.cc386
-rw-r--r--src/lib/asiolink/tests/dummy_io_callback_unittest.cc27
-rw-r--r--src/lib/asiolink/tests/hash_address_unittest.cc58
-rw-r--r--src/lib/asiolink/tests/interval_timer_unittest.cc377
-rw-r--r--src/lib/asiolink/tests/io_address_unittest.cc341
-rw-r--r--src/lib/asiolink/tests/io_endpoint_unittest.cc286
-rw-r--r--src/lib/asiolink/tests/io_service_signal_unittests.cc279
-rw-r--r--src/lib/asiolink/tests/io_service_unittest.cc48
-rw-r--r--src/lib/asiolink/tests/io_socket_unittest.cc25
-rw-r--r--src/lib/asiolink/tests/process_spawn_app.sh.in75
-rw-r--r--src/lib/asiolink/tests/process_spawn_unittest.cc347
-rw-r--r--src/lib/asiolink/tests/run_unittests.cc19
-rw-r--r--src/lib/asiolink/tests/tcp_acceptor_unittest.cc444
-rw-r--r--src/lib/asiolink/tests/tcp_endpoint_unittest.cc46
-rw-r--r--src/lib/asiolink/tests/tcp_socket_unittest.cc515
-rw-r--r--src/lib/asiolink/tests/tls_acceptor_unittest.cc450
-rw-r--r--src/lib/asiolink/tests/tls_socket_unittest.cc560
-rw-r--r--src/lib/asiolink/tests/tls_unittest.cc2693
-rw-r--r--src/lib/asiolink/tests/udp_endpoint_unittest.cc46
-rw-r--r--src/lib/asiolink/tests/udp_socket_unittest.cc324
-rw-r--r--src/lib/asiolink/tests/unix_domain_socket_unittest.cc310
-rw-r--r--src/lib/asiolink/testutils/Makefile.am93
-rw-r--r--src/lib/asiolink/testutils/Makefile.in1052
-rw-r--r--src/lib/asiolink/testutils/botan_boost_sample_client.cc229
-rw-r--r--src/lib/asiolink/testutils/botan_boost_sample_server.cc220
-rw-r--r--src/lib/asiolink/testutils/ca/00af7a28.019
-rw-r--r--src/lib/asiolink/testutils/ca/0c7eedb9.024
-rw-r--r--src/lib/asiolink/testutils/ca/28f5a777.023
-rw-r--r--src/lib/asiolink/testutils/ca/2eefa08b.030
-rw-r--r--src/lib/asiolink/testutils/ca/71336a4d.023
-rw-r--r--src/lib/asiolink/testutils/ca/7a5b785e.023
-rw-r--r--src/lib/asiolink/testutils/ca/ad950210.024
-rw-r--r--src/lib/asiolink/testutils/ca/doc.txt129
-rw-r--r--src/lib/asiolink/testutils/ca/ext-addr-conf.cnf1
-rw-r--r--src/lib/asiolink/testutils/ca/ext-conf.cnf1
-rw-r--r--src/lib/asiolink/testutils/ca/kea-ca.crt30
-rw-r--r--src/lib/asiolink/testutils/ca/kea-ca.key54
-rw-r--r--src/lib/asiolink/testutils/ca/kea-client.crt23
-rw-r--r--src/lib/asiolink/testutils/ca/kea-client.csr16
-rw-r--r--src/lib/asiolink/testutils/ca/kea-client.key28
-rw-r--r--src/lib/asiolink/testutils/ca/kea-client.p12bin0 -> 2597 bytes
-rw-r--r--src/lib/asiolink/testutils/ca/kea-other.crt23
-rw-r--r--src/lib/asiolink/testutils/ca/kea-other.key28
-rw-r--r--src/lib/asiolink/testutils/ca/kea-self.crt19
-rw-r--r--src/lib/asiolink/testutils/ca/kea-self.key28
-rw-r--r--src/lib/asiolink/testutils/ca/kea-server-addr.crt24
-rw-r--r--src/lib/asiolink/testutils/ca/kea-server-addr.csr17
-rw-r--r--src/lib/asiolink/testutils/ca/kea-server-raw.crt23
-rw-r--r--src/lib/asiolink/testutils/ca/kea-server-raw.csr16
-rw-r--r--src/lib/asiolink/testutils/ca/kea-server.crt24
-rw-r--r--src/lib/asiolink/testutils/ca/kea-server.csr17
-rw-r--r--src/lib/asiolink/testutils/ca/kea-server.key28
-rw-r--r--src/lib/asiolink/testutils/ca/server-addr-conf.cnf355
-rw-r--r--src/lib/asiolink/testutils/ca/server-conf.cnf354
-rw-r--r--src/lib/asiolink/testutils/openssl_sample_client.cc186
-rw-r--r--src/lib/asiolink/testutils/openssl_sample_server.cc192
-rw-r--r--src/lib/asiolink/testutils/test_server_unix_socket.cc331
-rw-r--r--src/lib/asiolink/testutils/test_server_unix_socket.h171
-rw-r--r--src/lib/asiolink/testutils/test_tls.cc74
-rw-r--r--src/lib/asiolink/testutils/test_tls.h47
-rw-r--r--src/lib/asiolink/testutils/timed_signal.cc17
-rw-r--r--src/lib/asiolink/testutils/timed_signal.h86
-rw-r--r--src/lib/asiolink/tls_acceptor.h62
-rw-r--r--src/lib/asiolink/tls_socket.h519
-rw-r--r--src/lib/asiolink/udp_endpoint.h115
-rw-r--r--src/lib/asiolink/udp_socket.h323
-rw-r--r--src/lib/asiolink/unix_domain_socket.cc371
-rw-r--r--src/lib/asiolink/unix_domain_socket.h137
-rw-r--r--src/lib/asiolink/unix_domain_socket_acceptor.h65
-rw-r--r--src/lib/asiolink/unix_domain_socket_endpoint.h50
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: [&lt;address&gt;]:port (e.g., [2001:db8::5300]:53)
+/// - For IPv4 address: &lt;address&gt;: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=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(srcdir)/process_spawn_app.sh.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_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
new file mode 100644
index 0000000..baf4420
--- /dev/null
+++ b/src/lib/asiolink/testutils/ca/kea-client.p12
Binary files differ
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