diff options
author | Yves-Alexis Perez <corsac@debian.org> | 2014-07-11 07:23:31 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@debian.org> | 2014-07-11 07:23:31 +0200 |
commit | 81c63b0eed39432878f78727f60a1e7499645199 (patch) | |
tree | 82387d8fecd1c20788fd8bd784a9b0bde091fb6b /src/libcharon/plugins/vici | |
parent | c5ebfc7b9c16551fe825dc1d79c3f7e2f096f6c9 (diff) | |
download | vyos-strongswan-81c63b0eed39432878f78727f60a1e7499645199.tar.gz vyos-strongswan-81c63b0eed39432878f78727f60a1e7499645199.zip |
Imported Upstream version 5.2.0
Diffstat (limited to 'src/libcharon/plugins/vici')
33 files changed, 11731 insertions, 0 deletions
diff --git a/src/libcharon/plugins/vici/Makefile.am b/src/libcharon/plugins/vici/Makefile.am new file mode 100644 index 000000000..7e459c58d --- /dev/null +++ b/src/libcharon/plugins/vici/Makefile.am @@ -0,0 +1,69 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon \ + -DIPSEC_PIDDIR=\"${piddir}\" + +AM_CFLAGS = \ + $(PLUGIN_CFLAGS) + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-vici.la +else +plugin_LTLIBRARIES = libstrongswan-vici.la +endif + +libstrongswan_vici_la_SOURCES = \ + vici_socket.h vici_socket.c \ + vici_message.h vici_message.c \ + vici_builder.h vici_builder.c \ + vici_dispatcher.h vici_dispatcher.c \ + vici_query.h vici_query.c \ + vici_control.h vici_control.c \ + vici_config.h vici_config.c \ + vici_cred.h vici_cred.c \ + vici_attribute.h vici_attribute.c \ + vici_logger.h vici_logger.c \ + vici_plugin.h vici_plugin.c + +libstrongswan_vici_la_LDFLAGS = -module -avoid-version + + +EXTRA_DIST = README.md + + +ipseclib_LTLIBRARIES = libvici.la + +libvici_la_SOURCES = \ + vici_message.c vici_message.h \ + vici_builder.c vici_builder.h \ + libvici.c libvici.h + +libvici_la_LIBADD = $(top_builddir)/src/libstrongswan/libstrongswan.la + + +TESTS = vici_tests + +check_PROGRAMS = $(TESTS) + +vici_tests_SOURCES = \ + suites/test_socket.c \ + suites/test_message.c \ + suites/test_request.c \ + suites/test_event.c \ + vici_socket.c \ + vici_message.c \ + vici_builder.c \ + vici_dispatcher.c \ + libvici.c \ + vici_tests.h vici_tests.c + +vici_tests_CFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libstrongswan/tests \ + @COVERAGE_CFLAGS@ + +vici_tests_LDFLAGS = @COVERAGE_LDFLAGS@ +vici_tests_LDADD = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libstrongswan/tests/libtest.la diff --git a/src/libcharon/plugins/vici/Makefile.in b/src/libcharon/plugins/vici/Makefile.in new file mode 100644 index 000000000..e0a6a1b5d --- /dev/null +++ b/src/libcharon/plugins/vici/Makefile.in @@ -0,0 +1,1183 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 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 = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +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 = vici_tests$(EXEEXT) +check_PROGRAMS = $(am__EXEEXT_1) +subdir = src/libcharon/plugins/vici +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ + $(top_srcdir)/m4/config/ltoptions.m4 \ + $(top_srcdir)/m4/config/ltsugar.m4 \ + $(top_srcdir)/m4/config/ltversion.m4 \ + $(top_srcdir)/m4/config/lt~obsolete.m4 \ + $(top_srcdir)/m4/macros/split-package-version.m4 \ + $(top_srcdir)/m4/macros/with.m4 \ + $(top_srcdir)/m4/macros/enable-disable.m4 \ + $(top_srcdir)/m4/macros/add-plugin.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +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)$(ipseclibdir)" "$(DESTDIR)$(plugindir)" +LTLIBRARIES = $(ipseclib_LTLIBRARIES) $(noinst_LTLIBRARIES) \ + $(plugin_LTLIBRARIES) +libstrongswan_vici_la_LIBADD = +am_libstrongswan_vici_la_OBJECTS = vici_socket.lo vici_message.lo \ + vici_builder.lo vici_dispatcher.lo vici_query.lo \ + vici_control.lo vici_config.lo vici_cred.lo vici_attribute.lo \ + vici_logger.lo vici_plugin.lo +libstrongswan_vici_la_OBJECTS = $(am_libstrongswan_vici_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 = +libstrongswan_vici_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libstrongswan_vici_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@MONOLITHIC_FALSE@am_libstrongswan_vici_la_rpath = -rpath $(plugindir) +@MONOLITHIC_TRUE@am_libstrongswan_vici_la_rpath = +libvici_la_DEPENDENCIES = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la +am_libvici_la_OBJECTS = vici_message.lo vici_builder.lo libvici.lo +libvici_la_OBJECTS = $(am_libvici_la_OBJECTS) +am__EXEEXT_1 = vici_tests$(EXEEXT) +am__dirstamp = $(am__leading_dot)dirstamp +am_vici_tests_OBJECTS = suites/vici_tests-test_socket.$(OBJEXT) \ + suites/vici_tests-test_message.$(OBJEXT) \ + suites/vici_tests-test_request.$(OBJEXT) \ + suites/vici_tests-test_event.$(OBJEXT) \ + vici_tests-vici_socket.$(OBJEXT) \ + vici_tests-vici_message.$(OBJEXT) \ + vici_tests-vici_builder.$(OBJEXT) \ + vici_tests-vici_dispatcher.$(OBJEXT) \ + vici_tests-libvici.$(OBJEXT) vici_tests-vici_tests.$(OBJEXT) +vici_tests_OBJECTS = $(am_vici_tests_OBJECTS) +vici_tests_DEPENDENCIES = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libstrongswan/tests/libtest.la +vici_tests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(vici_tests_CFLAGS) \ + $(CFLAGS) $(vici_tests_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__depfiles_maybe = depfiles +am__mv = mv -f +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 = $(libstrongswan_vici_la_SOURCES) $(libvici_la_SOURCES) \ + $(vici_tests_SOURCES) +DIST_SOURCES = $(libstrongswan_vici_la_SOURCES) $(libvici_la_SOURCES) \ + $(vici_tests_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BFDLIB = @BFDLIB@ +BTLIB = @BTLIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +COVERAGE_CFLAGS = @COVERAGE_CFLAGS@ +COVERAGE_LDFLAGS = @COVERAGE_LDFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLIB = @DLLIB@ +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@ +GPERF = @GPERF@ +GPRBUILD = @GPRBUILD@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +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@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQLCFLAG = @MYSQLCFLAG@ +MYSQLCONFIG = @MYSQLCONFIG@ +MYSQLLIB = @MYSQLLIB@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_LIB = @OPENSSL_LIB@ +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_BUILD = @PACKAGE_VERSION_BUILD@ +PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@ +PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@ +PACKAGE_VERSION_REVIEW = @PACKAGE_VERSION_REVIEW@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PLUGIN_CFLAGS = @PLUGIN_CFLAGS@ +PTHREADLIB = @PTHREADLIB@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +RTLIB = @RTLIB@ +RUBY = @RUBY@ +RUBYINCLUDE = @RUBYINCLUDE@ +RUBYLIB = @RUBYLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SOCKLIB = @SOCKLIB@ +STRIP = @STRIP@ +UNWINDLIB = @UNWINDLIB@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +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_DUMPBIN = @ac_ct_DUMPBIN@ +aikgen_plugins = @aikgen_plugins@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +attest_plugins = @attest_plugins@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +c_plugins = @c_plugins@ +charon_natt_port = @charon_natt_port@ +charon_plugins = @charon_plugins@ +charon_udp_port = @charon_udp_port@ +clearsilver_LIBS = @clearsilver_LIBS@ +cmd_plugins = @cmd_plugins@ +datadir = @datadir@ +datarootdir = @datarootdir@ +dbusservicedir = @dbusservicedir@ +dev_headers = @dev_headers@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fips_mode = @fips_mode@ +gtk_CFLAGS = @gtk_CFLAGS@ +gtk_LIBS = @gtk_LIBS@ +h_plugins = @h_plugins@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +imcvdir = @imcvdir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsec_script = @ipsec_script@ +ipsec_script_upper = @ipsec_script_upper@ +ipsecdir = @ipsecdir@ +ipsecgroup = @ipsecgroup@ +ipseclibdir = @ipseclibdir@ +ipsecuser = @ipsecuser@ +libdir = @libdir@ +libexecdir = @libexecdir@ +linux_headers = @linux_headers@ +localedir = @localedir@ +localstatedir = @localstatedir@ +maemo_CFLAGS = @maemo_CFLAGS@ +maemo_LIBS = @maemo_LIBS@ +manager_plugins = @manager_plugins@ +mandir = @mandir@ +medsrv_plugins = @medsrv_plugins@ +mkdir_p = @mkdir_p@ +nm_CFLAGS = @nm_CFLAGS@ +nm_LIBS = @nm_LIBS@ +nm_ca_dir = @nm_ca_dir@ +nm_plugins = @nm_plugins@ +oldincludedir = @oldincludedir@ +pcsclite_CFLAGS = @pcsclite_CFLAGS@ +pcsclite_LIBS = @pcsclite_LIBS@ +pdfdir = @pdfdir@ +piddir = @piddir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +pki_plugins = @pki_plugins@ +plugindir = @plugindir@ +pool_plugins = @pool_plugins@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +random_device = @random_device@ +resolv_conf = @resolv_conf@ +routing_table = @routing_table@ +routing_table_prio = @routing_table_prio@ +s_plugins = @s_plugins@ +sbindir = @sbindir@ +scepclient_plugins = @scepclient_plugins@ +scripts_plugins = @scripts_plugins@ +sharedstatedir = @sharedstatedir@ +soup_CFLAGS = @soup_CFLAGS@ +soup_LIBS = @soup_LIBS@ +srcdir = @srcdir@ +starter_plugins = @starter_plugins@ +strongswan_conf = @strongswan_conf@ +strongswan_options = @strongswan_options@ +swanctldir = @swanctldir@ +sysconfdir = @sysconfdir@ +systemdsystemunitdir = @systemdsystemunitdir@ +t_plugins = @t_plugins@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +urandom_device = @urandom_device@ +xml_CFLAGS = @xml_CFLAGS@ +xml_LIBS = @xml_LIBS@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon \ + -DIPSEC_PIDDIR=\"${piddir}\" + +AM_CFLAGS = \ + $(PLUGIN_CFLAGS) + +@MONOLITHIC_TRUE@noinst_LTLIBRARIES = libstrongswan-vici.la +@MONOLITHIC_FALSE@plugin_LTLIBRARIES = libstrongswan-vici.la +libstrongswan_vici_la_SOURCES = \ + vici_socket.h vici_socket.c \ + vici_message.h vici_message.c \ + vici_builder.h vici_builder.c \ + vici_dispatcher.h vici_dispatcher.c \ + vici_query.h vici_query.c \ + vici_control.h vici_control.c \ + vici_config.h vici_config.c \ + vici_cred.h vici_cred.c \ + vici_attribute.h vici_attribute.c \ + vici_logger.h vici_logger.c \ + vici_plugin.h vici_plugin.c + +libstrongswan_vici_la_LDFLAGS = -module -avoid-version +EXTRA_DIST = README.md +ipseclib_LTLIBRARIES = libvici.la +libvici_la_SOURCES = \ + vici_message.c vici_message.h \ + vici_builder.c vici_builder.h \ + libvici.c libvici.h + +libvici_la_LIBADD = $(top_builddir)/src/libstrongswan/libstrongswan.la +vici_tests_SOURCES = \ + suites/test_socket.c \ + suites/test_message.c \ + suites/test_request.c \ + suites/test_event.c \ + vici_socket.c \ + vici_message.c \ + vici_builder.c \ + vici_dispatcher.c \ + libvici.c \ + vici_tests.h vici_tests.c + +vici_tests_CFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libstrongswan/tests \ + @COVERAGE_CFLAGS@ + +vici_tests_LDFLAGS = @COVERAGE_LDFLAGS@ +vici_tests_LDADD = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libstrongswan/tests/libtest.la + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .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) --gnu src/libcharon/plugins/vici/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/libcharon/plugins/vici/Makefile +.PRECIOUS: 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__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + 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-ipseclibLTLIBRARIES: $(ipseclib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(ipseclib_LTLIBRARIES)'; test -n "$(ipseclibdir)" || 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)$(ipseclibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(ipseclibdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(ipseclibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(ipseclibdir)"; \ + } + +uninstall-ipseclibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(ipseclib_LTLIBRARIES)'; test -n "$(ipseclibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(ipseclibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(ipseclibdir)/$$f"; \ + done + +clean-ipseclibLTLIBRARIES: + -test -z "$(ipseclib_LTLIBRARIES)" || rm -f $(ipseclib_LTLIBRARIES) + @list='$(ipseclib_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}; \ + } + +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}; \ + } + +install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || 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)$(plugindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(plugindir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \ + } + +uninstall-pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \ + done + +clean-pluginLTLIBRARIES: + -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES) + @list='$(plugin_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}; \ + } + +libstrongswan-vici.la: $(libstrongswan_vici_la_OBJECTS) $(libstrongswan_vici_la_DEPENDENCIES) $(EXTRA_libstrongswan_vici_la_DEPENDENCIES) + $(AM_V_CCLD)$(libstrongswan_vici_la_LINK) $(am_libstrongswan_vici_la_rpath) $(libstrongswan_vici_la_OBJECTS) $(libstrongswan_vici_la_LIBADD) $(LIBS) + +libvici.la: $(libvici_la_OBJECTS) $(libvici_la_DEPENDENCIES) $(EXTRA_libvici_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) -rpath $(ipseclibdir) $(libvici_la_OBJECTS) $(libvici_la_LIBADD) $(LIBS) + +clean-checkPROGRAMS: + @list='$(check_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 +suites/$(am__dirstamp): + @$(MKDIR_P) suites + @: > suites/$(am__dirstamp) +suites/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) suites/$(DEPDIR) + @: > suites/$(DEPDIR)/$(am__dirstamp) +suites/vici_tests-test_socket.$(OBJEXT): suites/$(am__dirstamp) \ + suites/$(DEPDIR)/$(am__dirstamp) +suites/vici_tests-test_message.$(OBJEXT): suites/$(am__dirstamp) \ + suites/$(DEPDIR)/$(am__dirstamp) +suites/vici_tests-test_request.$(OBJEXT): suites/$(am__dirstamp) \ + suites/$(DEPDIR)/$(am__dirstamp) +suites/vici_tests-test_event.$(OBJEXT): suites/$(am__dirstamp) \ + suites/$(DEPDIR)/$(am__dirstamp) + +vici_tests$(EXEEXT): $(vici_tests_OBJECTS) $(vici_tests_DEPENDENCIES) $(EXTRA_vici_tests_DEPENDENCIES) + @rm -f vici_tests$(EXEEXT) + $(AM_V_CCLD)$(vici_tests_LINK) $(vici_tests_OBJECTS) $(vici_tests_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f suites/*.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvici.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_attribute.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_builder.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_config.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_control.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_cred.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_dispatcher.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_logger.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_message.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_plugin.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_query.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_socket.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_tests-libvici.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_tests-vici_builder.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_tests-vici_dispatcher.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_tests-vici_message.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_tests-vici_socket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_tests-vici_tests.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/vici_tests-test_event.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/vici_tests-test_message.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/vici_tests-test_request.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/vici_tests-test_socket.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +suites/vici_tests-test_socket.o: suites/test_socket.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT suites/vici_tests-test_socket.o -MD -MP -MF suites/$(DEPDIR)/vici_tests-test_socket.Tpo -c -o suites/vici_tests-test_socket.o `test -f 'suites/test_socket.c' || echo '$(srcdir)/'`suites/test_socket.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/vici_tests-test_socket.Tpo suites/$(DEPDIR)/vici_tests-test_socket.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_socket.c' object='suites/vici_tests-test_socket.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o suites/vici_tests-test_socket.o `test -f 'suites/test_socket.c' || echo '$(srcdir)/'`suites/test_socket.c + +suites/vici_tests-test_socket.obj: suites/test_socket.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT suites/vici_tests-test_socket.obj -MD -MP -MF suites/$(DEPDIR)/vici_tests-test_socket.Tpo -c -o suites/vici_tests-test_socket.obj `if test -f 'suites/test_socket.c'; then $(CYGPATH_W) 'suites/test_socket.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_socket.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/vici_tests-test_socket.Tpo suites/$(DEPDIR)/vici_tests-test_socket.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_socket.c' object='suites/vici_tests-test_socket.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o suites/vici_tests-test_socket.obj `if test -f 'suites/test_socket.c'; then $(CYGPATH_W) 'suites/test_socket.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_socket.c'; fi` + +suites/vici_tests-test_message.o: suites/test_message.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT suites/vici_tests-test_message.o -MD -MP -MF suites/$(DEPDIR)/vici_tests-test_message.Tpo -c -o suites/vici_tests-test_message.o `test -f 'suites/test_message.c' || echo '$(srcdir)/'`suites/test_message.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/vici_tests-test_message.Tpo suites/$(DEPDIR)/vici_tests-test_message.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_message.c' object='suites/vici_tests-test_message.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o suites/vici_tests-test_message.o `test -f 'suites/test_message.c' || echo '$(srcdir)/'`suites/test_message.c + +suites/vici_tests-test_message.obj: suites/test_message.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT suites/vici_tests-test_message.obj -MD -MP -MF suites/$(DEPDIR)/vici_tests-test_message.Tpo -c -o suites/vici_tests-test_message.obj `if test -f 'suites/test_message.c'; then $(CYGPATH_W) 'suites/test_message.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_message.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/vici_tests-test_message.Tpo suites/$(DEPDIR)/vici_tests-test_message.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_message.c' object='suites/vici_tests-test_message.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o suites/vici_tests-test_message.obj `if test -f 'suites/test_message.c'; then $(CYGPATH_W) 'suites/test_message.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_message.c'; fi` + +suites/vici_tests-test_request.o: suites/test_request.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT suites/vici_tests-test_request.o -MD -MP -MF suites/$(DEPDIR)/vici_tests-test_request.Tpo -c -o suites/vici_tests-test_request.o `test -f 'suites/test_request.c' || echo '$(srcdir)/'`suites/test_request.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/vici_tests-test_request.Tpo suites/$(DEPDIR)/vici_tests-test_request.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_request.c' object='suites/vici_tests-test_request.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o suites/vici_tests-test_request.o `test -f 'suites/test_request.c' || echo '$(srcdir)/'`suites/test_request.c + +suites/vici_tests-test_request.obj: suites/test_request.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT suites/vici_tests-test_request.obj -MD -MP -MF suites/$(DEPDIR)/vici_tests-test_request.Tpo -c -o suites/vici_tests-test_request.obj `if test -f 'suites/test_request.c'; then $(CYGPATH_W) 'suites/test_request.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_request.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/vici_tests-test_request.Tpo suites/$(DEPDIR)/vici_tests-test_request.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_request.c' object='suites/vici_tests-test_request.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o suites/vici_tests-test_request.obj `if test -f 'suites/test_request.c'; then $(CYGPATH_W) 'suites/test_request.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_request.c'; fi` + +suites/vici_tests-test_event.o: suites/test_event.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT suites/vici_tests-test_event.o -MD -MP -MF suites/$(DEPDIR)/vici_tests-test_event.Tpo -c -o suites/vici_tests-test_event.o `test -f 'suites/test_event.c' || echo '$(srcdir)/'`suites/test_event.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/vici_tests-test_event.Tpo suites/$(DEPDIR)/vici_tests-test_event.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_event.c' object='suites/vici_tests-test_event.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o suites/vici_tests-test_event.o `test -f 'suites/test_event.c' || echo '$(srcdir)/'`suites/test_event.c + +suites/vici_tests-test_event.obj: suites/test_event.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT suites/vici_tests-test_event.obj -MD -MP -MF suites/$(DEPDIR)/vici_tests-test_event.Tpo -c -o suites/vici_tests-test_event.obj `if test -f 'suites/test_event.c'; then $(CYGPATH_W) 'suites/test_event.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_event.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/vici_tests-test_event.Tpo suites/$(DEPDIR)/vici_tests-test_event.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_event.c' object='suites/vici_tests-test_event.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o suites/vici_tests-test_event.obj `if test -f 'suites/test_event.c'; then $(CYGPATH_W) 'suites/test_event.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_event.c'; fi` + +vici_tests-vici_socket.o: vici_socket.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT vici_tests-vici_socket.o -MD -MP -MF $(DEPDIR)/vici_tests-vici_socket.Tpo -c -o vici_tests-vici_socket.o `test -f 'vici_socket.c' || echo '$(srcdir)/'`vici_socket.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/vici_tests-vici_socket.Tpo $(DEPDIR)/vici_tests-vici_socket.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vici_socket.c' object='vici_tests-vici_socket.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o vici_tests-vici_socket.o `test -f 'vici_socket.c' || echo '$(srcdir)/'`vici_socket.c + +vici_tests-vici_socket.obj: vici_socket.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT vici_tests-vici_socket.obj -MD -MP -MF $(DEPDIR)/vici_tests-vici_socket.Tpo -c -o vici_tests-vici_socket.obj `if test -f 'vici_socket.c'; then $(CYGPATH_W) 'vici_socket.c'; else $(CYGPATH_W) '$(srcdir)/vici_socket.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/vici_tests-vici_socket.Tpo $(DEPDIR)/vici_tests-vici_socket.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vici_socket.c' object='vici_tests-vici_socket.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o vici_tests-vici_socket.obj `if test -f 'vici_socket.c'; then $(CYGPATH_W) 'vici_socket.c'; else $(CYGPATH_W) '$(srcdir)/vici_socket.c'; fi` + +vici_tests-vici_message.o: vici_message.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT vici_tests-vici_message.o -MD -MP -MF $(DEPDIR)/vici_tests-vici_message.Tpo -c -o vici_tests-vici_message.o `test -f 'vici_message.c' || echo '$(srcdir)/'`vici_message.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/vici_tests-vici_message.Tpo $(DEPDIR)/vici_tests-vici_message.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vici_message.c' object='vici_tests-vici_message.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o vici_tests-vici_message.o `test -f 'vici_message.c' || echo '$(srcdir)/'`vici_message.c + +vici_tests-vici_message.obj: vici_message.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT vici_tests-vici_message.obj -MD -MP -MF $(DEPDIR)/vici_tests-vici_message.Tpo -c -o vici_tests-vici_message.obj `if test -f 'vici_message.c'; then $(CYGPATH_W) 'vici_message.c'; else $(CYGPATH_W) '$(srcdir)/vici_message.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/vici_tests-vici_message.Tpo $(DEPDIR)/vici_tests-vici_message.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vici_message.c' object='vici_tests-vici_message.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o vici_tests-vici_message.obj `if test -f 'vici_message.c'; then $(CYGPATH_W) 'vici_message.c'; else $(CYGPATH_W) '$(srcdir)/vici_message.c'; fi` + +vici_tests-vici_builder.o: vici_builder.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT vici_tests-vici_builder.o -MD -MP -MF $(DEPDIR)/vici_tests-vici_builder.Tpo -c -o vici_tests-vici_builder.o `test -f 'vici_builder.c' || echo '$(srcdir)/'`vici_builder.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/vici_tests-vici_builder.Tpo $(DEPDIR)/vici_tests-vici_builder.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vici_builder.c' object='vici_tests-vici_builder.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o vici_tests-vici_builder.o `test -f 'vici_builder.c' || echo '$(srcdir)/'`vici_builder.c + +vici_tests-vici_builder.obj: vici_builder.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT vici_tests-vici_builder.obj -MD -MP -MF $(DEPDIR)/vici_tests-vici_builder.Tpo -c -o vici_tests-vici_builder.obj `if test -f 'vici_builder.c'; then $(CYGPATH_W) 'vici_builder.c'; else $(CYGPATH_W) '$(srcdir)/vici_builder.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/vici_tests-vici_builder.Tpo $(DEPDIR)/vici_tests-vici_builder.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vici_builder.c' object='vici_tests-vici_builder.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o vici_tests-vici_builder.obj `if test -f 'vici_builder.c'; then $(CYGPATH_W) 'vici_builder.c'; else $(CYGPATH_W) '$(srcdir)/vici_builder.c'; fi` + +vici_tests-vici_dispatcher.o: vici_dispatcher.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT vici_tests-vici_dispatcher.o -MD -MP -MF $(DEPDIR)/vici_tests-vici_dispatcher.Tpo -c -o vici_tests-vici_dispatcher.o `test -f 'vici_dispatcher.c' || echo '$(srcdir)/'`vici_dispatcher.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/vici_tests-vici_dispatcher.Tpo $(DEPDIR)/vici_tests-vici_dispatcher.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vici_dispatcher.c' object='vici_tests-vici_dispatcher.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o vici_tests-vici_dispatcher.o `test -f 'vici_dispatcher.c' || echo '$(srcdir)/'`vici_dispatcher.c + +vici_tests-vici_dispatcher.obj: vici_dispatcher.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT vici_tests-vici_dispatcher.obj -MD -MP -MF $(DEPDIR)/vici_tests-vici_dispatcher.Tpo -c -o vici_tests-vici_dispatcher.obj `if test -f 'vici_dispatcher.c'; then $(CYGPATH_W) 'vici_dispatcher.c'; else $(CYGPATH_W) '$(srcdir)/vici_dispatcher.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/vici_tests-vici_dispatcher.Tpo $(DEPDIR)/vici_tests-vici_dispatcher.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vici_dispatcher.c' object='vici_tests-vici_dispatcher.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o vici_tests-vici_dispatcher.obj `if test -f 'vici_dispatcher.c'; then $(CYGPATH_W) 'vici_dispatcher.c'; else $(CYGPATH_W) '$(srcdir)/vici_dispatcher.c'; fi` + +vici_tests-libvici.o: libvici.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT vici_tests-libvici.o -MD -MP -MF $(DEPDIR)/vici_tests-libvici.Tpo -c -o vici_tests-libvici.o `test -f 'libvici.c' || echo '$(srcdir)/'`libvici.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/vici_tests-libvici.Tpo $(DEPDIR)/vici_tests-libvici.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libvici.c' object='vici_tests-libvici.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o vici_tests-libvici.o `test -f 'libvici.c' || echo '$(srcdir)/'`libvici.c + +vici_tests-libvici.obj: libvici.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT vici_tests-libvici.obj -MD -MP -MF $(DEPDIR)/vici_tests-libvici.Tpo -c -o vici_tests-libvici.obj `if test -f 'libvici.c'; then $(CYGPATH_W) 'libvici.c'; else $(CYGPATH_W) '$(srcdir)/libvici.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/vici_tests-libvici.Tpo $(DEPDIR)/vici_tests-libvici.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libvici.c' object='vici_tests-libvici.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o vici_tests-libvici.obj `if test -f 'libvici.c'; then $(CYGPATH_W) 'libvici.c'; else $(CYGPATH_W) '$(srcdir)/libvici.c'; fi` + +vici_tests-vici_tests.o: vici_tests.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT vici_tests-vici_tests.o -MD -MP -MF $(DEPDIR)/vici_tests-vici_tests.Tpo -c -o vici_tests-vici_tests.o `test -f 'vici_tests.c' || echo '$(srcdir)/'`vici_tests.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/vici_tests-vici_tests.Tpo $(DEPDIR)/vici_tests-vici_tests.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vici_tests.c' object='vici_tests-vici_tests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o vici_tests-vici_tests.o `test -f 'vici_tests.c' || echo '$(srcdir)/'`vici_tests.c + +vici_tests-vici_tests.obj: vici_tests.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -MT vici_tests-vici_tests.obj -MD -MP -MF $(DEPDIR)/vici_tests-vici_tests.Tpo -c -o vici_tests-vici_tests.obj `if test -f 'vici_tests.c'; then $(CYGPATH_W) 'vici_tests.c'; else $(CYGPATH_W) '$(srcdir)/vici_tests.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/vici_tests-vici_tests.Tpo $(DEPDIR)/vici_tests-vici_tests.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vici_tests.c' object='vici_tests-vici_tests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vici_tests_CFLAGS) $(CFLAGS) -c -o vici_tests-vici_tests.obj `if test -f 'vici_tests.c'; then $(CYGPATH_W) 'vici_tests.c'; else $(CYGPATH_W) '$(srcdir)/vici_tests.c'; 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: $(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_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(ipseclibdir)" "$(DESTDIR)$(plugindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +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: + +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) + -rm -f suites/$(DEPDIR)/$(am__dirstamp) + -rm -f suites/$(am__dirstamp) + +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-checkPROGRAMS clean-generic clean-ipseclibLTLIBRARIES \ + clean-libtool clean-noinstLTLIBRARIES clean-pluginLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) suites/$(DEPDIR) + -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-ipseclibLTLIBRARIES install-pluginLTLIBRARIES + +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 -rf ./$(DEPDIR) suites/$(DEPDIR) + -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: uninstall-ipseclibLTLIBRARIES \ + uninstall-pluginLTLIBRARIES + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \ + clean-checkPROGRAMS clean-generic clean-ipseclibLTLIBRARIES \ + clean-libtool clean-noinstLTLIBRARIES clean-pluginLTLIBRARIES \ + 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-ipseclibLTLIBRARIES \ + install-man install-pdf install-pdf-am \ + install-pluginLTLIBRARIES 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 \ + uninstall-ipseclibLTLIBRARIES uninstall-pluginLTLIBRARIES + + +# 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/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md new file mode 100644 index 000000000..aeabbbd4d --- /dev/null +++ b/src/libcharon/plugins/vici/README.md @@ -0,0 +1,176 @@ +# The Versatile IKE Control Interface (VICI) protocol # + +The vici plugin implements the server side of an IPC protocol to configure, +monitor and control the IKE daemon charon. It uses request/response and event +messages to communicate over a reliable stream based transport. + +## Transport protocol ## + +To provide the service, the plugin opens a listening socket using a reliable, +stream based transport. charon relies on the different stream service +abstractions provided by libstrongswan, such as TCP and UNIX sockets. + +A client connects to this service to access functionality. It may send an +arbitrary number of packets over the connection before closing it. + +To exchange data, the transport protocol is segmented into byte sequences. +Each byte sequence is prefixed by a 32-bit length header in network order, +followed by the data. The maximum segment length is currently limited to 512KB +of data, and the length field contains the length of the data only, not +including the length field itself. + +The order of byte sequences must be strict, byte sequences must arrive in the +same order as sent. + +## Packet layer ## + +Within the byte sequences defined by the transport layer, both the client +and the server can exchange packets. The type of packet defines its structure +and purpose. The packet type is a 8-bit identifier, and is the first byte +in a transport layer byte sequence. The length of the packet is given by the +transport layer. + +While a packet type may define the format of the wrapped data freely, currently +all types either contain a name, a message or both. The following packet types +are currently defined: + +* _CMD_REQUEST = 0_: A named request message +* _CMD_RESPONSE = 1_: An unnamed response message for a request +* _CMD_UNKNOWN = 2_: An unnamed response if requested command is unknown +* _EVENT_REGISTER = 3_: A named event registration request +* _EVENT_UNREGISTER = 4_: A named event deregistration request +* _EVENT_CONFIRM = 5_: An unnamed response for successful event (de-)registration +* _EVENT_UNKNOWN = 6_: A unnamed response if event (de-)registration failed +* _EVENT = 7_: A named event message + +For packets having a named type, after the packet type an 8-bit length header +of the name follows, indicating the string length in bytes of the name tag, not +including the length field itself. The name is an ASCII string that is not +null-terminated. + +The rest of the packet forms the exchanged message, the length is determined +by the transport byte sequence length, subtracting the packet type and +the optional name tag in some messages. + +### Commands ### + +Commands are currently always requested by the client. The server replies with +a response, or with a CMD_UNKNOWN failure message to let the client know +that it does not have a handler for such a command. There is no sequence number +to associate responses to requests, so only one command can be active at +a time on a single connection. + +### Events ### + +To receive event messages, the client explicitly registers for events by name, +and also unregisters if it does not want to receive events of the named kind +anymore. The server confirms event registration using EVENT_CONFIRM, or +indicates that there is no such event source with EVENT_UNKNOWN. + +Events may get raised at any time while registered, even during an active +request command. This mechanism is used to feed continuous data during a request, +for example. + +## Message format ## + +The defined packet types optionally wrap a message with additional data. +Messages are currently used in CMD_REQUEST/CMD_RESPONSE, and in EVENT packets. +A message uses a hierarchial tree of sections. Each section (or the implicit +root section) contains an arbitrary set of key/value pairs, lists and +sub-sections. The length of a message is not part of the message itself, but +the wrapping layer, usually calculated from the transport byte sequence length. + +The message encoding consists of a sequence of elements. Each element starts +with the element type, optionally followed by an element name and/or an element +value. Currently the following message element types are defined: + +* _SECTION_START = 0_: Begin a new section having a name +* _SECTION_END = 1_: End a previously started section +* _KEY_VALUE = 2_: Define a value for a named key in the current section +* _LIST_START = 3_: Begin a named list for list items +* _LIST_ITEM = 4_: Define an unnamed item value in the current list +* _LIST_END = 5_: End a previously started list + +Types are encoded as 8-bit values. Types having a name (SECTION_START, +KEY_VALUE and LIST_START) have an ASCII string following the type, which itself +uses an 8-bit length header. The string must not be null-terminated, the string +length does not include the length field itself. + +Types having a value (KEY_VALUE and LIST_ITEM) have a raw blob sequence, +prefixed with a 16-bit network order length. The blob follows the type or the +name tag if available, the length defined by the length field does not include +the length field itself. + +The interpretation of any value is not defined by the message format; it can +take arbitrary blobs. The application may specify types for specific keys, such +as strings or integer representations. + +### Sections ### + +Sections may be opened in the implicit root section, or any previously section. +They can be nested to arbitrary levels. A SECTION_END marker always closes +the last opened section; SECTION_START and SECTION_END items must be balanced +in a valid message. + +### Key/Values ### + +Key/Value pair elements may appear in the implicit root section or any explicit +sub-section at any level. Key names must be unique in the current section, use +lists to define multiple values for a key. Key/values may not appear in lists, +use a sub-section instead. + +### Lists ### + +Lists may appear at the same locations as Key/Values, and may not be nested. +Only a single list may be opened at the same time, and all lists must be closed +in valid messages. After opening a list, only list items may appear before the +list closing element. Empty lists are allowed, list items may appear within +lists only. + +### Encoding example ### + +Consider the following structure using pseudo-markup for this example: + + key1 = value1 + section1 = { + sub-section = { + key2 = value2 + } + list1 = [ item1, item2 ] + } + +The example above reprensents a valid tree structure, that gets encoded as +the following C array: + + char msg[] = { + /* key1 = value1 */ + 2, 4,'k','e','y','1', 0,6,'v','a','l','u','e','1', + /* section1 */ + 0, 8,'s','e','c','t','i','o','n','1', + /* sub-section */ + 0, 11,'s','u','b','-','s','e','c','t','i','o','n', + /* key2 = value2 */ + 2, 4,'k','e','y','2', 0,6,'v','a','l','u','e','2', + /* sub-section end */ + 1, + /* list1 */ + 3, 5, 'l','i','s','t','1', + /* item1 */ + 4, 0,5,'i','t','e','m','1', + /* item2 */ + 4, 0,5,'i','t','e','m','2', + /* list1 end */ + 5, + /* section1 end */ + 1, + }; + +# libvici C client library # + +libvici is the reference implementation of a C client library implementing +the vici protocol. It builds upon libstrongswan, but provides a stable API +to implement client applications in the C programming language. libvici uses +the libstrongswan thread pool to deliver event messages asynchronously. + +More information about the libvici API is available in the libvici.h header +file. diff --git a/src/libcharon/plugins/vici/libvici.c b/src/libcharon/plugins/vici/libvici.c new file mode 100644 index 000000000..a2cbb3082 --- /dev/null +++ b/src/libcharon/plugins/vici/libvici.c @@ -0,0 +1,764 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "libvici.h" +#include "vici_builder.h" +#include "vici_dispatcher.h" +#include "vici_socket.h" + +#include <library.h> +#include <threading/mutex.h> +#include <threading/condvar.h> +#include <collections/hashtable.h> + +#include <errno.h> + +/** + * Event registration + */ +typedef struct { + /** name of event */ + char *name; + /** callback function */ + vici_event_cb_t cb; + /** user data for callback */ + void *user; +} event_t; + +/** + * Wait state signaled by asynchronous on_read callback + */ +typedef enum { + WAIT_IDLE = 0, + WAIT_SUCCESS, + WAIT_FAILURE, + WAIT_READ_ERROR, +} wait_state_t; + +/** + * Private vici connection contex. + */ +struct vici_conn_t { + /** connection stream */ + stream_t *stream; + /** event registrations, as char* => event_t */ + hashtable_t *events; + /** connection lock */ + mutex_t *mutex; + /** condvar to signal incoming response */ + condvar_t *cond; + /** queued response message */ + chunk_t queue; + /** asynchronous read error */ + int error; + /** wait state */ + wait_state_t wait; +}; + +/** + * Private vici request message. + */ +struct vici_req_t { + /** connection context */ + vici_conn_t *conn; + /** name of request message */ + char *name; + /** message builder */ + vici_builder_t *b; +}; + +/** + * Private vici response/event message. + */ +struct vici_res_t { + /** response message */ + vici_message_t *message; + /** allocated strings */ + linked_list_t *strings; + /** item enumerator */ + enumerator_t *enumerator; + /** currently enumerating type */ + vici_type_t type; + /** currently enumerating name */ + char *name; + /** currently enumerating value */ + chunk_t value; + /** section nesting level of callback parser */ + int level; +}; + +/** + * Signal wait result for waiting user thread + */ +static bool wait_result(vici_conn_t *conn, wait_state_t wait) +{ + conn->mutex->lock(conn->mutex); + conn->wait = wait; + conn->mutex->unlock(conn->mutex); + conn->cond->signal(conn->cond); + return FALSE; +} + +/** + * Signal wait error result for waiting user thread + */ +static bool read_error(vici_conn_t *conn, int err) +{ + conn->error = err; + return wait_result(conn, WAIT_READ_ERROR); +} + +/** + * Handle a command response message + */ +static bool handle_response(vici_conn_t *conn, u_int32_t len) +{ + chunk_t buf; + + buf = chunk_alloc(len); + if (!conn->stream->read_all(conn->stream, buf.ptr, buf.len)) + { + free(buf.ptr); + return read_error(conn, errno); + } + conn->queue = buf; + return wait_result(conn, WAIT_SUCCESS); +} + +/** + * Dispatch received event message + */ +static bool handle_event(vici_conn_t *conn, u_int32_t len) +{ + vici_message_t *message; + event_t *event; + u_int8_t namelen; + char name[257], *buf; + + if (len < sizeof(namelen)) + { + return read_error(conn, EBADMSG); + } + if (!conn->stream->read_all(conn->stream, &namelen, sizeof(namelen))) + { + return read_error(conn, errno); + } + if (namelen > len - sizeof(namelen)) + { + return read_error(conn, EBADMSG); + } + if (!conn->stream->read_all(conn->stream, name, namelen)) + { + return read_error(conn, errno); + } + name[namelen] = '\0'; + len -= sizeof(namelen) + namelen; + buf = malloc(len); + if (!conn->stream->read_all(conn->stream, buf, len)) + { + free(buf); + return read_error(conn, errno); + } + message = vici_message_create_from_data(chunk_create(buf, len), TRUE); + + conn->mutex->lock(conn->mutex); + event = conn->events->get(conn->events, name); + if (event) + { + vici_res_t res = { + .message = message, + .enumerator = message->create_enumerator(message), + .strings = linked_list_create(), + }; + + event->cb(event->user, name, &res); + + res.enumerator->destroy(res.enumerator); + res.strings->destroy_function(res.strings, free); + } + conn->mutex->unlock(conn->mutex); + + message->destroy(message); + + return TRUE; +} + +CALLBACK(on_read, bool, + vici_conn_t *conn, stream_t *stream) +{ + u_int32_t len; + u_int8_t op; + ssize_t hlen; + + hlen = stream->read(stream, &len, sizeof(len), FALSE); + if (hlen <= 0) + { + if (errno == EWOULDBLOCK) + { + return TRUE; + } + return read_error(conn, errno); + } + if (hlen < sizeof(len)) + { + if (!stream->read_all(stream, ((void*)&len) + hlen, sizeof(len) - hlen)) + { + return read_error(conn, errno); + } + } + + len = ntohl(len); + if (len > VICI_MESSAGE_SIZE_MAX) + { + return read_error(conn, EBADMSG); + } + if (len-- < sizeof(op)) + { + return read_error(conn, EBADMSG); + } + if (!stream->read_all(stream, &op, sizeof(op))) + { + return read_error(conn, errno); + } + switch (op) + { + case VICI_EVENT: + return handle_event(conn, len); + case VICI_CMD_RESPONSE: + return handle_response(conn, len); + case VICI_EVENT_CONFIRM: + return wait_result(conn, WAIT_SUCCESS); + case VICI_CMD_UNKNOWN: + case VICI_EVENT_UNKNOWN: + return wait_result(conn, WAIT_FAILURE); + case VICI_CMD_REQUEST: + case VICI_EVENT_REGISTER: + case VICI_EVENT_UNREGISTER: + default: + return read_error(conn, EBADMSG); + } +} + +vici_conn_t* vici_connect(char *uri) +{ + vici_conn_t *conn; + stream_t *stream; + + stream = lib->streams->connect(lib->streams, uri ?: VICI_DEFAULT_URI); + if (!stream) + { + return NULL; + } + + INIT(conn, + .stream = stream, + .events = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .cond = condvar_create(CONDVAR_TYPE_DEFAULT), + ); + + stream->on_read(stream, on_read, conn); + + return conn; +} + +void vici_disconnect(vici_conn_t *conn) +{ + enumerator_t *enumerator; + event_t *event; + + conn->stream->destroy(conn->stream); + enumerator = conn->events->create_enumerator(conn->events); + while (enumerator->enumerate(enumerator, NULL, &event)) + { + free(event->name); + free(event); + } + enumerator->destroy(enumerator); + conn->events->destroy(conn->events); + conn->mutex->destroy(conn->mutex); + conn->cond->destroy(conn->cond); + free(conn); +} + +vici_req_t* vici_begin(char *name) +{ + vici_req_t *req; + + INIT(req, + .name = strdup(name), + .b = vici_builder_create(), + ); + + return req; +} + +void vici_begin_section(vici_req_t *req, char *name) +{ + req->b->add(req->b, VICI_SECTION_START, name); +} + +void vici_end_section(vici_req_t *req) +{ + req->b->add(req->b, VICI_SECTION_END); +} + +void vici_add_key_value(vici_req_t *req, char *key, void *buf, int len) +{ + req->b->add(req->b, VICI_KEY_VALUE, key, chunk_create(buf, len)); +} + +void vici_add_key_valuef(vici_req_t *req, char *key, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + req->b->vadd_kv(req->b, key, fmt, args); + va_end(args); +} + +void vici_begin_list(vici_req_t *req, char *name) +{ + req->b->add(req->b, VICI_LIST_START, name); +} + +void vici_add_list_item(vici_req_t *req, void *buf, int len) +{ + req->b->add(req->b, VICI_LIST_ITEM, chunk_create(buf, len)); +} + +void vici_add_list_itemf(vici_req_t *req, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + req->b->vadd_li(req->b, fmt, args); + va_end(args); +} + +void vici_end_list(vici_req_t *req) +{ + req->b->add(req->b, VICI_LIST_END); +} + +vici_res_t* vici_submit(vici_req_t *req, vici_conn_t *conn) +{ + vici_message_t *message; + vici_res_t *res; + chunk_t data; + u_int32_t len; + u_int8_t namelen, op; + + message = req->b->finalize(req->b); + if (!message) + { + errno = EINVAL; + return NULL; + } + + op = VICI_CMD_REQUEST; + namelen = strlen(req->name); + data = message->get_encoding(message); + len = htonl(sizeof(op) + sizeof(namelen) + namelen + data.len); + + if (!conn->stream->write_all(conn->stream, &len, sizeof(len)) || + !conn->stream->write_all(conn->stream, &op, sizeof(op)) || + !conn->stream->write_all(conn->stream, &namelen, sizeof(namelen)) || + !conn->stream->write_all(conn->stream, req->name, namelen) || + !conn->stream->write_all(conn->stream, data.ptr, data.len)) + { + free(req->name); + free(req); + message->destroy(message); + return NULL; + } + free(req->name); + free(req); + message->destroy(message); + + message = NULL; + conn->mutex->lock(conn->mutex); + while (conn->wait == WAIT_IDLE) + { + conn->cond->wait(conn->cond, conn->mutex); + } + switch (conn->wait) + { + case WAIT_SUCCESS: + message = vici_message_create_from_data(conn->queue, TRUE); + conn->queue = chunk_empty; + break; + case WAIT_READ_ERROR: + errno = conn->error; + break; + case WAIT_FAILURE: + default: + errno = ENOENT; + break; + } + conn->wait = WAIT_IDLE; + conn->mutex->unlock(conn->mutex); + + conn->stream->on_read(conn->stream, on_read, conn); + + if (message) + { + INIT(res, + .message = message, + .enumerator = message->create_enumerator(message), + .strings = linked_list_create(), + ); + return res; + } + return NULL; +} + +void vici_free_req(vici_req_t *req) +{ + vici_message_t *message; + + free(req->name); + message = req->b->finalize(req->b); + if (message) + { + message->destroy(message); + } + free(req); +} + +int vici_dump(vici_res_t *res, char *label, bool pretty, FILE *out) +{ + if (res->message->dump(res->message, label, pretty, out)) + { + return 0; + } + errno = EBADMSG; + return 1; +} + +vici_parse_t vici_parse(vici_res_t *res) +{ + if (!res->enumerator->enumerate(res->enumerator, + &res->type, &res->name, &res->value)) + { + return VICI_PARSE_ERROR; + } + switch (res->type) + { + case VICI_END: + return VICI_PARSE_END; + case VICI_SECTION_START: + return VICI_PARSE_BEGIN_SECTION; + case VICI_SECTION_END: + return VICI_PARSE_END_SECTION; + case VICI_LIST_START: + return VICI_PARSE_BEGIN_LIST; + case VICI_LIST_ITEM: + return VICI_PARSE_LIST_ITEM; + case VICI_LIST_END: + return VICI_PARSE_END_LIST; + case VICI_KEY_VALUE: + return VICI_PARSE_KEY_VALUE; + default: + return VICI_PARSE_ERROR; + } +} + +char* vici_parse_name(vici_res_t *res) +{ + char *name; + + switch (res->type) + { + case VICI_SECTION_START: + case VICI_LIST_START: + case VICI_KEY_VALUE: + name = strdup(res->name); + res->strings->insert_last(res->strings, name); + return name; + default: + errno = EINVAL; + return NULL; + } +} + +int vici_parse_name_eq(vici_res_t *res, char *name) +{ + switch (res->type) + { + case VICI_SECTION_START: + case VICI_LIST_START: + case VICI_KEY_VALUE: + return streq(name, res->name) ? 1 : 0; + default: + return 0; + } +} + +void* vici_parse_value(vici_res_t *res, int *len) +{ + switch (res->type) + { + case VICI_LIST_ITEM: + case VICI_KEY_VALUE: + *len = res->value.len; + return res->value.ptr; + default: + *len = 0; + errno = EINVAL; + return NULL; + } +} + +char* vici_parse_value_str(vici_res_t *res) +{ + char *val; + + switch (res->type) + { + case VICI_LIST_ITEM: + case VICI_KEY_VALUE: + if (!chunk_printable(res->value, NULL, 0)) + { + errno = EBADMSG; + return NULL; + } + val = strndup(res->value.ptr, res->value.len); + res->strings->insert_last(res->strings, val); + return val; + default: + errno = EINVAL; + return NULL; + } +} + +int vici_parse_cb(vici_res_t *res, vici_parse_section_cb_t section, + vici_parse_value_cb_t kv, vici_parse_value_cb_t li, + void *user) +{ + char *name, *list = NULL; + void *value; + int base, len, ret; + + base = res->level; + + while (TRUE) + { + switch (vici_parse(res)) + { + case VICI_PARSE_KEY_VALUE: + if (res->level == base) + { + if (kv) + { + name = vici_parse_name(res); + value = vici_parse_value(res, &len); + if (name && value) + { + ret = kv(user, res, name, value, len); + if (ret) + { + return ret; + } + } + } + } + break; + case VICI_PARSE_BEGIN_SECTION: + if (res->level++ == base) + { + if (section) + { + name = vici_parse_name(res); + if (name) + { + ret = section(user, res, name); + if (ret) + { + return ret; + } + } + } + } + break; + case VICI_PARSE_END_SECTION: + if (res->level-- == base) + { + return 0; + } + break; + case VICI_PARSE_END: + res->level = 0; + return 0; + case VICI_PARSE_BEGIN_LIST: + if (res->level == base) + { + list = vici_parse_name(res); + } + break; + case VICI_PARSE_LIST_ITEM: + if (list && li) + { + value = vici_parse_value(res, &len); + if (value) + { + ret = li(user, res, list, value, len); + if (ret) + { + return ret; + } + } + } + break; + case VICI_PARSE_END_LIST: + if (res->level == base) + { + list = NULL; + } + break; + case VICI_PARSE_ERROR: + res->level = 0; + errno = EBADMSG; + return 1; + } + } +} + +void* vici_find(vici_res_t *res, int *len, char *fmt, ...) +{ + va_list args; + chunk_t value; + + va_start(args, fmt); + value = res->message->vget_value(res->message, chunk_empty, fmt, args); + va_end(args); + + *len = value.len; + return value.ptr; +} + +char* vici_find_str(vici_res_t *res, char *def, char *fmt, ...) +{ + va_list args; + char *str; + + va_start(args, fmt); + str = res->message->vget_str(res->message, def, fmt, args); + va_end(args); + + return str; +} + +int vici_find_int(vici_res_t *res, int def, char *fmt, ...) +{ + va_list args; + int val; + + va_start(args, fmt); + val = res->message->vget_int(res->message, def, fmt, args); + va_end(args); + + return val; +} + +void vici_free_res(vici_res_t *res) +{ + res->strings->destroy_function(res->strings, free); + res->message->destroy(res->message); + res->enumerator->destroy(res->enumerator); + free(res); +} + +int vici_register(vici_conn_t *conn, char *name, vici_event_cb_t cb, void *user) +{ + event_t *event; + u_int32_t len; + u_int8_t namelen, op; + int ret = 1; + + op = cb ? VICI_EVENT_REGISTER : VICI_EVENT_UNREGISTER; + namelen = strlen(name); + len = htonl(sizeof(op) + sizeof(namelen) + namelen); + if (!conn->stream->write_all(conn->stream, &len, sizeof(len)) || + !conn->stream->write_all(conn->stream, &op, sizeof(op)) || + !conn->stream->write_all(conn->stream, &namelen, sizeof(namelen)) || + !conn->stream->write_all(conn->stream, name, namelen)) + { + return 1; + } + + conn->mutex->lock(conn->mutex); + while (conn->wait == WAIT_IDLE) + { + conn->cond->wait(conn->cond, conn->mutex); + } + switch (conn->wait) + { + case WAIT_SUCCESS: + ret = 0; + break; + case WAIT_READ_ERROR: + errno = conn->error; + break; + case WAIT_FAILURE: + default: + errno = ENOENT; + break; + } + conn->wait = WAIT_IDLE; + conn->mutex->unlock(conn->mutex); + + conn->stream->on_read(conn->stream, on_read, conn); + + if (ret == 0) + { + conn->mutex->lock(conn->mutex); + if (cb) + { + INIT(event, + .name = strdup(name), + .cb = cb, + .user = user, + ); + event = conn->events->put(conn->events, event->name, event); + } + else + { + event = conn->events->remove(conn->events, name); + } + conn->mutex->unlock(conn->mutex); + + if (event) + { + free(event->name); + free(event); + } + } + return ret; +} + +void vici_init() +{ + library_init(NULL, "vici"); + if (lib->processor->get_total_threads(lib->processor) < 4) + { + lib->processor->set_threads(lib->processor, 4); + } +} + +void vici_deinit() +{ + library_deinit(); +} diff --git a/src/libcharon/plugins/vici/libvici.h b/src/libcharon/plugins/vici/libvici.h new file mode 100644 index 000000000..58595d8cc --- /dev/null +++ b/src/libcharon/plugins/vici/libvici.h @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup libvici libvici + * @{ @ingroup vici + * + * libvici is a low-level client library for the "Versatile IKE Control + * Interface" protocol. While it uses libstrongswan and its thread-pool for + * asynchronous message delivery, this interface does not directly depend on + * libstrongswan interfaces and should be stable. + * + * This interface provides the following basic functions: + * + * - vici_init()/vici_deinit(): Library initialization functions + * - vici_connect(): Connect to a vici service + * - vici_disconnect(): Disconnect from a vici service + * + * Library initialization implicitly initializes libstrongswan and a small + * thread pool. + * + * Connecting requires an uri, which is currently either a UNIX socket path + * prefixed with unix://, or a hostname:port touple prefixed with tcp://. + * Passing NULL takes the system default socket path. + * + * After the connection has been established, request messages can be sent. + * Only a single thread may operate on a single connection instance + * simultaneously. To construct request messages, use the following functions: + * + * - vici_add_key_value() / vici_add_key_valuef(): Add key/value pairs + * - vici_begin(): Start constructing a new request message + * - vici_begin_section(): Open a new section to add contents to + * - vici_end_section(): Close a previously opened session + * - vici_begin_list(): Open a new list to add list items to + * - vici_end_list(): Close a previously opened list + * - vici_add_list_item() / vici_add_list_itemf(): Add list item + * + * Once the request message is complete, it can be sent or cancelled with: + * + * - vici_submit() + * - vici_free_req() + * + * If submitting a message is successful, a response message is returned. It + * can be processed using the following functions: + * + * - vici_parse(): Parse content type + * - vici_parse_name(): Parse name if content type provides one + * - vici_parse_name_eq(): Parse name and check if matches string + * - vici_parse_value() / vici_parse_value_str(): Parse value for content type + * - vici_dump(): Dump a full response to a FILE stream + * - vici_free_res(): Free response after use + * + * Usually vici_parse() is called in a loop, and depending on the returned + * type the name and value can be inspected. + * + * To register or unregister for asynchronous event messages vici_register() is + * used. The registered callback gets invoked by an asynchronous thread. To + * parse the event message, the vici_parse*() functions can be used. + */ + +#ifndef LIBVICI_H_ +#define LIBVICI_H_ + +#include <stdio.h> + +#include <utils/utils.h> + +/** + * Opaque vici connection contex. + */ +typedef struct vici_conn_t vici_conn_t; + +/** + * Opaque vici request message. + */ +typedef struct vici_req_t vici_req_t; + +/** + * Opaque vici response/event message. + */ +typedef struct vici_res_t vici_res_t; + +/** + * Vici parse result, as returned by vici_parse(). + */ +typedef enum { + /** encountered a section start, has a name */ + VICI_PARSE_BEGIN_SECTION, + /** encountered a section end */ + VICI_PARSE_END_SECTION, + /** encountered a list start, has a name */ + VICI_PARSE_BEGIN_LIST, + /** encountered a list element, has a value */ + VICI_PARSE_LIST_ITEM, + /** encountered a list end */ + VICI_PARSE_END_LIST, + /** encountered a key/value pair, has a name and a value */ + VICI_PARSE_KEY_VALUE, + /** encountered valid end of message */ + VICI_PARSE_END, + /** parse error */ + VICI_PARSE_ERROR, +} vici_parse_t; + +/** + * Callback function invoked for received event messages. + * + * It is not allowed to call vici_submit() from this callback. + * + * @param user user data, as passed to vici_connect + * @param name name of received event + * @param msg associated event message, destroyed by libvici + */ +typedef void (*vici_event_cb_t)(void *user, char *name, vici_res_t *msg); + +/** + * Callback function for key/value and list items, invoked by vici_parse_cb(). + * + * @param user user data, as passed to vici_parse_cb() + * @param res message currently parsing + * @param name name of key or list + * @param value value buffer + * @param len length of value buffer + * @return 0 if parsed successfully + */ +typedef int (*vici_parse_value_cb_t)(void *user, vici_res_t *res, char *name, + void *value, int len); + +/** + * Callback function for sections, invoked by vici_parse_cb(). + * + * @param user user data, as passed to vici_parse_cb() + * @param res message currently parsing + * @param name name of the section + * @return 0 if parsed successfully + */ +typedef int (*vici_parse_section_cb_t)(void *user, vici_res_t *res, char *name); + +/** + * Open a new vici connection. + * + * On error, NULL is returned and errno is set appropriately. + * + * @param uri URI to connect to, NULL to use system default + * @return opaque vici connection context, NULL on error + */ +vici_conn_t* vici_connect(char *uri); + +/** + * Close a vici connection. + * + * @param conn connection context + */ +void vici_disconnect(vici_conn_t *conn); + +/** + * Begin a new vici message request. + * + * This function always succeeds. + * + * @param name name of request command + * @return request message, to add contents + */ +vici_req_t* vici_begin(char *name); + +/** + * Begin a new section in a vici request message. + * + * @param req request message to create a new section in + * @param name name of section to create + */ +void vici_begin_section(vici_req_t *req, char *name); + +/** + * End a previously opened section. + * + * @param req request message to close an open section in + */ +void vici_end_section(vici_req_t *req); + +/** + * Add a key/value pair, using an as-is blob as value. + * + * @param req request message to add key/value pair to + * @param key key name of key/value pair + * @param buf pointer to blob to add as value + * @param len length of value blob to add + */ +void vici_add_key_value(vici_req_t *req, char *key, void *buf, int len); + +/** + * Add a key/value pair, setting value from a printf() format string. + * + * @param req request message to add key/value pair to + * @param key key name of key/value pair + * @param fmt format string for value + * @param ... arguments to format string + */ +void vici_add_key_valuef(vici_req_t *req, char *key, char *fmt, ...); + +/** + * Begin a list in a request message. + * + * After starting a list, only list items can be added until the list gets + * closed by vici_end_list(). + * + * @param req request message to begin list in + * @param name name of list to begin + */ +void vici_begin_list(vici_req_t *req, char *name); + +/** + * Add a list item to a currently open list, using an as-is blob. + * + * @param req request message to add list item to + * @param buf pointer to blob to add as value + * @param len length of value blob to add + */ +void vici_add_list_item(vici_req_t *req, void *buf, int len); + +/** + * Add a list item to a currently open list, using a printf() format string. + * + * @param req request message to add list item to + * @param fmt format string to create value from + * @param ... arguments to format string + */ +void vici_add_list_itemf(vici_req_t *req, char *fmt, ...); + +/** + * End a previously opened list in a request message. + * + * @param req request message to end list in + */ +void vici_end_list(vici_req_t *req); + +/** + * Submit a request message, and wait for response. + * + * The request messages gets cleaned up by this call and gets invalid. + * On error, NULL is returned an errno is set to: + * - EINVAL if the request is invalid/incomplete + * - ENOENT if the command is unknown + * - EBADMSG if the response is invalid + * - Any other IO related errno + * + * @param req request message to send + * @param conn connection context to send message over + * @return response message, NULL on error + */ +vici_res_t* vici_submit(vici_req_t *req, vici_conn_t *conn); + +/** + * Cancel a request message started. + * + * If a request created by vici_begin() does not get submitted using + * vici_submit(), it has to get freed using this call. + * + * @param req request message to clean up + */ +void vici_free_req(vici_req_t *req); + +/** + * Dump a message text representation to a FILE stream. + * + * On error, errno is set to: + * - EBADMSG if the message is invalid + * + * @param res response message to dump + * @param label a label to print for this message + * @param pretty use pretty print with indentation + * @param out FILE to dump to + * @return 0 if dumped complete message, 1 on error + */ +int vici_dump(vici_res_t *res, char *label, bool pretty, FILE *out); + +/** + * Parse next element from a vici response message. + * + * @param res response message to parse + * @return parse result + */ +vici_parse_t vici_parse(vici_res_t *res); + +/** + * Parse name tag / key of a previously parsed element. + * + * This call is valid only after vici_parse() returned VICI_PARSE_KEY_VALUE, + * VICI_PARSE_BEGIN_SECTION or VICI_PARSE_BEGIN_LIST. + * + * The string is valid until vici_free_res() is called. + * + * On error, errno is set to: + *- EINVAL if not in valid parser state + * + * @param res response message to parse + * @return name tag / key, NULL on error + */ +char* vici_parse_name(vici_res_t *res); + +/** + * Compare name tag / key of a previusly parsed element. + * + * This call is valid only after vici_parse() returned VICI_PARSE_KEY_VALUE, + * VICI_PARSE_BEGIN_SECTION or VICI_PARSE_BEGIN_LIST. + * + * @param res response message to parse + * @param name string to compare + * @return 1 if name equals, 0 if not + */ +int vici_parse_name_eq(vici_res_t *res, char *name); + +/** + * Parse value of a previously parsed element, as a blob. + * + * This call is valid only after vici_parse() returned VICI_PARSE_KEY_VALUE or + * VICI_PARSE_LIST_ITEM. + * + * The string is valid until vici_free_res() is called. + * + * On error, errno is set to: + * - EINVAL if not in valid parser state + * + * @param res response message to parse + * @param len pointer receiving value length + * @return pointer to value, NULL on error + */ +void* vici_parse_value(vici_res_t *res, int *len); + +/** + * Parse value of a previously parsed element, as a string. + * + * This call is valid only after vici_parse() returned VICI_PARSE_KEY_VALUE or + * VICI_PARSE_LIST_ITEM. + * + * This call is successful only if the value contains no non-printable + * characters. The string is valid until vici_free_res() is called. + * + * On error, errno is set to: + * - EBADMSG if value is not a printable string + * - EINVAL if not in valid parser state + * + * @param res response message to parse + * @return value as string, NULL on error + */ +char* vici_parse_value_str(vici_res_t *res); + +/** + * Parse a complete message with callbacks. + * + * Any of the callbacks may be NULL to skip this kind of item. Callbacks are + * invoked for the current section level only. To descent into sections, call + * vici_parse_cb() from within a section callback. + * + * On error, errno is set to: + * - EBADMSG if message encoding invalid + * - Any other errno set by the invoked callbacks + * + * @param res message to parse + * @param section callback invoked for each section + * @param kv callback invoked for key/value pairs + * @param li callback invoked for list items + * @param user user data to pass to callbacks + * @return 0 if parsing successful + */ +int vici_parse_cb(vici_res_t *res, vici_parse_section_cb_t section, + vici_parse_value_cb_t kv, vici_parse_value_cb_t li, + void *user); + +/* + * Find a blob value in a message for a given key. + * + * Sections can be selected by prefixing them separated by dots. + * + * @param res response message to parse + * @param len length of returned object + * @param fmt printf format string of key and sections + * @param ... arguments to format string + * @return blob value, having *len bytes, NULL if not found + */ +void *vici_find(vici_res_t *res, int *len, char *fmt, ...); + +/** + * Find a string value in a message for a given key. + * + * Sections can be selected by prefixing them separated by dots. + * + * @param res response message to parse + * @param def default value, if key not found + * @param fmt printf format string of key and sections + * @param ... arguments to format string + * @return string, def if not found + */ +char* vici_find_str(vici_res_t *res, char *def, char *fmt, ...); + +/** + * Find an integer value in a message for a given key. + * + * Sections can be selected by prefixing them separated by dots. + * + * @param res response message to parse + * @param def default value, if key not found + * @param fmt printf format string of key and sections + * @param ... arguments to format string + * @return integer value, def if not found + */ +int vici_find_int(vici_res_t *res, int def, char *fmt, ...); + +/** + * Clean up a received response message. + * + * Event messages get cleaned up by the library, it is not allowed to call + * vici_free_res() from within a vici_event_cb_t. + * + * @param res response message to free + */ +void vici_free_res(vici_res_t *res); + +/** + * (Un-)Register for events of a given kind. + * + * Events callbacks get invoked by a different thread from the libstrongswan + * thread pool. + * On failure, errno is set to: + * - ENOENT if the event name is unknown + * - EBADMSG if the response is invalid + * - Any other IO related errno + * + * @param conn connection context + * @param name name of event messages to register to + * @param cb callback function to register, NULL to unregister + * @param user user data passed to callback invocations + * @return 0 if registered successfully + */ +int vici_register(vici_conn_t *conn, char *name, vici_event_cb_t cb, void *user); + +/** + * Initialize libvici before first time use. + */ +void vici_init(); + +/** + * Deinitialize libvici after use. + */ +void vici_deinit(); + +#endif /** LIBVICI_H_ @}*/ diff --git a/src/libcharon/plugins/vici/suites/test_event.c b/src/libcharon/plugins/vici/suites/test_event.c new file mode 100644 index 000000000..b923ad393 --- /dev/null +++ b/src/libcharon/plugins/vici/suites/test_event.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <test_suite.h> + +#include "../vici_dispatcher.h" +#include "../libvici.h" + +#include <unistd.h> + +#ifdef WIN32 +# define URI "tcp://127.0.0.1:6543" +#else /* !WIN32 */ +# define URI "unix:///tmp/strongswan-vici-event-test" +#endif /* !WIN32 */ + +static void event_cb(void *user, char *name, vici_res_t *ev) +{ + int *count = (int*)user; + + ck_assert_str_eq(name, "test"); + ck_assert(vici_parse(ev) == VICI_PARSE_KEY_VALUE); + ck_assert_str_eq(vici_parse_name(ev), "key1"); + ck_assert_str_eq(vici_parse_value_str(ev), "value1"); + ck_assert(vici_parse(ev) == VICI_PARSE_END); + + (*count)++; +} + +START_TEST(test_event) +{ + vici_dispatcher_t *dispatcher; + vici_conn_t *conn; + int count = 0; + + lib->processor->set_threads(lib->processor, 8); + + dispatcher = vici_dispatcher_create(URI); + ck_assert(dispatcher); + + dispatcher->manage_event(dispatcher, "test", TRUE); + + vici_init(); + conn = vici_connect(URI); + ck_assert(conn); + + ck_assert(vici_register(conn, "test", event_cb, &count) == 0); + ck_assert(vici_register(conn, "nonexistent", event_cb, &count) != 0); + + dispatcher->raise_event(dispatcher, "test", 0, vici_message_create_from_args( + VICI_KEY_VALUE, "key1", chunk_from_str("value1"), + VICI_END)); + + while (count == 0) + { + usleep(1000); + } + + vici_disconnect(conn); + + dispatcher->manage_event(dispatcher, "test", FALSE); + + lib->processor->cancel(lib->processor); + dispatcher->destroy(dispatcher); + + vici_deinit(); +} +END_TEST + +#define EVENT_COUNT 500 + +CALLBACK(raise_cb, vici_message_t*, + vici_dispatcher_t *dispatcher, char *name, u_int id, vici_message_t *req) +{ + u_int i; + + for (i = 0; i < EVENT_COUNT; i++) + { + dispatcher->raise_event(dispatcher, "event", id, + vici_message_create_from_args( + VICI_KEY_VALUE, "counter", chunk_from_thing(i), + VICI_END)); + } + return vici_message_create_from_args(VICI_END); +} + +CALLBACK(raise_event_cb, void, + int *count, char *name, vici_res_t *ev) +{ + u_int *value, len; + + ck_assert_str_eq(name, "event"); + ck_assert(vici_parse(ev) == VICI_PARSE_KEY_VALUE); + ck_assert_str_eq(vici_parse_name(ev), "counter"); + value = vici_parse_value(ev, &len); + ck_assert_int_eq(len, sizeof(*value)); + ck_assert(vici_parse(ev) == VICI_PARSE_END); + + ck_assert_int_eq(*count, *value); + (*count)++; +} + +START_TEST(test_raise_events) +{ + vici_dispatcher_t *dispatcher; + vici_res_t *res; + vici_conn_t *conn; + int count = 0; + + lib->processor->set_threads(lib->processor, 8); + + dispatcher = vici_dispatcher_create(URI); + ck_assert(dispatcher); + + dispatcher->manage_event(dispatcher, "event", TRUE); + dispatcher->manage_command(dispatcher, "raise", raise_cb, dispatcher); + + vici_init(); + conn = vici_connect(URI); + ck_assert(conn); + + ck_assert(vici_register(conn, "event", raise_event_cb, &count) == 0); + + res = vici_submit(vici_begin("raise"), conn); + + ck_assert_int_eq(count, EVENT_COUNT); + ck_assert(res); + vici_free_res(res); + + vici_disconnect(conn); + + dispatcher->manage_event(dispatcher, "event", FALSE); + dispatcher->manage_command(dispatcher, "raise", NULL, NULL); + + lib->processor->cancel(lib->processor); + dispatcher->destroy(dispatcher); + + vici_deinit(); +} +END_TEST + +START_TEST(test_stress) +{ + vici_dispatcher_t *dispatcher; + vici_conn_t *conn; + int count = 0, i, total = 50; + + lib->processor->set_threads(lib->processor, 8); + + dispatcher = vici_dispatcher_create(URI); + ck_assert(dispatcher); + + dispatcher->manage_event(dispatcher, "test", TRUE); + dispatcher->manage_event(dispatcher, "dummy", TRUE); + + vici_init(); + conn = vici_connect(URI); + ck_assert(conn); + + vici_register(conn, "test", event_cb, &count); + + for (i = 0; i < total; i++) + { + /* do some event re/deregistration in between */ + ck_assert(vici_register(conn, "dummy", event_cb, NULL) == 0); + + dispatcher->raise_event(dispatcher, "test", 0, + vici_message_create_from_args( + VICI_KEY_VALUE, "key1", chunk_from_str("value1"), + VICI_END)); + + ck_assert(vici_register(conn, "dummy", NULL, NULL) == 0); + } + + while (count < total) + { + usleep(1000); + } + + vici_disconnect(conn); + + dispatcher->manage_event(dispatcher, "test", FALSE); + dispatcher->manage_event(dispatcher, "dummy", FALSE); + + lib->processor->cancel(lib->processor); + dispatcher->destroy(dispatcher); + + vici_deinit(); +} +END_TEST + +Suite *event_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("vici events"); + + tc = tcase_create("single"); + tcase_add_test(tc, test_event); + suite_add_tcase(s, tc); + + tc = tcase_create("raise events"); + tcase_add_test(tc, test_raise_events); + suite_add_tcase(s, tc); + + tc = tcase_create("stress"); + tcase_add_test(tc, test_stress); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libcharon/plugins/vici/suites/test_message.c b/src/libcharon/plugins/vici/suites/test_message.c new file mode 100644 index 000000000..293117348 --- /dev/null +++ b/src/libcharon/plugins/vici/suites/test_message.c @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <test_suite.h> + +#include "../vici_message.h" +#include "../vici_builder.h" + +#include <unistd.h> + +static char blob[] = { + 0xd3,0xe5,0xee,0x37,0x7b,0x96,0x2f,0x3e,0x5f,0x3e,0x91,0xea,0x38,0x44,0xba,0x6c, + 0x75,0xc8,0x42,0x32,0xaf,0x7a,0x66,0x43,0x33,0x92,0xd2,0xef,0x7d,0x91,0x7b,0x59, + 0x9f,0x9f,0xd1,0x44,0xb6,0x1e,0x8c,0xd1,0xc5,0xa0,0xd9,0xe4,0xf2,0x31,0xfd,0x7b, + 0x5b,0x56,0xa7,0xfe,0x63,0x0d,0xcb,0x31,0x74,0xd8,0xd6,0x4a,0x42,0x3a,0x88,0xf3, + 0x79,0xf9,0x41,0xa6,0xc0,0x64,0x53,0x31,0x42,0xe2,0xd4,0x4a,0x22,0x5f,0x3f,0x99, + 0xe0,0x1a,0xcb,0x93,0x26,0xd0,0xec,0xac,0x90,0x97,0x0a,0x5f,0x69,0x86,0xf1,0xda, + 0xfc,0xa7,0xac,0xd0,0xd8,0x81,0xcf,0x7d,0x47,0x22,0xbe,0xbf,0x00,0x9b,0x6b,0x86, + 0x92,0x89,0xbe,0x7f,0x74,0x13,0x53,0xf1,0x4c,0x2b,0xc9,0xe1,0x39,0xd6,0xfc,0x50, + 0x3f,0x00,0xfb,0x76,0x42,0xa6,0xa4,0x70,0xfc,0x93,0x17,0x4a,0x35,0xce,0x5e,0x78, + 0x41,0x88,0x24,0x50,0x78,0xf2,0x38,0x08,0xff,0x40,0xef,0x61,0xbb,0xbf,0x16,0xff, + 0x0b,0xf6,0x33,0x21,0xcb,0x48,0xbd,0x7d,0xd1,0x73,0xfa,0x6d,0xd6,0xab,0xde,0x69, + 0x63,0x17,0xdb,0x52,0xe2,0x75,0x4b,0xb7,0x1e,0xf0,0x8a,0x55,0x4f,0x70,0x8d,0x18, + 0xe5,0x38,0x6a,0x9f,0xb8,0x06,0xb5,0x91,0x90,0x2b,0xc5,0x67,0xa9,0x12,0xe5,0xf3, + 0x48,0x2f,0x80,0x03,0xa1,0xa0,0xfc,0x43,0xe9,0x0f,0x83,0x2b,0xbc,0x7c,0xa8,0x3b, + 0x6c,0xc1,0xc8,0x72,0x5f,0x87,0x63,0x77,0x93,0x9b,0xe2,0xd7,0x4e,0xe6,0x65,0xa1, + 0x69,0x00,0xda,0xf8,0xb4,0x61,0xee,0xb7,0x20,0xe7,0x2a,0x35,0x23,0xf0,0x37,0x4b, + 0x67,0xcf,0x8d,0x85,0x72,0x22,0x6d,0x7a,0xb2,0x96,0xff,0x49,0xf4,0x94,0x3e,0x7e, + 0x87,0x26,0x5d,0x34,0x05,0x26,0x60,0x9b,0x89,0xfe,0xf9,0x91,0xd3,0x03,0xe7,0x8a, + 0x03,0xf6,0x4e,0xbf,0x68,0x13,0xc6,0xf2,0x7b,0x9c,0xe6,0x36,0x1b,0xe2,0x22,0x44, + 0xb1,0x19,0x34,0x5f,0xe8,0x44,0x48,0x3a,0x19,0xe4,0xbd,0xb0,0x4e,0xb5,0x2c,0x40, + 0x55,0x39,0xe6,0x4c,0xd5,0x68,0x34,0x72,0x6b,0x6d,0x88,0xce,0x7e,0x77,0x95,0x17, + 0x2e,0x68,0x3f,0x0e,0x9d,0x70,0x9a,0x22,0xfa,0x19,0xcc,0x15,0x9d,0xba,0xaa,0xec, + 0xb1,0x67,0x19,0x51,0xce,0x60,0x9a,0x38,0xf8,0xa7,0x4e,0xe3,0x25,0x47,0x1e,0x1d, + 0x30,0x76,0x91,0x8f,0x4d,0x13,0x59,0x06,0x2f,0x01,0x10,0x95,0xdb,0x08,0x7c,0x46, + 0xed,0x47,0xa1,0x19,0x4c,0x46,0xd1,0x3a,0x3f,0x88,0x7a,0x63,0xae,0x29,0x13,0x42, + 0xe9,0x17,0xe8,0xa9,0x95,0xfc,0xd1,0xea,0xfa,0x59,0x90,0xfe,0xb7,0xbb,0x7f,0x61, + 0x1b,0xcb,0x3d,0x12,0x99,0x96,0x3e,0x23,0x23,0xec,0x3a,0x4d,0x86,0x86,0x74,0xef, + 0x38,0xa6,0xdc,0x3a,0x83,0x85,0xf8,0xb8,0xad,0x5b,0x33,0x94,0x4d,0x0e,0x68,0xbc, + 0xf2,0xc7,0x6f,0x84,0x18,0x1e,0x5a,0x66,0x1f,0x6c,0x98,0x33,0xda,0xde,0x9e,0xda, + 0x82,0xd0,0x56,0x44,0x47,0x08,0x0c,0x07,0x81,0x9d,0x8b,0x64,0x16,0x73,0x9d,0x80, + 0x54,0x9c,0x4c,0x42,0xde,0x27,0x4e,0x97,0xb2,0xcf,0x48,0xaf,0x7e,0x85,0xc1,0xcd, + 0x6a,0x4d,0x04,0x40,0x89,0xa3,0x9d,0x4e,0x89,0x56,0x60,0x31,0x1f,0x3f,0x49,0x16, +}; + +typedef struct { + vici_type_t type; + char *name; + chunk_t data; +} endecode_test_t; + +static endecode_test_t endecode_test_simple[] = { + { VICI_SECTION_START, "section1", {} }, + { VICI_KEY_VALUE, "key1", { "value1", 6 } }, + { VICI_KEY_VALUE, "key2", { "value2", 6 } }, + { VICI_SECTION_END, NULL, {} }, + { VICI_END, NULL, {} }, +}; + +static endecode_test_t endecode_test_nested[] = { + { VICI_SECTION_START, "section1", {} }, + { VICI_SECTION_START, "section2", {} }, + { VICI_SECTION_START, "section3", {} }, + { VICI_KEY_VALUE, "key1", { "value1", 6 } }, + { VICI_SECTION_START, "section4", {} }, + { VICI_KEY_VALUE, "key2", { "value2", 6 } }, + { VICI_SECTION_END, NULL, {} }, + { VICI_SECTION_END, NULL, {} }, + { VICI_SECTION_END, NULL, {} }, + { VICI_KEY_VALUE, "key3", { "value3", 6 } }, + { VICI_SECTION_END, NULL, {} }, + { VICI_END, NULL, {} }, +}; + +static endecode_test_t endecode_test_list[] = { + { VICI_SECTION_START, "section1", {} }, + { VICI_LIST_START, "list1", {} }, + { VICI_LIST_ITEM, NULL, { "item1", 5 } }, + { VICI_LIST_ITEM, NULL, { "item2", 5 } }, + { VICI_LIST_END, NULL, {} }, + { VICI_KEY_VALUE, "key1", { "value1", 6 } }, + { VICI_SECTION_END, NULL, {} }, + { VICI_END, NULL, {} }, +}; + +static endecode_test_t endecode_test_blobs[] = { + { VICI_KEY_VALUE, "key1", { blob, countof(blob) } }, + { VICI_SECTION_START, "section1", {} }, + { VICI_LIST_START, "list1", {} }, + { VICI_LIST_ITEM, NULL, { blob, countof(blob) } }, + { VICI_LIST_ITEM, NULL, { blob, countof(blob) } }, + { VICI_LIST_END, NULL, {} }, + { VICI_KEY_VALUE, "key2", { blob, countof(blob) } }, + { VICI_SECTION_END, NULL, {} }, + { VICI_END, NULL, {} }, +}; + +static endecode_test_t *endecode_tests[] = { + endecode_test_simple, + endecode_test_nested, + endecode_test_list, + endecode_test_blobs, +}; + +typedef struct { + enumerator_t public; + endecode_test_t *next; +} endecode_enum_t; + +static bool endecode_enumerate(endecode_enum_t *this, vici_type_t *type, + char **name, chunk_t *data) +{ + if (this->next) + { + *type = this->next->type; + *name = this->next->name; + *data = this->next->data; + if (this->next->type == VICI_END) + { + this->next = NULL; + } + else + { + this->next++; + } + return TRUE; + } + return FALSE; +} + +static enumerator_t *endecode_create_enumerator(endecode_test_t *test) +{ + endecode_enum_t *enumerator; + + INIT(enumerator, + .public = { + .enumerate = (void*)endecode_enumerate, + .destroy = (void*)free, + }, + .next = test, + ); + + return &enumerator->public; +} + +static void compare_vici(enumerator_t *parse, enumerator_t *tmpl) +{ + vici_type_t type, ttype; + char *name, *tname; + chunk_t data, tdata;; + + while (TRUE) + { + ck_assert(parse->enumerate(parse, &type, &name, &data)); + ck_assert(tmpl->enumerate(tmpl, &ttype, &tname, &tdata)); + ck_assert_int_eq(type, ttype); + switch (type) + { + case VICI_END: + return; + case VICI_SECTION_START: + case VICI_LIST_START: + ck_assert(streq(name, tname)); + break; + case VICI_LIST_ITEM: + ck_assert(chunk_equals(data, tdata)); + break; + case VICI_KEY_VALUE: + ck_assert(streq(name, tname)); + ck_assert(chunk_equals(data, tdata)); + break; + case VICI_SECTION_END: + case VICI_LIST_END: + break; + default: + ck_assert(FALSE); + break; + } + } +} + +START_TEST(test_endecode) +{ + enumerator_t *parse, *tmpl; + vici_message_t *m; + chunk_t data; + + tmpl = endecode_create_enumerator(endecode_tests[_i]); + m = vici_message_create_from_enumerator(tmpl); + ck_assert(m); + data = chunk_clone(m->get_encoding(m)); + tmpl = endecode_create_enumerator(endecode_tests[_i]); + parse = m->create_enumerator(m); + ck_assert(parse); + compare_vici(parse, tmpl); + tmpl->destroy(tmpl); + parse->destroy(parse); + m->destroy(m); + + m = vici_message_create_from_data(data, TRUE); + ck_assert(m); + tmpl = endecode_create_enumerator(endecode_tests[_i]); + parse = m->create_enumerator(m); + ck_assert(parse); + compare_vici(parse, tmpl); + tmpl->destroy(tmpl); + parse->destroy(parse); + m->destroy(m); +} +END_TEST + +START_TEST(test_vararg) +{ + enumerator_t *parse, *tmpl; + vici_message_t *m; + + m = vici_message_create_from_args( + VICI_SECTION_START, "section1", + VICI_LIST_START, "list1", + VICI_LIST_ITEM, chunk_from_str("item1"), + VICI_LIST_ITEM, chunk_from_str("item2"), + VICI_LIST_END, + VICI_KEY_VALUE, "key1", chunk_from_str("value1"), + VICI_SECTION_END, + VICI_END); + ck_assert(m); + tmpl = endecode_create_enumerator(endecode_test_list); + parse = m->create_enumerator(m); + ck_assert(parse); + + compare_vici(parse, tmpl); + + m->destroy(m); + tmpl->destroy(tmpl); + parse->destroy(parse); +} +END_TEST + +START_TEST(test_builder) +{ + enumerator_t *parse, *tmpl; + vici_message_t *m; + vici_builder_t *b; + + b = vici_builder_create(); + b->add(b, VICI_SECTION_START, "section1"); + b->add(b, VICI_LIST_START, "list1"); + b->add(b, VICI_LIST_ITEM, chunk_from_str("item1")); + b->add(b, VICI_LIST_ITEM, chunk_from_str("item2")); + b->add(b, VICI_LIST_END); + b->add(b, VICI_KEY_VALUE, "key1", chunk_from_str("value1")); + b->add(b, VICI_SECTION_END); + m = b->finalize(b); + ck_assert(m); + tmpl = endecode_create_enumerator(endecode_test_list); + parse = m->create_enumerator(m); + ck_assert(parse); + + compare_vici(parse, tmpl); + + m->destroy(m); + tmpl->destroy(tmpl); + parse->destroy(parse); +} +END_TEST + +START_TEST(test_builder_fmt) +{ + enumerator_t *parse, *tmpl; + vici_message_t *m; + vici_builder_t *b; + + b = vici_builder_create(); + b->begin_section(b, "section1"); + b->begin_list(b, "list1"); + b->add_li(b, "item%u", 1); + b->add_li(b, "%s%u", "item", 2); + b->end_list(b); + b->add_kv(b, "key1", "value%u", 1); + b->end_section(b); + m = b->finalize(b); + ck_assert(m); + tmpl = endecode_create_enumerator(endecode_test_list); + parse = m->create_enumerator(m); + ck_assert(parse); + + compare_vici(parse, tmpl); + + m->destroy(m); + tmpl->destroy(tmpl); + parse->destroy(parse); +} +END_TEST + +static vici_message_t* build_getter_msg() +{ + return vici_message_create_from_args( + VICI_KEY_VALUE, "key1", chunk_from_str("1"), + VICI_SECTION_START, "section1", + VICI_KEY_VALUE, "key2", chunk_from_str("0x12"), + VICI_SECTION_START, "section2", + VICI_KEY_VALUE, "key3", chunk_from_str("-1"), + VICI_SECTION_END, + VICI_KEY_VALUE, "key4", chunk_from_str("asdf"), + VICI_SECTION_END, + VICI_KEY_VALUE, "key5", chunk_from_str(""), + VICI_END); +} + +START_TEST(test_get_str) +{ + vici_message_t *m; + + m = build_getter_msg(); + + ck_assert_str_eq(m->get_str(m, "def", "key1"), "1"); + ck_assert_str_eq(m->get_str(m, "def", "section1.key2"), "0x12"); + ck_assert_str_eq(m->get_str(m, "def", "section%d.section2.key3", 1), "-1"); + ck_assert_str_eq(m->get_str(m, "def", "section1.key4"), "asdf"); + ck_assert_str_eq(m->get_str(m, "def", "key5"), ""); + ck_assert_str_eq(m->get_str(m, "no", "nonexistent"), "no"); + ck_assert_str_eq(m->get_str(m, "no", "n.o.n.e.x.i.s.t.e.n.t"), "no"); + + m->destroy(m); +} +END_TEST + +START_TEST(test_get_int) +{ + vici_message_t *m; + + m = build_getter_msg(); + + ck_assert_int_eq(m->get_int(m, 2, "key1"), 1); + ck_assert_int_eq(m->get_int(m, 2, "section1.key2"), 0x12); + ck_assert_int_eq(m->get_int(m, 2, "section1.section2.key3"), -1); + ck_assert_int_eq(m->get_int(m, 2, "section1.key4"), 2); + ck_assert_int_eq(m->get_int(m, 2, "key5"), 0); + ck_assert_int_eq(m->get_int(m, 2, "nonexistent"), 2); + ck_assert_int_eq(m->get_int(m, 2, "n.o.n.e.x.i.s.t.e.n.t"), 2); + + m->destroy(m); +} +END_TEST + +START_TEST(test_get_value) +{ + vici_message_t *m; + chunk_t d = chunk_from_chars('d','e','f'); + + m = build_getter_msg(); + + ck_assert_chunk_eq(m->get_value(m, d, "key1"), chunk_from_str("1")); + ck_assert_chunk_eq(m->get_value(m, d, "section1.key2"), chunk_from_str("0x12")); + ck_assert_chunk_eq(m->get_value(m, d, "section1.section2.key3"), chunk_from_str("-1")); + ck_assert_chunk_eq(m->get_value(m, d, "section1.key4"), chunk_from_str("asdf")); + ck_assert_chunk_eq(m->get_value(m, d, "key5"), chunk_empty); + ck_assert_chunk_eq(m->get_value(m, d, "nonexistent"), d); + ck_assert_chunk_eq(m->get_value(m, d, "n.o.n.e.x.i.s.t.e.n.t"), d); + + m->destroy(m); +} +END_TEST + +Suite *message_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("vici message"); + + tc = tcase_create("enumerator en/decode"); + tcase_add_loop_test(tc, test_endecode, 0, countof(endecode_tests)); + suite_add_tcase(s, tc); + + tc = tcase_create("vararg encode"); + tcase_add_test(tc, test_vararg); + suite_add_tcase(s, tc); + + tc = tcase_create("builder encode"); + tcase_add_test(tc, test_builder); + suite_add_tcase(s, tc); + + tc = tcase_create("builder format encode"); + tcase_add_test(tc, test_builder_fmt); + suite_add_tcase(s, tc); + + tc = tcase_create("convenience getters"); + tcase_add_test(tc, test_get_str); + tcase_add_test(tc, test_get_int); + tcase_add_test(tc, test_get_value); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libcharon/plugins/vici/suites/test_request.c b/src/libcharon/plugins/vici/suites/test_request.c new file mode 100644 index 000000000..8eeb37bc9 --- /dev/null +++ b/src/libcharon/plugins/vici/suites/test_request.c @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <test_suite.h> + +#include "../vici_dispatcher.h" +#include "../libvici.h" + +#include <unistd.h> + +#ifdef WIN32 +# define URI "tcp://127.0.0.1:6543" +#else /* !WIN32 */ +# define URI "unix:///tmp/strongswan-vici-request-test" +#endif /* !WIN32 */ + +static void encode_section(vici_req_t *req) +{ + vici_begin_section(req, "section1"); + vici_add_key_valuef(req, "key1", "value%u", 1); + vici_add_key_value(req, "key2", "value2", strlen("value2")); + vici_end_section(req); +} + +static void decode_section(vici_res_t *res) +{ + char *str; + int len; + + ck_assert(vici_parse(res) == VICI_PARSE_BEGIN_SECTION); + ck_assert_str_eq(vici_parse_name(res), "section1"); + ck_assert(vici_parse(res) == VICI_PARSE_KEY_VALUE); + ck_assert_str_eq(vici_parse_name(res), "key1"); + ck_assert_str_eq(vici_parse_value_str(res), "value1"); + ck_assert(vici_parse(res) == VICI_PARSE_KEY_VALUE); + ck_assert_str_eq(vici_parse_name(res), "key2"); + str = vici_parse_value(res, &len); + ck_assert(chunk_equals(chunk_from_str("value2"), chunk_create(str, len))); + ck_assert(vici_parse(res) == VICI_PARSE_END_SECTION); + ck_assert(vici_parse(res) == VICI_PARSE_END); +} + +static void encode_list(vici_req_t *req) +{ + vici_begin_list(req, "list1"); + vici_add_list_item(req, "item1", strlen("item1")); + vici_add_list_itemf(req, "item%u", 2); + vici_end_list(req); +} + +static void decode_list(vici_res_t *res) +{ + char *str; + int len; + + ck_assert(vici_parse(res) == VICI_PARSE_BEGIN_LIST); + ck_assert_str_eq(vici_parse_name(res), "list1"); + ck_assert(vici_parse(res) == VICI_PARSE_LIST_ITEM); + ck_assert_str_eq(vici_parse_value_str(res), "item1"); + ck_assert(vici_parse(res) == VICI_PARSE_LIST_ITEM); + str = vici_parse_value(res, &len); + ck_assert(chunk_equals(chunk_from_str("item2"), chunk_create(str, len))); + ck_assert(vici_parse(res) == VICI_PARSE_END_LIST); + ck_assert(vici_parse(res) == VICI_PARSE_END); +} + +static struct { + void (*encode)(vici_req_t* req); + void (*decode)(vici_res_t* res); +} echo_tests[] = { + { encode_section, decode_section }, + { encode_list, decode_list }, +}; + +static vici_message_t* echo_cb(void *user, char *name, + u_int id, vici_message_t *request) +{ + ck_assert_str_eq(name, "echo"); + ck_assert_int_eq((uintptr_t)user, 1); + + return vici_message_create_from_enumerator(request->create_enumerator(request)); +} + +START_TEST(test_echo) +{ + vici_dispatcher_t *dispatcher; + vici_conn_t *conn; + vici_req_t *req; + vici_res_t *res; + + lib->processor->set_threads(lib->processor, 8); + + dispatcher = vici_dispatcher_create(URI); + ck_assert(dispatcher); + + dispatcher->manage_command(dispatcher, "echo", echo_cb, (void*)(uintptr_t)1); + + vici_init(); + conn = vici_connect(URI); + ck_assert(conn); + + req = vici_begin("echo"); + echo_tests[_i].encode(req); + res = vici_submit(req, conn); + ck_assert(res); + echo_tests[_i].decode(res); + vici_free_res(res); + + vici_disconnect(conn); + + dispatcher->manage_command(dispatcher, "echo", NULL, NULL); + + lib->processor->cancel(lib->processor); + dispatcher->destroy(dispatcher); + + vici_deinit(); +} +END_TEST + +START_TEST(test_missing) +{ + vici_dispatcher_t *dispatcher; + vici_conn_t *conn; + vici_req_t *req; + vici_res_t *res; + + lib->processor->set_threads(lib->processor, 8); + + dispatcher = vici_dispatcher_create(URI); + ck_assert(dispatcher); + + vici_init(); + conn = vici_connect(URI); + ck_assert(conn); + + req = vici_begin("nonexistent"); + encode_section(req); + res = vici_submit(req, conn); + ck_assert(res == NULL); + + vici_disconnect(conn); + + dispatcher->manage_command(dispatcher, "echo", NULL, NULL); + + lib->processor->cancel(lib->processor); + dispatcher->destroy(dispatcher); + + vici_deinit(); +} +END_TEST + +static void event_cb(void *user, char *name, vici_res_t *ev) +{ + int *events = (int*)user; + + (*events)++; +} + +START_TEST(test_stress) +{ + vici_dispatcher_t *dispatcher; + vici_conn_t *conn; + vici_req_t *req; + vici_res_t *res; + int i, total = 50, events = 0; + + lib->processor->set_threads(lib->processor, 8); + + dispatcher = vici_dispatcher_create(URI); + ck_assert(dispatcher); + + dispatcher->manage_command(dispatcher, "echo", echo_cb, (void*)(uintptr_t)1); + dispatcher->manage_event(dispatcher, "dummy", TRUE); + + vici_init(); + conn = vici_connect(URI); + ck_assert(conn); + + for (i = 0; i < total; i++) + { + /* do some event management in between */ + ck_assert(vici_register(conn, "dummy", event_cb, &events) == 0); + dispatcher->raise_event(dispatcher, "dummy", 0, + vici_message_create_from_args( + VICI_KEY_VALUE, "key1", chunk_from_str("value1"), + VICI_END)); + + req = vici_begin("echo"); + encode_section(req); + res = vici_submit(req, conn); + ck_assert(res); + decode_section(res); + vici_free_res(res); + + ck_assert(vici_register(conn, "dummy", NULL, NULL) == 0); + } + + while (events < total) + { + usleep(1000); + } + + vici_disconnect(conn); + + dispatcher->manage_command(dispatcher, "echo", NULL, NULL); + dispatcher->manage_event(dispatcher, "dummy", FALSE); + + lib->processor->cancel(lib->processor); + dispatcher->destroy(dispatcher); + + vici_deinit(); +} +END_TEST + +Suite *request_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("vici request"); + + tc = tcase_create("echo"); + tcase_add_loop_test(tc, test_echo, 0, countof(echo_tests)); + suite_add_tcase(s, tc); + + tc = tcase_create("missing"); + tcase_add_test(tc, test_missing); + suite_add_tcase(s, tc); + + tc = tcase_create("stress"); + tcase_add_test(tc, test_stress); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libcharon/plugins/vici/suites/test_socket.c b/src/libcharon/plugins/vici/suites/test_socket.c new file mode 100644 index 000000000..8d545c6c1 --- /dev/null +++ b/src/libcharon/plugins/vici/suites/test_socket.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <test_suite.h> + +#include "../vici_socket.h" + +#include <unistd.h> + +typedef struct { + vici_socket_t *s; + int disconnect; + int bytes; + u_int id; +} test_data_t; + +static void echo_inbound(void *user, u_int id, chunk_t buf) +{ + test_data_t *data = user; + + ck_assert_int_eq(data->id, id); + /* count number of bytes, including the header */ + data->bytes += buf.len + sizeof(u_int32_t); + /* echo back data chunk */ + data->s->send(data->s, id, chunk_clone(buf)); +} + +static void echo_connect(void *user, u_int id) +{ + test_data_t *data = user; + + data->id = id; +} + +static void echo_disconnect(void *user, u_int id) +{ + test_data_t *data = user; + + ck_assert(id == data->id); + data->disconnect++; +} + +static struct { + char *uri; + u_int chunksize; +} echo_tests[] = { + { "tcp://127.0.0.1:6543", ~0 }, + { "tcp://127.0.0.1:6543", 1 }, + { "tcp://127.0.0.1:6543", 2 }, + { "tcp://127.0.0.1:6543", 3 }, + { "tcp://127.0.0.1:6543", 7 }, +#ifndef WIN32 + { "unix:///tmp/strongswan-tests-vici-socket", ~0 }, + { "unix:///tmp/strongswan-tests-vici-socket", 1 }, + { "unix:///tmp/strongswan-tests-vici-socket", 2 }, + { "unix:///tmp/strongswan-tests-vici-socket", 3 }, + { "unix:///tmp/strongswan-tests-vici-socket", 7 }, +#endif /* !WIN32 */ +}; + +START_TEST(test_echo) +{ + stream_t *c; + test_data_t data = {}; + chunk_t x, m = chunk_from_chars( + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x01, 0x01, + 0x00,0x00,0x00,0x05, 0x11,0x12,0x13,0x14,0x15, + 0x00,0x00,0x00,0x0A, 0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x02A, + ); + char buf[m.len]; + u_int32_t len; + + lib->processor->set_threads(lib->processor, 4); + + /* create socket, connect with stream */ + data.s = vici_socket_create(echo_tests[_i].uri, echo_inbound, echo_connect, + echo_disconnect, &data); + ck_assert(data.s != NULL); + c = lib->streams->connect(lib->streams, echo_tests[_i].uri); + ck_assert(c != NULL); + + /* write arbitrary chunks of messages blob depending on test */ + x = m; + while (x.len) + { + len = min(x.len, echo_tests[_i].chunksize); + ck_assert(c->write_all(c, x.ptr, len)); + x = chunk_skip(x, len); + } + + /* verify echo */ + ck_assert(c->read_all(c, buf, sizeof(buf))); + ck_assert(chunk_equals(m, chunk_from_thing(buf))); + + /* wait for completion */ + c->destroy(c); + while (data.disconnect != 1) + { + usleep(1000); + } + /* check that we got correct number of bytes/invocations */ + ck_assert_int_eq(data.bytes, m.len); + + data.s->destroy(data.s); +} +END_TEST + +Suite *socket_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("vici socket"); + + tc = tcase_create("echo"); + tcase_add_loop_test(tc, test_echo, 0, countof(echo_tests)); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libcharon/plugins/vici/vici_attribute.c b/src/libcharon/plugins/vici/vici_attribute.c new file mode 100644 index 000000000..2178116c9 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_attribute.c @@ -0,0 +1,713 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "vici_attribute.h" +#include "vici_builder.h" + +#include <daemon.h> +#include <collections/hashtable.h> +#include <collections/array.h> +#include <threading/rwlock.h> +#include <attributes/mem_pool.h> + +typedef struct private_vici_attribute_t private_vici_attribute_t; + +/** + * private data of vici_attribute + */ +struct private_vici_attribute_t { + + /** + * public functions + */ + vici_attribute_t public; + + /** + * vici connection dispatcher + */ + vici_dispatcher_t *dispatcher; + + /** + * Configured pools, as char* => pool_t + */ + hashtable_t *pools; + + /** + * rwlock to lock access to pools + */ + rwlock_t *lock; +}; + +/** + * Single configuration attribute with type + */ +typedef struct { + /** type of attribute */ + configuration_attribute_type_t type; + /** attribute value */ + chunk_t value; +} attribute_t; + +/** + * Clean up an attribute + */ +static void attribute_destroy(attribute_t *attr) +{ + free(attr->value.ptr); + free(attr); +} + +/** + * Pool instances with associated attributes + */ +typedef struct { + /** in-memory virtual IP pool */ + mem_pool_t *vips; + /** configuration attributes, as attribute_t */ + array_t *attrs; +} pool_t; + +/** + * Clean up a pool instance + */ +static void pool_destroy(pool_t *pool) +{ + DESTROY_IF(pool->vips); + array_destroy_function(pool->attrs, (void*)attribute_destroy, NULL); + free(pool); +} + +/** + * Find an existing or not yet existing lease + */ +static host_t *find_addr(private_vici_attribute_t *this, linked_list_t *pools, + identification_t *id, host_t *requested, mem_pool_op_t op) +{ + enumerator_t *enumerator; + host_t *addr = NULL; + pool_t *pool; + char *name; + + enumerator = pools->create_enumerator(pools); + while (enumerator->enumerate(enumerator, &name)) + { + pool = this->pools->get(this->pools, name); + if (pool) + { + addr = pool->vips->acquire_address(pool->vips, id, requested, op); + if (addr) + { + break; + } + } + } + enumerator->destroy(enumerator); + + return addr; +} + +METHOD(attribute_provider_t, acquire_address, host_t*, + private_vici_attribute_t *this, linked_list_t *pools, identification_t *id, + host_t *requested) +{ + host_t *addr; + + this->lock->read_lock(this->lock); + + addr = find_addr(this, pools, id, requested, MEM_POOL_EXISTING); + if (!addr) + { + addr = find_addr(this, pools, id, requested, MEM_POOL_NEW); + if (!addr) + { + addr = find_addr(this, pools, id, requested, MEM_POOL_REASSIGN); + } + } + + this->lock->unlock(this->lock); + + return addr; +} + +METHOD(attribute_provider_t, release_address, bool, + private_vici_attribute_t *this, linked_list_t *pools, host_t *address, + identification_t *id) +{ + enumerator_t *enumerator; + bool found = FALSE; + pool_t *pool; + char *name; + + this->lock->read_lock(this->lock); + + enumerator = pools->create_enumerator(pools); + while (enumerator->enumerate(enumerator, &name)) + { + pool = this->pools->get(this->pools, name); + if (pool) + { + found = pool->vips->release_address(pool->vips, address, id); + if (found) + { + break; + } + } + } + enumerator->destroy(enumerator); + + this->lock->unlock(this->lock); + + return found; +} + +/** + * Filter mapping attribute_t to enumerated type/value arguments + */ +static bool attr_filter(void *data, attribute_t **attr, + configuration_attribute_type_t *type, + void *in, chunk_t *value) +{ + *type = (*attr)->type; + *value = (*attr)->value; + return TRUE; +} + +/** + * Create nested inner enumerator over pool attributes + */ +CALLBACK(create_nested, enumerator_t*, + pool_t *pool, void *this) +{ + return enumerator_create_filter(array_create_enumerator(pool->attrs), + (void*)attr_filter, NULL, NULL); +} + +/** + * Data associated to nested enumerator cleanup + */ +typedef struct { + private_vici_attribute_t *this; + linked_list_t *list; +} nested_data_t; + +/** + * Clean up nested enumerator data + */ +CALLBACK(nested_cleanup, void, + nested_data_t *data) +{ + data->this->lock->unlock(data->this->lock); + data->list->destroy(data->list); + free(data); +} + +/** + * Check if any of vips is from pool + */ +static bool have_vips_from_pool(mem_pool_t *pool, linked_list_t *vips) +{ + enumerator_t *enumerator; + host_t *host; + chunk_t start, end, current; + u_int32_t size; + bool found = FALSE; + + host = pool->get_base(pool); + start = host->get_address(host); + + if (start.len >= sizeof(size)) + { + end = chunk_clone(start); + + /* mem_pool is currenty limited to 2^31 addresses, so 32-bit + * calculations should be sufficient. */ + size = untoh32(start.ptr + start.len - sizeof(size)); + htoun32(end.ptr + end.len - sizeof(size), size + pool->get_size(pool)); + + enumerator = vips->create_enumerator(vips); + while (enumerator->enumerate(enumerator, &host)) + { + current = host->get_address(host); + if (chunk_compare(current, start) >= 0 && + chunk_compare(current, end) < 0) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + free(end.ptr); + } + return found; +} + +METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, + private_vici_attribute_t *this, linked_list_t *pools, + identification_t *id, linked_list_t *vips) +{ + enumerator_t *enumerator; + nested_data_t *data; + pool_t *pool; + char *name; + + INIT(data, + .this = this, + .list = linked_list_create(), + ); + + this->lock->read_lock(this->lock); + + enumerator = pools->create_enumerator(pools); + while (enumerator->enumerate(enumerator, &name)) + { + pool = this->pools->get(this->pools, name); + if (pool && have_vips_from_pool(pool->vips, vips)) + { + data->list->insert_last(data->list, pool); + } + } + enumerator->destroy(enumerator); + + return enumerator_create_nested(data->list->create_enumerator(data->list), + create_nested, data, nested_cleanup); +} + +/** + * Merge a pool configuration with existing ones + */ +static bool merge_pool(private_vici_attribute_t *this, pool_t *new) +{ + mem_pool_t *tmp; + host_t *base; + pool_t *old; + const char *name; + u_int size; + + name = new->vips->get_name(new->vips); + base = new->vips->get_base(new->vips); + size = new->vips->get_size(new->vips); + + old = this->pools->remove(this->pools, name); + if (!old) + { + this->pools->put(this->pools, name, new); + DBG1(DBG_CFG, "added vici pool %s: %H, %u entries", name, base, size); + return TRUE; + } + + if (base->ip_equals(base, old->vips->get_base(old->vips)) && + size == old->vips->get_size(old->vips)) + { + /* no changes in pool, so keep existing, but use new attributes */ + DBG1(DBG_CFG, "updated vici pool %s: %H, %u entries", name, base, size); + tmp = new->vips; + new->vips = old->vips; + old->vips = tmp; + this->pools->put(this->pools, new->vips->get_name(new->vips), new); + pool_destroy(old); + return TRUE; + } + if (old->vips->get_online(old->vips) == 0) + { + /* can replace old pool, no online leases */ + DBG1(DBG_CFG, "replaced vici pool %s: %H, %u entries", name, base, size); + this->pools->put(this->pools, name, new); + pool_destroy(old); + return TRUE; + } + /* have online leases, unable to replace, TODO: migrate leases? */ + DBG1(DBG_CFG, "vici pool %s has %u online leases, unable to replace", + name, old->vips->get_online(old->vips)); + this->pools->put(this->pools, old->vips->get_name(old->vips), old); + return FALSE; +} + +/** + * Create a (error) reply message + */ +static vici_message_t* create_reply(char *fmt, ...) +{ + vici_builder_t *builder; + va_list args; + + builder = vici_builder_create(); + builder->add_kv(builder, "success", fmt ? "no" : "yes"); + if (fmt) + { + va_start(args, fmt); + builder->vadd_kv(builder, "errmsg", fmt, args); + va_end(args); + } + return builder->finalize(builder); +} + +/** + * Parse callback data, passed to each callback + */ +typedef struct { + private_vici_attribute_t *this; + vici_message_t *reply; +} request_data_t; + +/** + * Data associated to a pool load + */ +typedef struct { + request_data_t *request; + char *name; + pool_t *pool; +} load_data_t; + +CALLBACK(pool_li, bool, + load_data_t *data, vici_message_t *message, char *name, chunk_t value) +{ + struct { + char *name; + configuration_attribute_type_t v4; + configuration_attribute_type_t v6; + } keys[] = { + {"address", INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS }, + {"dns", INTERNAL_IP4_DNS, INTERNAL_IP6_DNS }, + {"nbns", INTERNAL_IP4_NBNS, INTERNAL_IP6_NBNS }, + {"dhcp", INTERNAL_IP4_DHCP, INTERNAL_IP6_DHCP }, + {"netmask", INTERNAL_IP4_NETMASK, INTERNAL_IP6_NETMASK }, + {"server", INTERNAL_IP4_SERVER, INTERNAL_IP6_SERVER }, + {"subnet", INTERNAL_IP4_SUBNET, INTERNAL_IP6_SUBNET }, + {"split_include", UNITY_SPLIT_INCLUDE, UNITY_SPLIT_INCLUDE }, + {"split_exclude", UNITY_LOCAL_LAN, UNITY_LOCAL_LAN }, + }; + char buf[256]; + int i, index = -1, mask = -1, type = 0; + chunk_t encoding; + attribute_t *attr; + host_t *host = NULL; + + for (i = 0; i < countof(keys); i++) + { + if (streq(name, keys[i].name)) + { + index = i; + break; + } + } + if (index == -1) + { + type = atoi(name); + if (!type) + { + data->request->reply = create_reply("invalid attribute: %s", name); + return FALSE; + } + } + + if (vici_stringify(value, buf, sizeof(buf))) + { + if (strchr(buf, '/')) + { + host = host_create_from_subnet(buf, &mask); + } + else + { + host = host_create_from_string(buf, 0); + } + } + if (host) + { + if (index != -1) + { + switch (host->get_family(host)) + { + case AF_INET: + type = keys[index].v4; + break; + case AF_INET6: + default: + type = keys[index].v6; + break; + } + } + if (mask == -1) + { + encoding = chunk_clone(host->get_address(host)); + } + else + { + if (host->get_family(host) == AF_INET) + { /* IPv4 attributes contain a subnet mask */ + u_int32_t netmask = 0; + + if (mask) + { /* shifting u_int32_t by 32 or more is undefined */ + mask = 32 - mask; + netmask = htonl((0xFFFFFFFF >> mask) << mask); + } + encoding = chunk_cat("cc", host->get_address(host), + chunk_from_thing(netmask)); + } + else + { /* IPv6 addresses the prefix only */ + encoding = chunk_cat("cc", host->get_address(host), + chunk_from_chars(mask)); + } + } + host->destroy(host); + } + else + { + if (index != -1) + { + data->request->reply = create_reply("invalid attribute value " + "for %s", name); + return FALSE; + } + /* use raw binary data for numbered attributes */ + encoding = chunk_clone(value); + } + INIT(attr, + .type = type, + .value = encoding, + ); + array_insert_create(&data->pool->attrs, ARRAY_TAIL, attr); + return TRUE; +} + +CALLBACK(pool_kv, bool, + load_data_t *data, vici_message_t *message, char *name, chunk_t value) +{ + if (streq(name, "addrs")) + { + char buf[128]; + host_t *base; + int bits; + + if (data->pool->vips) + { + data->request->reply = create_reply("multiple addrs defined"); + return FALSE; + } + if (!vici_stringify(value, buf, sizeof(buf))) + { + data->request->reply = create_reply("invalid addrs value"); + return FALSE; + } + base = host_create_from_subnet(buf, &bits); + if (!base) + { + data->request->reply = create_reply("invalid addrs value: %s", buf); + return FALSE; + } + data->pool->vips = mem_pool_create(data->name, base, bits); + base->destroy(base); + return TRUE; + } + data->request->reply = create_reply("invalid attribute: %s", name); + return FALSE; +} + +CALLBACK(pool_sn, bool, + request_data_t *request, vici_message_t *message, + vici_parse_context_t *ctx, char *name) +{ + load_data_t data = { + .request = request, + .name = name, + }; + bool merged; + + INIT(data.pool); + + if (!message->parse(message, ctx, NULL, pool_kv, pool_li, &data)) + { + pool_destroy(data.pool); + return FALSE; + } + + if (!data.pool->vips) + { + request->reply = create_reply("missing addrs for pool '%s'", name); + pool_destroy(data.pool); + return FALSE; + } + + request->this->lock->write_lock(request->this->lock); + merged = merge_pool(request->this, data.pool); + request->this->lock->unlock(request->this->lock); + + if (!merged) + { + request->reply = create_reply("vici pool %s has online leases, " + "unable to replace", name); + pool_destroy(data.pool); + } + return merged; +} + +CALLBACK(load_pool, vici_message_t*, + private_vici_attribute_t *this, char *name, u_int id, + vici_message_t *message) +{ + request_data_t request = { + .this = this, + }; + + if (!message->parse(message, NULL, pool_sn, NULL, NULL, &request)) + { + if (request.reply) + { + return request.reply; + } + return create_reply("parsing request failed"); + } + return create_reply(NULL); +} + +CALLBACK(unload_pool, vici_message_t*, + private_vici_attribute_t *this, char *name, u_int id, + vici_message_t *message) +{ + vici_message_t *reply; + u_int online; + pool_t *pool; + + name = message->get_str(message, NULL, "name"); + if (!name) + { + return create_reply("missing pool name to unload"); + } + + this->lock->write_lock(this->lock); + + pool = this->pools->remove(this->pools, name); + if (pool) + { + online = pool->vips->get_online(pool->vips); + if (online) + { + DBG1(DBG_CFG, "vici pool %s has %u online leases, unable to unload", + name, online); + reply = create_reply("%s has online leases, unable to unload", name); + this->pools->put(this->pools, pool->vips->get_name(pool->vips), pool); + } + else + { + DBG1(DBG_CFG, "unloaded vici pool %s", name); + reply = create_reply(NULL); + pool_destroy(pool); + } + } + else + { + reply = create_reply("%s not found", name); + } + + this->lock->unlock(this->lock); + + return reply; +} + +CALLBACK(get_pools, vici_message_t*, + private_vici_attribute_t *this, char *name, u_int id, + vici_message_t *message) +{ + vici_builder_t *builder; + enumerator_t *enumerator; + mem_pool_t *vips; + pool_t *pool; + + builder = vici_builder_create(); + + this->lock->read_lock(this->lock); + enumerator = this->pools->create_enumerator(this->pools); + while (enumerator->enumerate(enumerator, &name, &pool)) + { + vips = pool->vips; + + builder->begin_section(builder, name); + + builder->add_kv(builder, "base", "%H", vips->get_base(vips)); + builder->add_kv(builder, "size", "%u", vips->get_size(vips)); + builder->add_kv(builder, "online", "%u", vips->get_online(vips)); + builder->add_kv(builder, "offline", "%u", vips->get_offline(vips)); + + builder->end_section(builder); + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + return builder->finalize(builder); +} + +static void manage_command(private_vici_attribute_t *this, + char *name, vici_command_cb_t cb, bool reg) +{ + this->dispatcher->manage_command(this->dispatcher, name, + reg ? cb : NULL, this); +} + +/** + * (Un-)register dispatcher functions + */ +static void manage_commands(private_vici_attribute_t *this, bool reg) +{ + manage_command(this, "load-pool", load_pool, reg); + manage_command(this, "unload-pool", unload_pool, reg); + manage_command(this, "get-pools", get_pools, reg); +} + +METHOD(vici_attribute_t, destroy, void, + private_vici_attribute_t *this) +{ + enumerator_t *enumerator; + pool_t *pool; + + manage_commands(this, FALSE); + + enumerator = this->pools->create_enumerator(this->pools); + while (enumerator->enumerate(enumerator, NULL, &pool)) + { + pool_destroy(pool); + } + enumerator->destroy(enumerator); + this->pools->destroy(this->pools); + this->lock->destroy(this->lock); + free(this); +} + +/* + * see header file + */ +vici_attribute_t *vici_attribute_create(vici_dispatcher_t *dispatcher) +{ + private_vici_attribute_t *this; + + INIT(this, + .public = { + .provider = { + .acquire_address = _acquire_address, + .release_address = _release_address, + .create_attribute_enumerator = _create_attribute_enumerator, + }, + .destroy = _destroy, + }, + .dispatcher = dispatcher, + .pools = hashtable_create(hashtable_hash_str, hashtable_equals_str, 4), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + manage_commands(this, TRUE); + + return &this->public; +} diff --git a/src/libcharon/plugins/vici/vici_attribute.h b/src/libcharon/plugins/vici/vici_attribute.h new file mode 100644 index 000000000..652a96d39 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_attribute.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup vici_attribute vici_attribute + * @{ @ingroup vici + */ + +#ifndef VICI_ATTRIBUTE_H_ +#define VICI_ATTRIBUTE_H_ + +#include "vici_dispatcher.h" + +#include <attributes/attribute_provider.h> + +typedef struct vici_attribute_t vici_attribute_t; + +/** + * IKE configuration attribute backend for vici. + */ +struct vici_attribute_t { + + /** + * Implements attribute provider interface + */ + attribute_provider_t provider; + + /** + * Destroy a vici_attribute_t. + */ + void (*destroy)(vici_attribute_t *this); +}; + +/** + * Create a vici_attribute instance. + * + * @param dispatcher vici dispatcher context + * @return vici attribute handler + */ +vici_attribute_t *vici_attribute_create(vici_dispatcher_t *dispatcher); + +#endif /** VICI_ATTRIBUTE_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_builder.c b/src/libcharon/plugins/vici/vici_builder.c new file mode 100644 index 000000000..561632049 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_builder.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "vici_builder.h" + +#include <bio/bio_writer.h> + +typedef struct private_vici_builder_t private_vici_builder_t; + +/** + * Private data of an vici_builder_t object. + */ +struct private_vici_builder_t { + + /** + * Public vici_builder_t interface. + */ + vici_builder_t public; + + /** + * Writer for elements + */ + bio_writer_t *writer; + + /** + * Errors encountered + */ + u_int error; + + /** + * Section nesting level + */ + u_int section; + + /** + * In list element? + */ + bool list; +}; + +METHOD(vici_builder_t, add, void, + private_vici_builder_t *this, vici_type_t type, ...) +{ + va_list args; + char *name = NULL; + chunk_t value = chunk_empty; + + va_start(args, type); + switch (type) + { + case VICI_SECTION_END: + case VICI_LIST_END: + case VICI_END: + break; + case VICI_LIST_START: + case VICI_SECTION_START: + name = va_arg(args, char*); + break; + case VICI_KEY_VALUE: + name = va_arg(args, char*); + value = va_arg(args, chunk_t); + break; + case VICI_LIST_ITEM: + value = va_arg(args, chunk_t); + break; + default: + va_end(args); + this->error++; + return; + } + va_end(args); + + if (value.len > 0xffff) + { + this->error++; + return; + } + if (!vici_verify_type(type, this->section, this->list)) + { + this->error++; + return; + } + if (type != VICI_END) + { + this->writer->write_uint8(this->writer, type); + } + switch (type) + { + case VICI_SECTION_START: + this->writer->write_data8(this->writer, chunk_from_str(name)); + this->section++; + break; + case VICI_SECTION_END: + this->section--; + break; + case VICI_KEY_VALUE: + this->writer->write_data8(this->writer, chunk_from_str(name)); + this->writer->write_data16(this->writer, value); + break; + case VICI_LIST_START: + this->writer->write_data8(this->writer, chunk_from_str(name)); + this->list = TRUE; + break; + case VICI_LIST_ITEM: + this->writer->write_data16(this->writer, value); + break; + case VICI_LIST_END: + this->list = FALSE; + break; + default: + this->error++; + break; + } +} + +METHOD(vici_builder_t, vadd_kv, void, + private_vici_builder_t *this, char *key, char *fmt, va_list args) +{ + char buf[2048]; + ssize_t len; + + len = vsnprintf(buf, sizeof(buf), fmt, args); + if (len < 0 || len >= sizeof(buf)) + { + DBG1(DBG_ENC, "vici builder format buffer exceeds limit"); + this->error++; + } + else + { + add(this, VICI_KEY_VALUE, key, chunk_create(buf, len)); + } +} + +METHOD(vici_builder_t, add_kv, void, + private_vici_builder_t *this, char *key, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vadd_kv(this, key, fmt, args); + va_end(args); +} + + +METHOD(vici_builder_t, vadd_li, void, + private_vici_builder_t *this, char *fmt, va_list args) +{ + char buf[2048]; + ssize_t len; + + len = vsnprintf(buf, sizeof(buf), fmt, args); + if (len < 0 || len >= sizeof(buf)) + { + DBG1(DBG_ENC, "vici builder format buffer exceeds limit"); + this->error++; + } + else + { + add(this, VICI_LIST_ITEM, chunk_create(buf, len)); + } +} + +METHOD(vici_builder_t, add_li, void, + private_vici_builder_t *this, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vadd_li(this, fmt, args); + va_end(args); +} + +METHOD(vici_builder_t, begin_section, void, + private_vici_builder_t *this, char *name) +{ + add(this, VICI_SECTION_START, name); +} + +METHOD(vici_builder_t, end_section, void, + private_vici_builder_t *this) +{ + add(this, VICI_SECTION_END); +} + +METHOD(vici_builder_t, begin_list, void, + private_vici_builder_t *this, char *name) +{ + add(this, VICI_LIST_START, name); +} + +METHOD(vici_builder_t, end_list, void, + private_vici_builder_t *this) +{ + add(this, VICI_LIST_END); +} + +METHOD(vici_builder_t, finalize, vici_message_t*, + private_vici_builder_t *this) +{ + vici_message_t *product; + + if (this->error || this->section || this->list) + { + DBG1(DBG_ENC, "vici builder error: %u errors (section: %u, list %u)", + this->error, this->section, this->list); + this->writer->destroy(this->writer); + free(this); + return NULL; + } + product = vici_message_create_from_data( + this->writer->extract_buf(this->writer), TRUE); + this->writer->destroy(this->writer); + free(this); + return product; +} + +/** + * See header + */ +vici_builder_t *vici_builder_create() +{ + private_vici_builder_t *this; + + INIT(this, + .public = { + .add = _add, + .add_kv = _add_kv, + .vadd_kv = _vadd_kv, + .add_li = _add_li, + .vadd_li = _vadd_li, + .begin_section = _begin_section, + .end_section = _end_section, + .begin_list = _begin_list, + .end_list = _end_list, + .finalize = _finalize, + }, + .writer = bio_writer_create(0), + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/vici/vici_builder.h b/src/libcharon/plugins/vici/vici_builder.h new file mode 100644 index 000000000..5a5cc8a03 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_builder.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup vici_builder vici_builder + * @{ @ingroup vici + */ + +#ifndef VICI_BUILDER_H_ +#define VICI_BUILDER_H_ + +#include "vici_message.h" + +typedef struct vici_builder_t vici_builder_t; + +/** + * Build helper for vici message + */ +struct vici_builder_t { + + /** + * Append a generic message element to message. + * + * The additional arguments are type specific, it may be nothing, a string, + * a chunk value or both. + * + * @param type element type to add + * @param ... additional type specific arguments + */ + void (*add)(vici_builder_t *this, vici_type_t type, ...); + + /** + * Append a key/value element using a format string. + * + * Instead of passing the type specific value as a chunk, this method + * takes a printf() style format string followed by its arguments. The + * key name for a key/value type is still a fixed string. + * + * @param key key name of the key/value to add + * @param fmt value format string + * @param ... arguments to value format string + */ + void (*add_kv)(vici_builder_t *this, char *key, char *fmt, ...); + + /** + * Append a message element using a format string and va_list. + * + * Instead of passing the type specific value as a chunk, this method + * takes a printf() style format string followed by its arguments. The + * key name for a key/value type is still a fixed string. + * + * @param key key name of the key/value to add + * @param fmt value format string + * @param args arguments to value format string + */ + void (*vadd_kv)(vici_builder_t *this, char *key, char *fmt, va_list args); + + /** + * Append a list item element using a format string. + * + * Instead of passing the type specific value as a chunk, this method + * takes a printf() style format string followed by its arguments. + * + * @param fmt value format string + * @param ... arguments to value format string + */ + void (*add_li)(vici_builder_t *this, char *fmt, ...); + + /** + * Append a list item element using a format string and va_list. + * + * Instead of passing the type specific value as a chunk, this method + * takes a printf() style format string followed by its arguments. + * + * @param fmt value format string + * @param args arguments to value format string + */ + void (*vadd_li)(vici_builder_t *this, char *fmt, va_list args); + + /** + * Begin a new section. + * + * @param name name of section to begin + */ + void (*begin_section)(vici_builder_t *this, char *name); + + /** + * End the currently open section. + */ + void (*end_section)(vici_builder_t *this); + + /** + * Begin a new list. + * + * @param name name of list to begin + */ + void (*begin_list)(vici_builder_t *this, char *name); + + /** + * End the currently open list. + */ + void (*end_list)(vici_builder_t *this); + + /** + * Finalize a vici message with all added elements, destroy builder. + * + * @return vici message, NULL on error + */ + vici_message_t* (*finalize)(vici_builder_t *this); +}; + +/** + * Create a vici_builder instance. + */ +vici_builder_t *vici_builder_create(); + +#endif /** VICI_BUILDER_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c new file mode 100644 index 000000000..113d48084 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_config.c @@ -0,0 +1,2006 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define _GNU_SOURCE + +#include "vici_config.h" +#include "vici_builder.h" + +#include <daemon.h> +#include <threading/rwlock.h> +#include <collections/array.h> +#include <collections/linked_list.h> + +#include <stdio.h> + +/** + * Magic value for an undefined lifetime + */ +#define LFT_UNDEFINED (~(u_int64_t)0) + +/** + * Default IKE rekey time + */ +#define LFT_DEFAULT_IKE_REKEY (4 * 60 * 60) + +/** + * Default CHILD rekey time + */ +#define LFT_DEFAULT_CHILD_REKEY (1 * 60 * 60) + +/** + * Undefined replay window + */ +#define REPLAY_UNDEFINED (~(u_int32_t)0) + +typedef struct private_vici_config_t private_vici_config_t; + +/** + * Private data of an vici_config_t object. + */ +struct private_vici_config_t { + + /** + * Public vici_config_t interface. + */ + vici_config_t public; + + /** + * Dispatcher + */ + vici_dispatcher_t *dispatcher; + + /** + * List of loaded connections, as peer_cfg_t + */ + linked_list_t *conns; + + /** + * Lock for conns list + */ + rwlock_t *lock; +}; + +METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*, + private_vici_config_t *this, identification_t *me, identification_t *other) +{ + this->lock->read_lock(this->lock); + return enumerator_create_cleaner(this->conns->create_enumerator(this->conns), + (void*)this->lock->unlock, this->lock); +} + +/** + * Enumerator filter function for ike configs + */ +static bool ike_filter(void *data, peer_cfg_t **in, ike_cfg_t **out) +{ + *out = (*in)->get_ike_cfg(*in); + return TRUE; +} + +METHOD(backend_t, create_ike_cfg_enumerator, enumerator_t*, + private_vici_config_t *this, host_t *me, host_t *other) +{ + this->lock->read_lock(this->lock); + return enumerator_create_filter(this->conns->create_enumerator(this->conns), + (void*)ike_filter, this->lock, + (void*)this->lock->unlock); +} + +METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, + private_vici_config_t *this, char *name) +{ + peer_cfg_t *current, *found = NULL; + enumerator_t *enumerator; + + this->lock->read_lock(this->lock); + enumerator = this->conns->create_enumerator(this->conns); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (streq(current->get_name(current), name)) + { + found = current; + found->get_ref(found); + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + return found; +} + +/** + * Create a (error) reply message + */ +static vici_message_t* create_reply(char *fmt, ...) +{ + vici_builder_t *builder; + va_list args; + + builder = vici_builder_create(); + builder->add_kv(builder, "success", fmt ? "no" : "yes"); + if (fmt) + { + va_start(args, fmt); + builder->vadd_kv(builder, "errmsg", fmt, args); + va_end(args); + } + return builder->finalize(builder); +} + +/** + * A rule to parse a key/value or list item + */ +typedef struct { + /** name of the key/value or list */ + char *name; + /** function to parse value */ + bool (*parse)(void *out, chunk_t value); + /** result, passed to parse() */ + void *out; +} parse_rule_t; + +/** + * Parse key/values using a rule-set + */ +static bool parse_rules(parse_rule_t *rules, int count, char *name, + chunk_t value, vici_message_t **reply) +{ + int i; + + for (i = 0; i < count; i++) + { + if (streq(name, rules[i].name)) + { + if (rules[i].parse(rules[i].out, value)) + { + return TRUE; + } + *reply = create_reply("invalid value for: %s, config discarded", + name); + return FALSE; + } + } + *reply = create_reply("unknown option: %s, config discarded", name); + return FALSE; +} + +/** + * Parse callback data, passed to each callback + */ +typedef struct { + private_vici_config_t *this; + vici_message_t *reply; +} request_data_t; + +/** + * Data associated to a peer config + */ +typedef struct { + request_data_t *request; + u_int32_t version; + bool aggressive; + bool encap; + bool mobike; + bool send_certreq; + bool pull; + cert_policy_t send_cert; + u_int64_t dpd_delay; + u_int64_t dpd_timeout; + fragmentation_t fragmentation; + unique_policy_t unique; + u_int32_t keyingtries; + u_int32_t local_port; + u_int32_t remote_port; + char *local_addrs; + char *remote_addrs; + linked_list_t *local; + linked_list_t *remote; + linked_list_t *proposals; + linked_list_t *children; + linked_list_t *vips; + char *pools; + u_int64_t reauth_time; + u_int64_t rekey_time; + u_int64_t over_time; + u_int64_t rand_time; +} peer_data_t; + +/** + * Log relevant auth config data + */ +static void log_auth(auth_cfg_t *auth) +{ + enumerator_t *enumerator; + auth_rule_t rule; + union { + uintptr_t u; + identification_t *id; + char *str; + } v; + + enumerator = auth->create_enumerator(auth); + while (enumerator->enumerate(enumerator, &rule, &v)) + { + switch (rule) + { + case AUTH_RULE_AUTH_CLASS: + DBG2(DBG_CFG, " class = %N", auth_class_names, v.u); + break; + case AUTH_RULE_EAP_TYPE: + DBG2(DBG_CFG, " eap-type = %N", eap_type_names, v.u); + break; + case AUTH_RULE_EAP_VENDOR: + DBG2(DBG_CFG, " eap-vendor = %u", v.u); + break; + case AUTH_RULE_XAUTH_BACKEND: + DBG2(DBG_CFG, " xauth = %s", v.str); + break; + case AUTH_RULE_CRL_VALIDATION: + DBG2(DBG_CFG, " revocation = %N", cert_validation_names, v.u); + break; + case AUTH_RULE_IDENTITY: + DBG2(DBG_CFG, " id = %Y", v.id); + break; + case AUTH_RULE_AAA_IDENTITY: + DBG2(DBG_CFG, " aaa_id = %Y", v.id); + break; + case AUTH_RULE_EAP_IDENTITY: + DBG2(DBG_CFG, " eap_id = %Y", v.id); + break; + case AUTH_RULE_XAUTH_IDENTITY: + DBG2(DBG_CFG, " xauth_id = %Y", v.id); + break; + case AUTH_RULE_GROUP: + DBG2(DBG_CFG, " group = %Y", v.id); + break; + default: + break; + } + } + enumerator->destroy(enumerator); +} + +/** + * Log parsed peer data + */ +static void log_peer_data(peer_data_t *data) +{ + enumerator_t *enumerator; + auth_cfg_t *auth; + host_t *host; + + DBG2(DBG_CFG, " version = %u", data->version); + DBG2(DBG_CFG, " local_addrs = %s", data->local_addrs); + DBG2(DBG_CFG, " remote_addrs = %s", data->remote_addrs); + DBG2(DBG_CFG, " local_port = %u", data->local_port); + DBG2(DBG_CFG, " remote_port = %u", data->remote_port); + DBG2(DBG_CFG, " send_certreq = %u", data->send_certreq); + DBG2(DBG_CFG, " send_cert = %N", cert_policy_names, data->send_cert); + DBG2(DBG_CFG, " mobike = %u", data->mobike); + DBG2(DBG_CFG, " aggressive = %u", data->aggressive); + DBG2(DBG_CFG, " encap = %u", data->encap); + DBG2(DBG_CFG, " dpd_delay = %llu", data->dpd_delay); + DBG2(DBG_CFG, " dpd_timeout = %llu", data->dpd_timeout); + DBG2(DBG_CFG, " fragmentation = %u", data->fragmentation); + DBG2(DBG_CFG, " unique = %N", unique_policy_names, data->unique); + DBG2(DBG_CFG, " keyingtries = %u", data->keyingtries); + DBG2(DBG_CFG, " reauth_time = %llu", data->reauth_time); + DBG2(DBG_CFG, " rekey_time = %llu", data->rekey_time); + DBG2(DBG_CFG, " over_time = %llu", data->over_time); + DBG2(DBG_CFG, " rand_time = %llu", data->rand_time); + DBG2(DBG_CFG, " proposals = %#P", data->proposals); + + if (data->vips->get_count(data->vips)) + { + DBG2(DBG_CFG, " vips:"); + } + enumerator = data->vips->create_enumerator(data->vips); + while (enumerator->enumerate(enumerator, &host)) + { + DBG2(DBG_CFG, " %H", host); + } + enumerator->destroy(enumerator); + + enumerator = data->local->create_enumerator(data->local); + while (enumerator->enumerate(enumerator, &auth)) + { + DBG2(DBG_CFG, " local:"); + log_auth(auth); + } + enumerator->destroy(enumerator); + + enumerator = data->remote->create_enumerator(data->remote); + while (enumerator->enumerate(enumerator, &auth)) + { + DBG2(DBG_CFG, " remote:"); + log_auth(auth); + } + enumerator->destroy(enumerator); +} + +/** + * Clean up peer config data + */ +static void free_peer_data(peer_data_t *data) +{ + data->local->destroy_offset(data->local, + offsetof(auth_cfg_t, destroy)); + data->remote->destroy_offset(data->remote, + offsetof(auth_cfg_t, destroy)); + data->children->destroy_offset(data->children, + offsetof(child_cfg_t, destroy)); + data->proposals->destroy_offset(data->proposals, + offsetof(proposal_t, destroy)); + data->vips->destroy_offset(data->vips, offsetof(host_t, destroy)); + free(data->pools); + free(data->local_addrs); + free(data->remote_addrs); +} + +/** + * CHILD config data + */ +typedef struct { + request_data_t *request; + lifetime_cfg_t lft; + char* updown; + bool hostaccess; + bool ipcomp; + bool route; + ipsec_mode_t mode; + u_int32_t replay_window; + action_t dpd_action; + action_t start_action; + action_t close_action; + u_int32_t reqid; + u_int32_t tfc; + mark_t mark_in; + mark_t mark_out; + u_int64_t inactivity; + linked_list_t *proposals; + linked_list_t *local_ts; + linked_list_t *remote_ts; +} child_data_t; + +/** + * Log parsed CHILD config data + */ +static void log_child_data(child_data_t *data, char *name) +{ + DBG2(DBG_CFG, " child %s:", name); + DBG2(DBG_CFG, " rekey_time = %llu", data->lft.time.rekey); + DBG2(DBG_CFG, " life_time = %llu", data->lft.time.life); + DBG2(DBG_CFG, " rand_time = %llu", data->lft.time.jitter); + DBG2(DBG_CFG, " rekey_bytes = %llu", data->lft.bytes.rekey); + DBG2(DBG_CFG, " life_bytes = %llu", data->lft.bytes.life); + DBG2(DBG_CFG, " rand_bytes = %llu", data->lft.bytes.jitter); + DBG2(DBG_CFG, " rekey_packets = %llu", data->lft.packets.rekey); + DBG2(DBG_CFG, " life_packets = %llu", data->lft.packets.life); + DBG2(DBG_CFG, " rand_packets = %llu", data->lft.packets.jitter); + DBG2(DBG_CFG, " updown = %s", data->updown); + DBG2(DBG_CFG, " hostaccess = %u", data->hostaccess); + DBG2(DBG_CFG, " ipcomp = %u", data->ipcomp); + DBG2(DBG_CFG, " mode = %N", ipsec_mode_names, data->mode); + if (data->replay_window != REPLAY_UNDEFINED) + { + DBG2(DBG_CFG, " replay_window = %u", data->replay_window); + } + DBG2(DBG_CFG, " dpd_action = %N", action_names, data->dpd_action); + DBG2(DBG_CFG, " start_action = %N", action_names, data->start_action); + DBG2(DBG_CFG, " close_action = %N", action_names, data->close_action); + DBG2(DBG_CFG, " reqid = %u", data->reqid); + DBG2(DBG_CFG, " tfc = %d", data->tfc); + DBG2(DBG_CFG, " mark_in = %u/%u", + data->mark_in.value, data->mark_in.mask); + DBG2(DBG_CFG, " mark_out = %u/%u", + data->mark_out.value, data->mark_out.mask); + DBG2(DBG_CFG, " inactivity = %llu", data->inactivity); + DBG2(DBG_CFG, " proposals = %#P", data->proposals); + DBG2(DBG_CFG, " local_ts = %#R", data->local_ts); + DBG2(DBG_CFG, " remote_ts = %#R", data->remote_ts); +} + +/** + * Clean up CHILD config data + */ +static void free_child_data(child_data_t *data) +{ + data->proposals->destroy_offset(data->proposals, + offsetof(proposal_t, destroy)); + data->local_ts->destroy_offset(data->local_ts, + offsetof(traffic_selector_t, destroy)); + data->remote_ts->destroy_offset(data->remote_ts, + offsetof(traffic_selector_t, destroy)); + free(data->updown); +} + +/** + * Auth config data + */ +typedef struct { + request_data_t *request; + auth_cfg_t *cfg; +} auth_data_t; + +/** + * Common proposal parsing + */ +static bool parse_proposal(linked_list_t *list, protocol_id_t proto, chunk_t v) +{ + char buf[128]; + proposal_t *proposal; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + if (strcaseeq("default", buf)) + { + proposal = proposal_create_default(proto); + if (proposal) + { + list->insert_last(list, proposal); + } + proposal = proposal_create_default_aead(proto); + if (proposal) + { + list->insert_last(list, proposal); + } + return TRUE; + } + proposal = proposal_create_from_string(proto, buf); + if (proposal) + { + list->insert_last(list, proposal); + return TRUE; + } + return FALSE; +} + +/** + * Parse IKE proposal + */ +CALLBACK(parse_ike_proposal, bool, + linked_list_t *out, chunk_t v) +{ + return parse_proposal(out, PROTO_IKE, v); +} + +/** + * Parse ESP proposal + */ +CALLBACK(parse_esp_proposal, bool, + linked_list_t *out, chunk_t v) +{ + return parse_proposal(out, PROTO_ESP, v); +} + +/** + * Parse AH proposal + */ +CALLBACK(parse_ah_proposal, bool, + linked_list_t *out, chunk_t v) +{ + return parse_proposal(out, PROTO_AH, v); +} + +/** + * Parse a traffic selector + */ +CALLBACK(parse_ts, bool, + linked_list_t *out, chunk_t v) +{ + char buf[128], *protoport, *sep, *port = "", *end; + traffic_selector_t *ts; + struct protoent *protoent; + struct servent *svc; + long int p; + u_int16_t from = 0, to = 0xffff; + u_int8_t proto = 0; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + + protoport = strchr(buf, '['); + if (protoport) + { + *(protoport++) = '\0'; + + sep = strrchr(protoport, ']'); + if (!sep) + { + return FALSE; + } + *sep = '\0'; + + sep = strchr(protoport, '/'); + if (sep) + { /* protocol/port */ + *sep = '\0'; + port = sep + 1; + } + + if (streq(protoport, "any")) + { + proto = 0; + } + else + { + protoent = getprotobyname(protoport); + if (protoent) + { + proto = protoent->p_proto; + } + else + { + p = strtol(protoport, &end, 0); + if ((*protoport && *end) || p < 0 || p > 0xff) + { + return FALSE; + } + proto = (u_int8_t)p; + } + } + if (streq(port, "opaque")) + { + from = 0xffff; + to = 0; + } + else if (*port && !streq(port, "any")) + { + svc = getservbyname(port, NULL); + if (svc) + { + from = to = ntohs(svc->s_port); + } + else + { + p = strtol(port, &end, 0); + if (p < 0 || p > 0xffff) + { + return FALSE; + } + from = p; + if (*end == '-') + { + port = end + 1; + p = strtol(port, &end, 0); + if (p < 0 || p > 0xffff) + { + return FALSE; + } + } + to = p; + if (*end) + { + return FALSE; + } + } + } + } + if (streq(buf, "dynamic")) + { + ts = traffic_selector_create_dynamic(proto, from, to); + } + else + { + ts = traffic_selector_create_from_cidr(buf, proto, from, to); + } + if (!ts) + { + return FALSE; + } + out->insert_last(out, ts); + return TRUE; +} + +/** + * Parse a string + */ +CALLBACK(parse_string, bool, + char **out, chunk_t v) +{ + if (!chunk_printable(v, NULL, ' ')) + { + return FALSE; + } + free(*out); + *out = NULL; + if (asprintf(out, "%.*s", (int)v.len, v.ptr) == -1) + { + return FALSE; + } + return TRUE; +} + +/** + * Map a string to an integer + */ +typedef struct { + char *str; + int d; +} enum_map_t; + +/** + * Parse a string to an integer mapping + */ +static bool parse_map(enum_map_t *map, int count, int *out, chunk_t v) +{ + char buf[128]; + int i; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + for (i = 0; i < count; i++) + { + if (strcaseeq(map[i].str, buf)) + { + *out = map[i].d; + return TRUE; + } + } + return FALSE; +} + +/** + * Parse a boolean + */ +CALLBACK(parse_bool, bool, + bool *out, chunk_t v) +{ + enum_map_t map[] = { + { "yes", TRUE }, + { "true", TRUE }, + { "enabled", TRUE }, + { "1", TRUE }, + { "no", FALSE }, + { "false", FALSE }, + { "disabled", FALSE }, + { "0", FALSE }, + }; + int d; + + if (parse_map(map, countof(map), &d, v)) + { + *out = d; + return TRUE; + } + return FALSE; +} + +/** + * Parse a ipsec_mode_t + */ +CALLBACK(parse_mode, bool, + ipsec_mode_t *out, chunk_t v) +{ + enum_map_t map[] = { + { "tunnel", MODE_TUNNEL }, + { "transport", MODE_TRANSPORT }, + { "beet", MODE_BEET }, + { "drop", MODE_DROP }, + { "pass", MODE_PASS }, + }; + int d; + + if (parse_map(map, countof(map), &d, v)) + { + *out = d; + return TRUE; + } + return FALSE; +} + +/** + * Parse an action_t + */ +CALLBACK(parse_action, bool, + action_t *out, chunk_t v) +{ + enum_map_t map[] = { + { "start", ACTION_RESTART }, + { "restart", ACTION_RESTART }, + { "route", ACTION_ROUTE }, + { "trap", ACTION_ROUTE }, + { "none", ACTION_NONE }, + { "clear", ACTION_NONE }, + }; + int d; + + if (parse_map(map, countof(map), &d, v)) + { + *out = d; + return TRUE; + } + return FALSE; +} + +/** + * Parse a u_int32_t + */ +CALLBACK(parse_uint32, bool, + u_int32_t *out, chunk_t v) +{ + char buf[16], *end; + u_long l; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + l = strtoul(buf, &end, 0); + if (*end == 0) + { + *out = l; + return TRUE; + } + return FALSE; +} + +/** + * Parse a u_int64_t + */ +CALLBACK(parse_uint64, bool, + u_int64_t *out, chunk_t v) +{ + char buf[16], *end; + unsigned long long l; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + l = strtoull(buf, &end, 0); + if (*end == 0) + { + *out = l; + return TRUE; + } + return FALSE; +} + +/** + * Parse a relative time + */ +CALLBACK(parse_time, bool, + u_int64_t *out, chunk_t v) +{ + char buf[16], *end; + u_long l; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + + l = strtoul(buf, &end, 0); + while (*end == ' ') + { + end++; + } + switch (*end) + { + case 'd': + case 'D': + l *= 24; + /* fall */ + case 'h': + case 'H': + l *= 60; + /* fall */ + case 'm': + case 'M': + l *= 60; + /* fall */ + case 's': + case 'S': + end++; + break; + case '\0': + break; + default: + return FALSE; + } + if (*end) + { + return FALSE; + } + *out = l; + return TRUE; +} + +/** + * Parse byte volume + */ +CALLBACK(parse_bytes, bool, + u_int64_t *out, chunk_t v) +{ + char buf[16], *end; + unsigned long long l; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + + l = strtoull(buf, &end, 0); + while (*end == ' ') + { + end++; + } + switch (*end) + { + case 'g': + case 'G': + l *= 1024; + /* fall */ + case 'm': + case 'M': + l *= 1024; + /* fall */ + case 'k': + case 'K': + l *= 1024; + end++; + break; + case '\0': + break; + default: + return FALSE; + } + if (*end) + { + return FALSE; + } + *out = l; + return TRUE; +} + +/** + * Parse a mark_t + */ +CALLBACK(parse_mark, bool, + mark_t *out, chunk_t v) +{ + char buf[32]; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + return mark_from_string(buf, out); +} + +/** + * Parse TFC padding option + */ +CALLBACK(parse_tfc, bool, + u_int32_t *out, chunk_t v) +{ + if (chunk_equals(v, chunk_from_str("mtu"))) + { + *out = -1; + return TRUE; + } + return parse_uint32(out, v); +} + +/** + * Parse authentication config + */ +CALLBACK(parse_auth, bool, + auth_cfg_t *cfg, chunk_t v) +{ + char buf[64], *pos; + eap_vendor_type_t *type; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + if (strcaseeq(buf, "pubkey")) + { + cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); + return TRUE; + } + if (strcaseeq(buf, "psk")) + { + cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK); + return TRUE; + } + if (strcasepfx(buf, "xauth")) + { + pos = strchr(buf, '-'); + if (pos) + { + cfg->add(cfg, AUTH_RULE_XAUTH_BACKEND, strdup(++pos)); + } + cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_XAUTH); + return TRUE; + } + if (strcasepfx(buf, "eap")) + { + cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); + + type = eap_vendor_type_from_string(buf); + if (type) + { + cfg->add(cfg, AUTH_RULE_EAP_TYPE, type->type); + if (type->vendor) + { + cfg->add(cfg, AUTH_RULE_EAP_VENDOR, type->vendor); + } + free(type); + } + return TRUE; + } + return FALSE; +} + +/** + * Parse identity; add as auth rule to config + */ +static bool parse_id(auth_cfg_t *cfg, auth_rule_t rule, chunk_t v) +{ + char buf[256]; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + cfg->add(cfg, rule, identification_create_from_string(buf)); + return TRUE; +} + +/** + * Parse IKE identity + */ +CALLBACK(parse_ike_id, bool, + auth_cfg_t *cfg, chunk_t v) +{ + return parse_id(cfg, AUTH_RULE_IDENTITY, v); +} + +/** + * Parse AAA identity + */ +CALLBACK(parse_aaa_id, bool, + auth_cfg_t *cfg, chunk_t v) +{ + return parse_id(cfg, AUTH_RULE_AAA_IDENTITY, v); +} + +/** + * Parse EAP identity + */ +CALLBACK(parse_eap_id, bool, + auth_cfg_t *cfg, chunk_t v) +{ + return parse_id(cfg, AUTH_RULE_EAP_IDENTITY, v); +} + +/** + * Parse XAuth identity + */ +CALLBACK(parse_xauth_id, bool, + auth_cfg_t *cfg, chunk_t v) +{ + return parse_id(cfg, AUTH_RULE_XAUTH_IDENTITY, v); +} + +/** + * Parse group membership + */ +CALLBACK(parse_group, bool, + auth_cfg_t *cfg, chunk_t v) +{ + return parse_id(cfg, AUTH_RULE_GROUP, v); +} + +/** + * Parse a certificate; add as auth rule to config + */ +static bool parse_cert(auth_cfg_t *cfg, auth_rule_t rule, chunk_t v) +{ + certificate_t *cert; + + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_PEM, v, BUILD_END); + if (cert) + { + cfg->add(cfg, rule, cert); + return TRUE; + } + return FALSE; +} + +/** + * Parse subject certificates + */ +CALLBACK(parse_certs, bool, + auth_cfg_t *cfg, chunk_t v) +{ + return parse_cert(cfg, AUTH_RULE_SUBJECT_CERT, v); +} + +/** + * Parse CA certificates + */ +CALLBACK(parse_cacerts, bool, + auth_cfg_t *cfg, chunk_t v) +{ + return parse_cert(cfg, AUTH_RULE_CA_CERT, v); +} + +/** + * Parse revocation status + */ +CALLBACK(parse_revocation, bool, + auth_cfg_t *cfg, chunk_t v) +{ + enum_map_t map[] = { + { "strict", VALIDATION_GOOD }, + { "ifuri", VALIDATION_SKIPPED }, + { "relaxed", VALIDATION_FAILED }, + }; + int d; + + if (parse_map(map, countof(map), &d, v)) + { + if (d != VALIDATION_FAILED) + { + cfg->add(cfg, AUTH_RULE_CRL_VALIDATION, d); + } + return TRUE; + } + return FALSE; +} + +/** + * Parse list items to comma separated strings + */ +CALLBACK(parse_stringlist, bool, + char **out, chunk_t v) +{ + char *current; + + if (!chunk_printable(v, NULL, ' ')) + { + return FALSE; + } + current = *out; + if (current) + { + if (asprintf(out, "%s, %.*s", current, (int)v.len, v.ptr) == -1) + { + return FALSE; + } + free(current); + } + else + { + if (asprintf(out, "%.*s", (int)v.len, v.ptr) == -1) + { + return FALSE; + } + } + return TRUE; +} + +/** + * Parse an fragmentation_t + */ +CALLBACK(parse_frag, bool, + fragmentation_t *out, chunk_t v) +{ + enum_map_t map[] = { + { "yes", FRAGMENTATION_YES }, + { "no", FRAGMENTATION_NO }, + { "force", FRAGMENTATION_FORCE }, + }; + int d; + + if (parse_map(map, countof(map), &d, v)) + { + *out = d; + return TRUE; + } + return FALSE; +} + +/** + * Parse a cert_policy_t + */ +CALLBACK(parse_send_cert, bool, + cert_policy_t *out, chunk_t v) +{ + enum_map_t map[] = { + { "ifasked", CERT_SEND_IF_ASKED }, + { "always", CERT_ALWAYS_SEND }, + { "never", CERT_NEVER_SEND }, + }; + int d; + + if (parse_map(map, countof(map), &d, v)) + { + *out = d; + return TRUE; + } + return FALSE; +} + +/** + * Parse a unique_policy_t + */ +CALLBACK(parse_unique, bool, + unique_policy_t *out, chunk_t v) +{ + enum_map_t map[] = { + { "never", UNIQUE_NEVER }, + { "no", UNIQUE_NO }, + { "replace", UNIQUE_REPLACE }, + { "keep", UNIQUE_KEEP }, + }; + int d; + + if (parse_map(map, countof(map), &d, v)) + { + *out = d; + return TRUE; + } + return FALSE; +} + +/** + * Parse host_t into a list + */ +CALLBACK(parse_hosts, bool, + linked_list_t *list, chunk_t v) +{ + char buf[64]; + host_t *host; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + host = host_create_from_string(buf, 0); + if (!host) + { + return FALSE; + } + list->insert_last(list, host); + return TRUE; +} + +CALLBACK(child_li, bool, + child_data_t *child, vici_message_t *message, char *name, chunk_t value) +{ + parse_rule_t rules[] = { + { "ah_proposals", parse_ah_proposal, child->proposals }, + { "esp_proposals", parse_esp_proposal, child->proposals }, + { "local_ts", parse_ts, child->local_ts }, + { "remote_ts", parse_ts, child->remote_ts }, + }; + + return parse_rules(rules, countof(rules), name, value, + &child->request->reply); +} + +CALLBACK(child_kv, bool, + child_data_t *child, vici_message_t *message, char *name, chunk_t value) +{ + parse_rule_t rules[] = { + { "updown", parse_string, &child->updown }, + { "hostaccess", parse_bool, &child->hostaccess }, + { "mode", parse_mode, &child->mode }, + { "replay_window", parse_uint32, &child->replay_window }, + { "rekey_time", parse_time, &child->lft.time.rekey }, + { "life_time", parse_time, &child->lft.time.life }, + { "rand_time", parse_time, &child->lft.time.jitter }, + { "rekey_bytes", parse_bytes, &child->lft.bytes.rekey }, + { "life_bytes", parse_bytes, &child->lft.bytes.life }, + { "rand_bytes", parse_bytes, &child->lft.bytes.jitter }, + { "rekey_packets", parse_uint64, &child->lft.packets.rekey }, + { "life_packets", parse_uint64, &child->lft.packets.life }, + { "rand_packets", parse_uint64, &child->lft.packets.jitter }, + { "dpd_action", parse_action, &child->dpd_action }, + { "start_action", parse_action, &child->start_action }, + { "close_action", parse_action, &child->close_action }, + { "ipcomp", parse_bool, &child->ipcomp }, + { "inactivity", parse_time, &child->inactivity }, + { "reqid", parse_uint32, &child->reqid }, + { "mark_in", parse_mark, &child->mark_in }, + { "mark_out", parse_mark, &child->mark_out }, + { "tfc_padding", parse_tfc, &child->tfc }, + }; + + return parse_rules(rules, countof(rules), name, value, + &child->request->reply); +} + +CALLBACK(auth_li, bool, + auth_data_t *auth, vici_message_t *message, char *name, chunk_t value) +{ + parse_rule_t rules[] = { + { "groups", parse_group, auth->cfg }, + { "certs", parse_certs, auth->cfg }, + { "cacerts", parse_cacerts, auth->cfg }, + }; + + return parse_rules(rules, countof(rules), name, value, + &auth->request->reply); +} + +CALLBACK(auth_kv, bool, + auth_data_t *auth, vici_message_t *message, char *name, chunk_t value) +{ + parse_rule_t rules[] = { + { "auth", parse_auth, auth->cfg }, + { "id", parse_ike_id, auth->cfg }, + { "aaa_id", parse_aaa_id, auth->cfg }, + { "eap_id", parse_eap_id, auth->cfg }, + { "xauth_id", parse_xauth_id, auth->cfg }, + { "revocation", parse_revocation, auth->cfg }, + }; + + return parse_rules(rules, countof(rules), name, value, + &auth->request->reply); +} + +CALLBACK(peer_li, bool, + peer_data_t *peer, vici_message_t *message, char *name, chunk_t value) +{ + parse_rule_t rules[] = { + { "local_addrs", parse_stringlist, &peer->local_addrs }, + { "remote_addrs", parse_stringlist, &peer->remote_addrs }, + { "proposals", parse_ike_proposal, peer->proposals }, + { "vips", parse_hosts, peer->vips }, + { "pools", parse_stringlist, &peer->pools }, + }; + + return parse_rules(rules, countof(rules), name, value, + &peer->request->reply); +} + +CALLBACK(peer_kv, bool, + peer_data_t *peer, vici_message_t *message, char *name, chunk_t value) +{ + parse_rule_t rules[] = { + { "version", parse_uint32, &peer->version }, + { "aggressive", parse_bool, &peer->aggressive }, + { "pull", parse_bool, &peer->pull }, + { "encap", parse_bool, &peer->encap }, + { "mobike", parse_bool, &peer->mobike }, + { "dpd_delay", parse_time, &peer->dpd_delay }, + { "dpd_timeout", parse_time, &peer->dpd_timeout }, + { "fragmentation", parse_frag, &peer->fragmentation }, + { "send_certreq", parse_bool, &peer->send_certreq }, + { "send_cert", parse_send_cert, &peer->send_cert }, + { "keyingtries", parse_uint32, &peer->keyingtries }, + { "unique", parse_unique, &peer->unique }, + { "local_port", parse_uint32, &peer->local_port }, + { "remote_port", parse_uint32, &peer->remote_port }, + { "reauth_time", parse_time, &peer->reauth_time }, + { "rekey_time", parse_time, &peer->rekey_time }, + { "over_time", parse_time, &peer->over_time }, + { "rand_time", parse_time, &peer->rand_time }, + }; + + return parse_rules(rules, countof(rules), name, value, + &peer->request->reply); +} + +CALLBACK(children_sn, bool, + peer_data_t *peer, vici_message_t *message, vici_parse_context_t *ctx, + char *name) +{ + child_data_t child = { + .request = peer->request, + .proposals = linked_list_create(), + .local_ts = linked_list_create(), + .remote_ts = linked_list_create(), + .mode = MODE_TUNNEL, + .replay_window = REPLAY_UNDEFINED, + .dpd_action = ACTION_NONE, + .start_action = ACTION_NONE, + .close_action = ACTION_NONE, + .lft = { + .time = { + .rekey = LFT_DEFAULT_CHILD_REKEY, + .life = LFT_UNDEFINED, + .jitter = LFT_UNDEFINED, + }, + .bytes = { + .life = LFT_UNDEFINED, + .jitter = LFT_UNDEFINED, + }, + .packets = { + .life = LFT_UNDEFINED, + .jitter = LFT_UNDEFINED, + }, + } + }; + child_cfg_t *cfg; + proposal_t *proposal; + traffic_selector_t *ts; + + if (!message->parse(message, ctx, NULL, child_kv, child_li, &child)) + { + free_child_data(&child); + return FALSE; + } + + if (child.local_ts->get_count(child.local_ts) == 0) + { + child.local_ts->insert_last(child.local_ts, + traffic_selector_create_dynamic(0, 0, 65535)); + } + if (child.remote_ts->get_count(child.remote_ts) == 0) + { + child.remote_ts->insert_last(child.remote_ts, + traffic_selector_create_dynamic(0, 0, 65535)); + } + if (child.proposals->get_count(child.proposals) == 0) + { + proposal = proposal_create_default(PROTO_ESP); + if (proposal) + { + child.proposals->insert_last(child.proposals, proposal); + } + proposal = proposal_create_default_aead(PROTO_ESP); + if (proposal) + { + child.proposals->insert_last(child.proposals, proposal); + } + } + + /* if no hard lifetime specified, add one at soft lifetime + 10% */ + if (child.lft.time.life == LFT_UNDEFINED) + { + child.lft.time.life = child.lft.time.rekey * 110 / 100; + } + if (child.lft.bytes.life == LFT_UNDEFINED) + { + child.lft.bytes.life = child.lft.bytes.rekey * 110 / 100; + } + if (child.lft.packets.life == LFT_UNDEFINED) + { + child.lft.packets.life = child.lft.packets.rekey * 110 / 100; + } + /* if no rand time defined, use difference of hard and soft */ + if (child.lft.time.jitter == LFT_UNDEFINED) + { + child.lft.time.jitter = child.lft.time.life - + min(child.lft.time.life, child.lft.time.rekey); + } + if (child.lft.bytes.jitter == LFT_UNDEFINED) + { + child.lft.bytes.jitter = child.lft.bytes.life - + min(child.lft.bytes.life, child.lft.bytes.rekey); + } + if (child.lft.packets.jitter == LFT_UNDEFINED) + { + child.lft.packets.jitter = child.lft.packets.life - + min(child.lft.packets.life, child.lft.packets.rekey); + } + + log_child_data(&child, name); + + cfg = child_cfg_create(name, &child.lft, child.updown, + child.hostaccess, child.mode, child.start_action, + child.dpd_action, child.close_action, child.ipcomp, + child.inactivity, child.reqid, &child.mark_in, + &child.mark_out, child.tfc); + + if (child.replay_window != REPLAY_UNDEFINED) + { + cfg->set_replay_window(cfg, child.replay_window); + } + while (child.local_ts->remove_first(child.local_ts, + (void**)&ts) == SUCCESS) + { + cfg->add_traffic_selector(cfg, TRUE, ts); + } + while (child.remote_ts->remove_first(child.remote_ts, + (void**)&ts) == SUCCESS) + { + cfg->add_traffic_selector(cfg, FALSE, ts); + } + while (child.proposals->remove_first(child.proposals, + (void**)&proposal) == SUCCESS) + { + cfg->add_proposal(cfg, proposal); + } + + peer->children->insert_last(peer->children, cfg); + + free_child_data(&child); + + return TRUE; +} + +CALLBACK(peer_sn, bool, + peer_data_t *peer, vici_message_t *message, vici_parse_context_t *ctx, + char *name) +{ + if (strcaseeq(name, "children")) + { + return message->parse(message, ctx, children_sn, NULL, NULL, peer); + } + if (strcasepfx(name, "local") || + strcasepfx(name, "remote")) + { + auth_data_t auth = { + .request = peer->request, + .cfg = auth_cfg_create(), + }; + + if (!message->parse(message, ctx, NULL, auth_kv, auth_li, &auth)) + { + auth.cfg->destroy(auth.cfg); + return FALSE; + } + + if (strcasepfx(name, "local")) + { + peer->local->insert_last(peer->local, auth.cfg); + } + else + { + peer->remote->insert_last(peer->remote, auth.cfg); + } + return TRUE; + } + peer->request->reply = create_reply("invalid section: %s", name); + return FALSE; +} + +/** + * Find reqid of an existing CHILD_SA + */ +static u_int32_t find_reqid(child_cfg_t *cfg) +{ + enumerator_t *enumerator, *children; + child_sa_t *child_sa; + ike_sa_t *ike_sa; + u_int32_t reqid; + + reqid = charon->traps->find_reqid(charon->traps, cfg); + if (reqid) + { /* already trapped */ + return reqid; + } + + enumerator = charon->controller->create_ike_sa_enumerator( + charon->controller, TRUE); + while (!reqid && enumerator->enumerate(enumerator, &ike_sa)) + { + children = ike_sa->create_child_sa_enumerator(ike_sa); + while (children->enumerate(children, &child_sa)) + { + if (streq(cfg->get_name(cfg), child_sa->get_name(child_sa))) + { + reqid = child_sa->get_reqid(child_sa); + break; + } + } + children->destroy(children); + } + enumerator->destroy(enumerator); + return reqid; +} + +/** + * Perform start actions associated to a child config + */ +static void run_start_action(private_vici_config_t *this, peer_cfg_t *peer_cfg, + child_cfg_t *child_cfg) +{ + switch (child_cfg->get_start_action(child_cfg)) + { + case ACTION_RESTART: + DBG1(DBG_CFG, "initiating '%s'", child_cfg->get_name(child_cfg)); + charon->controller->initiate(charon->controller, + peer_cfg->get_ref(peer_cfg), child_cfg->get_ref(child_cfg), + NULL, NULL, 0); + break; + case ACTION_ROUTE: + DBG1(DBG_CFG, "installing '%s'", child_cfg->get_name(child_cfg)); + switch (child_cfg->get_mode(child_cfg)) + { + case MODE_PASS: + case MODE_DROP: + charon->shunts->install(charon->shunts, child_cfg); + break; + default: + charon->traps->install(charon->traps, peer_cfg, child_cfg, + find_reqid(child_cfg)); + break; + } + break; + default: + break; + } +} + +/** + * Undo start actions associated to a child config + */ +static void clear_start_action(private_vici_config_t *this, + child_cfg_t *child_cfg) +{ + enumerator_t *enumerator, *children; + child_sa_t *child_sa; + ike_sa_t *ike_sa; + u_int32_t reqid = 0, *del; + array_t *reqids = NULL; + char *name; + + name = child_cfg->get_name(child_cfg); + switch (child_cfg->get_start_action(child_cfg)) + { + case ACTION_RESTART: + enumerator = charon->controller->create_ike_sa_enumerator( + charon->controller, TRUE); + while (enumerator->enumerate(enumerator, &ike_sa)) + { + children = ike_sa->create_child_sa_enumerator(ike_sa); + while (children->enumerate(children, &child_sa)) + { + if (streq(name, child_sa->get_name(child_sa))) + { + reqid = child_sa->get_reqid(child_sa); + array_insert_create(&reqids, ARRAY_TAIL, &reqid); + } + } + children->destroy(children); + } + enumerator->destroy(enumerator); + + if (array_count(reqids)) + { + while (array_remove(reqids, ARRAY_HEAD, &del)) + { + DBG1(DBG_CFG, "closing '%s' #%u", name, *del); + charon->controller->terminate_child(charon->controller, + *del, NULL, NULL, 0); + } + array_destroy(reqids); + } + break; + case ACTION_ROUTE: + DBG1(DBG_CFG, "uninstalling '%s'", name); + switch (child_cfg->get_mode(child_cfg)) + { + case MODE_PASS: + case MODE_DROP: + charon->shunts->uninstall(charon->shunts, name); + break; + default: + enumerator = charon->traps->create_enumerator(charon->traps); + while (enumerator->enumerate(enumerator, NULL, &child_sa)) + { + if (streq(name, child_sa->get_name(child_sa))) + { + reqid = child_sa->get_reqid(child_sa); + break; + } + } + enumerator->destroy(enumerator); + if (reqid) + { + charon->traps->uninstall(charon->traps, reqid); + } + break; + } + break; + default: + break; + } +} + +/** + * Run start actions associated to all child configs of a peer config + */ +static void run_start_actions(private_vici_config_t *this, peer_cfg_t *peer_cfg) +{ + enumerator_t *enumerator; + child_cfg_t *child_cfg; + + enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg); + while (enumerator->enumerate(enumerator, &child_cfg)) + { + run_start_action(this, peer_cfg, child_cfg); + } + enumerator->destroy(enumerator); +} + +/** + * Undo start actions associated to all child configs of a peer config + */ +static void clear_start_actions(private_vici_config_t *this, + peer_cfg_t *peer_cfg) +{ + enumerator_t *enumerator; + child_cfg_t *child_cfg; + + enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg); + while (enumerator->enumerate(enumerator, &child_cfg)) + { + clear_start_action(this, child_cfg); + } + enumerator->destroy(enumerator); +} + +/** + * Replace children of a peer config by a new config + */ +static void replace_children(private_vici_config_t *this, + peer_cfg_t *from, peer_cfg_t *to) +{ + enumerator_t *enumerator; + child_cfg_t *child; + + enumerator = to->create_child_cfg_enumerator(to); + while (enumerator->enumerate(enumerator, &child)) + { + to->remove_child_cfg(to, enumerator); + clear_start_action(this, child); + child->destroy(child); + } + enumerator->destroy(enumerator); + + enumerator = from->create_child_cfg_enumerator(from); + while (enumerator->enumerate(enumerator, &child)) + { + from->remove_child_cfg(from, enumerator); + to->add_child_cfg(to, child); + run_start_action(this, to, child); + } + enumerator->destroy(enumerator); +} + +/** + * Merge/replace a peer config with existing configs + */ +static void merge_config(private_vici_config_t *this, peer_cfg_t *peer_cfg) +{ + enumerator_t *enumerator; + peer_cfg_t *current; + ike_cfg_t *ike_cfg; + bool merged = FALSE; + + this->lock->write_lock(this->lock); + + enumerator = this->conns->create_enumerator(this->conns); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (streq(peer_cfg->get_name(peer_cfg), current->get_name(current))) + { + ike_cfg = current->get_ike_cfg(current); + if (peer_cfg->equals(peer_cfg, current) && + ike_cfg->equals(ike_cfg, peer_cfg->get_ike_cfg(peer_cfg))) + { + DBG1(DBG_CFG, "updated vici connection: %s", + peer_cfg->get_name(peer_cfg)); + replace_children(this, peer_cfg, current); + peer_cfg->destroy(peer_cfg); + } + else + { + DBG1(DBG_CFG, "replaced vici connection: %s", + peer_cfg->get_name(peer_cfg)); + this->conns->remove_at(this->conns, enumerator); + clear_start_actions(this, current); + current->destroy(current); + this->conns->insert_last(this->conns, peer_cfg); + run_start_actions(this, peer_cfg); + } + merged = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + if (!merged) + { + DBG1(DBG_CFG, "added vici connection: %s", peer_cfg->get_name(peer_cfg)); + this->conns->insert_last(this->conns, peer_cfg); + run_start_actions(this, peer_cfg); + } + + this->lock->unlock(this->lock); +} + +CALLBACK(config_sn, bool, + request_data_t *request, vici_message_t *message, + vici_parse_context_t *ctx, char *name) +{ + peer_data_t peer = { + .request = request, + .local = linked_list_create(), + .remote = linked_list_create(), + .vips = linked_list_create(), + .children = linked_list_create(), + .proposals = linked_list_create(), + .mobike = TRUE, + .send_certreq = TRUE, + .pull = TRUE, + .send_cert = CERT_SEND_IF_ASKED, + .version = IKE_ANY, + .remote_port = IKEV2_UDP_PORT, + .fragmentation = FRAGMENTATION_NO, + .unique = UNIQUE_NO, + .keyingtries = 1, + .rekey_time = LFT_DEFAULT_IKE_REKEY, + .over_time = LFT_UNDEFINED, + .rand_time = LFT_UNDEFINED, + }; + enumerator_t *enumerator; + peer_cfg_t *peer_cfg; + ike_cfg_t *ike_cfg; + child_cfg_t *child_cfg; + auth_cfg_t *auth_cfg; + proposal_t *proposal; + host_t *host; + char *str; + + DBG2(DBG_CFG, " conn %s:", name); + + if (!message->parse(message, ctx, peer_sn, peer_kv, peer_li, &peer)) + { + free_peer_data(&peer); + return FALSE; + } + + if (peer.local->get_count(peer.local) == 0) + { + free_peer_data(&peer); + peer.request->reply = create_reply("missing local auth config"); + return FALSE; + } + if (peer.remote->get_count(peer.remote) == 0) + { + auth_cfg = auth_cfg_create(); + peer.remote->insert_last(peer.remote, auth_cfg); + } + if (peer.proposals->get_count(peer.proposals) == 0) + { + proposal = proposal_create_default(PROTO_IKE); + if (proposal) + { + peer.proposals->insert_last(peer.proposals, proposal); + } + proposal = proposal_create_default_aead(PROTO_IKE); + if (proposal) + { + peer.proposals->insert_last(peer.proposals, proposal); + } + } + if (!peer.local_addrs) + { + peer.local_addrs = strdup("%any"); + } + if (!peer.remote_addrs) + { + peer.remote_addrs = strdup("%any"); + } + if (!peer.local_port) + { + peer.local_port = charon->socket->get_port(charon->socket, FALSE); + } + + if (peer.over_time == LFT_UNDEFINED) + { + /* default over_time to 10% of rekey/reauth time if not given */ + peer.over_time = max(peer.rekey_time, peer.reauth_time) / 10; + } + if (peer.rand_time == LFT_UNDEFINED) + { + /* default rand_time to over_time if not given */ + peer.rand_time = min(peer.over_time, + max(peer.rekey_time, peer.reauth_time) / 2); + } + + log_peer_data(&peer); + + ike_cfg = ike_cfg_create(peer.version, peer.send_certreq, peer.encap, + peer.local_addrs, peer.local_port, + peer.remote_addrs, peer.remote_port, + peer.fragmentation, 0); + peer_cfg = peer_cfg_create(name, ike_cfg, peer.send_cert, peer.unique, + peer.keyingtries, peer.rekey_time, peer.reauth_time, + peer.rand_time, peer.over_time, peer.mobike, + peer.aggressive, peer.pull, + peer.dpd_delay, peer.dpd_timeout, + FALSE, NULL, NULL); + + while (peer.local->remove_first(peer.local, + (void**)&auth_cfg) == SUCCESS) + { + peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, TRUE); + } + while (peer.remote->remove_first(peer.remote, + (void**)&auth_cfg) == SUCCESS) + { + peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, FALSE); + } + while (peer.children->remove_first(peer.children, + (void**)&child_cfg) == SUCCESS) + { + peer_cfg->add_child_cfg(peer_cfg, child_cfg); + } + while (peer.proposals->remove_first(peer.proposals, + (void**)&proposal) == SUCCESS) + { + ike_cfg->add_proposal(ike_cfg, proposal); + } + while (peer.vips->remove_first(peer.vips, (void**)&host) == SUCCESS) + { + peer_cfg->add_virtual_ip(peer_cfg, host); + } + if (peer.pools) + { + enumerator = enumerator_create_token(peer.pools, ",", " "); + while (enumerator->enumerate(enumerator, &str)) + { + peer_cfg->add_pool(peer_cfg, str); + } + enumerator->destroy(enumerator); + } + + free_peer_data(&peer); + + merge_config(request->this, peer_cfg); + + return TRUE; +} + +CALLBACK(load_conn, vici_message_t*, + private_vici_config_t *this, char *name, u_int id, vici_message_t *message) +{ + request_data_t request = { + .this = this, + }; + + if (!message->parse(message, NULL, config_sn, NULL, NULL, &request)) + { + if (request.reply) + { + return request.reply; + } + return create_reply("parsing request failed"); + } + return create_reply(NULL); +} + +CALLBACK(unload_conn, vici_message_t*, + private_vici_config_t *this, char *name, u_int id, vici_message_t *message) +{ + enumerator_t *enumerator; + peer_cfg_t *cfg; + bool found = FALSE; + char *conn; + + conn = message->get_str(message, NULL, "name"); + if (!conn) + { + return create_reply("missing connection name to unload"); + } + + this->lock->write_lock(this->lock); + enumerator = this->conns->create_enumerator(this->conns); + while (enumerator->enumerate(enumerator, &cfg)) + { + if (streq(cfg->get_name(cfg), conn)) + { + this->conns->remove_at(this->conns, enumerator); + cfg->destroy(cfg); + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + if (!found) + { + return create_reply("connection '%s' not found for unloading", conn); + } + return create_reply(NULL); +} + +CALLBACK(get_conns, vici_message_t*, + private_vici_config_t *this, char *name, u_int id, vici_message_t *message) +{ + vici_builder_t *builder; + enumerator_t *enumerator; + peer_cfg_t *cfg; + + builder = vici_builder_create(); + builder->begin_list(builder, "conns"); + + this->lock->read_lock(this->lock); + enumerator = this->conns->create_enumerator(this->conns); + while (enumerator->enumerate(enumerator, &cfg)) + { + builder->add_li(builder, "%s", cfg->get_name(cfg)); + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + builder->end_list(builder); + + return builder->finalize(builder); +} + +static void manage_command(private_vici_config_t *this, + char *name, vici_command_cb_t cb, bool reg) +{ + this->dispatcher->manage_command(this->dispatcher, name, + reg ? cb : NULL, this); +} + +/** + * (Un-)register dispatcher functions + */ +static void manage_commands(private_vici_config_t *this, bool reg) +{ + manage_command(this, "load-conn", load_conn, reg); + manage_command(this, "unload-conn", unload_conn, reg); + manage_command(this, "get-conns", get_conns, reg); +} + +METHOD(vici_config_t, destroy, void, + private_vici_config_t *this) +{ + manage_commands(this, FALSE); + this->conns->destroy_offset(this->conns, offsetof(peer_cfg_t, destroy)); + this->lock->destroy(this->lock); + free(this); +} + +/** + * See header + */ +vici_config_t *vici_config_create(vici_dispatcher_t *dispatcher) +{ + private_vici_config_t *this; + + INIT(this, + .public = { + .backend = { + .create_peer_cfg_enumerator = _create_peer_cfg_enumerator, + .create_ike_cfg_enumerator = _create_ike_cfg_enumerator, + .get_peer_cfg_by_name = _get_peer_cfg_by_name, + }, + .destroy = _destroy, + }, + .dispatcher = dispatcher, + .conns = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + manage_commands(this, TRUE); + + return &this->public; +} diff --git a/src/libcharon/plugins/vici/vici_config.h b/src/libcharon/plugins/vici/vici_config.h new file mode 100644 index 000000000..820d5f300 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_config.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup vici_config vici_config + * @{ @ingroup vici + */ + +#ifndef VICI_CONFIG_H_ +#define VICI_CONFIG_H_ + +#include "vici_dispatcher.h" + +#include <config/backend.h> + +typedef struct vici_config_t vici_config_t; + +/** + * In-memory configuration backend, managed by VICI. + */ +struct vici_config_t { + + /** + * Implements a configuraiton backend. + */ + backend_t backend; + + /** + * Destroy a vici_config_t. + */ + void (*destroy)(vici_config_t *this); +}; +/** + * Create a vici_config instance. + * + * @param dispatcher dispatcher to receive requests from + * @return config backend + */ +vici_config_t *vici_config_create(vici_dispatcher_t *dispatcher); + +#endif /** VICI_CONFIG_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c new file mode 100644 index 000000000..3cd008162 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_control.c @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "vici_control.h" +#include "vici_builder.h" + +#include <inttypes.h> + +#include <daemon.h> +#include <collections/array.h> + +typedef struct private_vici_control_t private_vici_control_t; + +/** + * Private data of an vici_control_t object. + */ +struct private_vici_control_t { + + /** + * Public vici_control_t interface. + */ + vici_control_t public; + + /** + * Dispatcher + */ + vici_dispatcher_t *dispatcher; +}; + +/** + * Log callback helper data + */ +typedef struct { + /** dispatcher to send log messages over */ + vici_dispatcher_t *dispatcher; + /** connection ID to send messages to */ + u_int id; + /** loglevel */ + level_t level; + /** prevent recursive log */ + u_int recursive; +} log_info_t; + +/** + * Log using vici event messages + */ +static bool log_vici(log_info_t *info, debug_t group, level_t level, + ike_sa_t *ike_sa, char *text) +{ + if (level <= info->level) + { + if (info->recursive++ == 0) + { + vici_message_t *message; + vici_builder_t *builder; + + builder = vici_builder_create(); + builder->add_kv(builder, "group", "%N", debug_names, group); + builder->add_kv(builder, "level", "%d", level); + if (ike_sa) + { + builder->add_kv(builder, "ikesa-name", "%s", + ike_sa->get_name(ike_sa)); + builder->add_kv(builder, "ikesa-uniqueid", "%u", + ike_sa->get_unique_id(ike_sa)); + } + builder->add_kv(builder, "msg", "%s", text); + + message = builder->finalize(builder); + if (message) + { + info->dispatcher->raise_event(info->dispatcher, "control-log", + info->id, message); + } + } + info->recursive--; + } + return TRUE; +} + +/** + * Send a (error) reply message + */ +static vici_message_t* send_reply(private_vici_control_t *this, char *fmt, ...) +{ + vici_builder_t *builder; + va_list args; + + builder = vici_builder_create(); + builder->add_kv(builder, "success", fmt ? "no" : "yes"); + if (fmt) + { + va_start(args, fmt); + builder->vadd_kv(builder, "errmsg", fmt, args); + va_end(args); + } + return builder->finalize(builder); +} + +/** + * Get the child_cfg having name from peer_cfg + */ +static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name) +{ + child_cfg_t *current, *found = NULL; + enumerator_t *enumerator; + + enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (streq(current->get_name(current), name)) + { + found = current; + found->get_ref(found); + break; + } + } + enumerator->destroy(enumerator); + return found; +} + +/** + * Find a peer/child config from a child config name + */ +static child_cfg_t* find_child_cfg(char *name, peer_cfg_t **out) +{ + enumerator_t *enumerator; + peer_cfg_t *peer_cfg; + child_cfg_t *child_cfg; + + enumerator = charon->backends->create_peer_cfg_enumerator( + charon->backends, NULL, NULL, NULL, NULL, IKE_ANY); + while (enumerator->enumerate(enumerator, &peer_cfg)) + { + child_cfg = get_child_from_peer(peer_cfg, name); + if (child_cfg) + { + *out = peer_cfg->get_ref(peer_cfg); + break; + } + } + enumerator->destroy(enumerator); + + return child_cfg; +} + +CALLBACK(initiate, vici_message_t*, + private_vici_control_t *this, char *name, u_int id, vici_message_t *request) +{ + child_cfg_t *child_cfg = NULL; + peer_cfg_t *peer_cfg; + char *child; + u_int timeout; + log_info_t log = { + .dispatcher = this->dispatcher, + .id = id, + }; + + child = request->get_str(request, NULL, "child"); + timeout = request->get_int(request, 0, "timeout"); + log.level = request->get_int(request, 1, "loglevel"); + + if (!child) + { + return send_reply(this, "missing configuration name"); + } + + DBG1(DBG_CFG, "vici initiate '%s'", child); + + child_cfg = find_child_cfg(child, &peer_cfg); + if (!child_cfg) + { + return send_reply(this, "CHILD_SA config '%s' not found", child); + } + switch (charon->controller->initiate(charon->controller, + peer_cfg, child_cfg, (controller_cb_t)log_vici, &log, timeout)) + { + case SUCCESS: + return send_reply(this, NULL); + case OUT_OF_RES: + return send_reply(this, "CHILD_SA '%s' not established after %dms", + child, timeout); + case FAILED: + default: + return send_reply(this, "establishing CHILD_SA '%s' failed", child); + } +} + +CALLBACK(terminate, vici_message_t*, + private_vici_control_t *this, char *name, u_int id, vici_message_t *request) +{ + enumerator_t *enumerator, *isas, *csas; + char *child, *ike, *errmsg = NULL; + u_int timeout, child_id, ike_id, current, *del, done = 0; + ike_sa_t *ike_sa; + child_sa_t *child_sa; + array_t *ids; + vici_builder_t *builder; + log_info_t log = { + .dispatcher = this->dispatcher, + .id = id, + }; + + child = request->get_str(request, NULL, "child"); + ike = request->get_str(request, NULL, "ike"); + child_id = request->get_int(request, 0, "child-id"); + ike_id = request->get_int(request, 0, "ike-id"); + timeout = request->get_int(request, 0, "timeout"); + log.level = request->get_int(request, 1, "loglevel"); + + if (!child && !ike && !ike_id && !child_id) + { + return send_reply(this, "missing terminate selector"); + } + + if (ike_id) + { + DBG1(DBG_CFG, "vici terminate IKE_SA #%d", ike_id); + } + if (child_id) + { + DBG1(DBG_CFG, "vici terminate CHILD_SA #%d", child_id); + } + if (ike) + { + DBG1(DBG_CFG, "vici terminate IKE_SA '%s'", ike); + } + if (child) + { + DBG1(DBG_CFG, "vici terminate CHILD_SA '%s'", child); + } + + ids = array_create(sizeof(u_int), 0); + + isas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE); + while (isas->enumerate(isas, &ike_sa)) + { + if (child || child_id) + { + if (ike && !streq(ike, ike_sa->get_name(ike_sa))) + { + continue; + } + if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa)) + { + continue; + } + csas = ike_sa->create_child_sa_enumerator(ike_sa); + while (csas->enumerate(csas, &child_sa)) + { + if (child && !streq(child, child_sa->get_name(child_sa))) + { + continue; + } + if (child_id && child_sa->get_reqid(child_sa) != child_id) + { + continue; + } + current = child_sa->get_reqid(child_sa); + array_insert(ids, ARRAY_TAIL, ¤t); + } + csas->destroy(csas); + } + else if (ike && streq(ike, ike_sa->get_name(ike_sa))) + { + current = ike_sa->get_unique_id(ike_sa); + array_insert(ids, ARRAY_TAIL, ¤t); + } + else if (ike_id && ike_id == ike_sa->get_unique_id(ike_sa)) + { + array_insert(ids, ARRAY_TAIL, &ike_id); + } + } + isas->destroy(isas); + + enumerator = array_create_enumerator(ids); + while (enumerator->enumerate(enumerator, &del)) + { + if (child || child_id) + { + if (charon->controller->terminate_child(charon->controller, *del, + (controller_cb_t)log_vici, &log, timeout) == SUCCESS) + { + done++; + } + } + else + { + if (charon->controller->terminate_ike(charon->controller, *del, + (controller_cb_t)log_vici, &log, timeout) == SUCCESS) + { + done++; + } + } + } + enumerator->destroy(enumerator); + + builder = vici_builder_create(); + if (array_count(ids) == 0) + { + errmsg = "no matching SAs to terminate found"; + } + else if (done < array_count(ids)) + { + if (array_count(ids) == 1) + { + errmsg = "terminating SA failed"; + } + else + { + errmsg = "not all matching SAs could be terminated"; + } + } + builder->add_kv(builder, "success", errmsg ? "no" : "yes"); + builder->add_kv(builder, "matches", "%u", array_count(ids)); + builder->add_kv(builder, "terminated", "%u", done); + if (errmsg) + { + builder->add_kv(builder, "errmsg", "%s", errmsg); + } + array_destroy(ids); + return builder->finalize(builder); +} + +/** + * Find reqid of an existing CHILD_SA + */ +static u_int32_t find_reqid(child_cfg_t *cfg) +{ + enumerator_t *enumerator, *children; + child_sa_t *child_sa; + ike_sa_t *ike_sa; + u_int32_t reqid; + + reqid = charon->traps->find_reqid(charon->traps, cfg); + if (reqid) + { /* already trapped */ + return reqid; + } + + enumerator = charon->controller->create_ike_sa_enumerator( + charon->controller, TRUE); + while (!reqid && enumerator->enumerate(enumerator, &ike_sa)) + { + children = ike_sa->create_child_sa_enumerator(ike_sa); + while (children->enumerate(children, &child_sa)) + { + if (streq(cfg->get_name(cfg), child_sa->get_name(child_sa))) + { + reqid = child_sa->get_reqid(child_sa); + break; + } + } + children->destroy(children); + } + enumerator->destroy(enumerator); + return reqid; +} + +CALLBACK(install, vici_message_t*, + private_vici_control_t *this, char *name, u_int id, vici_message_t *request) +{ + child_cfg_t *child_cfg = NULL; + peer_cfg_t *peer_cfg; + char *child; + bool ok; + + child = request->get_str(request, NULL, "child"); + if (!child) + { + return send_reply(this, "missing configuration name"); + } + + DBG1(DBG_CFG, "vici install '%s'", child); + + child_cfg = find_child_cfg(child, &peer_cfg); + if (!child_cfg) + { + return send_reply(this, "configuration name not found"); + } + switch (child_cfg->get_mode(child_cfg)) + { + case MODE_PASS: + case MODE_DROP: + ok = charon->shunts->install(charon->shunts, child_cfg); + break; + default: + ok = charon->traps->install(charon->traps, peer_cfg, child_cfg, + find_reqid(child_cfg)); + break; + } + peer_cfg->destroy(peer_cfg); + child_cfg->destroy(child_cfg); + + return send_reply(this, ok ? NULL : "installing policy '%s' failed", child); +} + +CALLBACK(uninstall, vici_message_t*, + private_vici_control_t *this, char *name, u_int id, vici_message_t *request) +{ + child_sa_t *child_sa; + enumerator_t *enumerator; + u_int32_t reqid = 0; + char *child; + + child = request->get_str(request, NULL, "child"); + if (!child) + { + return send_reply(this, "missing configuration name"); + } + + DBG1(DBG_CFG, "vici uninstall '%s'", child); + + if (charon->shunts->uninstall(charon->shunts, child)) + { + return send_reply(this, NULL); + } + + enumerator = charon->traps->create_enumerator(charon->traps); + while (enumerator->enumerate(enumerator, NULL, &child_sa)) + { + if (streq(child, child_sa->get_name(child_sa))) + { + reqid = child_sa->get_reqid(child_sa); + break; + } + } + enumerator->destroy(enumerator); + + if (reqid) + { + if (charon->traps->uninstall(charon->traps, reqid)) + { + return send_reply(this, NULL); + } + return send_reply(this, "uninstalling policy '%s' failed", child); + } + return send_reply(this, "policy '%s' not found", child); +} + +static void manage_command(private_vici_control_t *this, + char *name, vici_command_cb_t cb, bool reg) +{ + this->dispatcher->manage_command(this->dispatcher, name, + reg ? cb : NULL, this); +} + +/** + * (Un-)register dispatcher functions + */ +static void manage_commands(private_vici_control_t *this, bool reg) +{ + manage_command(this, "initiate", initiate, reg); + manage_command(this, "terminate", terminate, reg); + manage_command(this, "install", install, reg); + manage_command(this, "uninstall", uninstall, reg); + this->dispatcher->manage_event(this->dispatcher, "control-log", reg); +} + +METHOD(vici_control_t, destroy, void, + private_vici_control_t *this) +{ + manage_commands(this, FALSE); + free(this); +} + +/** + * See header + */ +vici_control_t *vici_control_create(vici_dispatcher_t *dispatcher) +{ + private_vici_control_t *this; + + INIT(this, + .public = { + .destroy = _destroy, + }, + .dispatcher = dispatcher, + ); + + manage_commands(this, TRUE); + + return &this->public; +} diff --git a/src/libcharon/plugins/vici/vici_control.h b/src/libcharon/plugins/vici/vici_control.h new file mode 100644 index 000000000..71a13a074 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_control.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup vici_control vici_control + * @{ @ingroup vici + */ + +#include "vici_dispatcher.h" + +#ifndef VICI_CONTROL_H_ +#define VICI_CONTROL_H_ + +typedef struct vici_control_t vici_control_t; + +/** + * Control helper, provides initiate/terminate and other commands. + */ +struct vici_control_t { + + /** + * Destroy a vici_control_t. + */ + void (*destroy)(vici_control_t *this); +}; + +/** + * Create a vici_control instance. + * + * @param dispatcher dispatcher to receive requests from + * @return query handler + */ +vici_control_t *vici_control_create(vici_dispatcher_t *dispatcher); + +#endif /** VICI_CONTROL_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_cred.c b/src/libcharon/plugins/vici/vici_cred.c new file mode 100644 index 000000000..cc6434b62 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_cred.c @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "vici_cred.h" +#include "vici_builder.h" + +#include <credentials/sets/mem_cred.h> +#include <credentials/certificates/ac.h> +#include <credentials/certificates/crl.h> +#include <credentials/certificates/x509.h> + +typedef struct private_vici_cred_t private_vici_cred_t; + +/** + * Private data of an vici_cred_t object. + */ +struct private_vici_cred_t { + + /** + * Public vici_cred_t interface. + */ + vici_cred_t public; + + /** + * Dispatcher + */ + vici_dispatcher_t *dispatcher; + + /** + * credentials + */ + mem_cred_t *creds; +}; + +/** + * Create a (error) reply message + */ +static vici_message_t* create_reply(char *fmt, ...) +{ + vici_builder_t *builder; + va_list args; + + builder = vici_builder_create(); + builder->add_kv(builder, "success", fmt ? "no" : "yes"); + if (fmt) + { + va_start(args, fmt); + builder->vadd_kv(builder, "errmsg", fmt, args); + va_end(args); + } + return builder->finalize(builder); +} + +CALLBACK(load_cert, vici_message_t*, + private_vici_cred_t *this, char *name, u_int id, vici_message_t *message) +{ + certificate_type_t type; + x509_flag_t required_flags = 0, additional_flags = 0; + certificate_t *cert; + x509_t *x509; + chunk_t data; + char *str; + + str = message->get_str(message, NULL, "type"); + if (!str) + { + return create_reply("certificate type missing"); + } + if (strcaseeq(str, "x509")) + { + type = CERT_X509; + } + else if (strcaseeq(str, "x509ca")) + { + type = CERT_X509; + required_flags = X509_CA; + } + else if (strcaseeq(str, "x509aa")) + { + type = CERT_X509; + additional_flags = X509_AA; + } + else if (strcaseeq(str, "x509crl")) + { + type = CERT_X509_CRL; + } + else if (strcaseeq(str, "x509ac")) + { + type = CERT_X509_AC; + } + else + { + return create_reply("invalid certificate type: %s", str); + } + data = message->get_value(message, chunk_empty, "data"); + if (!data.len) + { + return create_reply("certificate data missing"); + } + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, type, + BUILD_BLOB_PEM, data, + BUILD_X509_FLAG, additional_flags, + BUILD_END); + if (!cert) + { + return create_reply("parsing %N certificate failed", + certificate_type_names, type); + } + if (cert->get_type(cert) == CERT_X509) + { + x509 = (x509_t*)cert; + + if ((required_flags & x509->get_flags(x509)) != required_flags) + { + cert->destroy(cert); + return create_reply("certificate misses required flag, rejected"); + } + } + + DBG1(DBG_CFG, "loaded certificate '%Y'", cert->get_subject(cert)); + + this->creds->add_cert(this->creds, TRUE, cert); + + return create_reply(NULL); +} + +CALLBACK(load_key, vici_message_t*, + private_vici_cred_t *this, char *name, u_int id, vici_message_t *message) +{ + key_type_t type; + private_key_t *key; + chunk_t data; + char *str; + + str = message->get_str(message, NULL, "type"); + if (!str) + { + return create_reply("key type missing"); + } + if (strcaseeq(str, "any")) + { + type = KEY_ANY; + } + else if (strcaseeq(str, "rsa")) + { + type = KEY_RSA; + } + else if (strcaseeq(str, "ecdsa")) + { + type = KEY_ECDSA; + } + else + { + return create_reply("invalid key type: %s", str); + } + data = message->get_value(message, chunk_empty, "data"); + if (!data.len) + { + return create_reply("key data missing"); + } + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type, + BUILD_BLOB_PEM, data, BUILD_END); + if (!key) + { + return create_reply("parsing %N private key failed", + key_type_names, type); + } + + DBG1(DBG_CFG, "loaded %N private key", key_type_names, type); + + this->creds->add_key(this->creds, key); + + return create_reply(NULL); +} + +CALLBACK(shared_owners, bool, + linked_list_t *owners, vici_message_t *message, char *name, chunk_t value) +{ + if (streq(name, "owners")) + { + char buf[256]; + + if (!vici_stringify(value, buf, sizeof(buf))) + { + return FALSE; + } + owners->insert_last(owners, identification_create_from_string(buf)); + } + return TRUE; +} + +CALLBACK(load_shared, vici_message_t*, + private_vici_cred_t *this, char *name, u_int id, vici_message_t *message) +{ + shared_key_type_t type; + linked_list_t *owners; + chunk_t data; + char *str, buf[512] = ""; + enumerator_t *enumerator; + identification_t *owner; + int len; + + str = message->get_str(message, NULL, "type"); + if (!str) + { + return create_reply("shared key type missing"); + } + if (strcaseeq(str, "ike")) + { + type = SHARED_IKE; + } + else if (strcaseeq(str, "eap") || streq(str, "xauth")) + { + type = SHARED_EAP; + } + else + { + return create_reply("invalid shared key type: %s", str); + } + data = message->get_value(message, chunk_empty, "data"); + if (!data.len) + { + return create_reply("shared key data missing"); + } + + owners = linked_list_create(); + if (!message->parse(message, NULL, NULL, NULL, shared_owners, owners)) + { + owners->destroy_offset(owners, offsetof(identification_t, destroy)); + return create_reply("parsing shared key owners failed"); + } + if (owners->get_count(owners) == 0) + { + owners->insert_last(owners, identification_create_from_string("%any")); + } + + enumerator = owners->create_enumerator(owners); + while (enumerator->enumerate(enumerator, &owner)) + { + len = strlen(buf); + if (len < sizeof(buf)) + { + snprintf(buf + len, sizeof(buf) - len, "%s'%Y'", + len ? ", " : "", owner); + } + } + enumerator->destroy(enumerator); + + DBG1(DBG_CFG, "loaded %N shared key for: %s", + shared_key_type_names, type, buf); + + this->creds->add_shared_list(this->creds, + shared_key_create(type, chunk_clone(data)), owners); + + return create_reply(NULL); +} + +CALLBACK(clear_creds, vici_message_t*, + private_vici_cred_t *this, char *name, u_int id, vici_message_t *message) +{ + vici_builder_t *builder; + + this->creds->clear(this->creds); + lib->credmgr->flush_cache(lib->credmgr, CERT_ANY); + + builder = vici_builder_create(); + return builder->finalize(builder); +} + +static void manage_command(private_vici_cred_t *this, + char *name, vici_command_cb_t cb, bool reg) +{ + this->dispatcher->manage_command(this->dispatcher, name, + reg ? cb : NULL, this); +} + +/** + * (Un-)register dispatcher functions + */ +static void manage_commands(private_vici_cred_t *this, bool reg) +{ + manage_command(this, "clear-creds", clear_creds, reg); + manage_command(this, "load-cert", load_cert, reg); + manage_command(this, "load-key", load_key, reg); + manage_command(this, "load-shared", load_shared, reg); +} + +METHOD(vici_cred_t, destroy, void, + private_vici_cred_t *this) +{ + manage_commands(this, FALSE); + + lib->credmgr->remove_set(lib->credmgr, &this->creds->set); + this->creds->destroy(this->creds); + free(this); +} + +/** + * See header + */ +vici_cred_t *vici_cred_create(vici_dispatcher_t *dispatcher) +{ + private_vici_cred_t *this; + + INIT(this, + .public = { + .destroy = _destroy, + }, + .dispatcher = dispatcher, + .creds = mem_cred_create(), + ); + + lib->credmgr->add_set(lib->credmgr, &this->creds->set); + + manage_commands(this, TRUE); + + return &this->public; +} diff --git a/src/libcharon/plugins/vici/vici_cred.h b/src/libcharon/plugins/vici/vici_cred.h new file mode 100644 index 000000000..e109a27da --- /dev/null +++ b/src/libcharon/plugins/vici/vici_cred.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup vici_cred vici_cred + * @{ @ingroup vici + */ + +#ifndef VICI_CRED_H_ +#define VICI_CRED_H_ + +#include "vici_dispatcher.h" + +typedef struct vici_cred_t vici_cred_t; + +/** + * In-memory credential backend, managed by VICI. + */ +struct vici_cred_t { + + /** + * Destroy a vici_cred_t. + */ + void (*destroy)(vici_cred_t *this); +}; + +/** + * Create a vici_cred instance. + * + * @param dispatcher dispatcher to receive requests from + * @return credential backend + */ +vici_cred_t *vici_cred_create(vici_dispatcher_t *dispatcher); + +#endif /** VICI_CRED_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_dispatcher.c b/src/libcharon/plugins/vici/vici_dispatcher.c new file mode 100644 index 000000000..6db36fbe0 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_dispatcher.c @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "vici_dispatcher.h" +#include "vici_socket.h" + +#include <bio/bio_reader.h> +#include <bio/bio_writer.h> +#include <threading/mutex.h> +#include <threading/condvar.h> +#include <threading/thread.h> +#include <collections/array.h> +#include <collections/hashtable.h> + +typedef struct private_vici_dispatcher_t private_vici_dispatcher_t; + +/** + * Private data of an vici_dispatcher_t object. + */ +struct private_vici_dispatcher_t { + + /** + * Public vici_dispatcher_t interface. + */ + vici_dispatcher_t public; + + /** + * Socket to send/receive messages + */ + vici_socket_t *socket; + + /** + * List of registered commands (char* => command_t*) + */ + hashtable_t *cmds; + + /** + * List of known events, and registered clients (char* => event_t*) + */ + hashtable_t *events; + + /** + * Mutex to lock hashtables + */ + mutex_t *mutex; + + /** + * Condvar to signal command termination + */ + condvar_t *cond; +}; + +/** + * Registered command + */ +typedef struct { + /** command name */ + char *name; + /** callback for command */ + vici_command_cb_t cb; + /** user data to pass to callback */ + void *user; + /** command currently in use? */ + u_int uses; +} command_t; + +/** + * Registered event + */ +typedef struct { + /** event name */ + char *name; + /** registered clients, as u_int */ + array_t *clients; + /** event currently in use? */ + u_int uses; +} event_t; + +/** + * Send a operation code, optionally with name and message + */ +static void send_op(private_vici_dispatcher_t *this, u_int id, + vici_operation_t op, char *name, vici_message_t *message) +{ + bio_writer_t *writer; + u_int len; + + len = sizeof(u_int8_t); + if (name) + { + len += sizeof(u_int8_t) + strlen(name); + } + if (message) + { + len += message->get_encoding(message).len; + } + writer = bio_writer_create(len); + writer->write_uint8(writer, op); + if (name) + { + writer->write_data8(writer, chunk_from_str(name)); + } + if (message) + { + writer->write_data(writer, message->get_encoding(message)); + } + this->socket->send(this->socket, id, writer->extract_buf(writer)); + writer->destroy(writer); +} + +/** + * Register client for event + */ +static void register_event(private_vici_dispatcher_t *this, char *name, + u_int id) +{ + event_t *event; + + this->mutex->lock(this->mutex); + while (TRUE) + { + event = this->events->get(this->events, name); + if (!event) + { + break; + } + if (!event->uses) + { + array_insert(event->clients, ARRAY_TAIL, &id); + break; + } + this->cond->wait(this->cond, this->mutex); + } + this->mutex->unlock(this->mutex); + + if (event) + { + DBG2(DBG_CFG, "vici client %u registered for: %s", id, name); + send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL); + } + else + { + DBG1(DBG_CFG, "vici client %u invalid registration: %s", id, name); + send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL); + } +} + +/** + * Unregister client for event + */ +static void unregister_event(private_vici_dispatcher_t *this, char *name, + u_int id) +{ + enumerator_t *enumerator; + event_t *event; + u_int *current; + bool found = FALSE; + + this->mutex->lock(this->mutex); + while (TRUE) + { + event = this->events->get(this->events, name); + if (!event) + { + break; + } + if (!event->uses) + { + enumerator = array_create_enumerator(event->clients); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (*current == id) + { + array_remove_at(event->clients, enumerator); + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + break; + } + this->cond->wait(this->cond, this->mutex); + } + this->mutex->unlock(this->mutex); + + DBG2(DBG_CFG, "vici client %u unregistered for: %s", id, name); + + if (found) + { + send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL); + } + else + { + send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL); + } +} + +/** + * Data to release on thread cancellation + */ +typedef struct { + private_vici_dispatcher_t *this; + command_t *cmd; + vici_message_t *request; +} release_data_t; + +/** + * Release command after execution/cancellation + */ +CALLBACK(release_command, void, + release_data_t *release) +{ + release->request->destroy(release->request); + + release->this->mutex->lock(release->this->mutex); + if (--release->cmd->uses == 0) + { + release->this->cond->broadcast(release->this->cond); + } + release->this->mutex->unlock(release->this->mutex); + + free(release); +} + +/** + * Process a request message + */ +void process_request(private_vici_dispatcher_t *this, char *name, u_int id, + chunk_t data) +{ + vici_message_t *response = NULL; + release_data_t *release; + command_t *cmd; + + this->mutex->lock(this->mutex); + cmd = this->cmds->get(this->cmds, name); + if (cmd) + { + cmd->uses++; + } + this->mutex->unlock(this->mutex); + + if (cmd) + { + INIT(release, + .this = this, + .cmd = cmd, + ); + + DBG2(DBG_CFG, "vici client %u requests: %s", id, name); + + thread_cleanup_push(release_command, release); + + release->request = vici_message_create_from_data(data, FALSE); + response = release->cmd->cb(cmd->user, cmd->name, id, release->request); + + thread_cleanup_pop(TRUE); + + if (response) + { + send_op(this, id, VICI_CMD_RESPONSE, NULL, response); + response->destroy(response); + } + } + else + { + DBG1(DBG_CFG, "vici client %u invalid request: %s", id, name); + send_op(this, id, VICI_CMD_UNKNOWN, NULL, NULL); + } +} + +CALLBACK(inbound, void, + private_vici_dispatcher_t *this, u_int id, chunk_t data) +{ + bio_reader_t *reader; + chunk_t chunk; + u_int8_t type; + char name[257]; + + reader = bio_reader_create(data); + if (reader->read_uint8(reader, &type)) + { + switch (type) + { + case VICI_EVENT_REGISTER: + if (reader->read_data8(reader, &chunk) && + vici_stringify(chunk, name, sizeof(name))) + { + register_event(this, name, id); + } + else + { + DBG1(DBG_CFG, "invalid vici register message"); + } + break; + case VICI_EVENT_UNREGISTER: + if (reader->read_data8(reader, &chunk) && + vici_stringify(chunk, name, sizeof(name))) + { + unregister_event(this, name, id); + } + else + { + DBG1(DBG_CFG, "invalid vici unregister message"); + } + break; + case VICI_CMD_REQUEST: + if (reader->read_data8(reader, &chunk) && + vici_stringify(chunk, name, sizeof(name))) + { + thread_cleanup_push((void*)reader->destroy, reader); + process_request(this, name, id, reader->peek(reader)); + thread_cleanup_pop(FALSE); + } + else + { + DBG1(DBG_CFG, "invalid vici request message"); + } + break; + case VICI_CMD_RESPONSE: + case VICI_EVENT_CONFIRM: + case VICI_EVENT_UNKNOWN: + case VICI_EVENT: + default: + DBG1(DBG_CFG, "unsupported vici operation: %u", type); + break; + } + } + else + { + DBG1(DBG_CFG, "invalid vici message"); + } + reader->destroy(reader); +} + +CALLBACK(connect_, void, + private_vici_dispatcher_t *this, u_int id) +{ + DBG2(DBG_CFG, "vici client %u connected", id); +} + +CALLBACK(disconnect, void, + private_vici_dispatcher_t *this, u_int id) +{ + enumerator_t *events, *ids; + event_t *event; + u_int *current; + + /* deregister client from all events */ + this->mutex->lock(this->mutex); + events = this->events->create_enumerator(this->events); + while (events->enumerate(events, NULL, &event)) + { + while (event->uses) + { + this->cond->wait(this->cond, this->mutex); + } + ids = array_create_enumerator(event->clients); + while (ids->enumerate(ids, ¤t)) + { + if (id == *current) + { + array_remove_at(event->clients, ids); + } + } + ids->destroy(ids); + } + events->destroy(events); + this->mutex->unlock(this->mutex); + + DBG2(DBG_CFG, "vici client %u disconnected", id); +} + +METHOD(vici_dispatcher_t, manage_command, void, + private_vici_dispatcher_t *this, char *name, + vici_command_cb_t cb, void *user) +{ + command_t *cmd; + + this->mutex->lock(this->mutex); + if (cb) + { + INIT(cmd, + .name = strdup(name), + .cb = cb, + .user = user, + ); + cmd = this->cmds->put(this->cmds, cmd->name, cmd); + } + else + { + cmd = this->cmds->remove(this->cmds, name); + } + if (cmd) + { + while (cmd->uses) + { + this->cond->wait(this->cond, this->mutex); + } + free(cmd->name); + free(cmd); + } + this->mutex->unlock(this->mutex); +} + +METHOD(vici_dispatcher_t, manage_event, void, + private_vici_dispatcher_t *this, char *name, bool reg) +{ + event_t *event; + + this->mutex->lock(this->mutex); + if (reg) + { + INIT(event, + .name = strdup(name), + .clients = array_create(sizeof(u_int), 0), + ); + event = this->events->put(this->events, event->name, event); + } + else + { + event = this->events->remove(this->events, name); + } + if (event) + { + while (event->uses) + { + this->cond->wait(this->cond, this->mutex); + } + array_destroy(event->clients); + free(event->name); + free(event); + } + this->mutex->unlock(this->mutex); +} + +METHOD(vici_dispatcher_t, raise_event, void, + private_vici_dispatcher_t *this, char *name, u_int id, + vici_message_t *message) +{ + enumerator_t *enumerator; + event_t *event; + u_int *current; + + this->mutex->lock(this->mutex); + event = this->events->get(this->events, name); + if (event) + { + event->uses++; + this->mutex->unlock(this->mutex); + + enumerator = array_create_enumerator(event->clients); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (id == 0 || id == *current) + { + send_op(this, *current, VICI_EVENT, name, message); + } + } + enumerator->destroy(enumerator); + + this->mutex->lock(this->mutex); + if (--event->uses == 0) + { + this->cond->broadcast(this->cond); + } + } + this->mutex->unlock(this->mutex); + + message->destroy(message); +} + +METHOD(vici_dispatcher_t, destroy, void, + private_vici_dispatcher_t *this) +{ + DESTROY_IF(this->socket); + this->mutex->destroy(this->mutex); + this->cond->destroy(this->cond); + this->cmds->destroy(this->cmds); + this->events->destroy(this->events); + free(this); +} + +/** + * See header + */ +vici_dispatcher_t *vici_dispatcher_create(char *uri) +{ + private_vici_dispatcher_t *this; + + INIT(this, + .public = { + .manage_command = _manage_command, + .manage_event = _manage_event, + .raise_event = _raise_event, + .destroy = _destroy, + }, + .cmds = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1), + .events = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .cond = condvar_create(CONDVAR_TYPE_DEFAULT), + ); + + this->socket = vici_socket_create(uri, inbound, connect_, disconnect, this); + if (!this->socket) + { + destroy(this); + return NULL; + } + + return &this->public; +} diff --git a/src/libcharon/plugins/vici/vici_dispatcher.h b/src/libcharon/plugins/vici/vici_dispatcher.h new file mode 100644 index 000000000..2297a80bd --- /dev/null +++ b/src/libcharon/plugins/vici/vici_dispatcher.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup vici_dispatcher vici_dispatcher + * @{ @ingroup vici + */ + +#ifndef VICI_DISPATCHER_H_ +#define VICI_DISPATCHER_H_ + +#include "vici_message.h" + +typedef struct vici_dispatcher_t vici_dispatcher_t; +typedef enum vici_operation_t vici_operation_t; + +/** + * Default socket URI of vici service + */ +#ifdef WIN32 +# define VICI_DEFAULT_URI "tcp://127.0.0.1:4502" +#else +# define VICI_DEFAULT_URI "unix://" IPSEC_PIDDIR "/charon.vici" +#endif + +/** + * Kind of vici operation + */ +enum vici_operation_t { + /** a named request message */ + VICI_CMD_REQUEST, + /** an unnamed response message to a request */ + VICI_CMD_RESPONSE, + /** unnamed response if requested command is unknown */ + VICI_CMD_UNKNOWN, + /** a named event registration request */ + VICI_EVENT_REGISTER, + /** a named event unregistration request */ + VICI_EVENT_UNREGISTER, + /** unnamed response for successful event (un-)registration */ + VICI_EVENT_CONFIRM, + /** unnamed response if event (un-)registration failed */ + VICI_EVENT_UNKNOWN, + /** a named event message */ + VICI_EVENT, +}; + +/** + * Vici command callback function + * + * @param user user data, as supplied during registration + * @param name name of the command it has been registered under + * @param id client connection identifier + * @param request request message data + * @return response message + */ +typedef vici_message_t* (*vici_command_cb_t)(void *user, char *name, u_int id, + vici_message_t *request); + +/** + * Vici command dispatcher. + */ +struct vici_dispatcher_t { + + /** + * Register/Unregister a callback invoked for a specific command request. + * + * @param name name of the command + * @param cb callback function to register, NULL to unregister + * @param user user data to pass to callback + */ + void (*manage_command)(vici_dispatcher_t *this, char *name, + vici_command_cb_t cb, void *user); + + /** + * Register/Unregister an event type to send. + * + * The dispatcher internally manages event subscriptions. Clients registered + * for an event will receive such messages when the event is raised. + * + * @param name event name to manager + * @param reg TRUE to register, FALSE to unregister + */ + void (*manage_event)(vici_dispatcher_t *this, char *name, bool reg); + + /** + * Raise an event to a specific or all clients registered to that event. + * + * @param name event name to raise + * @param id client connection ID, 0 for all + * @param message event message to send, gets destroyed + */ + void (*raise_event)(vici_dispatcher_t *this, char *name, u_int id, + vici_message_t *message); + + /** + * Destroy a vici_dispatcher_t. + */ + void (*destroy)(vici_dispatcher_t *this); +}; + +/** + * Create a vici_dispatcher instance. + * + * @param uri uri for listening stream service + * @return dispatcher instance + */ +vici_dispatcher_t *vici_dispatcher_create(char *uri); + +#endif /** VICI_DISPATCHER_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_logger.c b/src/libcharon/plugins/vici/vici_logger.c new file mode 100644 index 000000000..cffd65bad --- /dev/null +++ b/src/libcharon/plugins/vici/vici_logger.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "vici_logger.h" +#include "vici_builder.h" + +#include <daemon.h> +#include <threading/mutex.h> + +typedef struct private_vici_logger_t private_vici_logger_t; + +/** + * Private data of an vici_logger_t object. + */ +struct private_vici_logger_t { + + /** + * Public vici_logger_t interface. + */ + vici_logger_t public; + + /** + * Dispatcher + */ + vici_dispatcher_t *dispatcher; + + /** + * Recursiveness avoidance counter + */ + int recursive; + + /** + * Mutex to synchronize logging + */ + mutex_t *mutex; +}; + +METHOD(logger_t, log_, void, + private_vici_logger_t *this, debug_t group, level_t level, int thread, + ike_sa_t* ike_sa, const char *msg) +{ + this->mutex->lock(this->mutex); + + /* avoid recursive invocations by the vici subsystem */ + if (this->recursive++ == 0) + { + vici_message_t *message; + vici_builder_t *builder; + + builder = vici_builder_create(); + builder->add_kv(builder, "group", "%N", debug_names, group); + builder->add_kv(builder, "level", "%d", level); + builder->add_kv(builder, "thread", "%d", thread); + if (ike_sa) + { + builder->add_kv(builder, "ikesa-name", "%s", + ike_sa->get_name(ike_sa)); + builder->add_kv(builder, "ikesa-uniqueid", "%u", + ike_sa->get_unique_id(ike_sa)); + } + builder->add_kv(builder, "msg", "%s", msg); + + message = builder->finalize(builder); + if (message) + { + this->dispatcher->raise_event(this->dispatcher, "log", 0, message); + } + } + this->recursive--; + + this->mutex->unlock(this->mutex); +} + +METHOD(logger_t, get_level, level_t, + private_vici_logger_t *this, debug_t group) +{ + return LEVEL_CTRL; +} + +/** + * (Un-)register dispatcher functions/events + */ +static void manage_commands(private_vici_logger_t *this, bool reg) +{ + this->dispatcher->manage_event(this->dispatcher, "log", reg); +} + +METHOD(vici_logger_t, destroy, void, + private_vici_logger_t *this) +{ + manage_commands(this, FALSE); + this->mutex->destroy(this->mutex); + free(this); +} + +/** + * See header + */ +vici_logger_t *vici_logger_create(vici_dispatcher_t *dispatcher) +{ + private_vici_logger_t *this; + + INIT(this, + .public = { + .logger = { + .log = _log_, + .get_level = _get_level, + }, + .destroy = _destroy, + }, + .dispatcher = dispatcher, + .mutex = mutex_create(MUTEX_TYPE_RECURSIVE), + ); + + manage_commands(this, TRUE); + + return &this->public; +} diff --git a/src/libcharon/plugins/vici/vici_logger.h b/src/libcharon/plugins/vici/vici_logger.h new file mode 100644 index 000000000..7be1d60d4 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_logger.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup vici_logger vici_logger + * @{ @ingroup vici + */ + +#ifndef VICI_LOGGER_H_ +#define VICI_LOGGER_H_ + +#include "vici_dispatcher.h" + +#include <bus/listeners/logger.h> + +typedef struct vici_logger_t vici_logger_t; + +/** + * Generic debugging logger over vici. + */ +struct vici_logger_t { + + /** + * Implements logger interface. + */ + logger_t logger; + + /** + * Destroy a vici_logger_t. + */ + void (*destroy)(vici_logger_t *this); +}; + +/** + * Create a vici_logger instance. + * + * @param dispatcher dispatcher to receive requests from + * @return loggerential backend + */ +vici_logger_t *vici_logger_create(vici_dispatcher_t *dispatcher); + +#endif /** VICI_LOGGER_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_message.c b/src/libcharon/plugins/vici/vici_message.c new file mode 100644 index 000000000..dcc175f67 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_message.c @@ -0,0 +1,727 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "vici_message.h" +#include "vici_builder.h" + +#include <bio/bio_reader.h> +#include <bio/bio_writer.h> + +#include <errno.h> + +typedef struct private_vici_message_t private_vici_message_t; + +/** + * Private data of an vici_message_t object. + */ +struct private_vici_message_t { + + /** + * Public vici_message_t interface. + */ + vici_message_t public; + + /** + * Message encoding + */ + chunk_t encoding; + + /** + * Free encoding during destruction? + */ + bool cleanup; + + /** + * Allocated strings we maintain for get_str() + */ + linked_list_t *strings; +}; + +ENUM(vici_type_names, VICI_START, VICI_END, + "start", + "section-start", + "section-end", + "key-value", + "list-start", + "list-item", + "list-end", + "end" +); + +/** + * See header. + */ +bool vici_stringify(chunk_t chunk, char *buf, size_t size) +{ + if (!chunk_printable(chunk, NULL, 0)) + { + return FALSE; + } + snprintf(buf, size, "%.*s", (int)chunk.len, chunk.ptr); + return TRUE; +} + +/** + * See header. + */ +bool vici_verify_type(vici_type_t type, u_int section, bool list) +{ + if (list) + { + if (type != VICI_LIST_END && type != VICI_LIST_ITEM) + { + DBG1(DBG_ENC, "'%N' within list", vici_type_names, type); + return FALSE; + } + } + else + { + if (type == VICI_LIST_ITEM || type == VICI_LIST_END) + { + DBG1(DBG_ENC, "'%N' outside list", vici_type_names, type); + return FALSE; + } + } + if (type == VICI_SECTION_END && section == 0) + { + DBG1(DBG_ENC, "'%N' outside of section", vici_type_names, type); + return FALSE; + } + if (type == VICI_END) + { + if (section) + { + DBG1(DBG_ENC, "'%N' within section", vici_type_names, type); + return FALSE; + } + if (list) + { + DBG1(DBG_ENC, "'%N' within list", vici_type_names, type); + return FALSE; + } + } + return TRUE; +} + +/** + * Enumerator parsing message + */ +typedef struct { + /* implements enumerator */ + enumerator_t public; + /** reader to parse from */ + bio_reader_t *reader; + /** section nesting level */ + int section; + /** currently parsing list? */ + bool list; + /** string currently enumerating */ + char name[257]; +} parse_enumerator_t; + +METHOD(enumerator_t, parse_enumerate, bool, + parse_enumerator_t *this, vici_type_t *out, char **name, chunk_t *value) +{ + u_int8_t type; + chunk_t data; + + if (!this->reader->remaining(this->reader) || + !this->reader->read_uint8(this->reader, &type)) + { + *out = VICI_END; + return TRUE; + } + if (!vici_verify_type(type, this->section, this->list)) + { + return FALSE; + } + + switch (type) + { + case VICI_SECTION_START: + if (!this->reader->read_data8(this->reader, &data) || + !vici_stringify(data, this->name, sizeof(this->name))) + { + DBG1(DBG_ENC, "invalid '%N' encoding", vici_type_names, type); + return FALSE; + } + *name = this->name; + this->section++; + break; + case VICI_SECTION_END: + this->section--; + break; + case VICI_KEY_VALUE: + if (!this->reader->read_data8(this->reader, &data) || + !vici_stringify(data, this->name, sizeof(this->name)) || + !this->reader->read_data16(this->reader, value)) + { + DBG1(DBG_ENC, "invalid '%N' encoding", vici_type_names, type); + return FALSE; + } + *name = this->name; + break; + case VICI_LIST_START: + if (!this->reader->read_data8(this->reader, &data) || + !vici_stringify(data, this->name, sizeof(this->name))) + { + DBG1(DBG_ENC, "invalid '%N' encoding", vici_type_names, type); + return FALSE; + } + *name = this->name; + this->list = TRUE; + break; + case VICI_LIST_ITEM: + this->reader->read_data16(this->reader, value); + break; + case VICI_LIST_END: + this->list = FALSE; + break; + case VICI_END: + return TRUE; + default: + DBG1(DBG_ENC, "unknown encoding type: %u", type); + return FALSE; + } + + *out = type; + + return TRUE; +} + +METHOD(enumerator_t, parse_destroy, void, + parse_enumerator_t *this) +{ + this->reader->destroy(this->reader); + free(this); +} + +METHOD(vici_message_t, create_enumerator, enumerator_t*, + private_vici_message_t *this) +{ + parse_enumerator_t *enumerator; + + INIT(enumerator, + .public = { + .enumerate = (void*)_parse_enumerate, + .destroy = _parse_destroy, + }, + .reader = bio_reader_create(this->encoding), + ); + + return &enumerator->public; +} + +/** + * Find a value for given vararg key + */ +static bool find_value(private_vici_message_t *this, chunk_t *value, + char *fmt, va_list args) +{ + enumerator_t *enumerator; + char buf[128], *name, *key, *dot, *next; + int section = 0, keysection = 0; + bool found = FALSE; + chunk_t current; + vici_type_t type; + + vsnprintf(buf, sizeof(buf), fmt, args); + next = buf; + + enumerator = create_enumerator(this); + + /* descent into section */ + while (TRUE) + { + dot = strchr(next, '.'); + if (!dot) + { + key = next; + break; + } + *dot = '\0'; + key = next; + next = dot + 1; + keysection++; + + while (enumerator->enumerate(enumerator, &type, &name, ¤t)) + { + switch (type) + { + case VICI_SECTION_START: + section++; + if (section == keysection && streq(name, key)) + { + break; + } + continue; + case VICI_SECTION_END: + section--; + continue; + case VICI_END: + break; + default: + continue; + } + break; + } + } + + /* find key/value in current section */ + while (enumerator->enumerate(enumerator, &type, &name, ¤t)) + { + switch (type) + { + case VICI_KEY_VALUE: + if (section == keysection && streq(key, name)) + { + *value = current; + found = TRUE; + break; + } + continue; + case VICI_SECTION_START: + section++; + continue; + case VICI_SECTION_END: + section--; + continue; + case VICI_END: + break; + default: + continue; + } + break; + } + + enumerator->destroy(enumerator); + + return found; +} + +METHOD(vici_message_t, vget_str, char*, + private_vici_message_t *this, char *def, char *fmt, va_list args) +{ + chunk_t value; + bool found; + char *str; + + found = find_value(this, &value, fmt, args); + if (found) + { + if (chunk_printable(value, NULL, 0)) + { + str = strndup(value.ptr, value.len); + /* keep a reference to string, so caller doesn't have to care */ + this->strings->insert_last(this->strings, str); + return str; + } + } + return def; +} + +METHOD(vici_message_t, get_str, char*, + private_vici_message_t *this, char *def, char *fmt, ...) +{ + va_list args; + char *str; + + va_start(args, fmt); + str = vget_str(this, def, fmt, args); + va_end(args); + return str; +} + +METHOD(vici_message_t, vget_int, int, + private_vici_message_t *this, int def, char *fmt, va_list args) +{ + chunk_t value; + bool found; + char buf[32], *pos; + int ret; + + found = find_value(this, &value, fmt, args); + if (found) + { + if (chunk_printable(value, NULL, 0)) + { + snprintf(buf, sizeof(buf), "%.*s", (int)value.len, value.ptr); + errno = 0; + ret = strtol(buf, &pos, 0); + if (errno == 0 && pos == buf + strlen(buf)) + { + return ret; + } + } + } + return def; +} + +METHOD(vici_message_t, get_int, int, + private_vici_message_t *this, int def, char *fmt, ...) +{ + va_list args; + int val; + + va_start(args, fmt); + val = vget_int(this, def, fmt, args); + va_end(args); + return val; +} + +METHOD(vici_message_t, vget_value, chunk_t, + private_vici_message_t *this, chunk_t def, char *fmt, va_list args) +{ + chunk_t value; + bool found; + + found = find_value(this, &value, fmt, args); + if (found) + { + return value; + } + return def; +} + +METHOD(vici_message_t, get_value, chunk_t, + private_vici_message_t *this, chunk_t def, char *fmt, ...) +{ + va_list args; + chunk_t value; + + va_start(args, fmt); + value = vget_value(this, def, fmt, args); + va_end(args); + return value; +} + +METHOD(vici_message_t, get_encoding, chunk_t, + private_vici_message_t *this) +{ + return this->encoding; +} + +/** + * Private parse context data + */ +struct vici_parse_context_t { + /** current section nesting level */ + int level; + /** parse enumerator */ + enumerator_t *e; +}; + +METHOD(vici_message_t, parse, bool, + private_vici_message_t *this, vici_parse_context_t *ctx, + vici_section_cb_t section, vici_value_cb_t kv, vici_value_cb_t li, + void *user) +{ + vici_parse_context_t root = {}; + char *name, *list = NULL; + vici_type_t type; + chunk_t value; + int base; + bool ok = TRUE; + + if (!ctx) + { + ctx = &root; + root.e = create_enumerator(this); + } + + base = ctx->level; + + while (ok) + { + ok = ctx->e->enumerate(ctx->e, &type, &name, &value); + if (ok) + { + switch (type) + { + case VICI_START: + /* should never occur */ + continue; + case VICI_KEY_VALUE: + if (ctx->level == base && kv) + { + name = strdup(name); + this->strings->insert_last(this->strings, name); + ok = kv(user, &this->public, name, value); + } + continue; + case VICI_LIST_START: + if (ctx->level == base) + { + list = strdup(name); + this->strings->insert_last(this->strings, list); + } + continue; + case VICI_LIST_ITEM: + if (list && li) + { + name = strdup(name); + this->strings->insert_last(this->strings, name); + ok = li(user, &this->public, list, value); + } + continue; + case VICI_LIST_END: + if (ctx->level == base) + { + list = NULL; + } + continue; + case VICI_SECTION_START: + if (ctx->level++ == base && section) + { + name = strdup(name); + this->strings->insert_last(this->strings, name); + ok = section(user, &this->public, ctx, name); + } + continue; + case VICI_SECTION_END: + if (ctx->level-- == base) + { + break; + } + continue; + case VICI_END: + break; + } + } + break; + } + + if (ctx == &root) + { + root.e->destroy(root.e); + } + return ok; +} + +METHOD(vici_message_t, dump, bool, + private_vici_message_t *this, char *label, bool pretty, FILE *out) +{ + enumerator_t *enumerator; + int ident = 0, delta; + vici_type_t type, last_type = VICI_START; + char *name, *term, *sep, *separ, *assign; + chunk_t value; + + /* pretty print uses indentation on multiple lines */ + if (pretty) + { + delta = 2; + term = "\n"; + separ = ""; + assign = " = "; + } + else + { + delta = 0; + term = ""; + separ = " "; + assign = "="; + } + + fprintf(out, "%s {%s", label, term); + ident += delta; + + enumerator = create_enumerator(this); + while (enumerator->enumerate(enumerator, &type, &name, &value)) + { + switch (type) + { + case VICI_START: + /* should never occur */ + break; + case VICI_SECTION_START: + sep = (last_type != VICI_SECTION_START && + last_type != VICI_START) ? separ : ""; + fprintf(out, "%*s%s%s {%s", ident, "", sep, name, term); + ident += delta; + break; + case VICI_SECTION_END: + ident -= delta; + fprintf(out, "%*s}%s", ident, "", term); + break; + case VICI_KEY_VALUE: + sep = (last_type != VICI_SECTION_START && + last_type != VICI_START) ? separ : ""; + if (chunk_printable(value, NULL, ' ')) + { + fprintf(out, "%*s%s%s%s%.*s%s", ident, "", sep, name, + assign, (int)value.len, value.ptr, term); + } + else + { + fprintf(out, "%*s%s%s%s0x%+#B%s", ident, "", sep, name, + assign, &value, term); + } + break; + case VICI_LIST_START: + sep = (last_type != VICI_SECTION_START && + last_type != VICI_START) ? separ : ""; + fprintf(out, "%*s%s%s%s[%s", ident, "", sep, name, assign, term); + ident += delta; + break; + case VICI_LIST_END: + ident -= delta; + fprintf(out, "%*s]%s", ident, "", term); + break; + case VICI_LIST_ITEM: + sep = (last_type != VICI_LIST_START) ? separ : ""; + if (chunk_printable(value, NULL, ' ')) + { + fprintf(out, "%*s%s%.*s%s", ident, "", sep, + (int)value.len, value.ptr, term); + } + else + { + fprintf(out, "%*s%s0x%+#B%s", ident, "", sep, + &value, term); + } + break; + case VICI_END: + fprintf(out, "}\n"); + enumerator->destroy(enumerator); + return TRUE; + } + last_type = type; + } + enumerator->destroy(enumerator); + return FALSE; +} + +METHOD(vici_message_t, destroy, void, + private_vici_message_t *this) +{ + if (this->cleanup) + { + chunk_clear(&this->encoding); + } + this->strings->destroy_function(this->strings, free); + free(this); +} + +/** + * See header + */ +vici_message_t *vici_message_create_from_data(chunk_t data, bool cleanup) +{ + private_vici_message_t *this; + + INIT(this, + .public = { + .create_enumerator = _create_enumerator, + .get_str = _get_str, + .vget_str = _vget_str, + .get_int = _get_int, + .vget_int = _vget_int, + .get_value = _get_value, + .vget_value = _vget_value, + .get_encoding = _get_encoding, + .parse = _parse, + .dump = _dump, + .destroy = _destroy, + }, + .strings = linked_list_create(), + .encoding = data, + .cleanup = cleanup, + ); + + return &this->public; +} + +/** + * See header + */ +vici_message_t *vici_message_create_from_enumerator(enumerator_t *enumerator) +{ + vici_builder_t *builder; + vici_type_t type; + char *name; + chunk_t value; + + builder = vici_builder_create(); + while (enumerator->enumerate(enumerator, &type, &name, &value)) + { + switch (type) + { + case VICI_SECTION_START: + case VICI_LIST_START: + builder->add(builder, type, name); + continue; + case VICI_KEY_VALUE: + builder->add(builder, type, name, value); + continue; + case VICI_LIST_ITEM: + builder->add(builder, type, value); + continue; + case VICI_SECTION_END: + case VICI_LIST_END: + default: + builder->add(builder, type); + continue; + case VICI_END: + break; + } + break; + } + enumerator->destroy(enumerator); + + return builder->finalize(builder); +} + +/** + * See header + */ +vici_message_t *vici_message_create_from_args(vici_type_t type, ...) +{ + vici_builder_t *builder; + va_list args; + char *name; + chunk_t value; + + builder = vici_builder_create(); + va_start(args, type); + while (type != VICI_END) + { + switch (type) + { + case VICI_LIST_START: + case VICI_SECTION_START: + name = va_arg(args, char*); + builder->add(builder, type, name); + break; + case VICI_KEY_VALUE: + name = va_arg(args, char*); + value = va_arg(args, chunk_t); + builder->add(builder, type, name, value); + break; + case VICI_LIST_ITEM: + value = va_arg(args, chunk_t); + builder->add(builder, type, value); + break; + case VICI_SECTION_END: + case VICI_LIST_END: + default: + builder->add(builder, type); + break; + } + type = va_arg(args, vici_type_t); + } + va_end(args); + return builder->finalize(builder); +} diff --git a/src/libcharon/plugins/vici/vici_message.h b/src/libcharon/plugins/vici/vici_message.h new file mode 100644 index 000000000..1a89cf829 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_message.h @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup vici_message vici_message + * @{ @ingroup vici_dispatcher + */ + +#ifndef VICI_MESSAGE_H_ +#define VICI_MESSAGE_H_ + +#include <library.h> + +typedef struct vici_message_t vici_message_t; +typedef struct vici_parse_context_t vici_parse_context_t; +typedef enum vici_type_t vici_type_t; + +/** + * Vici message encoding types + */ +enum vici_type_t { + /** never used in an argument list, needed by dump as initial value */ + VICI_START = 0, + + /** begin of new section, argument is section name as char* */ + VICI_SECTION_START = 1, + /** end of current section, no arguments */ + VICI_SECTION_END = 2, + /** key/value, arguments are key as char*, value as chunk_t */ + VICI_KEY_VALUE = 3, + /** list start, argument is list name as char* */ + VICI_LIST_START = 4, + /** list item, argument is item value as chunk_t */ + VICI_LIST_ITEM = 5, + /** end of list, no arguments */ + VICI_LIST_END = 6, + + /** end of argument list, no arguments (never encoded) */ + VICI_END = 7 +}; + +/** + * Callback function for key/value and list items, invoked by parse(). + * + * @param user user data, as passed to parse() + * @param message message currently parsing + * @param name name of key or list + * @param value parsed value + * @return TRUE if parsed successfully + */ +typedef bool (*vici_value_cb_t)(void *user, vici_message_t *message, + char *name, chunk_t value); + +/** + * Callback function for sections, invoked by parse(). + * + * @param user user data, as passed to parse() + * @param message message currently parsing + * @param ctx parse context, to pass to recursive parse() invocations. + * @param name name of the section + * @return TRUE if parsed successfully + */ +typedef bool (*vici_section_cb_t)(void *user, vici_message_t *message, + vici_parse_context_t *ctx, char *name); + +/** + * Names for vici encoding types + */ +extern enum_name_t *vici_type_names; + +/** + * Vici message representation, encoding/decoding routines. + */ +struct vici_message_t { + + /** + * Create an enumerator over message contents. + * + * The enumerator takes a fixed list of arguments, but depending on the + * type may set not all of them. It returns VICI_END as last argument + * to indicate the message end, and returns FALSE if parsing the message + * failed. + * + * @return enumerator over (vici_type_t, char*, chunk_t) + */ + enumerator_t* (*create_enumerator)(vici_message_t *this); + + /** + * Get the value of a key/value pair as a string. + * + * @param def default value if not found + * @param fmt printf style format string for key, with sections + * @param ... arguments to fmt string + * @return string + */ + char* (*get_str)(vici_message_t *this, char *def, char *fmt, ...); + + /** + * Get the value of a key/value pair as a string, va_list variant. + * + * @param def default value if not found + * @param fmt printf style format string for key, with sections + * @param args arguments to fmt string + * @return string + */ + char* (*vget_str)(vici_message_t *this, char *def, char *fmt, va_list args); + + /** + * Get the value of a key/value pair as integer. + * + * @param def default value if not found + * @param fmt printf style format string for key, with sections + * @param ... arguments to fmt string + * @return value + */ + int (*get_int)(vici_message_t *this, int def, char *fmt, ...); + + /** + * Get the value of a key/value pair as integer, va_list variant + * + * @param def default value if not found + * @param fmt printf style format string for key, with sections + * @param args arguments to fmt string + * @return value + */ + int (*vget_int)(vici_message_t *this, int def, char *fmt, va_list args); + + /** + * Get the raw value of a key/value pair. + * + * @param def default value if not found + * @param fmt printf style format string for key, with sections + * @param ... arguments to fmt string + * @return value + */ + chunk_t (*get_value)(vici_message_t *this, chunk_t def, char *fmt, ...); + + /** + * Get the raw value of a key/value pair, va_list variant. + * + * @param def default value if not found + * @param fmt printf style format string for key, with sections + * @param args arguments to fmt string + * @return value + */ + chunk_t (*vget_value)(vici_message_t *this, chunk_t def, + char *fmt, va_list args); + + /** + * Get encoded message. + * + * @return message data, points to internal data + */ + chunk_t (*get_encoding)(vici_message_t *this); + + /** + * Parse a message using callback functions. + * + * Any of the callbacks may be NULL to skip this kind of item. Callbacks are + * invoked for the current section level only. To descent into sections, + * call parse() from within a section callback using the provided parse + * context. + * + * @param ctx parse context, NULL for root level + * @param section callback invoked for each section + * @param kv callback invoked for key/value pairs + * @param li callback invoked for list items + * @param user user data to pass to callbacks + * @return TRUE if parsed successfully + */ + bool (*parse)(vici_message_t *this, vici_parse_context_t *ctx, + vici_section_cb_t section, vici_value_cb_t kv, + vici_value_cb_t li, void *user); + + /** + * Dump a message text representation to a FILE stream. + * + * @param label label to print for message + * @param pretty use pretty print with indentation + * @param out FILE stream to dump to + * @return TRUE if message valid + */ + bool (*dump)(vici_message_t *this, char *label, bool pretty, FILE *out); + + /** + * Destroy a vici_message_t. + */ + void (*destroy)(vici_message_t *this); +}; + +/** + * Create a vici_message from encoded data. + * + * @param data message encoding + * @param cleanup TRUE to free data during + * @return message representation + */ +vici_message_t *vici_message_create_from_data(chunk_t data, bool cleanup); + +/** + * Create a vici_message from an enumerator. + * + * The enumerator uses the same signature as the enumerator returned + * by create_enumerator(), and gets destroyed by this function. It should + * return VICI_END to close the message, return FALSE to indicate a failure. + * + * @param enumerator enumerator over (vici_type_t, char*, chunk_t) + * @return message representation, NULL on error + */ +vici_message_t *vici_message_create_from_enumerator(enumerator_t *enumerator); + +/** + * Create vici message from a variable argument list. + * + * @param type first type beginning message + * @param ... vici_type_t and args, terminated by VICI_END + * @return message representation, NULL on error + */ +vici_message_t *vici_message_create_from_args(vici_type_t type, ...); + +/** + * Check if a chunk has a printable string, and print it to buf. + * + * @param chunk chunk containing potential string + * @param buf buffer to write string to + * @param size size of buf + * @return TRUE if printable and string written to buf + */ +bool vici_stringify(chunk_t chunk, char *buf, size_t size); + +/** + * Verify the occurrence of a given type for given section/list nesting + */ +bool vici_verify_type(vici_type_t type, u_int section, bool list); + +#endif /** VICI_MESSAGE_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_plugin.c b/src/libcharon/plugins/vici/vici_plugin.c new file mode 100644 index 000000000..8881feca9 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_plugin.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "vici_plugin.h" +#include "vici_dispatcher.h" +#include "vici_query.h" +#include "vici_control.h" +#include "vici_cred.h" +#include "vici_config.h" +#include "vici_attribute.h" +#include "vici_logger.h" + +#include <library.h> +#include <hydra.h> +#include <daemon.h> + +typedef struct private_vici_plugin_t private_vici_plugin_t; + +/** + * Private members of vici_plugin_t + */ +struct private_vici_plugin_t { + + /** + * public functions + */ + vici_plugin_t public; + + /** + * Dispatcher, creating socket + */ + vici_dispatcher_t *dispatcher; + + /** + * Query commands + */ + vici_query_t *query; + + /** + * Control commands + */ + vici_control_t *control; + + /** + * Credential backend + */ + vici_cred_t *cred; + + /** + * Configuration backend + */ + vici_config_t *config; + + /** + * IKE attribute backend + */ + vici_attribute_t *attrs; + + /** + * Generic debug logger + */ + vici_logger_t *logger; +}; + +METHOD(plugin_t, get_name, char*, + private_vici_plugin_t *this) +{ + return "vici"; +} + +/** + * Register vici plugin features + */ +static bool register_vici(private_vici_plugin_t *this, + plugin_feature_t *feature, bool reg, void *data) +{ + if (reg) + { + char *uri; + + uri = lib->settings->get_str(lib->settings, "%s.plugins.vici.socket", + VICI_DEFAULT_URI, lib->ns); + this->dispatcher = vici_dispatcher_create(uri); + if (this->dispatcher) + { + this->query = vici_query_create(this->dispatcher); + this->control = vici_control_create(this->dispatcher); + this->cred = vici_cred_create(this->dispatcher); + this->config = vici_config_create(this->dispatcher); + this->attrs = vici_attribute_create(this->dispatcher); + this->logger = vici_logger_create(this->dispatcher); + + charon->backends->add_backend(charon->backends, + &this->config->backend); + hydra->attributes->add_provider(hydra->attributes, + &this->attrs->provider); + charon->bus->add_logger(charon->bus, &this->logger->logger); + return TRUE; + } + return FALSE; + } + else + { + charon->bus->remove_logger(charon->bus, &this->logger->logger); + hydra->attributes->remove_provider(hydra->attributes, + &this->attrs->provider); + charon->backends->remove_backend(charon->backends, + &this->config->backend); + + this->logger->destroy(this->logger); + this->attrs->destroy(this->attrs); + this->config->destroy(this->config); + this->cred->destroy(this->cred); + this->control->destroy(this->control); + this->query->destroy(this->query); + this->dispatcher->destroy(this->dispatcher); + } + return TRUE; +} + +METHOD(plugin_t, get_features, int, + private_vici_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK((plugin_feature_callback_t)register_vici, NULL), + PLUGIN_PROVIDE(CUSTOM, "vici"), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_vici_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *vici_plugin_create() +{ + private_vici_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .reload = (void*)return_false, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + ); + + return &this->public.plugin; +} diff --git a/src/libcharon/plugins/vici/vici_plugin.h b/src/libcharon/plugins/vici/vici_plugin.h new file mode 100644 index 000000000..b4c380200 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup vici vici + * @ingroup cplugins + * + * @defgroup vici_plugin vici_plugin + * @{ @ingroup vici + */ + +#ifndef VICI_PLUGIN_H_ +#define VICI_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct vici_plugin_t vici_plugin_t; + +/** + * vici plugin, the "Versatile IKE Control Interface" interface. + */ +struct vici_plugin_t { + + /** + * Implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** VICI_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c new file mode 100644 index 000000000..54833abde --- /dev/null +++ b/src/libcharon/plugins/vici/vici_query.c @@ -0,0 +1,1039 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "vici_query.h" +#include "vici_builder.h" + +#include <inttypes.h> +#include <time.h> +#ifndef WIN32 +#include <sys/utsname.h> +#endif +#ifdef HAVE_MALLINFO +#include <malloc.h> +#endif + +#include <daemon.h> + +typedef struct private_vici_query_t private_vici_query_t; + +/** + * Private data of an vici_query_t object. + */ +struct private_vici_query_t { + + /** + * Public vici_query_t interface. + */ + vici_query_t public; + + /** + * Dispatcher + */ + vici_dispatcher_t *dispatcher; + + /** + * Daemon startup timestamp + */ + time_t uptime; +}; + +/** + * List details of a CHILD_SA + */ +static void list_child(private_vici_query_t *this, vici_builder_t *b, + child_sa_t *child, time_t now) +{ + time_t t; + u_int64_t bytes, packets; + u_int16_t alg, ks; + proposal_t *proposal; + enumerator_t *enumerator; + traffic_selector_t *ts; + + b->add_kv(b, "reqid", "%u", child->get_reqid(child)); + b->add_kv(b, "state", "%N", child_sa_state_names, child->get_state(child)); + b->add_kv(b, "mode", "%N", ipsec_mode_names, child->get_mode(child)); + if (child->get_state(child) == CHILD_INSTALLED || + child->get_state(child) == CHILD_REKEYING) + { + b->add_kv(b, "protocol", "%N", protocol_id_names, + child->get_protocol(child)); + if (child->has_encap(child)) + { + b->add_kv(b, "encap", "yes"); + } + b->add_kv(b, "spi-in", "%.8x", ntohl(child->get_spi(child, TRUE))); + b->add_kv(b, "spi-out", "%.8x", ntohl(child->get_spi(child, FALSE))); + + if (child->get_ipcomp(child) != IPCOMP_NONE) + { + b->add_kv(b, "cpi-in", "%.4x", ntohs(child->get_cpi(child, TRUE))); + b->add_kv(b, "cpi-out", "%.4x", ntohs(child->get_cpi(child, FALSE))); + } + proposal = child->get_proposal(child); + if (proposal) + { + if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, + &alg, &ks) && alg != ENCR_UNDEFINED) + { + b->add_kv(b, "encr-alg", "%N", encryption_algorithm_names, alg); + if (ks) + { + b->add_kv(b, "encr-keysize", "%u", ks); + } + } + if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, + &alg, &ks) && alg != ENCR_UNDEFINED) + { + b->add_kv(b, "integ-alg", "%N", integrity_algorithm_names, alg); + if (ks) + { + b->add_kv(b, "integ-keysize", "%u", ks); + } + } + if (proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, + &alg, NULL)) + { + b->add_kv(b, "prf-alg", "%N", pseudo_random_function_names, alg); + } + if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, + &alg, NULL)) + { + b->add_kv(b, "dh-group", "%N", diffie_hellman_group_names, alg); + } + if (proposal->get_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS, + &alg, NULL) && alg == EXT_SEQ_NUMBERS) + { + b->add_kv(b, "esn", "1"); + } + } + + child->get_usestats(child, TRUE, &t, &bytes, &packets); + b->add_kv(b, "bytes-in", "%" PRIu64, bytes); + b->add_kv(b, "packets-in", "%" PRIu64, packets); + if (t) + { + b->add_kv(b, "use-in", "%"PRIu64, (u_int64_t)(now - t)); + } + + child->get_usestats(child, FALSE, &t, &bytes, &packets); + b->add_kv(b, "bytes-out", "%"PRIu64, bytes); + b->add_kv(b, "packets-out", "%"PRIu64, packets); + if (t) + { + b->add_kv(b, "use-out", "%"PRIu64, (u_int64_t)(now - t)); + } + + t = child->get_lifetime(child, FALSE); + if (t) + { + b->add_kv(b, "rekey-time", "%"PRId64, (int64_t)(t - now)); + } + t = child->get_lifetime(child, TRUE); + if (t) + { + b->add_kv(b, "life-time", "%"PRId64, (int64_t)(t - now)); + } + t = child->get_installtime(child); + b->add_kv(b, "install-time", "%"PRId64, (int64_t)(now - t)); + } + + b->begin_list(b, "local-ts"); + enumerator = child->create_ts_enumerator(child, TRUE); + while (enumerator->enumerate(enumerator, &ts)) + { + b->add_li(b, "%R", ts); + } + enumerator->destroy(enumerator); + b->end_list(b /* local-ts */); + + b->begin_list(b, "remote-ts"); + enumerator = child->create_ts_enumerator(child, FALSE); + while (enumerator->enumerate(enumerator, &ts)) + { + b->add_li(b, "%R", ts); + } + enumerator->destroy(enumerator); + b->end_list(b /* remote-ts */); +} + +/** + * List tasks in a specific queue + */ +static void list_task_queue(private_vici_query_t *this, vici_builder_t *b, + ike_sa_t *ike_sa, task_queue_t q, char *name) +{ + enumerator_t *enumerator; + bool has = FALSE; + task_t *task; + + enumerator = ike_sa->create_task_enumerator(ike_sa, q); + while (enumerator->enumerate(enumerator, &task)) + { + if (!has) + { + b->begin_list(b, name); + has = TRUE; + } + b->add_li(b, "%N", task_type_names, task->get_type(task)); + } + enumerator->destroy(enumerator); + if (has) + { + b->end_list(b); + } +} + +/** + * List details of an IKE_SA + */ +static void list_ike(private_vici_query_t *this, vici_builder_t *b, + ike_sa_t *ike_sa, time_t now) +{ + time_t t; + ike_sa_id_t *id; + identification_t *eap; + proposal_t *proposal; + u_int16_t alg, ks; + + b->add_kv(b, "uniqueid", "%u", ike_sa->get_unique_id(ike_sa)); + b->add_kv(b, "version", "%u", ike_sa->get_version(ike_sa)); + b->add_kv(b, "state", "%N", ike_sa_state_names, ike_sa->get_state(ike_sa)); + + b->add_kv(b, "local-host", "%H", ike_sa->get_my_host(ike_sa)); + b->add_kv(b, "local-id", "%Y", ike_sa->get_my_id(ike_sa)); + + b->add_kv(b, "remote-host", "%H", ike_sa->get_other_host(ike_sa)); + b->add_kv(b, "remote-id", "%Y", ike_sa->get_other_id(ike_sa)); + + eap = ike_sa->get_other_eap_id(ike_sa); + + if (!eap->equals(eap, ike_sa->get_other_id(ike_sa))) + { + if (ike_sa->get_version(ike_sa) == IKEV1) + { + b->add_kv(b, "remote-xauth-id", "%Y", eap); + } + else + { + b->add_kv(b, "remote-eap-id", "%Y", eap); + } + } + + id = ike_sa->get_id(ike_sa); + if (id->is_initiator(id)) + { + b->add_kv(b, "initiator", "yes"); + } + b->add_kv(b, "initiator-spi", "%.16"PRIx64, id->get_initiator_spi(id)); + b->add_kv(b, "responder-spi", "%.16"PRIx64, id->get_responder_spi(id)); + + proposal = ike_sa->get_proposal(ike_sa); + if (proposal) + { + if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &ks)) + { + b->add_kv(b, "encr-alg", "%N", encryption_algorithm_names, alg); + if (ks) + { + b->add_kv(b, "encr-keysize", "%u", ks); + } + } + if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, &ks)) + { + b->add_kv(b, "integ-alg", "%N", integrity_algorithm_names, alg); + if (ks) + { + b->add_kv(b, "integ-keysize", "%u", ks); + } + } + if (proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL)) + { + b->add_kv(b, "prf-alg", "%N", pseudo_random_function_names, alg); + } + if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &alg, NULL)) + { + b->add_kv(b, "dh-group", "%N", diffie_hellman_group_names, alg); + } + } + + if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED) + { + t = ike_sa->get_statistic(ike_sa, STAT_ESTABLISHED); + b->add_kv(b, "established", "%"PRId64, (int64_t)(now - t)); + t = ike_sa->get_statistic(ike_sa, STAT_REKEY); + if (t) + { + b->add_kv(b, "rekey-time", "%"PRId64, (int64_t)(t - now)); + } + t = ike_sa->get_statistic(ike_sa, STAT_REAUTH); + if (t) + { + b->add_kv(b, "reauth-time", "%"PRId64, (int64_t)(t - now)); + } + } + + list_task_queue(this, b, ike_sa, TASK_QUEUE_QUEUED, "tasks-queued"); + list_task_queue(this, b, ike_sa, TASK_QUEUE_ACTIVE, "tasks-active"); + list_task_queue(this, b, ike_sa, TASK_QUEUE_PASSIVE, "tasks-passive"); +} + +CALLBACK(list_sas, vici_message_t*, + private_vici_query_t *this, char *name, u_int id, vici_message_t *request) +{ + vici_builder_t *b; + enumerator_t *isas, *csas; + ike_sa_t *ike_sa; + child_sa_t *child_sa; + time_t now; + char *ike; + u_int ike_id; + bool bl; + + bl = request->get_str(request, NULL, "noblock") == NULL; + ike = request->get_str(request, NULL, "ike"); + ike_id = request->get_int(request, 0, "ike-id"); + + isas = charon->controller->create_ike_sa_enumerator(charon->controller, bl); + while (isas->enumerate(isas, &ike_sa)) + { + if (ike && !streq(ike, ike_sa->get_name(ike_sa))) + { + continue; + } + if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa)) + { + continue; + } + + now = time_monotonic(NULL); + + b = vici_builder_create(); + b->begin_section(b, ike_sa->get_name(ike_sa)); + + list_ike(this, b, ike_sa, now); + + b->begin_section(b, "child-sas"); + csas = ike_sa->create_child_sa_enumerator(ike_sa); + while (csas->enumerate(csas, &child_sa)) + { + b->begin_section(b, child_sa->get_name(child_sa)); + list_child(this, b, child_sa, now); + b->end_section(b); + } + csas->destroy(csas); + b->end_section(b /* child-sas */ ); + + b->end_section(b); + + this->dispatcher->raise_event(this->dispatcher, "list-sa", id, + b->finalize(b)); + } + isas->destroy(isas); + + b = vici_builder_create(); + return b->finalize(b); +} + +/** + * Raise a list-policy event for given CHILD_SA + */ +static void raise_policy(private_vici_query_t *this, u_int id, child_sa_t *child) +{ + enumerator_t *enumerator; + traffic_selector_t *ts; + vici_builder_t *b; + + b = vici_builder_create(); + b->begin_section(b, child->get_name(child)); + + b->add_kv(b, "mode", "%N", ipsec_mode_names, child->get_mode(child)); + + b->begin_list(b, "local-ts"); + enumerator = child->create_ts_enumerator(child, TRUE); + while (enumerator->enumerate(enumerator, &ts)) + { + b->add_li(b, "%R", ts); + } + enumerator->destroy(enumerator); + b->end_list(b /* local-ts */); + + b->begin_list(b, "remote-ts"); + enumerator = child->create_ts_enumerator(child, FALSE); + while (enumerator->enumerate(enumerator, &ts)) + { + b->add_li(b, "%R", ts); + } + enumerator->destroy(enumerator); + b->end_list(b /* remote-ts */); + + b->end_section(b); + + this->dispatcher->raise_event(this->dispatcher, "list-policy", id, + b->finalize(b)); +} + +/** + * Raise a list-policy event for given CHILD_SA config + */ +static void raise_policy_cfg(private_vici_query_t *this, u_int id, + child_cfg_t *cfg) +{ + enumerator_t *enumerator; + linked_list_t *list; + traffic_selector_t *ts; + vici_builder_t *b; + + b = vici_builder_create(); + b->begin_section(b, cfg->get_name(cfg)); + + b->add_kv(b, "mode", "%N", ipsec_mode_names, cfg->get_mode(cfg)); + + b->begin_list(b, "local-ts"); + list = cfg->get_traffic_selectors(cfg, TRUE, NULL, NULL); + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &ts)) + { + b->add_li(b, "%R", ts); + } + enumerator->destroy(enumerator); + list->destroy_offset(list, offsetof(traffic_selector_t, destroy)); + b->end_list(b /* local-ts */); + + b->begin_list(b, "remote-ts"); + list = cfg->get_traffic_selectors(cfg, FALSE, NULL, NULL); + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &ts)) + { + b->add_li(b, "%R", ts); + } + enumerator->destroy(enumerator); + list->destroy_offset(list, offsetof(traffic_selector_t, destroy)); + b->end_list(b /* remote-ts */); + + b->end_section(b); + + this->dispatcher->raise_event(this->dispatcher, "list-policy", id, + b->finalize(b)); +} + +CALLBACK(list_policies, vici_message_t*, + private_vici_query_t *this, char *name, u_int id, vici_message_t *request) +{ + enumerator_t *enumerator; + vici_builder_t *b; + child_sa_t *child_sa; + child_cfg_t *child_cfg; + bool drop, pass, trap; + char *child; + + drop = request->get_str(request, NULL, "drop") != NULL; + pass = request->get_str(request, NULL, "pass") != NULL; + trap = request->get_str(request, NULL, "trap") != NULL; + child = request->get_str(request, NULL, "child"); + + if (trap) + { + enumerator = charon->traps->create_enumerator(charon->traps); + while (enumerator->enumerate(enumerator, NULL, &child_sa)) + { + if (child && !streq(child, child_sa->get_name(child_sa))) + { + continue; + } + raise_policy(this, id, child_sa); + } + enumerator->destroy(enumerator); + } + + if (drop || pass) + { + enumerator = charon->shunts->create_enumerator(charon->shunts); + while (enumerator->enumerate(enumerator, &child_cfg)) + { + if (child && !streq(child, child_cfg->get_name(child_cfg))) + { + continue; + } + switch (child_cfg->get_mode(child_cfg)) + { + case MODE_DROP: + if (drop) + { + raise_policy_cfg(this, id, child_cfg); + } + break; + case MODE_PASS: + if (pass) + { + raise_policy_cfg(this, id, child_cfg); + } + break; + default: + break; + } + } + enumerator->destroy(enumerator); + } + + b = vici_builder_create(); + return b->finalize(b); +} + +/** + * Build sections for auth configs, local or remote + */ +static void build_auth_cfgs(peer_cfg_t *peer_cfg, bool local, vici_builder_t *b) +{ + enumerator_t *enumerator, *rules; + auth_rule_t rule; + auth_cfg_t *auth; + union { + uintptr_t u; + identification_t *id; + certificate_t *cert; + char *str; + } v; + + enumerator = peer_cfg->create_auth_cfg_enumerator(peer_cfg, local); + while (enumerator->enumerate(enumerator, &auth)) + { + b->begin_section(b, local ? "local" : "remote"); + + rules = auth->create_enumerator(auth); + while (rules->enumerate(rules, &rule, &v)) + { + switch (rule) + { + case AUTH_RULE_AUTH_CLASS: + b->add_kv(b, "class", "%N", auth_class_names, v.u); + break; + case AUTH_RULE_EAP_TYPE: + b->add_kv(b, "eap-type", "%N", eap_type_names, v.u); + break; + case AUTH_RULE_EAP_VENDOR: + b->add_kv(b, "eap-vendor", "%u", v.u); + break; + case AUTH_RULE_XAUTH_BACKEND: + b->add_kv(b, "xauth", "%s", v.str); + break; + case AUTH_RULE_CRL_VALIDATION: + b->add_kv(b, "revocation", "%N", cert_validation_names, v.u); + break; + case AUTH_RULE_IDENTITY: + b->add_kv(b, "id", "%Y", v.id); + break; + case AUTH_RULE_AAA_IDENTITY: + b->add_kv(b, "aaa_id", "%Y", v.id); + break; + case AUTH_RULE_EAP_IDENTITY: + b->add_kv(b, "eap_id", "%Y", v.id); + break; + case AUTH_RULE_XAUTH_IDENTITY: + b->add_kv(b, "xauth_id", "%Y", v.id); + break; + default: + break; + } + } + rules->destroy(rules); + + b->begin_list(b, "groups"); + rules = auth->create_enumerator(auth); + while (rules->enumerate(rules, &rule, &v)) + { + if (rule == AUTH_RULE_GROUP) + { + b->add_li(b, "%Y", v.id); + } + } + rules->destroy(rules); + b->end_list(b); + + b->begin_list(b, "certs"); + rules = auth->create_enumerator(auth); + while (rules->enumerate(rules, &rule, &v)) + { + if (rule == AUTH_RULE_SUBJECT_CERT) + { + b->add_li(b, "%Y", v.cert->get_subject(v.cert)); + } + } + rules->destroy(rules); + b->end_list(b); + + b->begin_list(b, "cacerts"); + rules = auth->create_enumerator(auth); + while (rules->enumerate(rules, &rule, &v)) + { + if (rule == AUTH_RULE_CA_CERT) + { + b->add_li(b, "%Y", v.cert->get_subject(v.cert)); + } + } + rules->destroy(rules); + b->end_list(b); + + b->end_section(b); + } + enumerator->destroy(enumerator); +} + +CALLBACK(list_conns, vici_message_t*, + private_vici_query_t *this, char *name, u_int id, vici_message_t *request) +{ + enumerator_t *enumerator, *tokens, *selectors, *children; + peer_cfg_t *peer_cfg; + ike_cfg_t *ike_cfg; + child_cfg_t *child_cfg; + char *ike, *str; + linked_list_t *list; + traffic_selector_t *ts; + vici_builder_t *b; + + ike = request->get_str(request, NULL, "ike"); + + enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends, + NULL, NULL, NULL, NULL, IKE_ANY); + while (enumerator->enumerate(enumerator, &peer_cfg)) + { + if (ike && !streq(ike, peer_cfg->get_name(peer_cfg))) + { + continue; + } + + b = vici_builder_create(); + b->begin_section(b, peer_cfg->get_name(peer_cfg)); + + ike_cfg = peer_cfg->get_ike_cfg(peer_cfg); + + b->begin_list(b, "local_addrs"); + str = ike_cfg->get_my_addr(ike_cfg); + tokens = enumerator_create_token(str, ",", " "); + while (tokens->enumerate(tokens, &str)) + { + b->add_li(b, "%s", str); + } + tokens->destroy(tokens); + b->end_list(b); + + b->begin_list(b, "remote_addrs"); + str = ike_cfg->get_other_addr(ike_cfg); + tokens = enumerator_create_token(str, ",", " "); + while (tokens->enumerate(tokens, &str)) + { + b->add_li(b, "%s", str); + } + tokens->destroy(tokens); + b->end_list(b); + + b->add_kv(b, "version", "%N", ike_version_names, + peer_cfg->get_ike_version(peer_cfg)); + + build_auth_cfgs(peer_cfg, TRUE, b); + build_auth_cfgs(peer_cfg, FALSE, b); + + b->begin_section(b, "children"); + + children = peer_cfg->create_child_cfg_enumerator(peer_cfg); + while (children->enumerate(children, &child_cfg)) + { + b->begin_section(b, child_cfg->get_name(child_cfg)); + + b->add_kv(b, "mode", "%N", ipsec_mode_names, + child_cfg->get_mode(child_cfg)); + + b->begin_list(b, "local-ts"); + list = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL); + selectors = list->create_enumerator(list); + while (selectors->enumerate(selectors, &ts)) + { + b->add_li(b, "%R", ts); + } + selectors->destroy(selectors); + list->destroy_offset(list, offsetof(traffic_selector_t, destroy)); + b->end_list(b /* local-ts */); + + b->begin_list(b, "remote-ts"); + list = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, NULL); + selectors = list->create_enumerator(list); + while (selectors->enumerate(selectors, &ts)) + { + b->add_li(b, "%R", ts); + } + selectors->destroy(selectors); + list->destroy_offset(list, offsetof(traffic_selector_t, destroy)); + b->end_list(b /* remote-ts */); + + b->end_section(b); + } + children->destroy(children); + + b->end_section(b); /* children */ + + b->end_section(b); /* name */ + + this->dispatcher->raise_event(this->dispatcher, "list-conn", id, + b->finalize(b)); + } + enumerator->destroy(enumerator); + + b = vici_builder_create(); + return b->finalize(b); +} + +/** + * Do we have a private key for given certificate + */ +static bool has_privkey(private_vici_query_t *this, certificate_t *cert) +{ + private_key_t *private; + public_key_t *public; + identification_t *keyid; + chunk_t chunk; + bool found = FALSE; + + public = cert->get_public_key(cert); + if (public) + { + if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk)) + { + keyid = identification_create_from_encoding(ID_KEY_ID, chunk); + private = lib->credmgr->get_private(lib->credmgr, + public->get_type(public), keyid, NULL); + if (private) + { + found = TRUE; + private->destroy(private); + } + keyid->destroy(keyid); + } + public->destroy(public); + } + return found; +} + +CALLBACK(list_certs, vici_message_t*, + private_vici_query_t *this, char *name, u_int id, vici_message_t *request) +{ + enumerator_t *enumerator, *added; + linked_list_t *list; + certificate_t *cert, *current; + chunk_t encoding; + identification_t *subject = NULL; + int type; + vici_builder_t *b; + bool found; + char *str; + + str = request->get_str(request, "ANY", "type"); + if (!enum_from_name(certificate_type_names, str, &type)) + { + b = vici_builder_create(); + return b->finalize(b); + } + str = request->get_str(request, NULL, "subject"); + if (str) + { + subject = identification_create_from_string(str); + } + + list = linked_list_create(); + enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, + type, KEY_ANY, subject, FALSE); + while (enumerator->enumerate(enumerator, &cert)) + { + found = FALSE; + added = list->create_enumerator(list); + while (added->enumerate(added, ¤t)) + { + if (current->equals(current, cert)) + { + found = TRUE; + break; + } + } + added->destroy(added); + + if (!found && cert->get_encoding(cert, CERT_ASN1_DER, &encoding)) + { + b = vici_builder_create(); + b->add_kv(b, "type", "%N", + certificate_type_names, cert->get_type(cert)); + if (has_privkey(this, cert)) + { + b->add_kv(b, "has_privkey", "yes"); + } + b->add(b, VICI_KEY_VALUE, "data", encoding); + free(encoding.ptr); + + this->dispatcher->raise_event(this->dispatcher, "list-cert", id, + b->finalize(b)); + list->insert_last(list, cert->get_ref(cert)); + } + } + enumerator->destroy(enumerator); + + list->destroy_offset(list, offsetof(certificate_t, destroy)); + DESTROY_IF(subject); + + b = vici_builder_create(); + return b->finalize(b); +} + +CALLBACK(version, vici_message_t*, + private_vici_query_t *this, char *name, u_int id, vici_message_t *request) +{ + vici_builder_t *b; + + b = vici_builder_create(); + + b->add_kv(b, "daemon", "%s", lib->ns); + b->add_kv(b, "version", "%s", VERSION); + +#ifdef WIN32 + { + OSVERSIONINFOEX osvie; + + memset(&osvie, 0, sizeof(osvie)); + osvie.dwOSVersionInfoSize = sizeof(osvie); + + if (GetVersionEx((LPOSVERSIONINFO)&osvie)) + { + b->add_kv(b, "sysname", "Windows %s", + osvie.wProductType == VER_NT_WORKSTATION ? "Client" : "Server"); + b->add_kv(b, "release", "%d.%d.%d (SP %d.%d)", + osvie.dwMajorVersion, osvie.dwMinorVersion, osvie.dwBuildNumber, + osvie.wServicePackMajor, osvie.wServicePackMinor); + b->add_kv(b, "machine", "%s", +#ifdef WIN64 + "x86_64"); +#else + "x86"); +#endif /* !WIN64 */ + } + } +#else /* !WIN32 */ + { + struct utsname utsname; + + if (uname(&utsname) == 0) + { + b->add_kv(b, "sysname", "%s", utsname.sysname); + b->add_kv(b, "release", "%s", utsname.release); + b->add_kv(b, "machine", "%s", utsname.machine); + } + } +#endif /* !WIN32 */ + return b->finalize(b); +} + +/** + * Callback function for memusage summary + */ +CALLBACK(sum_usage, void, + vici_builder_t *b, int count, size_t bytes, int whitelisted) +{ + b->begin_section(b, "mem"); + b->add_kv(b, "total", "%zu", bytes); + b->add_kv(b, "allocs", "%d", count); + b->end_section(b); +} + +CALLBACK(stats, vici_message_t*, + private_vici_query_t *this, char *name, u_int id, vici_message_t *request) +{ + vici_builder_t *b; + enumerator_t *enumerator; + plugin_t *plugin; + time_t since, now; + int i; + + b = vici_builder_create(); + + now = time_monotonic(NULL); + since = time(NULL) - (now - this->uptime); + + b->begin_section(b, "uptime"); + b->add_kv(b, "running", "%V", &now, &this->uptime); + b->add_kv(b, "since", "%T", &since, FALSE); + b->end_section(b); + + b->begin_section(b, "workers"); + b->add_kv(b, "total", "%d", + lib->processor->get_total_threads(lib->processor)); + b->add_kv(b, "idle", "%d", + lib->processor->get_idle_threads(lib->processor)); + b->begin_section(b, "active"); + for (i = 0; i < JOB_PRIO_MAX; i++) + { + b->add_kv(b, enum_to_name(job_priority_names, i), "%d", + lib->processor->get_working_threads(lib->processor, i)); + } + b->end_section(b); + b->end_section(b); + + b->begin_section(b, "queues"); + for (i = 0; i < JOB_PRIO_MAX; i++) + { + b->add_kv(b, enum_to_name(job_priority_names, i), "%d", + lib->processor->get_job_load(lib->processor, i)); + } + b->end_section(b); + + b->add_kv(b, "scheduled", "%d", + lib->scheduler->get_job_load(lib->scheduler)); + + b->begin_section(b, "ikesas"); + b->add_kv(b, "total", "%u", + charon->ike_sa_manager->get_count(charon->ike_sa_manager)); + b->add_kv(b, "half-open", "%u", + charon->ike_sa_manager->get_half_open_count(charon->ike_sa_manager, + NULL)); + b->end_section(b); + + b->begin_list(b, "plugins"); + enumerator = lib->plugins->create_plugin_enumerator(lib->plugins); + while (enumerator->enumerate(enumerator, &plugin, NULL)) + { + b->add_li(b, "%s", plugin->get_name(plugin)); + } + enumerator->destroy(enumerator); + b->end_list(b); + + if (lib->leak_detective) + { + lib->leak_detective->usage(lib->leak_detective, NULL, sum_usage, b); + } +#ifdef WIN32 + else + { + DWORD lasterr = ERROR_INVALID_HANDLE; + HANDLE heaps[32]; + int i, count; + char buf[16]; + size_t total = 0; + int allocs = 0; + + b->begin_section(b, "mem"); + count = GetProcessHeaps(countof(heaps), heaps); + for (i = 0; i < count; i++) + { + PROCESS_HEAP_ENTRY entry = {}; + size_t heap_total = 0; + int heap_allocs = 0; + + if (HeapLock(heaps[i])) + { + while (HeapWalk(heaps[i], &entry)) + { + if (entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) + { + heap_total += entry.cbData; + heap_allocs++; + } + } + lasterr = GetLastError(); + HeapUnlock(heaps[i]); + } + if (lasterr != ERROR_NO_MORE_ITEMS) + { + break; + } + snprintf(buf, sizeof(buf), "heap-%d", i); + b->begin_section(b, buf); + b->add_kv(b, "total", "%zu", heap_total); + b->add_kv(b, "allocs", "%d", heap_allocs); + b->end_section(b); + + total += heap_total; + allocs += heap_allocs; + } + if (lasterr == ERROR_NO_MORE_ITEMS) + { + b->add_kv(b, "total", "%zu", total); + b->add_kv(b, "allocs", "%d", allocs); + } + b->end_section(b); + } +#endif + +#ifdef HAVE_MALLINFO + { + struct mallinfo mi = mallinfo(); + + b->begin_section(b, "mallinfo"); + b->add_kv(b, "sbrk", "%d", mi.arena); + b->add_kv(b, "mmap", "%d", mi.hblkhd); + b->add_kv(b, "used", "%d", mi.uordblks); + b->add_kv(b, "free", "%d", mi.fordblks); + b->end_section(b); + } +#endif /* HAVE_MALLINFO */ + + return b->finalize(b); +} + +static void manage_command(private_vici_query_t *this, + char *name, vici_command_cb_t cb, bool reg) +{ + this->dispatcher->manage_command(this->dispatcher, name, + reg ? cb : NULL, this); +} + +/** + * (Un-)register dispatcher functions + */ +static void manage_commands(private_vici_query_t *this, bool reg) +{ + this->dispatcher->manage_event(this->dispatcher, "list-sa", reg); + this->dispatcher->manage_event(this->dispatcher, "list-policy", reg); + this->dispatcher->manage_event(this->dispatcher, "list-conn", reg); + this->dispatcher->manage_event(this->dispatcher, "list-cert", reg); + manage_command(this, "list-sas", list_sas, reg); + manage_command(this, "list-policies", list_policies, reg); + manage_command(this, "list-conns", list_conns, reg); + manage_command(this, "list-certs", list_certs, reg); + manage_command(this, "version", version, reg); + manage_command(this, "stats", stats, reg); +} + +METHOD(vici_query_t, destroy, void, + private_vici_query_t *this) +{ + manage_commands(this, FALSE); + free(this); +} + +/** + * See header + */ +vici_query_t *vici_query_create(vici_dispatcher_t *dispatcher) +{ + private_vici_query_t *this; + + INIT(this, + .public = { + .destroy = _destroy, + }, + .dispatcher = dispatcher, + .uptime = time_monotonic(NULL), + ); + + manage_commands(this, TRUE); + + return &this->public; +} diff --git a/src/libcharon/plugins/vici/vici_query.h b/src/libcharon/plugins/vici/vici_query.h new file mode 100644 index 000000000..da72b1411 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_query.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup vici_query vici_query + * @{ @ingroup vici + */ + +#include "vici_dispatcher.h" + +#ifndef VICI_QUERY_H_ +#define VICI_QUERY_H_ + +typedef struct vici_query_t vici_query_t; + +/** + * Query helper, provides various commands to query/list daemon info. + */ +struct vici_query_t { + + /** + * Destroy a vici_query_t. + */ + void (*destroy)(vici_query_t *this); +}; + +/** + * Create a vici_query instance. + * + * @param dispatcher dispatcher to receive requests from + * @return query handler + */ +vici_query_t *vici_query_create(vici_dispatcher_t *dispatcher); + +#endif /** VICI_QUERY_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_socket.c b/src/libcharon/plugins/vici/vici_socket.c new file mode 100644 index 000000000..916772871 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_socket.c @@ -0,0 +1,679 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "vici_socket.h" + +#include <threading/mutex.h> +#include <threading/condvar.h> +#include <threading/thread.h> +#include <collections/array.h> +#include <collections/linked_list.h> +#include <processing/jobs/callback_job.h> + +#include <errno.h> +#include <string.h> + +typedef struct private_vici_socket_t private_vici_socket_t; + +/** + * Private members of vici_socket_t + */ +struct private_vici_socket_t { + + /** + * public functions + */ + vici_socket_t public; + + /** + * Inbound message callback + */ + vici_inbound_cb_t inbound; + + /** + * Client connect callback + */ + vici_connect_cb_t connect; + + /** + * Client disconnect callback + */ + vici_disconnect_cb_t disconnect; + + /** + * Next client connection identifier + */ + u_int nextid; + + /** + * User data for callbacks + */ + void *user; + + /** + * Service accepting vici connections + */ + stream_service_t *service; + + /** + * Client connections, as entry_t + */ + linked_list_t *connections; + + /** + * mutex for client connections + */ + mutex_t *mutex; +}; + +/** + * Data to securely reference an entry + */ +typedef struct { + /* reference to socket instance */ + private_vici_socket_t *this; + /** connection identifier of entry */ + u_int id; +} entry_selector_t; + +/** + * Partially processed message + */ +typedef struct { + /** bytes of length header sent/received */ + u_char hdrlen; + /** bytes of length header */ + char hdr[sizeof(u_int32_t)]; + /** send/receive buffer on heap */ + chunk_t buf; + /** bytes sent/received in buffer */ + u_int32_t done; +} msg_buf_t; + +/** + * Client connection entry + */ +typedef struct { + /** reference to socket */ + private_vici_socket_t *this; + /** associated stream */ + stream_t *stream; + /** queued messages to send, as msg_buf_t pointers */ + array_t *out; + /** input message buffer */ + msg_buf_t in; + /** queued input messages to process, as chunk_t */ + array_t *queue; + /** do we have job processing input queue? */ + bool has_processor; + /** client connection identifier */ + u_int id; + /** any users reading over this connection? */ + int readers; + /** any users writing over this connection? */ + int writers; + /** condvar to wait for usage */ + condvar_t *cond; +} entry_t; + +/** + * Destroy an connection entry + */ +CALLBACK(destroy_entry, void, + entry_t *entry) +{ + msg_buf_t *out; + chunk_t chunk; + + entry->stream->destroy(entry->stream); + entry->this->disconnect(entry->this->user, entry->id); + entry->cond->destroy(entry->cond); + + while (array_remove(entry->out, ARRAY_TAIL, &out)) + { + chunk_clear(&out->buf); + free(out); + } + array_destroy(entry->out); + while (array_remove(entry->queue, ARRAY_TAIL, &chunk)) + { + chunk_clear(&chunk); + } + array_destroy(entry->queue); + chunk_clear(&entry->in.buf); + free(entry); +} + +/** + * Find entry by stream (if given) or id, claim use + */ +static entry_t* find_entry(private_vici_socket_t *this, stream_t *stream, + u_int id, bool reader, bool writer) +{ + enumerator_t *enumerator; + entry_t *entry, *found = NULL; + bool candidate = TRUE; + + this->mutex->lock(this->mutex); + while (candidate && !found) + { + candidate = FALSE; + enumerator = this->connections->create_enumerator(this->connections); + while (enumerator->enumerate(enumerator, &entry)) + { + if (stream) + { + if (entry->stream != stream) + { + continue; + } + } + else + { + if (entry->id != id) + { + continue; + } + } + candidate = TRUE; + + if ((reader && entry->readers) || + (writer && entry->writers)) + { + entry->cond->wait(entry->cond, this->mutex); + break; + } + if (reader) + { + entry->readers++; + } + if (writer) + { + entry->writers++; + } + found = entry; + break; + } + enumerator->destroy(enumerator); + } + this->mutex->unlock(this->mutex); + + return found; +} + +/** + * Remove entry by id, claim use + */ +static entry_t* remove_entry(private_vici_socket_t *this, u_int id) +{ + enumerator_t *enumerator; + entry_t *entry, *found = NULL; + bool candidate = TRUE; + + this->mutex->lock(this->mutex); + while (candidate && !found) + { + candidate = FALSE; + enumerator = this->connections->create_enumerator(this->connections); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->id == id) + { + candidate = TRUE; + if (entry->readers || entry->writers) + { + entry->cond->wait(entry->cond, this->mutex); + break; + } + this->connections->remove_at(this->connections, enumerator); + found = entry; + break; + } + } + enumerator->destroy(enumerator); + } + this->mutex->unlock(this->mutex); + + return found; +} + +/** + * Release a claimed entry + */ +static void put_entry(private_vici_socket_t *this, entry_t *entry, + bool reader, bool writer) +{ + this->mutex->lock(this->mutex); + if (reader) + { + entry->readers--; + } + if (writer) + { + entry->writers--; + } + entry->cond->signal(entry->cond); + this->mutex->unlock(this->mutex); +} + +/** + * Asynchronous callback to disconnect client + */ +CALLBACK(disconnect_async, job_requeue_t, + entry_selector_t *sel) +{ + entry_t *entry; + + entry = remove_entry(sel->this, sel->id); + if (entry) + { + destroy_entry(entry); + } + return JOB_REQUEUE_NONE; +} + +/** + * Disconnect a connected client + */ +static void disconnect(private_vici_socket_t *this, u_int id) +{ + entry_selector_t *sel; + + INIT(sel, + .this = this, + .id = id, + ); + + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create(disconnect_async, sel, free, NULL)); +} + +/** + * Write queued output data + */ +static bool do_write(private_vici_socket_t *this, entry_t *entry, + stream_t *stream) +{ + msg_buf_t *out; + ssize_t len; + + while (array_get(entry->out, ARRAY_HEAD, &out)) + { + /* write header */ + while (out->hdrlen < sizeof(out->hdr)) + { + len = stream->write(stream, out->hdr + out->hdrlen, + sizeof(out->hdr) - out->hdrlen, FALSE); + if (len == 0) + { + return FALSE; + } + if (len < 0) + { + if (errno == EWOULDBLOCK) + { + return TRUE; + } + DBG1(DBG_CFG, "vici header write error: %s", strerror(errno)); + return FALSE; + } + out->hdrlen += len; + } + + /* write buffer buffer */ + while (out->buf.len > out->done) + { + len = stream->write(stream, out->buf.ptr + out->done, + out->buf.len - out->done, FALSE); + if (len == 0) + { + DBG1(DBG_CFG, "premature vici disconnect"); + return FALSE; + } + if (len < 0) + { + if (errno == EWOULDBLOCK) + { + return TRUE; + } + DBG1(DBG_CFG, "vici write error: %s", strerror(errno)); + return FALSE; + } + out->done += len; + } + + if (array_remove(entry->out, ARRAY_HEAD, &out)) + { + chunk_clear(&out->buf); + free(out); + } + } + return TRUE; +} + +/** + * Send pending messages + */ +CALLBACK(on_write, bool, + private_vici_socket_t *this, stream_t *stream) +{ + entry_t *entry; + bool ret = FALSE; + + entry = find_entry(this, stream, 0, FALSE, TRUE); + if (entry) + { + ret = do_write(this, entry, stream); + if (ret) + { + /* unregister if we have no more messages to send */ + ret = array_count(entry->out) != 0; + } + else + { + disconnect(entry->this, entry->id); + } + put_entry(this, entry, FALSE, TRUE); + } + + return ret; +} + +/** + * Read in available header with data, non-blocking cumulating to buffer + */ +static bool do_read(private_vici_socket_t *this, entry_t *entry, + stream_t *stream) +{ + u_int32_t msglen; + ssize_t len; + + /* assemble the length header first */ + while (entry->in.hdrlen < sizeof(entry->in.hdr)) + { + len = stream->read(stream, entry->in.hdr + entry->in.hdrlen, + sizeof(entry->in.hdr) - entry->in.hdrlen, FALSE); + if (len == 0) + { + return FALSE; + } + if (len < 0) + { + if (errno == EWOULDBLOCK) + { + return TRUE; + } + DBG1(DBG_CFG, "vici header read error: %s", strerror(errno)); + return FALSE; + } + entry->in.hdrlen += len; + if (entry->in.hdrlen == sizeof(entry->in.hdr)) + { + msglen = untoh32(entry->in.hdr); + if (msglen > VICI_MESSAGE_SIZE_MAX) + { + DBG1(DBG_CFG, "vici message length %u exceeds %u bytes limit, " + "ignored", msglen, VICI_MESSAGE_SIZE_MAX); + return FALSE; + } + /* header complete, continue with data */ + entry->in.buf = chunk_alloc(msglen); + } + } + + /* assemble buffer */ + while (entry->in.buf.len > entry->in.done) + { + len = stream->read(stream, entry->in.buf.ptr + entry->in.done, + entry->in.buf.len - entry->in.done, FALSE); + if (len == 0) + { + DBG1(DBG_CFG, "premature vici disconnect"); + return FALSE; + } + if (len < 0) + { + if (errno == EWOULDBLOCK) + { + return TRUE; + } + DBG1(DBG_CFG, "vici read error: %s", strerror(errno)); + return FALSE; + } + entry->in.done += len; + } + + return TRUE; +} + +/** + * Callback processing incoming requestes in strict order + */ +CALLBACK(process_queue, job_requeue_t, + entry_selector_t *sel) +{ + entry_t *entry; + chunk_t chunk; + bool found; + u_int id; + + while (TRUE) + { + entry = find_entry(sel->this, NULL, sel->id, TRUE, FALSE); + if (!entry) + { + break; + } + + found = array_remove(entry->queue, ARRAY_HEAD, &chunk); + if (!found) + { + entry->has_processor = FALSE; + } + id = entry->id; + put_entry(sel->this, entry, TRUE, FALSE); + if (!found) + { + break; + } + + thread_cleanup_push(free, chunk.ptr); + sel->this->inbound(sel->this->user, id, chunk); + thread_cleanup_pop(TRUE); + } + return JOB_REQUEUE_NONE; +} + +/** + * Process incoming messages + */ +CALLBACK(on_read, bool, + private_vici_socket_t *this, stream_t *stream) +{ + entry_selector_t *sel; + entry_t *entry; + bool ret = FALSE; + + entry = find_entry(this, stream, 0, TRUE, FALSE); + if (entry) + { + ret = do_read(this, entry, stream); + if (!ret) + { + disconnect(this, entry->id); + } + else if (entry->in.hdrlen == sizeof(entry->in.hdr) && + entry->in.buf.len == entry->in.done) + { + array_insert(entry->queue, ARRAY_TAIL, &entry->in.buf); + entry->in.buf = chunk_empty; + entry->in.hdrlen = entry->in.done = 0; + + if (!entry->has_processor) + { + INIT(sel, + .this = this, + .id = entry->id, + ); + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create(process_queue, + sel, free, NULL)); + entry->has_processor = TRUE; + } + } + put_entry(this, entry, TRUE, FALSE); + } + + return ret; +} + +/** + * Process connection request + */ +CALLBACK(on_accept, bool, + private_vici_socket_t *this, stream_t *stream) +{ + entry_t *entry; + u_int id; + + id = ref_get(&this->nextid); + + INIT(entry, + .this = this, + .stream = stream, + .id = id, + .out = array_create(0, 0), + .queue = array_create(sizeof(chunk_t), 0), + .cond = condvar_create(CONDVAR_TYPE_DEFAULT), + .readers = 1, + ); + + this->mutex->lock(this->mutex); + this->connections->insert_last(this->connections, entry); + this->mutex->unlock(this->mutex); + + stream->on_read(stream, on_read, this); + + put_entry(this, entry, TRUE, FALSE); + + this->connect(this->user, id); + + return TRUE; +} + +/** + * Async callback to enable writer + */ +CALLBACK(enable_writer, job_requeue_t, + entry_selector_t *sel) +{ + entry_t *entry; + + entry = find_entry(sel->this, NULL, sel->id, FALSE, TRUE); + if (entry) + { + entry->stream->on_write(entry->stream, on_write, sel->this); + put_entry(sel->this, entry, FALSE, TRUE); + } + return JOB_REQUEUE_NONE; +} + +METHOD(vici_socket_t, send_, void, + private_vici_socket_t *this, u_int id, chunk_t msg) +{ + if (msg.len <= VICI_MESSAGE_SIZE_MAX) + { + entry_selector_t *sel; + msg_buf_t *out; + entry_t *entry; + + entry = find_entry(this, NULL, id, FALSE, TRUE); + if (entry) + { + INIT(out, + .buf = msg, + ); + htoun32(out->hdr, msg.len); + + array_insert(entry->out, ARRAY_TAIL, out); + if (array_count(entry->out) == 1) + { /* asynchronously re-enable on_write callback when we get data */ + INIT(sel, + .this = this, + .id = entry->id, + ); + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create(enable_writer, + sel, free, NULL)); + } + put_entry(this, entry, FALSE, TRUE); + } + else + { + DBG1(DBG_CFG, "vici connection %u unknown", id); + chunk_clear(&msg); + } + } + else + { + DBG1(DBG_CFG, "vici message size %zu exceeds maximum size of %u, " + "discarded", msg.len, VICI_MESSAGE_SIZE_MAX); + chunk_clear(&msg); + } +} + +METHOD(vici_socket_t, destroy, void, + private_vici_socket_t *this) +{ + DESTROY_IF(this->service); + this->connections->destroy_function(this->connections, destroy_entry); + this->mutex->destroy(this->mutex); + free(this); +} + +/* + * see header file + */ +vici_socket_t *vici_socket_create(char *uri, vici_inbound_cb_t inbound, + vici_connect_cb_t connect, + vici_disconnect_cb_t disconnect, void *user) +{ + private_vici_socket_t *this; + + INIT(this, + .public = { + .send = _send_, + .destroy = _destroy, + }, + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .connections = linked_list_create(), + .inbound = inbound, + .connect = connect, + .disconnect = disconnect, + .user = user, + ); + + this->service = lib->streams->create_service(lib->streams, uri, 3); + if (!this->service) + { + DBG1(DBG_CFG, "creating vici socket failed"); + destroy(this); + return NULL; + } + this->service->on_accept(this->service, on_accept, this, + JOB_PRIO_CRITICAL, 0); + + return &this->public; +} diff --git a/src/libcharon/plugins/vici/vici_socket.h b/src/libcharon/plugins/vici/vici_socket.h new file mode 100644 index 000000000..872783665 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_socket.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup vici_socket vici_socket + * @{ @ingroup vici + */ + +#ifndef VICI_SOCKET_H_ +#define VICI_SOCKET_H_ + +#include <library.h> + +/** + * Maximum size of a single message exchanged. + */ +#define VICI_MESSAGE_SIZE_MAX (512 * 1024) + +typedef struct vici_socket_t vici_socket_t; + +/** + * Callback function for dispatching inbound client messages. + * + * @param user user data, as passed during registration + * @param id unique client connection identifier + * @param data incoming message data + */ +typedef void (*vici_inbound_cb_t)(void *user, u_int id, chunk_t data); + +/** + * Callback function invoked when new clients connect + * + * @param user user data, as passed during registration + * @param id unique client connection identifier + * @return client connection context + */ +typedef void (*vici_connect_cb_t)(void *user, u_int id); + +/** + * Callback function invoked when connected clients disconnect + * + * @param user user data, as passed during registration + * @param id unique client connection identifier + */ +typedef void (*vici_disconnect_cb_t)(void *user, u_int id); + +/** + * Vici socket, low level socket input/output handling. + * + * On the socket, we pass raw chunks having a 2 byte network order length + * prefix. The length field does not count the length header itself, and + * is not included in the data passed over this interface. + */ +struct vici_socket_t { + + /** + * Send a message to a client identified by connection identifier. + * + * @param id unique client connection identifier + * @param data data to send to client, gets owned + */ + void (*send)(vici_socket_t *this, u_int id, chunk_t data); + + /** + * Destroy socket. + */ + void (*destroy)(vici_socket_t *this); +}; + +/** + * Create a vici_socket instance. + * + * @param uri socket URI to listen on + * @param inbound inbound message callback + * @param connect connect callback + * @param disconnect disconnect callback + * @param user user data to pass to callbacks + */ +vici_socket_t *vici_socket_create(char *uri, vici_inbound_cb_t inbound, + vici_connect_cb_t connect, + vici_disconnect_cb_t disconnect, void *user); + +#endif /** VICI_SOCKET_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_tests.c b/src/libcharon/plugins/vici/vici_tests.c new file mode 100644 index 000000000..434aa5e18 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_tests.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <test_runner.h> + +#include <daemon.h> +#include <hydra.h> + +/* declare test suite constructors */ +#define TEST_SUITE(x) test_suite_t* x(); +#include "vici_tests.h" +#undef TEST_SUITE + +static test_configuration_t tests[] = { +#define TEST_SUITE(x) \ + { .suite = x, }, +#include "vici_tests.h" + { .suite = NULL, } +}; + +static bool test_runner_init(bool init) +{ + if (!init) + { + lib->processor->set_threads(lib->processor, 0); + lib->processor->cancel(lib->processor); + } + return TRUE; +} + +int main(int argc, char *argv[]) +{ + return test_runner_run("vici", tests, test_runner_init); +} diff --git a/src/libcharon/plugins/vici/vici_tests.h b/src/libcharon/plugins/vici/vici_tests.h new file mode 100644 index 000000000..3e8f170e4 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_tests.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +TEST_SUITE(socket_suite_create) +TEST_SUITE(message_suite_create) +TEST_SUITE(request_suite_create) +TEST_SUITE(event_suite_create) |