summaryrefslogtreecommitdiff
path: root/src/libcharon/plugins/vici
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/plugins/vici')
-rw-r--r--src/libcharon/plugins/vici/Makefile.am69
-rw-r--r--src/libcharon/plugins/vici/Makefile.in1183
-rw-r--r--src/libcharon/plugins/vici/README.md176
-rw-r--r--src/libcharon/plugins/vici/libvici.c764
-rw-r--r--src/libcharon/plugins/vici/libvici.h459
-rw-r--r--src/libcharon/plugins/vici/suites/test_event.c224
-rw-r--r--src/libcharon/plugins/vici/suites/test_message.c407
-rw-r--r--src/libcharon/plugins/vici/suites/test_request.c247
-rw-r--r--src/libcharon/plugins/vici/suites/test_socket.c133
-rw-r--r--src/libcharon/plugins/vici/vici_attribute.c713
-rw-r--r--src/libcharon/plugins/vici/vici_attribute.h54
-rw-r--r--src/libcharon/plugins/vici/vici_builder.c253
-rw-r--r--src/libcharon/plugins/vici/vici_builder.h129
-rw-r--r--src/libcharon/plugins/vici/vici_config.c2006
-rw-r--r--src/libcharon/plugins/vici/vici_config.h53
-rw-r--r--src/libcharon/plugins/vici/vici_control.c496
-rw-r--r--src/libcharon/plugins/vici/vici_control.h47
-rw-r--r--src/libcharon/plugins/vici/vici_cred.c330
-rw-r--r--src/libcharon/plugins/vici/vici_cred.h47
-rw-r--r--src/libcharon/plugins/vici/vici_dispatcher.c524
-rw-r--r--src/libcharon/plugins/vici/vici_dispatcher.h122
-rw-r--r--src/libcharon/plugins/vici/vici_logger.c130
-rw-r--r--src/libcharon/plugins/vici/vici_logger.h54
-rw-r--r--src/libcharon/plugins/vici/vici_message.c727
-rw-r--r--src/libcharon/plugins/vici/vici_message.h248
-rw-r--r--src/libcharon/plugins/vici/vici_plugin.c169
-rw-r--r--src/libcharon/plugins/vici/vici_plugin.h42
-rw-r--r--src/libcharon/plugins/vici/vici_query.c1039
-rw-r--r--src/libcharon/plugins/vici/vici_query.h47
-rw-r--r--src/libcharon/plugins/vici/vici_socket.c679
-rw-r--r--src/libcharon/plugins/vici/vici_socket.h95
-rw-r--r--src/libcharon/plugins/vici/vici_tests.c46
-rw-r--r--src/libcharon/plugins/vici/vici_tests.h19
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=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ 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, &current))
+ {
+ 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, &current))
+ {
+ 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, &current))
+ {
+ 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, &current);
+ }
+ 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, &current);
+ }
+ 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, &current))
+ {
+ 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, &current))
+ {
+ 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, &current))
+ {
+ 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, &current))
+ {
+ 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, &current))
+ {
+ 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, &current))
+ {
+ 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)