Merge branch 'vici'
Introduces the vici plugin providing a Versatile IKE Configuration Interface to configure, monitor and control the IKE daemon charon over a stable IPC socket interface.
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
strongswan-5.2.0
|
||||
----------------
|
||||
|
||||
- The new vici plugin provides a Versatile IKE Configuration Interface for
|
||||
charon. Using the stable IPC interface, external applications can configure,
|
||||
control and monitor the IKE daemon. Instead of scripting the ipsec tool
|
||||
and generating ipsec.conf, third party applications can use the new interface
|
||||
for more control and better reliability.
|
||||
|
||||
- The SWID IMC can extract all installed packages from the dpkg (Debian,
|
||||
Ubuntu, etc.) or rpm (Fedora, RedHat, etc) package managers, respectively,
|
||||
using the swidGenerator (https://github.com/tnc-ba/swidGenerator) which
|
||||
|
||||
@@ -78,6 +78,7 @@ plugins = \
|
||||
plugins/tnccs-20.opt \
|
||||
plugins/unbound.opt \
|
||||
plugins/updown.opt \
|
||||
plugins/vici.opt \
|
||||
plugins/whitelist.opt \
|
||||
plugins/xauth-eap.opt \
|
||||
plugins/xauth-pam.opt
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
charon.plugins.vici.socket = unix://${piddir}/charon.vici
|
||||
Socket the vici plugin serves clients.
|
||||
@@ -210,6 +210,7 @@ ARG_DISBL_SET([stroke], [disable charons stroke configuration backend.])
|
||||
ARG_ENABL_SET([smp], [enable SMP configuration and control interface. Requires libxml.])
|
||||
ARG_ENABL_SET([sql], [enable SQL database configuration backend.])
|
||||
ARG_ENABL_SET([uci], [enable OpenWRT UCI configuration plugin.])
|
||||
ARG_ENABL_SET([vici], [enable strongSwan IKE generic IPC interface plugin.])
|
||||
# attribute provider/consumer plugins
|
||||
ARG_ENABL_SET([android-dns], [enable Android specific DNS handler.])
|
||||
ARG_DISBL_SET([attr], [disable strongswan.conf based configuration attribute plugin.])
|
||||
@@ -1131,6 +1132,7 @@ ADD_PLUGIN([socket-default], [c charon nm cmd])
|
||||
ADD_PLUGIN([socket-dynamic], [c charon cmd])
|
||||
ADD_PLUGIN([farp], [c charon])
|
||||
ADD_PLUGIN([stroke], [c charon])
|
||||
ADD_PLUGIN([vici], [c charon])
|
||||
ADD_PLUGIN([smp], [c charon])
|
||||
ADD_PLUGIN([sql], [c charon])
|
||||
ADD_PLUGIN([updown], [c charon])
|
||||
@@ -1261,6 +1263,7 @@ AM_CONDITIONAL(USE_NTRU, test x$ntru = xtrue)
|
||||
# charon plugins
|
||||
# ----------------
|
||||
AM_CONDITIONAL(USE_STROKE, test x$stroke = xtrue)
|
||||
AM_CONDITIONAL(USE_VICI, test x$vici = xtrue)
|
||||
AM_CONDITIONAL(USE_MEDSRV, test x$medsrv = xtrue)
|
||||
AM_CONDITIONAL(USE_MEDCLI, test x$medcli = xtrue)
|
||||
AM_CONDITIONAL(USE_UCI, test x$uci = xtrue)
|
||||
@@ -1578,6 +1581,7 @@ AC_CONFIG_FILES([
|
||||
src/libcharon/plugins/android_log/Makefile
|
||||
src/libcharon/plugins/maemo/Makefile
|
||||
src/libcharon/plugins/stroke/Makefile
|
||||
src/libcharon/plugins/vici/Makefile
|
||||
src/libcharon/plugins/updown/Makefile
|
||||
src/libcharon/plugins/dhcp/Makefile
|
||||
src/libcharon/plugins/unit_tester/Makefile
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
vici_tests
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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_ @}*/
|
||||
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Martin Willi
|
||||
* Copyright (C) 2014 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "vici_control.h"
|
||||
#include "vici_builder.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <daemon.h>
|
||||
#include <collections/array.h>
|
||||
|
||||
typedef struct private_vici_control_t private_vici_control_t;
|
||||
|
||||
/**
|
||||
* Private data of an vici_control_t object.
|
||||
*/
|
||||
struct private_vici_control_t {
|
||||
|
||||
/**
|
||||
* Public vici_control_t interface.
|
||||
*/
|
||||
vici_control_t public;
|
||||
|
||||
/**
|
||||
* Dispatcher
|
||||
*/
|
||||
vici_dispatcher_t *dispatcher;
|
||||
};
|
||||
|
||||
/**
|
||||
* Log callback helper data
|
||||
*/
|
||||
typedef struct {
|
||||
/** dispatcher to send log messages over */
|
||||
vici_dispatcher_t *dispatcher;
|
||||
/** connection ID to send messages to */
|
||||
u_int id;
|
||||
/** loglevel */
|
||||
level_t level;
|
||||
/** prevent recursive log */
|
||||
u_int recursive;
|
||||
} log_info_t;
|
||||
|
||||
/**
|
||||
* Log using vici event messages
|
||||
*/
|
||||
static bool log_vici(log_info_t *info, debug_t group, level_t level,
|
||||
ike_sa_t *ike_sa, char *text)
|
||||
{
|
||||
if (level <= info->level)
|
||||
{
|
||||
if (info->recursive++ == 0)
|
||||
{
|
||||
vici_message_t *message;
|
||||
vici_builder_t *builder;
|
||||
|
||||
builder = vici_builder_create();
|
||||
builder->add_kv(builder, "group", "%N", debug_names, group);
|
||||
builder->add_kv(builder, "level", "%d", level);
|
||||
if (ike_sa)
|
||||
{
|
||||
builder->add_kv(builder, "ikesa-name", "%s",
|
||||
ike_sa->get_name(ike_sa));
|
||||
builder->add_kv(builder, "ikesa-uniqueid", "%u",
|
||||
ike_sa->get_unique_id(ike_sa));
|
||||
}
|
||||
builder->add_kv(builder, "msg", "%s", text);
|
||||
|
||||
message = builder->finalize(builder);
|
||||
if (message)
|
||||
{
|
||||
info->dispatcher->raise_event(info->dispatcher, "control-log",
|
||||
info->id, message);
|
||||
}
|
||||
}
|
||||
info->recursive--;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a (error) reply message
|
||||
*/
|
||||
static vici_message_t* send_reply(private_vici_control_t *this, char *fmt, ...)
|
||||
{
|
||||
vici_builder_t *builder;
|
||||
va_list args;
|
||||
|
||||
builder = vici_builder_create();
|
||||
builder->add_kv(builder, "success", fmt ? "no" : "yes");
|
||||
if (fmt)
|
||||
{
|
||||
va_start(args, fmt);
|
||||
builder->vadd_kv(builder, "errmsg", fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
return builder->finalize(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the child_cfg having name from peer_cfg
|
||||
*/
|
||||
static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
|
||||
{
|
||||
child_cfg_t *current, *found = NULL;
|
||||
enumerator_t *enumerator;
|
||||
|
||||
enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
|
||||
while (enumerator->enumerate(enumerator, ¤t))
|
||||
{
|
||||
if (streq(current->get_name(current), name))
|
||||
{
|
||||
found = current;
|
||||
found->get_ref(found);
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a peer/child config from a child config name
|
||||
*/
|
||||
static child_cfg_t* find_child_cfg(char *name, peer_cfg_t **out)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
peer_cfg_t *peer_cfg;
|
||||
child_cfg_t *child_cfg;
|
||||
|
||||
enumerator = charon->backends->create_peer_cfg_enumerator(
|
||||
charon->backends, NULL, NULL, NULL, NULL, IKE_ANY);
|
||||
while (enumerator->enumerate(enumerator, &peer_cfg))
|
||||
{
|
||||
child_cfg = get_child_from_peer(peer_cfg, name);
|
||||
if (child_cfg)
|
||||
{
|
||||
*out = peer_cfg->get_ref(peer_cfg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
return child_cfg;
|
||||
}
|
||||
|
||||
CALLBACK(initiate, vici_message_t*,
|
||||
private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
|
||||
{
|
||||
child_cfg_t *child_cfg = NULL;
|
||||
peer_cfg_t *peer_cfg;
|
||||
char *child;
|
||||
u_int timeout;
|
||||
log_info_t log = {
|
||||
.dispatcher = this->dispatcher,
|
||||
.id = id,
|
||||
};
|
||||
|
||||
child = request->get_str(request, NULL, "child");
|
||||
timeout = request->get_int(request, 0, "timeout");
|
||||
log.level = request->get_int(request, 1, "loglevel");
|
||||
|
||||
if (!child)
|
||||
{
|
||||
return send_reply(this, "missing configuration name");
|
||||
}
|
||||
|
||||
DBG1(DBG_CFG, "vici initiate '%s'", child);
|
||||
|
||||
child_cfg = find_child_cfg(child, &peer_cfg);
|
||||
if (!child_cfg)
|
||||
{
|
||||
return send_reply(this, "CHILD_SA config '%s' not found", child);
|
||||
}
|
||||
switch (charon->controller->initiate(charon->controller,
|
||||
peer_cfg, child_cfg, (controller_cb_t)log_vici, &log, timeout))
|
||||
{
|
||||
case SUCCESS:
|
||||
return send_reply(this, NULL);
|
||||
case OUT_OF_RES:
|
||||
return send_reply(this, "CHILD_SA '%s' not established after %dms",
|
||||
child, timeout);
|
||||
case FAILED:
|
||||
default:
|
||||
return send_reply(this, "establishing CHILD_SA '%s' failed", child);
|
||||
}
|
||||
}
|
||||
|
||||
CALLBACK(terminate, vici_message_t*,
|
||||
private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
|
||||
{
|
||||
enumerator_t *enumerator, *isas, *csas;
|
||||
char *child, *ike, *errmsg = NULL;
|
||||
u_int timeout, child_id, ike_id, current, *del, done = 0;
|
||||
ike_sa_t *ike_sa;
|
||||
child_sa_t *child_sa;
|
||||
array_t *ids;
|
||||
vici_builder_t *builder;
|
||||
log_info_t log = {
|
||||
.dispatcher = this->dispatcher,
|
||||
.id = id,
|
||||
};
|
||||
|
||||
child = request->get_str(request, NULL, "child");
|
||||
ike = request->get_str(request, NULL, "ike");
|
||||
child_id = request->get_int(request, 0, "child-id");
|
||||
ike_id = request->get_int(request, 0, "ike-id");
|
||||
timeout = request->get_int(request, 0, "timeout");
|
||||
log.level = request->get_int(request, 1, "loglevel");
|
||||
|
||||
if (!child && !ike && !ike_id && !child_id)
|
||||
{
|
||||
return send_reply(this, "missing terminate selector");
|
||||
}
|
||||
|
||||
if (ike_id)
|
||||
{
|
||||
DBG1(DBG_CFG, "vici terminate IKE_SA #%d", ike_id);
|
||||
}
|
||||
if (child_id)
|
||||
{
|
||||
DBG1(DBG_CFG, "vici terminate CHILD_SA #%d", child_id);
|
||||
}
|
||||
if (ike)
|
||||
{
|
||||
DBG1(DBG_CFG, "vici terminate IKE_SA '%s'", ike);
|
||||
}
|
||||
if (child)
|
||||
{
|
||||
DBG1(DBG_CFG, "vici terminate CHILD_SA '%s'", child);
|
||||
}
|
||||
|
||||
ids = array_create(sizeof(u_int), 0);
|
||||
|
||||
isas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE);
|
||||
while (isas->enumerate(isas, &ike_sa))
|
||||
{
|
||||
if (child || child_id)
|
||||
{
|
||||
if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
csas = ike_sa->create_child_sa_enumerator(ike_sa);
|
||||
while (csas->enumerate(csas, &child_sa))
|
||||
{
|
||||
if (child && !streq(child, child_sa->get_name(child_sa)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (child_id && child_sa->get_reqid(child_sa) != child_id)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
current = child_sa->get_reqid(child_sa);
|
||||
array_insert(ids, ARRAY_TAIL, ¤t);
|
||||
}
|
||||
csas->destroy(csas);
|
||||
}
|
||||
else if (ike && streq(ike, ike_sa->get_name(ike_sa)))
|
||||
{
|
||||
current = ike_sa->get_unique_id(ike_sa);
|
||||
array_insert(ids, ARRAY_TAIL, ¤t);
|
||||
}
|
||||
else if (ike_id && ike_id == ike_sa->get_unique_id(ike_sa))
|
||||
{
|
||||
array_insert(ids, ARRAY_TAIL, &ike_id);
|
||||
}
|
||||
}
|
||||
isas->destroy(isas);
|
||||
|
||||
enumerator = array_create_enumerator(ids);
|
||||
while (enumerator->enumerate(enumerator, &del))
|
||||
{
|
||||
if (child || child_id)
|
||||
{
|
||||
if (charon->controller->terminate_child(charon->controller, *del,
|
||||
(controller_cb_t)log_vici, &log, timeout) == SUCCESS)
|
||||
{
|
||||
done++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (charon->controller->terminate_ike(charon->controller, *del,
|
||||
(controller_cb_t)log_vici, &log, timeout) == SUCCESS)
|
||||
{
|
||||
done++;
|
||||
}
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
builder = vici_builder_create();
|
||||
if (array_count(ids) == 0)
|
||||
{
|
||||
errmsg = "no matching SAs to terminate found";
|
||||
}
|
||||
else if (done < array_count(ids))
|
||||
{
|
||||
if (array_count(ids) == 1)
|
||||
{
|
||||
errmsg = "terminating SA failed";
|
||||
}
|
||||
else
|
||||
{
|
||||
errmsg = "not all matching SAs could be terminated";
|
||||
}
|
||||
}
|
||||
builder->add_kv(builder, "success", errmsg ? "no" : "yes");
|
||||
builder->add_kv(builder, "matches", "%u", array_count(ids));
|
||||
builder->add_kv(builder, "terminated", "%u", done);
|
||||
if (errmsg)
|
||||
{
|
||||
builder->add_kv(builder, "errmsg", "%s", errmsg);
|
||||
}
|
||||
array_destroy(ids);
|
||||
return builder->finalize(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find reqid of an existing CHILD_SA
|
||||
*/
|
||||
static u_int32_t find_reqid(child_cfg_t *cfg)
|
||||
{
|
||||
enumerator_t *enumerator, *children;
|
||||
child_sa_t *child_sa;
|
||||
ike_sa_t *ike_sa;
|
||||
u_int32_t reqid;
|
||||
|
||||
reqid = charon->traps->find_reqid(charon->traps, cfg);
|
||||
if (reqid)
|
||||
{ /* already trapped */
|
||||
return reqid;
|
||||
}
|
||||
|
||||
enumerator = charon->controller->create_ike_sa_enumerator(
|
||||
charon->controller, TRUE);
|
||||
while (!reqid && enumerator->enumerate(enumerator, &ike_sa))
|
||||
{
|
||||
children = ike_sa->create_child_sa_enumerator(ike_sa);
|
||||
while (children->enumerate(children, &child_sa))
|
||||
{
|
||||
if (streq(cfg->get_name(cfg), child_sa->get_name(child_sa)))
|
||||
{
|
||||
reqid = child_sa->get_reqid(child_sa);
|
||||
break;
|
||||
}
|
||||
}
|
||||
children->destroy(children);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
return reqid;
|
||||
}
|
||||
|
||||
CALLBACK(install, vici_message_t*,
|
||||
private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
|
||||
{
|
||||
child_cfg_t *child_cfg = NULL;
|
||||
peer_cfg_t *peer_cfg;
|
||||
char *child;
|
||||
bool ok;
|
||||
|
||||
child = request->get_str(request, NULL, "child");
|
||||
if (!child)
|
||||
{
|
||||
return send_reply(this, "missing configuration name");
|
||||
}
|
||||
|
||||
DBG1(DBG_CFG, "vici install '%s'", child);
|
||||
|
||||
child_cfg = find_child_cfg(child, &peer_cfg);
|
||||
if (!child_cfg)
|
||||
{
|
||||
return send_reply(this, "configuration name not found");
|
||||
}
|
||||
switch (child_cfg->get_mode(child_cfg))
|
||||
{
|
||||
case MODE_PASS:
|
||||
case MODE_DROP:
|
||||
ok = charon->shunts->install(charon->shunts, child_cfg);
|
||||
break;
|
||||
default:
|
||||
ok = charon->traps->install(charon->traps, peer_cfg, child_cfg,
|
||||
find_reqid(child_cfg));
|
||||
break;
|
||||
}
|
||||
peer_cfg->destroy(peer_cfg);
|
||||
child_cfg->destroy(child_cfg);
|
||||
|
||||
return send_reply(this, ok ? NULL : "installing policy '%s' failed", child);
|
||||
}
|
||||
|
||||
CALLBACK(uninstall, vici_message_t*,
|
||||
private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
|
||||
{
|
||||
child_sa_t *child_sa;
|
||||
enumerator_t *enumerator;
|
||||
u_int32_t reqid = 0;
|
||||
char *child;
|
||||
|
||||
child = request->get_str(request, NULL, "child");
|
||||
if (!child)
|
||||
{
|
||||
return send_reply(this, "missing configuration name");
|
||||
}
|
||||
|
||||
DBG1(DBG_CFG, "vici uninstall '%s'", child);
|
||||
|
||||
if (charon->shunts->uninstall(charon->shunts, child))
|
||||
{
|
||||
return send_reply(this, NULL);
|
||||
}
|
||||
|
||||
enumerator = charon->traps->create_enumerator(charon->traps);
|
||||
while (enumerator->enumerate(enumerator, NULL, &child_sa))
|
||||
{
|
||||
if (streq(child, child_sa->get_name(child_sa)))
|
||||
{
|
||||
reqid = child_sa->get_reqid(child_sa);
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
if (reqid)
|
||||
{
|
||||
if (charon->traps->uninstall(charon->traps, reqid))
|
||||
{
|
||||
return send_reply(this, NULL);
|
||||
}
|
||||
return send_reply(this, "uninstalling policy '%s' failed", child);
|
||||
}
|
||||
return send_reply(this, "policy '%s' not found", child);
|
||||
}
|
||||
|
||||
static void manage_command(private_vici_control_t *this,
|
||||
char *name, vici_command_cb_t cb, bool reg)
|
||||
{
|
||||
this->dispatcher->manage_command(this->dispatcher, name,
|
||||
reg ? cb : NULL, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Un-)register dispatcher functions
|
||||
*/
|
||||
static void manage_commands(private_vici_control_t *this, bool reg)
|
||||
{
|
||||
manage_command(this, "initiate", initiate, reg);
|
||||
manage_command(this, "terminate", terminate, reg);
|
||||
manage_command(this, "install", install, reg);
|
||||
manage_command(this, "uninstall", uninstall, reg);
|
||||
this->dispatcher->manage_event(this->dispatcher, "control-log", reg);
|
||||
}
|
||||
|
||||
METHOD(vici_control_t, destroy, void,
|
||||
private_vici_control_t *this)
|
||||
{
|
||||
manage_commands(this, FALSE);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
vici_control_t *vici_control_create(vici_dispatcher_t *dispatcher)
|
||||
{
|
||||
private_vici_control_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.dispatcher = dispatcher,
|
||||
);
|
||||
|
||||
manage_commands(this, TRUE);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Martin Willi
|
||||
* Copyright (C) 2014 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "vici_dispatcher.h"
|
||||
#include "vici_socket.h"
|
||||
|
||||
#include <bio/bio_reader.h>
|
||||
#include <bio/bio_writer.h>
|
||||
#include <threading/mutex.h>
|
||||
#include <threading/condvar.h>
|
||||
#include <threading/thread.h>
|
||||
#include <collections/array.h>
|
||||
#include <collections/hashtable.h>
|
||||
|
||||
typedef struct private_vici_dispatcher_t private_vici_dispatcher_t;
|
||||
|
||||
/**
|
||||
* Private data of an vici_dispatcher_t object.
|
||||
*/
|
||||
struct private_vici_dispatcher_t {
|
||||
|
||||
/**
|
||||
* Public vici_dispatcher_t interface.
|
||||
*/
|
||||
vici_dispatcher_t public;
|
||||
|
||||
/**
|
||||
* Socket to send/receive messages
|
||||
*/
|
||||
vici_socket_t *socket;
|
||||
|
||||
/**
|
||||
* List of registered commands (char* => command_t*)
|
||||
*/
|
||||
hashtable_t *cmds;
|
||||
|
||||
/**
|
||||
* List of known events, and registered clients (char* => event_t*)
|
||||
*/
|
||||
hashtable_t *events;
|
||||
|
||||
/**
|
||||
* Mutex to lock hashtables
|
||||
*/
|
||||
mutex_t *mutex;
|
||||
|
||||
/**
|
||||
* Condvar to signal command termination
|
||||
*/
|
||||
condvar_t *cond;
|
||||
};
|
||||
|
||||
/**
|
||||
* Registered command
|
||||
*/
|
||||
typedef struct {
|
||||
/** command name */
|
||||
char *name;
|
||||
/** callback for command */
|
||||
vici_command_cb_t cb;
|
||||
/** user data to pass to callback */
|
||||
void *user;
|
||||
/** command currently in use? */
|
||||
u_int uses;
|
||||
} command_t;
|
||||
|
||||
/**
|
||||
* Registered event
|
||||
*/
|
||||
typedef struct {
|
||||
/** event name */
|
||||
char *name;
|
||||
/** registered clients, as u_int */
|
||||
array_t *clients;
|
||||
/** event currently in use? */
|
||||
u_int uses;
|
||||
} event_t;
|
||||
|
||||
/**
|
||||
* Send a operation code, optionally with name and message
|
||||
*/
|
||||
static void send_op(private_vici_dispatcher_t *this, u_int id,
|
||||
vici_operation_t op, char *name, vici_message_t *message)
|
||||
{
|
||||
bio_writer_t *writer;
|
||||
u_int len;
|
||||
|
||||
len = sizeof(u_int8_t);
|
||||
if (name)
|
||||
{
|
||||
len += sizeof(u_int8_t) + strlen(name);
|
||||
}
|
||||
if (message)
|
||||
{
|
||||
len += message->get_encoding(message).len;
|
||||
}
|
||||
writer = bio_writer_create(len);
|
||||
writer->write_uint8(writer, op);
|
||||
if (name)
|
||||
{
|
||||
writer->write_data8(writer, chunk_from_str(name));
|
||||
}
|
||||
if (message)
|
||||
{
|
||||
writer->write_data(writer, message->get_encoding(message));
|
||||
}
|
||||
this->socket->send(this->socket, id, writer->extract_buf(writer));
|
||||
writer->destroy(writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register client for event
|
||||
*/
|
||||
static void register_event(private_vici_dispatcher_t *this, char *name,
|
||||
u_int id)
|
||||
{
|
||||
event_t *event;
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
while (TRUE)
|
||||
{
|
||||
event = this->events->get(this->events, name);
|
||||
if (!event)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!event->uses)
|
||||
{
|
||||
array_insert(event->clients, ARRAY_TAIL, &id);
|
||||
break;
|
||||
}
|
||||
this->cond->wait(this->cond, this->mutex);
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
if (event)
|
||||
{
|
||||
DBG2(DBG_CFG, "vici client %u registered for: %s", id, name);
|
||||
send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_CFG, "vici client %u invalid registration: %s", id, name);
|
||||
send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister client for event
|
||||
*/
|
||||
static void unregister_event(private_vici_dispatcher_t *this, char *name,
|
||||
u_int id)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
event_t *event;
|
||||
u_int *current;
|
||||
bool found = FALSE;
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
while (TRUE)
|
||||
{
|
||||
event = this->events->get(this->events, name);
|
||||
if (!event)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!event->uses)
|
||||
{
|
||||
enumerator = array_create_enumerator(event->clients);
|
||||
while (enumerator->enumerate(enumerator, ¤t))
|
||||
{
|
||||
if (*current == id)
|
||||
{
|
||||
array_remove_at(event->clients, enumerator);
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
break;
|
||||
}
|
||||
this->cond->wait(this->cond, this->mutex);
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
DBG2(DBG_CFG, "vici client %u unregistered for: %s", id, name);
|
||||
|
||||
if (found)
|
||||
{
|
||||
send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data to release on thread cancellation
|
||||
*/
|
||||
typedef struct {
|
||||
private_vici_dispatcher_t *this;
|
||||
command_t *cmd;
|
||||
vici_message_t *request;
|
||||
} release_data_t;
|
||||
|
||||
/**
|
||||
* Release command after execution/cancellation
|
||||
*/
|
||||
CALLBACK(release_command, void,
|
||||
release_data_t *release)
|
||||
{
|
||||
release->request->destroy(release->request);
|
||||
|
||||
release->this->mutex->lock(release->this->mutex);
|
||||
if (--release->cmd->uses == 0)
|
||||
{
|
||||
release->this->cond->broadcast(release->this->cond);
|
||||
}
|
||||
release->this->mutex->unlock(release->this->mutex);
|
||||
|
||||
free(release);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a request message
|
||||
*/
|
||||
void process_request(private_vici_dispatcher_t *this, char *name, u_int id,
|
||||
chunk_t data)
|
||||
{
|
||||
vici_message_t *response = NULL;
|
||||
release_data_t *release;
|
||||
command_t *cmd;
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
cmd = this->cmds->get(this->cmds, name);
|
||||
if (cmd)
|
||||
{
|
||||
cmd->uses++;
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
if (cmd)
|
||||
{
|
||||
INIT(release,
|
||||
.this = this,
|
||||
.cmd = cmd,
|
||||
);
|
||||
|
||||
DBG2(DBG_CFG, "vici client %u requests: %s", id, name);
|
||||
|
||||
thread_cleanup_push(release_command, release);
|
||||
|
||||
release->request = vici_message_create_from_data(data, FALSE);
|
||||
response = release->cmd->cb(cmd->user, cmd->name, id, release->request);
|
||||
|
||||
thread_cleanup_pop(TRUE);
|
||||
|
||||
if (response)
|
||||
{
|
||||
send_op(this, id, VICI_CMD_RESPONSE, NULL, response);
|
||||
response->destroy(response);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_CFG, "vici client %u invalid request: %s", id, name);
|
||||
send_op(this, id, VICI_CMD_UNKNOWN, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
CALLBACK(inbound, void,
|
||||
private_vici_dispatcher_t *this, u_int id, chunk_t data)
|
||||
{
|
||||
bio_reader_t *reader;
|
||||
chunk_t chunk;
|
||||
u_int8_t type;
|
||||
char name[257];
|
||||
|
||||
reader = bio_reader_create(data);
|
||||
if (reader->read_uint8(reader, &type))
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VICI_EVENT_REGISTER:
|
||||
if (reader->read_data8(reader, &chunk) &&
|
||||
vici_stringify(chunk, name, sizeof(name)))
|
||||
{
|
||||
register_event(this, name, id);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_CFG, "invalid vici register message");
|
||||
}
|
||||
break;
|
||||
case VICI_EVENT_UNREGISTER:
|
||||
if (reader->read_data8(reader, &chunk) &&
|
||||
vici_stringify(chunk, name, sizeof(name)))
|
||||
{
|
||||
unregister_event(this, name, id);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_CFG, "invalid vici unregister message");
|
||||
}
|
||||
break;
|
||||
case VICI_CMD_REQUEST:
|
||||
if (reader->read_data8(reader, &chunk) &&
|
||||
vici_stringify(chunk, name, sizeof(name)))
|
||||
{
|
||||
thread_cleanup_push((void*)reader->destroy, reader);
|
||||
process_request(this, name, id, reader->peek(reader));
|
||||
thread_cleanup_pop(FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_CFG, "invalid vici request message");
|
||||
}
|
||||
break;
|
||||
case VICI_CMD_RESPONSE:
|
||||
case VICI_EVENT_CONFIRM:
|
||||
case VICI_EVENT_UNKNOWN:
|
||||
case VICI_EVENT:
|
||||
default:
|
||||
DBG1(DBG_CFG, "unsupported vici operation: %u", type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_CFG, "invalid vici message");
|
||||
}
|
||||
reader->destroy(reader);
|
||||
}
|
||||
|
||||
CALLBACK(connect_, void,
|
||||
private_vici_dispatcher_t *this, u_int id)
|
||||
{
|
||||
DBG2(DBG_CFG, "vici client %u connected", id);
|
||||
}
|
||||
|
||||
CALLBACK(disconnect, void,
|
||||
private_vici_dispatcher_t *this, u_int id)
|
||||
{
|
||||
enumerator_t *events, *ids;
|
||||
event_t *event;
|
||||
u_int *current;
|
||||
|
||||
/* deregister client from all events */
|
||||
this->mutex->lock(this->mutex);
|
||||
events = this->events->create_enumerator(this->events);
|
||||
while (events->enumerate(events, NULL, &event))
|
||||
{
|
||||
while (event->uses)
|
||||
{
|
||||
this->cond->wait(this->cond, this->mutex);
|
||||
}
|
||||
ids = array_create_enumerator(event->clients);
|
||||
while (ids->enumerate(ids, ¤t))
|
||||
{
|
||||
if (id == *current)
|
||||
{
|
||||
array_remove_at(event->clients, ids);
|
||||
}
|
||||
}
|
||||
ids->destroy(ids);
|
||||
}
|
||||
events->destroy(events);
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
DBG2(DBG_CFG, "vici client %u disconnected", id);
|
||||
}
|
||||
|
||||
METHOD(vici_dispatcher_t, manage_command, void,
|
||||
private_vici_dispatcher_t *this, char *name,
|
||||
vici_command_cb_t cb, void *user)
|
||||
{
|
||||
command_t *cmd;
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
if (cb)
|
||||
{
|
||||
INIT(cmd,
|
||||
.name = strdup(name),
|
||||
.cb = cb,
|
||||
.user = user,
|
||||
);
|
||||
cmd = this->cmds->put(this->cmds, cmd->name, cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd = this->cmds->remove(this->cmds, name);
|
||||
}
|
||||
if (cmd)
|
||||
{
|
||||
while (cmd->uses)
|
||||
{
|
||||
this->cond->wait(this->cond, this->mutex);
|
||||
}
|
||||
free(cmd->name);
|
||||
free(cmd);
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
|
||||
METHOD(vici_dispatcher_t, manage_event, void,
|
||||
private_vici_dispatcher_t *this, char *name, bool reg)
|
||||
{
|
||||
event_t *event;
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
if (reg)
|
||||
{
|
||||
INIT(event,
|
||||
.name = strdup(name),
|
||||
.clients = array_create(sizeof(u_int), 0),
|
||||
);
|
||||
event = this->events->put(this->events, event->name, event);
|
||||
}
|
||||
else
|
||||
{
|
||||
event = this->events->remove(this->events, name);
|
||||
}
|
||||
if (event)
|
||||
{
|
||||
while (event->uses)
|
||||
{
|
||||
this->cond->wait(this->cond, this->mutex);
|
||||
}
|
||||
array_destroy(event->clients);
|
||||
free(event->name);
|
||||
free(event);
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
|
||||
METHOD(vici_dispatcher_t, raise_event, void,
|
||||
private_vici_dispatcher_t *this, char *name, u_int id,
|
||||
vici_message_t *message)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
event_t *event;
|
||||
u_int *current;
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
event = this->events->get(this->events, name);
|
||||
if (event)
|
||||
{
|
||||
event->uses++;
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
enumerator = array_create_enumerator(event->clients);
|
||||
while (enumerator->enumerate(enumerator, ¤t))
|
||||
{
|
||||
if (id == 0 || id == *current)
|
||||
{
|
||||
send_op(this, *current, VICI_EVENT, name, message);
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
if (--event->uses == 0)
|
||||
{
|
||||
this->cond->broadcast(this->cond);
|
||||
}
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
message->destroy(message);
|
||||
}
|
||||
|
||||
METHOD(vici_dispatcher_t, destroy, void,
|
||||
private_vici_dispatcher_t *this)
|
||||
{
|
||||
DESTROY_IF(this->socket);
|
||||
this->mutex->destroy(this->mutex);
|
||||
this->cond->destroy(this->cond);
|
||||
this->cmds->destroy(this->cmds);
|
||||
this->events->destroy(this->events);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
vici_dispatcher_t *vici_dispatcher_create(char *uri)
|
||||
{
|
||||
private_vici_dispatcher_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.manage_command = _manage_command,
|
||||
.manage_event = _manage_event,
|
||||
.raise_event = _raise_event,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.cmds = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
|
||||
.events = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
|
||||
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
||||
.cond = condvar_create(CONDVAR_TYPE_DEFAULT),
|
||||
);
|
||||
|
||||
this->socket = vici_socket_create(uri, inbound, connect_, disconnect, this);
|
||||
if (!this->socket)
|
||||
{
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -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, ¤t))
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VICI_SECTION_START:
|
||||
section++;
|
||||
if (section == keysection && streq(name, key))
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case VICI_SECTION_END:
|
||||
section--;
|
||||
continue;
|
||||
case VICI_END:
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* find key/value in current section */
|
||||
while (enumerator->enumerate(enumerator, &type, &name, ¤t))
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VICI_KEY_VALUE:
|
||||
if (section == keysection && streq(key, name))
|
||||
{
|
||||
*value = current;
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case VICI_SECTION_START:
|
||||
section++;
|
||||
continue;
|
||||
case VICI_SECTION_END:
|
||||
section--;
|
||||
continue;
|
||||
case VICI_END:
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
METHOD(vici_message_t, vget_str, char*,
|
||||
private_vici_message_t *this, char *def, char *fmt, va_list args)
|
||||
{
|
||||
chunk_t value;
|
||||
bool found;
|
||||
char *str;
|
||||
|
||||
found = find_value(this, &value, fmt, args);
|
||||
if (found)
|
||||
{
|
||||
if (chunk_printable(value, NULL, 0))
|
||||
{
|
||||
str = strndup(value.ptr, value.len);
|
||||
/* keep a reference to string, so caller doesn't have to care */
|
||||
this->strings->insert_last(this->strings, str);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
METHOD(vici_message_t, get_str, char*,
|
||||
private_vici_message_t *this, char *def, char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *str;
|
||||
|
||||
va_start(args, fmt);
|
||||
str = vget_str(this, def, fmt, args);
|
||||
va_end(args);
|
||||
return str;
|
||||
}
|
||||
|
||||
METHOD(vici_message_t, vget_int, int,
|
||||
private_vici_message_t *this, int def, char *fmt, va_list args)
|
||||
{
|
||||
chunk_t value;
|
||||
bool found;
|
||||
char buf[32], *pos;
|
||||
int ret;
|
||||
|
||||
found = find_value(this, &value, fmt, args);
|
||||
if (found)
|
||||
{
|
||||
if (chunk_printable(value, NULL, 0))
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "%.*s", (int)value.len, value.ptr);
|
||||
errno = 0;
|
||||
ret = strtol(buf, &pos, 0);
|
||||
if (errno == 0 && pos == buf + strlen(buf))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
METHOD(vici_message_t, get_int, int,
|
||||
private_vici_message_t *this, int def, char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int val;
|
||||
|
||||
va_start(args, fmt);
|
||||
val = vget_int(this, def, fmt, args);
|
||||
va_end(args);
|
||||
return val;
|
||||
}
|
||||
|
||||
METHOD(vici_message_t, vget_value, chunk_t,
|
||||
private_vici_message_t *this, chunk_t def, char *fmt, va_list args)
|
||||
{
|
||||
chunk_t value;
|
||||
bool found;
|
||||
|
||||
found = find_value(this, &value, fmt, args);
|
||||
if (found)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
METHOD(vici_message_t, get_value, chunk_t,
|
||||
private_vici_message_t *this, chunk_t def, char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
chunk_t value;
|
||||
|
||||
va_start(args, fmt);
|
||||
value = vget_value(this, def, fmt, args);
|
||||
va_end(args);
|
||||
return value;
|
||||
}
|
||||
|
||||
METHOD(vici_message_t, get_encoding, chunk_t,
|
||||
private_vici_message_t *this)
|
||||
{
|
||||
return this->encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private parse context data
|
||||
*/
|
||||
struct vici_parse_context_t {
|
||||
/** current section nesting level */
|
||||
int level;
|
||||
/** parse enumerator */
|
||||
enumerator_t *e;
|
||||
};
|
||||
|
||||
METHOD(vici_message_t, parse, bool,
|
||||
private_vici_message_t *this, vici_parse_context_t *ctx,
|
||||
vici_section_cb_t section, vici_value_cb_t kv, vici_value_cb_t li,
|
||||
void *user)
|
||||
{
|
||||
vici_parse_context_t root = {};
|
||||
char *name, *list = NULL;
|
||||
vici_type_t type;
|
||||
chunk_t value;
|
||||
int base;
|
||||
bool ok = TRUE;
|
||||
|
||||
if (!ctx)
|
||||
{
|
||||
ctx = &root;
|
||||
root.e = create_enumerator(this);
|
||||
}
|
||||
|
||||
base = ctx->level;
|
||||
|
||||
while (ok)
|
||||
{
|
||||
ok = ctx->e->enumerate(ctx->e, &type, &name, &value);
|
||||
if (ok)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VICI_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);
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -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, ¤t))
|
||||
{
|
||||
if (current->equals(current, cert))
|
||||
{
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
added->destroy(added);
|
||||
|
||||
if (!found && cert->get_encoding(cert, CERT_ASN1_DER, &encoding))
|
||||
{
|
||||
b = vici_builder_create();
|
||||
b->add_kv(b, "type", "%N",
|
||||
certificate_type_names, cert->get_type(cert));
|
||||
if (has_privkey(this, cert))
|
||||
{
|
||||
b->add_kv(b, "has_privkey", "yes");
|
||||
}
|
||||
b->add(b, VICI_KEY_VALUE, "data", encoding);
|
||||
free(encoding.ptr);
|
||||
|
||||
this->dispatcher->raise_event(this->dispatcher, "list-cert", id,
|
||||
b->finalize(b));
|
||||
list->insert_last(list, cert->get_ref(cert));
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
list->destroy_offset(list, offsetof(certificate_t, destroy));
|
||||
DESTROY_IF(subject);
|
||||
|
||||
b = vici_builder_create();
|
||||
return b->finalize(b);
|
||||
}
|
||||
|
||||
CALLBACK(version, vici_message_t*,
|
||||
private_vici_query_t *this, char *name, u_int id, vici_message_t *request)
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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, ¤t))
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -333,6 +350,44 @@ static void shared_entry_destroy(shared_entry_t *entry)
|
||||
free(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
|
||||
*/
|
||||
@@ -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, ¤t))
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -49,6 +49,11 @@ struct private_watcher_t {
|
||||
*/
|
||||
bool pending;
|
||||
|
||||
/**
|
||||
* Is watcher running?
|
||||
*/
|
||||
bool running;
|
||||
|
||||
/**
|
||||
* Lock to access FD list
|
||||
*/
|
||||
@@ -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;
|
||||
|
||||
@@ -203,6 +203,19 @@ static inline bool memeq(const void *x, const void *y, size_t len)
|
||||
static typeof(name) *_##name = (typeof(name)*)name; \
|
||||
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).
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user