aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libcharon/Makefile.am7
-rw-r--r--src/libcharon/plugins/vici/.gitignore1
-rw-r--r--src/libcharon/plugins/vici/Makefile.am69
-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.h455
-rw-r--r--src/libcharon/plugins/vici/suites/test_event.c220
-rw-r--r--src/libcharon/plugins/vici/suites/test_message.c407
-rw-r--r--src/libcharon/plugins/vici/suites/test_request.c243
-rw-r--r--src/libcharon/plugins/vici/suites/test_socket.c131
-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.c1963
-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.h118
-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.c695
-rw-r--r--src/libcharon/plugins/vici/vici_message.h244
-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.c855
-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
-rw-r--r--src/libstrongswan/credentials/sets/mem_cred.c76
-rw-r--r--src/libstrongswan/processing/processor.c12
-rw-r--r--src/libstrongswan/processing/watcher.c18
-rw-r--r--src/libstrongswan/utils/utils.h13
38 files changed, 10388 insertions, 6 deletions
diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am
index df58eaa10..3e7a96103 100644
--- a/src/libcharon/Makefile.am
+++ b/src/libcharon/Makefile.am
@@ -202,6 +202,13 @@ if MONOLITHIC
endif
endif
+if USE_VICI
+ SUBDIRS += plugins/vici
+if MONOLITHIC
+ libcharon_la_LIBADD += plugins/vici/libstrongswan-vici.la
+endif
+endif
+
if USE_SMP
SUBDIRS += plugins/smp
if MONOLITHIC
diff --git a/src/libcharon/plugins/vici/.gitignore b/src/libcharon/plugins/vici/.gitignore
new file mode 100644
index 000000000..a78cc13cf
--- /dev/null
+++ b/src/libcharon/plugins/vici/.gitignore
@@ -0,0 +1 @@
+vici_tests
diff --git a/src/libcharon/plugins/vici/Makefile.am b/src/libcharon/plugins/vici/Makefile.am
new file mode 100644
index 000000000..162827a73
--- /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 = \
+ -rdynamic
+
+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
+
+
+lib_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/README.md b/src/libcharon/plugins/vici/README.md
new file mode 100644
index 000000000..46fa5b528
--- /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 continous 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..6e3b85a72
--- /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_FAILED,
+ 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_FAILED);
+ 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_FAILED:
+ 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, FILE *out)
+{
+ if (res->message->dump(res->message, label, 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_FAILED:
+ 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..6957a00b6
--- /dev/null
+++ b/src/libcharon/plugins/vici/libvici.h
@@ -0,0 +1,455 @@
+/*
+ * 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>
+
+/**
+ * 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 out FILE to dump to
+ * @return 0 if dumped complete message, 1 on error
+ */
+int vici_dump(vici_res_t *res, char *label, 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 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..12fd03107
--- /dev/null
+++ b/src/libcharon/plugins/vici/suites/test_event.c
@@ -0,0 +1,220 @@
+/*
+ * 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>
+
+#define URI "unix:///tmp/strongswan-vici-event-test"
+
+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..8cb11a7ea
--- /dev/null
+++ b/src/libcharon/plugins/vici/suites/test_request.c
@@ -0,0 +1,243 @@
+/*
+ * 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>
+
+#define URI "unix:///tmp/strongswan-vici-request-test"
+
+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..032445bb0
--- /dev/null
+++ b/src/libcharon/plugins/vici/suites/test_socket.c
@@ -0,0 +1,131 @@
+/*
+ * 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 },
+ { "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 },
+};
+
+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..193859385
--- /dev/null
+++ b/src/libcharon/plugins/vici/vici_config.c
@@ -0,0 +1,1963 @@
+/*
+ * 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>
+#include <netdb.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)
+
+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;
+ action_t dpd_action;
+ action_t start_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);
+ DBG2(DBG_CFG, " dpd_action = %N", action_names, data->dpd_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);
+ }
+ else
+ {
+ proposal = proposal_create_from_string(proto, buf);
+ }
+ if (!proposal)
+ {
+ return FALSE;
+ }
+ list->insert_last(list, proposal);
+ return TRUE;
+}
+
+/**
+ * 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 },
+ { "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 },
+ { "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,
+ .dpd_action = ACTION_NONE,
+ .start_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)
+ {
+ child.proposals->insert_last(child.proposals,
+ proposal_create_default(PROTO_ESP));
+ }
+
+ /* 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, ACTION_NONE, child.ipcomp,
+ child.inactivity, child.reqid, &child.mark_in,
+ &child.mark_out, child.tfc);
+
+ 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)
+ {
+ peer.proposals->insert_last(peer.proposals,
+ proposal_create_default(PROTO_IKE));
+ }
+ 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..effe5a670
--- /dev/null
+++ b/src/libcharon/plugins/vici/vici_dispatcher.h
@@ -0,0 +1,118 @@
+/*
+ * 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
+ */
+#define VICI_DEFAULT_URI "unix://" IPSEC_PIDDIR "/charon.vici"
+
+/**
+ * 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..d3c1b23e2
--- /dev/null
+++ b/src/libcharon/plugins/vici/vici_message.c
@@ -0,0 +1,695 @@
+/*
+ * 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_SECTION_START, VICI_END,
+ "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_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, FILE *out)
+{
+ enumerator_t *enumerator;
+ int ident = 0, delta = 2;
+ vici_type_t type;
+ char *name;
+ chunk_t value;
+
+ fprintf(out, "%s {\n", label);
+ ident += delta;
+
+ enumerator = create_enumerator(this);
+ while (enumerator->enumerate(enumerator, &type, &name, &value))
+ {
+ switch (type)
+ {
+ case VICI_SECTION_START:
+ fprintf(out, "%*s%s {\n", ident, "", name);
+ ident += delta;
+ break;
+ case VICI_SECTION_END:
+ ident -= delta;
+ fprintf(out, "%*s}\n", ident, "");
+ break;
+ case VICI_KEY_VALUE:
+ if (chunk_printable(value, NULL, ' '))
+ {
+ fprintf(out, "%*s%s = %.*s\n",
+ ident, "", name, (int)value.len, value.ptr);
+ }
+ else
+ {
+ fprintf(out, "%*s%s = 0x%+#B\n",
+ ident, "", name, &value);
+ }
+ break;
+ case VICI_LIST_START:
+ fprintf(out, "%*s%s = [\n", ident, "", name);
+ ident += delta;
+ break;
+ case VICI_LIST_END:
+ ident -= delta;
+ fprintf(out, "%*s]\n", ident, "");
+ break;
+ case VICI_LIST_ITEM:
+ if (chunk_printable(value, NULL, ' '))
+ {
+ fprintf(out, "%*s%.*s\n",
+ ident, "", (int)value.len, value.ptr);
+ }
+ else
+ {
+ fprintf(out, "%*s 0x%+#B\n", ident, "", &value);
+ }
+ break;
+ case VICI_END:
+ fprintf(out, "}\n");
+ enumerator->destroy(enumerator);
+ return TRUE;
+ }
+ }
+ 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..8817959c1
--- /dev/null
+++ b/src/libcharon/plugins/vici/vici_message.h
@@ -0,0 +1,244 @@
+/*
+ * 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 {
+ /** begin of new section, argument is section name as char* */
+ VICI_SECTION_START = 0,
+ /** end of current section, no arguments */
+ VICI_SECTION_END,
+ /** key/value, arguments are key as char*, value as chunk_t */
+ VICI_KEY_VALUE,
+ /** list start, argument is list name as char* */
+ VICI_LIST_START,
+ /** list item, argument is item value as chunk_t */
+ VICI_LIST_ITEM,
+ /** end of list, no arguments */
+ VICI_LIST_END,
+
+ /** end of argument list, no arguments (never encoded) */
+ VICI_END
+};
+
+/**
+ * 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 out FILE stream to dump to
+ * @return TRUE if message valid
+ */
+ bool (*dump)(vici_message_t *this, char *label, 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 occurence 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..f89ae01a2
--- /dev/null
+++ b/src/libcharon/plugins/vici/vici_query.c
@@ -0,0 +1,855 @@
+/*
+ * 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 <sys/utsname.h>
+
+#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;
+};
+
+/**
+ * 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");
+ type = enum_from_name(certificate_type_names, str);
+ if (type == -1)
+ {
+ 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)
+{
+ struct utsname utsname;
+ vici_builder_t *b;
+
+ b = vici_builder_create();
+
+ b->add_kv(b, "daemon", "%s", lib->ns);
+ b->add_kv(b, "version", "%s", VERSION);
+
+ 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);
+ }
+
+ 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);
+}
+
+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,
+ );
+
+ 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)
diff --git a/src/libstrongswan/credentials/sets/mem_cred.c b/src/libstrongswan/credentials/sets/mem_cred.c
index b8da3f620..d8f568d36 100644
--- a/src/libstrongswan/credentials/sets/mem_cred.c
+++ b/src/libstrongswan/credentials/sets/mem_cred.c
@@ -307,8 +307,25 @@ METHOD(credential_set_t, create_private_enumerator, enumerator_t*,
METHOD(mem_cred_t, add_key, void,
private_mem_cred_t *this, private_key_t *key)
{
+ enumerator_t *enumerator;
+ private_key_t *current;
+
this->lock->write_lock(this->lock);
+
+ enumerator = this->keys->create_enumerator(this->keys);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ if (current->equals(current, key))
+ {
+ this->keys->remove_at(this->keys, enumerator);
+ current->destroy(current);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
this->keys->insert_first(this->keys, key);
+
this->lock->unlock(this->lock);
}
@@ -334,6 +351,44 @@ static void shared_entry_destroy(shared_entry_t *entry)
}
/**
+ * Check if two shared key entries equal
+ */
+static bool shared_entry_equals(shared_entry_t *a, shared_entry_t *b)
+{
+ enumerator_t *e1, *e2;
+ identification_t *id1, *id2;
+ bool equals = TRUE;
+
+ if (a->shared->get_type(a->shared) != b->shared->get_type(b->shared))
+ {
+ return FALSE;
+ }
+ if (!chunk_equals(a->shared->get_key(a->shared),
+ b->shared->get_key(b->shared)))
+ {
+ return FALSE;
+ }
+ if (a->owners->get_count(a->owners) != b->owners->get_count(b->owners))
+ {
+ return FALSE;
+ }
+ e1 = a->owners->create_enumerator(a->owners);
+ e2 = b->owners->create_enumerator(b->owners);
+ while (e1->enumerate(e1, &id1) && e2->enumerate(e2, &id2))
+ {
+ if (!id1->equals(id1, id2))
+ {
+ equals = FALSE;
+ break;
+ }
+ }
+ e1->destroy(e1);
+ e2->destroy(e2);
+
+ return equals;
+}
+
+/**
* Data for the shared_key enumerator
*/
typedef struct {
@@ -435,15 +490,30 @@ METHOD(credential_set_t, create_shared_enumerator, enumerator_t*,
METHOD(mem_cred_t, add_shared_list, void,
private_mem_cred_t *this, shared_key_t *shared, linked_list_t* owners)
{
- shared_entry_t *entry;
+ shared_entry_t *current, *new;
+ enumerator_t *enumerator;
- INIT(entry,
+ INIT(new,
.shared = shared,
.owners = owners,
);
this->lock->write_lock(this->lock);
- this->shared->insert_first(this->shared, entry);
+
+ enumerator = this->shared->create_enumerator(this->shared);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ if (shared_entry_equals(current, new))
+ {
+ this->shared->remove_at(this->shared, enumerator);
+ shared_entry_destroy(current);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ this->shared->insert_first(this->shared, new);
+
this->lock->unlock(this->lock);
}
diff --git a/src/libstrongswan/processing/processor.c b/src/libstrongswan/processing/processor.c
index 012b169e3..27e5ab5f6 100644
--- a/src/libstrongswan/processing/processor.c
+++ b/src/libstrongswan/processing/processor.c
@@ -467,6 +467,8 @@ METHOD(processor_t, cancel, void,
{
enumerator_t *enumerator;
worker_thread_t *worker;
+ job_t *job;
+ int i;
this->mutex->lock(this->mutex);
this->desired_threads = 0;
@@ -496,6 +498,14 @@ METHOD(processor_t, cancel, void,
worker->thread->join(worker->thread);
free(worker);
}
+ for (i = 0; i < JOB_PRIO_MAX; i++)
+ {
+ while (this->jobs[i]->remove_first(this->jobs[i],
+ (void**)&job) == SUCCESS)
+ {
+ job->destroy(job);
+ }
+ }
this->mutex->unlock(this->mutex);
}
@@ -510,7 +520,7 @@ METHOD(processor_t, destroy, void,
this->mutex->destroy(this->mutex);
for (i = 0; i < JOB_PRIO_MAX; i++)
{
- this->jobs[i]->destroy_offset(this->jobs[i], offsetof(job_t, destroy));
+ this->jobs[i]->destroy(this->jobs[i]);
}
this->threads->destroy(this->threads);
free(this);
diff --git a/src/libstrongswan/processing/watcher.c b/src/libstrongswan/processing/watcher.c
index cc3c3a788..09905646c 100644
--- a/src/libstrongswan/processing/watcher.c
+++ b/src/libstrongswan/processing/watcher.c
@@ -50,6 +50,11 @@ struct private_watcher_t {
bool pending;
/**
+ * Is watcher running?
+ */
+ bool running;
+
+ /**
* Lock to access FD list
*/
mutex_t *mutex;
@@ -225,6 +230,7 @@ static void activate_all(private_watcher_t *this)
entry->in_callback = 0;
}
enumerator->destroy(enumerator);
+ this->running = FALSE;
this->condvar->broadcast(this->condvar);
this->mutex->unlock(this->mutex);
}
@@ -238,6 +244,7 @@ static job_requeue_t watch(private_watcher_t *this)
entry_t *entry;
fd_set rd, wr, ex;
int maxfd = 0, res;
+ bool rebuild = FALSE;
FD_ZERO(&rd);
FD_ZERO(&wr);
@@ -282,7 +289,7 @@ static job_requeue_t watch(private_watcher_t *this)
enumerator->destroy(enumerator);
this->mutex->unlock(this->mutex);
- while (TRUE)
+ while (!rebuild)
{
char buf[1];
bool old;
@@ -308,6 +315,11 @@ static job_requeue_t watch(private_watcher_t *this)
enumerator = this->fds->create_enumerator(this->fds);
while (enumerator->enumerate(enumerator, &entry))
{
+ if (entry->in_callback)
+ {
+ rebuild = TRUE;
+ break;
+ }
if (FD_ISSET(entry->fd, &rd) && (entry->events & WATCHER_READ))
{
DBG2(DBG_JOB, "watched FD %d ready to read", entry->fd);
@@ -347,6 +359,7 @@ static job_requeue_t watch(private_watcher_t *this)
return JOB_REQUEUE_DIRECT;
}
}
+ return JOB_REQUEUE_DIRECT;
}
METHOD(watcher_t, add, void,
@@ -366,6 +379,7 @@ METHOD(watcher_t, add, void,
this->fds->insert_last(this->fds, entry);
if (this->fds->get_count(this->fds) == 1)
{
+ this->running = TRUE;
lib->processor->queue_job(lib->processor,
(job_t*)callback_job_create_with_prio((void*)watch, this,
NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
@@ -393,7 +407,7 @@ METHOD(watcher_t, remove_, void,
{
if (entry->fd == fd)
{
- if (entry->in_callback)
+ if (this->running && entry->in_callback)
{
is_in_callback = TRUE;
break;
diff --git a/src/libstrongswan/utils/utils.h b/src/libstrongswan/utils/utils.h
index 75b31ec1b..53bfceda6 100644
--- a/src/libstrongswan/utils/utils.h
+++ b/src/libstrongswan/utils/utils.h
@@ -204,6 +204,19 @@ static inline bool memeq(const void *x, const void *y, size_t len)
static ret name(this, ##__VA_ARGS__)
/**
+ * Callback declaration/definition macro, allowing casted first parameter.
+ *
+ * This is very similar to METHOD, but instead of casting the first parameter
+ * to a public interface, it uses a void*. This allows type safe definition
+ * of a callback function, while using the real type for the first parameter.
+ */
+#define CALLBACK(name, ret, param1, ...) \
+ static ret _cb_##name(union {void *_generic; param1;} \
+ __attribute__((transparent_union)), ##__VA_ARGS__); \
+ static typeof(_cb_##name) *name = (typeof(_cb_##name)*)_cb_##name; \
+ static ret _cb_##name(param1, ##__VA_ARGS__)
+
+/**
* Architecture independent bitfield definition helpers (at least with GCC).
*
* Defines a bitfield with a type t and a fixed size of bitfield members, e.g.: