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:
Martin Willi
2014-05-07 15:12:09 +02:00
42 changed files with 10401 additions and 6 deletions
+6
View File
@@ -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
+1
View File
@@ -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
+2
View File
@@ -0,0 +1,2 @@
charon.plugins.vici.socket = unix://${piddir}/charon.vici
Socket the vici plugin serves clients.
+4
View File
@@ -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
+7
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
vici_tests
+69
View File
@@ -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
+176
View File
@@ -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.
+764
View 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();
}
+455
View File
@@ -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;
}
+713
View File
@@ -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_ @}*/
+253
View File
@@ -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;
}
+129
View File
@@ -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
+53
View File
@@ -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_ @}*/
+496
View File
@@ -0,0 +1,496 @@
/*
* Copyright (C) 2014 Martin Willi
* Copyright (C) 2014 revosec AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "vici_control.h"
#include "vici_builder.h"
#include <inttypes.h>
#include <daemon.h>
#include <collections/array.h>
typedef struct private_vici_control_t private_vici_control_t;
/**
* Private data of an vici_control_t object.
*/
struct private_vici_control_t {
/**
* Public vici_control_t interface.
*/
vici_control_t public;
/**
* Dispatcher
*/
vici_dispatcher_t *dispatcher;
};
/**
* Log callback helper data
*/
typedef struct {
/** dispatcher to send log messages over */
vici_dispatcher_t *dispatcher;
/** connection ID to send messages to */
u_int id;
/** loglevel */
level_t level;
/** prevent recursive log */
u_int recursive;
} log_info_t;
/**
* Log using vici event messages
*/
static bool log_vici(log_info_t *info, debug_t group, level_t level,
ike_sa_t *ike_sa, char *text)
{
if (level <= info->level)
{
if (info->recursive++ == 0)
{
vici_message_t *message;
vici_builder_t *builder;
builder = vici_builder_create();
builder->add_kv(builder, "group", "%N", debug_names, group);
builder->add_kv(builder, "level", "%d", level);
if (ike_sa)
{
builder->add_kv(builder, "ikesa-name", "%s",
ike_sa->get_name(ike_sa));
builder->add_kv(builder, "ikesa-uniqueid", "%u",
ike_sa->get_unique_id(ike_sa));
}
builder->add_kv(builder, "msg", "%s", text);
message = builder->finalize(builder);
if (message)
{
info->dispatcher->raise_event(info->dispatcher, "control-log",
info->id, message);
}
}
info->recursive--;
}
return TRUE;
}
/**
* Send a (error) reply message
*/
static vici_message_t* send_reply(private_vici_control_t *this, char *fmt, ...)
{
vici_builder_t *builder;
va_list args;
builder = vici_builder_create();
builder->add_kv(builder, "success", fmt ? "no" : "yes");
if (fmt)
{
va_start(args, fmt);
builder->vadd_kv(builder, "errmsg", fmt, args);
va_end(args);
}
return builder->finalize(builder);
}
/**
* Get the child_cfg having name from peer_cfg
*/
static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
{
child_cfg_t *current, *found = NULL;
enumerator_t *enumerator;
enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
while (enumerator->enumerate(enumerator, &current))
{
if (streq(current->get_name(current), name))
{
found = current;
found->get_ref(found);
break;
}
}
enumerator->destroy(enumerator);
return found;
}
/**
* Find a peer/child config from a child config name
*/
static child_cfg_t* find_child_cfg(char *name, peer_cfg_t **out)
{
enumerator_t *enumerator;
peer_cfg_t *peer_cfg;
child_cfg_t *child_cfg;
enumerator = charon->backends->create_peer_cfg_enumerator(
charon->backends, NULL, NULL, NULL, NULL, IKE_ANY);
while (enumerator->enumerate(enumerator, &peer_cfg))
{
child_cfg = get_child_from_peer(peer_cfg, name);
if (child_cfg)
{
*out = peer_cfg->get_ref(peer_cfg);
break;
}
}
enumerator->destroy(enumerator);
return child_cfg;
}
CALLBACK(initiate, vici_message_t*,
private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
{
child_cfg_t *child_cfg = NULL;
peer_cfg_t *peer_cfg;
char *child;
u_int timeout;
log_info_t log = {
.dispatcher = this->dispatcher,
.id = id,
};
child = request->get_str(request, NULL, "child");
timeout = request->get_int(request, 0, "timeout");
log.level = request->get_int(request, 1, "loglevel");
if (!child)
{
return send_reply(this, "missing configuration name");
}
DBG1(DBG_CFG, "vici initiate '%s'", child);
child_cfg = find_child_cfg(child, &peer_cfg);
if (!child_cfg)
{
return send_reply(this, "CHILD_SA config '%s' not found", child);
}
switch (charon->controller->initiate(charon->controller,
peer_cfg, child_cfg, (controller_cb_t)log_vici, &log, timeout))
{
case SUCCESS:
return send_reply(this, NULL);
case OUT_OF_RES:
return send_reply(this, "CHILD_SA '%s' not established after %dms",
child, timeout);
case FAILED:
default:
return send_reply(this, "establishing CHILD_SA '%s' failed", child);
}
}
CALLBACK(terminate, vici_message_t*,
private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
{
enumerator_t *enumerator, *isas, *csas;
char *child, *ike, *errmsg = NULL;
u_int timeout, child_id, ike_id, current, *del, done = 0;
ike_sa_t *ike_sa;
child_sa_t *child_sa;
array_t *ids;
vici_builder_t *builder;
log_info_t log = {
.dispatcher = this->dispatcher,
.id = id,
};
child = request->get_str(request, NULL, "child");
ike = request->get_str(request, NULL, "ike");
child_id = request->get_int(request, 0, "child-id");
ike_id = request->get_int(request, 0, "ike-id");
timeout = request->get_int(request, 0, "timeout");
log.level = request->get_int(request, 1, "loglevel");
if (!child && !ike && !ike_id && !child_id)
{
return send_reply(this, "missing terminate selector");
}
if (ike_id)
{
DBG1(DBG_CFG, "vici terminate IKE_SA #%d", ike_id);
}
if (child_id)
{
DBG1(DBG_CFG, "vici terminate CHILD_SA #%d", child_id);
}
if (ike)
{
DBG1(DBG_CFG, "vici terminate IKE_SA '%s'", ike);
}
if (child)
{
DBG1(DBG_CFG, "vici terminate CHILD_SA '%s'", child);
}
ids = array_create(sizeof(u_int), 0);
isas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE);
while (isas->enumerate(isas, &ike_sa))
{
if (child || child_id)
{
if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
{
continue;
}
if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa))
{
continue;
}
csas = ike_sa->create_child_sa_enumerator(ike_sa);
while (csas->enumerate(csas, &child_sa))
{
if (child && !streq(child, child_sa->get_name(child_sa)))
{
continue;
}
if (child_id && child_sa->get_reqid(child_sa) != child_id)
{
continue;
}
current = child_sa->get_reqid(child_sa);
array_insert(ids, ARRAY_TAIL, &current);
}
csas->destroy(csas);
}
else if (ike && streq(ike, ike_sa->get_name(ike_sa)))
{
current = ike_sa->get_unique_id(ike_sa);
array_insert(ids, ARRAY_TAIL, &current);
}
else if (ike_id && ike_id == ike_sa->get_unique_id(ike_sa))
{
array_insert(ids, ARRAY_TAIL, &ike_id);
}
}
isas->destroy(isas);
enumerator = array_create_enumerator(ids);
while (enumerator->enumerate(enumerator, &del))
{
if (child || child_id)
{
if (charon->controller->terminate_child(charon->controller, *del,
(controller_cb_t)log_vici, &log, timeout) == SUCCESS)
{
done++;
}
}
else
{
if (charon->controller->terminate_ike(charon->controller, *del,
(controller_cb_t)log_vici, &log, timeout) == SUCCESS)
{
done++;
}
}
}
enumerator->destroy(enumerator);
builder = vici_builder_create();
if (array_count(ids) == 0)
{
errmsg = "no matching SAs to terminate found";
}
else if (done < array_count(ids))
{
if (array_count(ids) == 1)
{
errmsg = "terminating SA failed";
}
else
{
errmsg = "not all matching SAs could be terminated";
}
}
builder->add_kv(builder, "success", errmsg ? "no" : "yes");
builder->add_kv(builder, "matches", "%u", array_count(ids));
builder->add_kv(builder, "terminated", "%u", done);
if (errmsg)
{
builder->add_kv(builder, "errmsg", "%s", errmsg);
}
array_destroy(ids);
return builder->finalize(builder);
}
/**
* Find reqid of an existing CHILD_SA
*/
static u_int32_t find_reqid(child_cfg_t *cfg)
{
enumerator_t *enumerator, *children;
child_sa_t *child_sa;
ike_sa_t *ike_sa;
u_int32_t reqid;
reqid = charon->traps->find_reqid(charon->traps, cfg);
if (reqid)
{ /* already trapped */
return reqid;
}
enumerator = charon->controller->create_ike_sa_enumerator(
charon->controller, TRUE);
while (!reqid && enumerator->enumerate(enumerator, &ike_sa))
{
children = ike_sa->create_child_sa_enumerator(ike_sa);
while (children->enumerate(children, &child_sa))
{
if (streq(cfg->get_name(cfg), child_sa->get_name(child_sa)))
{
reqid = child_sa->get_reqid(child_sa);
break;
}
}
children->destroy(children);
}
enumerator->destroy(enumerator);
return reqid;
}
CALLBACK(install, vici_message_t*,
private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
{
child_cfg_t *child_cfg = NULL;
peer_cfg_t *peer_cfg;
char *child;
bool ok;
child = request->get_str(request, NULL, "child");
if (!child)
{
return send_reply(this, "missing configuration name");
}
DBG1(DBG_CFG, "vici install '%s'", child);
child_cfg = find_child_cfg(child, &peer_cfg);
if (!child_cfg)
{
return send_reply(this, "configuration name not found");
}
switch (child_cfg->get_mode(child_cfg))
{
case MODE_PASS:
case MODE_DROP:
ok = charon->shunts->install(charon->shunts, child_cfg);
break;
default:
ok = charon->traps->install(charon->traps, peer_cfg, child_cfg,
find_reqid(child_cfg));
break;
}
peer_cfg->destroy(peer_cfg);
child_cfg->destroy(child_cfg);
return send_reply(this, ok ? NULL : "installing policy '%s' failed", child);
}
CALLBACK(uninstall, vici_message_t*,
private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
{
child_sa_t *child_sa;
enumerator_t *enumerator;
u_int32_t reqid = 0;
char *child;
child = request->get_str(request, NULL, "child");
if (!child)
{
return send_reply(this, "missing configuration name");
}
DBG1(DBG_CFG, "vici uninstall '%s'", child);
if (charon->shunts->uninstall(charon->shunts, child))
{
return send_reply(this, NULL);
}
enumerator = charon->traps->create_enumerator(charon->traps);
while (enumerator->enumerate(enumerator, NULL, &child_sa))
{
if (streq(child, child_sa->get_name(child_sa)))
{
reqid = child_sa->get_reqid(child_sa);
break;
}
}
enumerator->destroy(enumerator);
if (reqid)
{
if (charon->traps->uninstall(charon->traps, reqid))
{
return send_reply(this, NULL);
}
return send_reply(this, "uninstalling policy '%s' failed", child);
}
return send_reply(this, "policy '%s' not found", child);
}
static void manage_command(private_vici_control_t *this,
char *name, vici_command_cb_t cb, bool reg)
{
this->dispatcher->manage_command(this->dispatcher, name,
reg ? cb : NULL, this);
}
/**
* (Un-)register dispatcher functions
*/
static void manage_commands(private_vici_control_t *this, bool reg)
{
manage_command(this, "initiate", initiate, reg);
manage_command(this, "terminate", terminate, reg);
manage_command(this, "install", install, reg);
manage_command(this, "uninstall", uninstall, reg);
this->dispatcher->manage_event(this->dispatcher, "control-log", reg);
}
METHOD(vici_control_t, destroy, void,
private_vici_control_t *this)
{
manage_commands(this, FALSE);
free(this);
}
/**
* See header
*/
vici_control_t *vici_control_create(vici_dispatcher_t *dispatcher)
{
private_vici_control_t *this;
INIT(this,
.public = {
.destroy = _destroy,
},
.dispatcher = dispatcher,
);
manage_commands(this, TRUE);
return &this->public;
}
+47
View File
@@ -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_ @}*/
+330
View File
@@ -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;
}
+47
View File
@@ -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, &current))
{
if (*current == id)
{
array_remove_at(event->clients, enumerator);
found = TRUE;
break;
}
}
enumerator->destroy(enumerator);
break;
}
this->cond->wait(this->cond, this->mutex);
}
this->mutex->unlock(this->mutex);
DBG2(DBG_CFG, "vici client %u unregistered for: %s", id, name);
if (found)
{
send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL);
}
else
{
send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL);
}
}
/**
* Data to release on thread cancellation
*/
typedef struct {
private_vici_dispatcher_t *this;
command_t *cmd;
vici_message_t *request;
} release_data_t;
/**
* Release command after execution/cancellation
*/
CALLBACK(release_command, void,
release_data_t *release)
{
release->request->destroy(release->request);
release->this->mutex->lock(release->this->mutex);
if (--release->cmd->uses == 0)
{
release->this->cond->broadcast(release->this->cond);
}
release->this->mutex->unlock(release->this->mutex);
free(release);
}
/**
* Process a request message
*/
void process_request(private_vici_dispatcher_t *this, char *name, u_int id,
chunk_t data)
{
vici_message_t *response = NULL;
release_data_t *release;
command_t *cmd;
this->mutex->lock(this->mutex);
cmd = this->cmds->get(this->cmds, name);
if (cmd)
{
cmd->uses++;
}
this->mutex->unlock(this->mutex);
if (cmd)
{
INIT(release,
.this = this,
.cmd = cmd,
);
DBG2(DBG_CFG, "vici client %u requests: %s", id, name);
thread_cleanup_push(release_command, release);
release->request = vici_message_create_from_data(data, FALSE);
response = release->cmd->cb(cmd->user, cmd->name, id, release->request);
thread_cleanup_pop(TRUE);
if (response)
{
send_op(this, id, VICI_CMD_RESPONSE, NULL, response);
response->destroy(response);
}
}
else
{
DBG1(DBG_CFG, "vici client %u invalid request: %s", id, name);
send_op(this, id, VICI_CMD_UNKNOWN, NULL, NULL);
}
}
CALLBACK(inbound, void,
private_vici_dispatcher_t *this, u_int id, chunk_t data)
{
bio_reader_t *reader;
chunk_t chunk;
u_int8_t type;
char name[257];
reader = bio_reader_create(data);
if (reader->read_uint8(reader, &type))
{
switch (type)
{
case VICI_EVENT_REGISTER:
if (reader->read_data8(reader, &chunk) &&
vici_stringify(chunk, name, sizeof(name)))
{
register_event(this, name, id);
}
else
{
DBG1(DBG_CFG, "invalid vici register message");
}
break;
case VICI_EVENT_UNREGISTER:
if (reader->read_data8(reader, &chunk) &&
vici_stringify(chunk, name, sizeof(name)))
{
unregister_event(this, name, id);
}
else
{
DBG1(DBG_CFG, "invalid vici unregister message");
}
break;
case VICI_CMD_REQUEST:
if (reader->read_data8(reader, &chunk) &&
vici_stringify(chunk, name, sizeof(name)))
{
thread_cleanup_push((void*)reader->destroy, reader);
process_request(this, name, id, reader->peek(reader));
thread_cleanup_pop(FALSE);
}
else
{
DBG1(DBG_CFG, "invalid vici request message");
}
break;
case VICI_CMD_RESPONSE:
case VICI_EVENT_CONFIRM:
case VICI_EVENT_UNKNOWN:
case VICI_EVENT:
default:
DBG1(DBG_CFG, "unsupported vici operation: %u", type);
break;
}
}
else
{
DBG1(DBG_CFG, "invalid vici message");
}
reader->destroy(reader);
}
CALLBACK(connect_, void,
private_vici_dispatcher_t *this, u_int id)
{
DBG2(DBG_CFG, "vici client %u connected", id);
}
CALLBACK(disconnect, void,
private_vici_dispatcher_t *this, u_int id)
{
enumerator_t *events, *ids;
event_t *event;
u_int *current;
/* deregister client from all events */
this->mutex->lock(this->mutex);
events = this->events->create_enumerator(this->events);
while (events->enumerate(events, NULL, &event))
{
while (event->uses)
{
this->cond->wait(this->cond, this->mutex);
}
ids = array_create_enumerator(event->clients);
while (ids->enumerate(ids, &current))
{
if (id == *current)
{
array_remove_at(event->clients, ids);
}
}
ids->destroy(ids);
}
events->destroy(events);
this->mutex->unlock(this->mutex);
DBG2(DBG_CFG, "vici client %u disconnected", id);
}
METHOD(vici_dispatcher_t, manage_command, void,
private_vici_dispatcher_t *this, char *name,
vici_command_cb_t cb, void *user)
{
command_t *cmd;
this->mutex->lock(this->mutex);
if (cb)
{
INIT(cmd,
.name = strdup(name),
.cb = cb,
.user = user,
);
cmd = this->cmds->put(this->cmds, cmd->name, cmd);
}
else
{
cmd = this->cmds->remove(this->cmds, name);
}
if (cmd)
{
while (cmd->uses)
{
this->cond->wait(this->cond, this->mutex);
}
free(cmd->name);
free(cmd);
}
this->mutex->unlock(this->mutex);
}
METHOD(vici_dispatcher_t, manage_event, void,
private_vici_dispatcher_t *this, char *name, bool reg)
{
event_t *event;
this->mutex->lock(this->mutex);
if (reg)
{
INIT(event,
.name = strdup(name),
.clients = array_create(sizeof(u_int), 0),
);
event = this->events->put(this->events, event->name, event);
}
else
{
event = this->events->remove(this->events, name);
}
if (event)
{
while (event->uses)
{
this->cond->wait(this->cond, this->mutex);
}
array_destroy(event->clients);
free(event->name);
free(event);
}
this->mutex->unlock(this->mutex);
}
METHOD(vici_dispatcher_t, raise_event, void,
private_vici_dispatcher_t *this, char *name, u_int id,
vici_message_t *message)
{
enumerator_t *enumerator;
event_t *event;
u_int *current;
this->mutex->lock(this->mutex);
event = this->events->get(this->events, name);
if (event)
{
event->uses++;
this->mutex->unlock(this->mutex);
enumerator = array_create_enumerator(event->clients);
while (enumerator->enumerate(enumerator, &current))
{
if (id == 0 || id == *current)
{
send_op(this, *current, VICI_EVENT, name, message);
}
}
enumerator->destroy(enumerator);
this->mutex->lock(this->mutex);
if (--event->uses == 0)
{
this->cond->broadcast(this->cond);
}
}
this->mutex->unlock(this->mutex);
message->destroy(message);
}
METHOD(vici_dispatcher_t, destroy, void,
private_vici_dispatcher_t *this)
{
DESTROY_IF(this->socket);
this->mutex->destroy(this->mutex);
this->cond->destroy(this->cond);
this->cmds->destroy(this->cmds);
this->events->destroy(this->events);
free(this);
}
/**
* See header
*/
vici_dispatcher_t *vici_dispatcher_create(char *uri)
{
private_vici_dispatcher_t *this;
INIT(this,
.public = {
.manage_command = _manage_command,
.manage_event = _manage_event,
.raise_event = _raise_event,
.destroy = _destroy,
},
.cmds = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
.events = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
.cond = condvar_create(CONDVAR_TYPE_DEFAULT),
);
this->socket = vici_socket_create(uri, inbound, connect_, disconnect, this);
if (!this->socket)
{
destroy(this);
return NULL;
}
return &this->public;
}
@@ -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_ @}*/
+130
View File
@@ -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;
}
+54
View File
@@ -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_ @}*/
+695
View File
@@ -0,0 +1,695 @@
/*
* Copyright (C) 2014 Martin Willi
* Copyright (C) 2014 revosec AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "vici_message.h"
#include "vici_builder.h"
#include <bio/bio_reader.h>
#include <bio/bio_writer.h>
#include <errno.h>
typedef struct private_vici_message_t private_vici_message_t;
/**
* Private data of an vici_message_t object.
*/
struct private_vici_message_t {
/**
* Public vici_message_t interface.
*/
vici_message_t public;
/**
* Message encoding
*/
chunk_t encoding;
/**
* Free encoding during destruction?
*/
bool cleanup;
/**
* Allocated strings we maintain for get_str()
*/
linked_list_t *strings;
};
ENUM(vici_type_names, VICI_SECTION_START, VICI_END,
"section-start",
"section-end",
"key-value",
"list-start",
"list-item",
"list-end",
"end"
);
/**
* See header.
*/
bool vici_stringify(chunk_t chunk, char *buf, size_t size)
{
if (!chunk_printable(chunk, NULL, 0))
{
return FALSE;
}
snprintf(buf, size, "%.*s", (int)chunk.len, chunk.ptr);
return TRUE;
}
/**
* See header.
*/
bool vici_verify_type(vici_type_t type, u_int section, bool list)
{
if (list)
{
if (type != VICI_LIST_END && type != VICI_LIST_ITEM)
{
DBG1(DBG_ENC, "'%N' within list", vici_type_names, type);
return FALSE;
}
}
else
{
if (type == VICI_LIST_ITEM || type == VICI_LIST_END)
{
DBG1(DBG_ENC, "'%N' outside list", vici_type_names, type);
return FALSE;
}
}
if (type == VICI_SECTION_END && section == 0)
{
DBG1(DBG_ENC, "'%N' outside of section", vici_type_names, type);
return FALSE;
}
if (type == VICI_END)
{
if (section)
{
DBG1(DBG_ENC, "'%N' within section", vici_type_names, type);
return FALSE;
}
if (list)
{
DBG1(DBG_ENC, "'%N' within list", vici_type_names, type);
return FALSE;
}
}
return TRUE;
}
/**
* Enumerator parsing message
*/
typedef struct {
/* implements enumerator */
enumerator_t public;
/** reader to parse from */
bio_reader_t *reader;
/** section nesting level */
int section;
/** currently parsing list? */
bool list;
/** string currently enumerating */
char name[257];
} parse_enumerator_t;
METHOD(enumerator_t, parse_enumerate, bool,
parse_enumerator_t *this, vici_type_t *out, char **name, chunk_t *value)
{
u_int8_t type;
chunk_t data;
if (!this->reader->remaining(this->reader) ||
!this->reader->read_uint8(this->reader, &type))
{
*out = VICI_END;
return TRUE;
}
if (!vici_verify_type(type, this->section, this->list))
{
return FALSE;
}
switch (type)
{
case VICI_SECTION_START:
if (!this->reader->read_data8(this->reader, &data) ||
!vici_stringify(data, this->name, sizeof(this->name)))
{
DBG1(DBG_ENC, "invalid '%N' encoding", vici_type_names, type);
return FALSE;
}
*name = this->name;
this->section++;
break;
case VICI_SECTION_END:
this->section--;
break;
case VICI_KEY_VALUE:
if (!this->reader->read_data8(this->reader, &data) ||
!vici_stringify(data, this->name, sizeof(this->name)) ||
!this->reader->read_data16(this->reader, value))
{
DBG1(DBG_ENC, "invalid '%N' encoding", vici_type_names, type);
return FALSE;
}
*name = this->name;
break;
case VICI_LIST_START:
if (!this->reader->read_data8(this->reader, &data) ||
!vici_stringify(data, this->name, sizeof(this->name)))
{
DBG1(DBG_ENC, "invalid '%N' encoding", vici_type_names, type);
return FALSE;
}
*name = this->name;
this->list = TRUE;
break;
case VICI_LIST_ITEM:
this->reader->read_data16(this->reader, value);
break;
case VICI_LIST_END:
this->list = FALSE;
break;
case VICI_END:
return TRUE;
default:
DBG1(DBG_ENC, "unknown encoding type: %u", type);
return FALSE;
}
*out = type;
return TRUE;
}
METHOD(enumerator_t, parse_destroy, void,
parse_enumerator_t *this)
{
this->reader->destroy(this->reader);
free(this);
}
METHOD(vici_message_t, create_enumerator, enumerator_t*,
private_vici_message_t *this)
{
parse_enumerator_t *enumerator;
INIT(enumerator,
.public = {
.enumerate = (void*)_parse_enumerate,
.destroy = _parse_destroy,
},
.reader = bio_reader_create(this->encoding),
);
return &enumerator->public;
}
/**
* Find a value for given vararg key
*/
static bool find_value(private_vici_message_t *this, chunk_t *value,
char *fmt, va_list args)
{
enumerator_t *enumerator;
char buf[128], *name, *key, *dot, *next;
int section = 0, keysection = 0;
bool found = FALSE;
chunk_t current;
vici_type_t type;
vsnprintf(buf, sizeof(buf), fmt, args);
next = buf;
enumerator = create_enumerator(this);
/* descent into section */
while (TRUE)
{
dot = strchr(next, '.');
if (!dot)
{
key = next;
break;
}
*dot = '\0';
key = next;
next = dot + 1;
keysection++;
while (enumerator->enumerate(enumerator, &type, &name, &current))
{
switch (type)
{
case VICI_SECTION_START:
section++;
if (section == keysection && streq(name, key))
{
break;
}
continue;
case VICI_SECTION_END:
section--;
continue;
case VICI_END:
break;
default:
continue;
}
break;
}
}
/* find key/value in current section */
while (enumerator->enumerate(enumerator, &type, &name, &current))
{
switch (type)
{
case VICI_KEY_VALUE:
if (section == keysection && streq(key, name))
{
*value = current;
found = TRUE;
break;
}
continue;
case VICI_SECTION_START:
section++;
continue;
case VICI_SECTION_END:
section--;
continue;
case VICI_END:
break;
default:
continue;
}
break;
}
enumerator->destroy(enumerator);
return found;
}
METHOD(vici_message_t, vget_str, char*,
private_vici_message_t *this, char *def, char *fmt, va_list args)
{
chunk_t value;
bool found;
char *str;
found = find_value(this, &value, fmt, args);
if (found)
{
if (chunk_printable(value, NULL, 0))
{
str = strndup(value.ptr, value.len);
/* keep a reference to string, so caller doesn't have to care */
this->strings->insert_last(this->strings, str);
return str;
}
}
return def;
}
METHOD(vici_message_t, get_str, char*,
private_vici_message_t *this, char *def, char *fmt, ...)
{
va_list args;
char *str;
va_start(args, fmt);
str = vget_str(this, def, fmt, args);
va_end(args);
return str;
}
METHOD(vici_message_t, vget_int, int,
private_vici_message_t *this, int def, char *fmt, va_list args)
{
chunk_t value;
bool found;
char buf[32], *pos;
int ret;
found = find_value(this, &value, fmt, args);
if (found)
{
if (chunk_printable(value, NULL, 0))
{
snprintf(buf, sizeof(buf), "%.*s", (int)value.len, value.ptr);
errno = 0;
ret = strtol(buf, &pos, 0);
if (errno == 0 && pos == buf + strlen(buf))
{
return ret;
}
}
}
return def;
}
METHOD(vici_message_t, get_int, int,
private_vici_message_t *this, int def, char *fmt, ...)
{
va_list args;
int val;
va_start(args, fmt);
val = vget_int(this, def, fmt, args);
va_end(args);
return val;
}
METHOD(vici_message_t, vget_value, chunk_t,
private_vici_message_t *this, chunk_t def, char *fmt, va_list args)
{
chunk_t value;
bool found;
found = find_value(this, &value, fmt, args);
if (found)
{
return value;
}
return def;
}
METHOD(vici_message_t, get_value, chunk_t,
private_vici_message_t *this, chunk_t def, char *fmt, ...)
{
va_list args;
chunk_t value;
va_start(args, fmt);
value = vget_value(this, def, fmt, args);
va_end(args);
return value;
}
METHOD(vici_message_t, get_encoding, chunk_t,
private_vici_message_t *this)
{
return this->encoding;
}
/**
* Private parse context data
*/
struct vici_parse_context_t {
/** current section nesting level */
int level;
/** parse enumerator */
enumerator_t *e;
};
METHOD(vici_message_t, parse, bool,
private_vici_message_t *this, vici_parse_context_t *ctx,
vici_section_cb_t section, vici_value_cb_t kv, vici_value_cb_t li,
void *user)
{
vici_parse_context_t root = {};
char *name, *list = NULL;
vici_type_t type;
chunk_t value;
int base;
bool ok = TRUE;
if (!ctx)
{
ctx = &root;
root.e = create_enumerator(this);
}
base = ctx->level;
while (ok)
{
ok = ctx->e->enumerate(ctx->e, &type, &name, &value);
if (ok)
{
switch (type)
{
case VICI_KEY_VALUE:
if (ctx->level == base && kv)
{
name = strdup(name);
this->strings->insert_last(this->strings, name);
ok = kv(user, &this->public, name, value);
}
continue;
case VICI_LIST_START:
if (ctx->level == base)
{
list = strdup(name);
this->strings->insert_last(this->strings, list);
}
continue;
case VICI_LIST_ITEM:
if (list && li)
{
name = strdup(name);
this->strings->insert_last(this->strings, name);
ok = li(user, &this->public, list, value);
}
continue;
case VICI_LIST_END:
if (ctx->level == base)
{
list = NULL;
}
continue;
case VICI_SECTION_START:
if (ctx->level++ == base && section)
{
name = strdup(name);
this->strings->insert_last(this->strings, name);
ok = section(user, &this->public, ctx, name);
}
continue;
case VICI_SECTION_END:
if (ctx->level-- == base)
{
break;
}
continue;
case VICI_END:
break;
}
}
break;
}
if (ctx == &root)
{
root.e->destroy(root.e);
}
return ok;
}
METHOD(vici_message_t, dump, bool,
private_vici_message_t *this, char *label, FILE *out)
{
enumerator_t *enumerator;
int ident = 0, delta = 2;
vici_type_t type;
char *name;
chunk_t value;
fprintf(out, "%s {\n", label);
ident += delta;
enumerator = create_enumerator(this);
while (enumerator->enumerate(enumerator, &type, &name, &value))
{
switch (type)
{
case VICI_SECTION_START:
fprintf(out, "%*s%s {\n", ident, "", name);
ident += delta;
break;
case VICI_SECTION_END:
ident -= delta;
fprintf(out, "%*s}\n", ident, "");
break;
case VICI_KEY_VALUE:
if (chunk_printable(value, NULL, ' '))
{
fprintf(out, "%*s%s = %.*s\n",
ident, "", name, (int)value.len, value.ptr);
}
else
{
fprintf(out, "%*s%s = 0x%+#B\n",
ident, "", name, &value);
}
break;
case VICI_LIST_START:
fprintf(out, "%*s%s = [\n", ident, "", name);
ident += delta;
break;
case VICI_LIST_END:
ident -= delta;
fprintf(out, "%*s]\n", ident, "");
break;
case VICI_LIST_ITEM:
if (chunk_printable(value, NULL, ' '))
{
fprintf(out, "%*s%.*s\n",
ident, "", (int)value.len, value.ptr);
}
else
{
fprintf(out, "%*s 0x%+#B\n", ident, "", &value);
}
break;
case VICI_END:
fprintf(out, "}\n");
enumerator->destroy(enumerator);
return TRUE;
}
}
enumerator->destroy(enumerator);
return FALSE;
}
METHOD(vici_message_t, destroy, void,
private_vici_message_t *this)
{
if (this->cleanup)
{
chunk_clear(&this->encoding);
}
this->strings->destroy_function(this->strings, free);
free(this);
}
/**
* See header
*/
vici_message_t *vici_message_create_from_data(chunk_t data, bool cleanup)
{
private_vici_message_t *this;
INIT(this,
.public = {
.create_enumerator = _create_enumerator,
.get_str = _get_str,
.vget_str = _vget_str,
.get_int = _get_int,
.vget_int = _vget_int,
.get_value = _get_value,
.vget_value = _vget_value,
.get_encoding = _get_encoding,
.parse = _parse,
.dump = _dump,
.destroy = _destroy,
},
.strings = linked_list_create(),
.encoding = data,
.cleanup = cleanup,
);
return &this->public;
}
/**
* See header
*/
vici_message_t *vici_message_create_from_enumerator(enumerator_t *enumerator)
{
vici_builder_t *builder;
vici_type_t type;
char *name;
chunk_t value;
builder = vici_builder_create();
while (enumerator->enumerate(enumerator, &type, &name, &value))
{
switch (type)
{
case VICI_SECTION_START:
case VICI_LIST_START:
builder->add(builder, type, name);
continue;
case VICI_KEY_VALUE:
builder->add(builder, type, name, value);
continue;
case VICI_LIST_ITEM:
builder->add(builder, type, value);
continue;
case VICI_SECTION_END:
case VICI_LIST_END:
default:
builder->add(builder, type);
continue;
case VICI_END:
break;
}
break;
}
enumerator->destroy(enumerator);
return builder->finalize(builder);
}
/**
* See header
*/
vici_message_t *vici_message_create_from_args(vici_type_t type, ...)
{
vici_builder_t *builder;
va_list args;
char *name;
chunk_t value;
builder = vici_builder_create();
va_start(args, type);
while (type != VICI_END)
{
switch (type)
{
case VICI_LIST_START:
case VICI_SECTION_START:
name = va_arg(args, char*);
builder->add(builder, type, name);
break;
case VICI_KEY_VALUE:
name = va_arg(args, char*);
value = va_arg(args, chunk_t);
builder->add(builder, type, name, value);
break;
case VICI_LIST_ITEM:
value = va_arg(args, chunk_t);
builder->add(builder, type, value);
break;
case VICI_SECTION_END:
case VICI_LIST_END:
default:
builder->add(builder, type);
break;
}
type = va_arg(args, vici_type_t);
}
va_end(args);
return builder->finalize(builder);
}
+244
View File
@@ -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_ @}*/
+169
View File
@@ -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;
}
+42
View File
@@ -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_ @}*/
+855
View File
@@ -0,0 +1,855 @@
/*
* Copyright (C) 2014 Martin Willi
* Copyright (C) 2014 revosec AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "vici_query.h"
#include "vici_builder.h"
#include <inttypes.h>
#include <sys/utsname.h>
#include <daemon.h>
typedef struct private_vici_query_t private_vici_query_t;
/**
* Private data of an vici_query_t object.
*/
struct private_vici_query_t {
/**
* Public vici_query_t interface.
*/
vici_query_t public;
/**
* Dispatcher
*/
vici_dispatcher_t *dispatcher;
};
/**
* List details of a CHILD_SA
*/
static void list_child(private_vici_query_t *this, vici_builder_t *b,
child_sa_t *child, time_t now)
{
time_t t;
u_int64_t bytes, packets;
u_int16_t alg, ks;
proposal_t *proposal;
enumerator_t *enumerator;
traffic_selector_t *ts;
b->add_kv(b, "reqid", "%u", child->get_reqid(child));
b->add_kv(b, "state", "%N", child_sa_state_names, child->get_state(child));
b->add_kv(b, "mode", "%N", ipsec_mode_names, child->get_mode(child));
if (child->get_state(child) == CHILD_INSTALLED ||
child->get_state(child) == CHILD_REKEYING)
{
b->add_kv(b, "protocol", "%N", protocol_id_names,
child->get_protocol(child));
if (child->has_encap(child))
{
b->add_kv(b, "encap", "yes");
}
b->add_kv(b, "spi-in", "%.8x", ntohl(child->get_spi(child, TRUE)));
b->add_kv(b, "spi-out", "%.8x", ntohl(child->get_spi(child, FALSE)));
if (child->get_ipcomp(child) != IPCOMP_NONE)
{
b->add_kv(b, "cpi-in", "%.4x", ntohs(child->get_cpi(child, TRUE)));
b->add_kv(b, "cpi-out", "%.4x", ntohs(child->get_cpi(child, FALSE)));
}
proposal = child->get_proposal(child);
if (proposal)
{
if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM,
&alg, &ks) && alg != ENCR_UNDEFINED)
{
b->add_kv(b, "encr-alg", "%N", encryption_algorithm_names, alg);
if (ks)
{
b->add_kv(b, "encr-keysize", "%u", ks);
}
}
if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM,
&alg, &ks) && alg != ENCR_UNDEFINED)
{
b->add_kv(b, "integ-alg", "%N", integrity_algorithm_names, alg);
if (ks)
{
b->add_kv(b, "integ-keysize", "%u", ks);
}
}
if (proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION,
&alg, NULL))
{
b->add_kv(b, "prf-alg", "%N", pseudo_random_function_names, alg);
}
if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP,
&alg, NULL))
{
b->add_kv(b, "dh-group", "%N", diffie_hellman_group_names, alg);
}
if (proposal->get_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS,
&alg, NULL) && alg == EXT_SEQ_NUMBERS)
{
b->add_kv(b, "esn", "1");
}
}
child->get_usestats(child, TRUE, &t, &bytes, &packets);
b->add_kv(b, "bytes-in", "%" PRIu64, bytes);
b->add_kv(b, "packets-in", "%" PRIu64, packets);
if (t)
{
b->add_kv(b, "use-in", "%"PRIu64, (u_int64_t)(now - t));
}
child->get_usestats(child, FALSE, &t, &bytes, &packets);
b->add_kv(b, "bytes-out", "%"PRIu64, bytes);
b->add_kv(b, "packets-out", "%"PRIu64, packets);
if (t)
{
b->add_kv(b, "use-out", "%"PRIu64, (u_int64_t)(now - t));
}
t = child->get_lifetime(child, FALSE);
if (t)
{
b->add_kv(b, "rekey-time", "%"PRId64, (int64_t)(t - now));
}
t = child->get_lifetime(child, TRUE);
if (t)
{
b->add_kv(b, "life-time", "%"PRId64, (int64_t)(t - now));
}
t = child->get_installtime(child);
b->add_kv(b, "install-time", "%"PRId64, (int64_t)(now - t));
}
b->begin_list(b, "local-ts");
enumerator = child->create_ts_enumerator(child, TRUE);
while (enumerator->enumerate(enumerator, &ts))
{
b->add_li(b, "%R", ts);
}
enumerator->destroy(enumerator);
b->end_list(b /* local-ts */);
b->begin_list(b, "remote-ts");
enumerator = child->create_ts_enumerator(child, FALSE);
while (enumerator->enumerate(enumerator, &ts))
{
b->add_li(b, "%R", ts);
}
enumerator->destroy(enumerator);
b->end_list(b /* remote-ts */);
}
/**
* List tasks in a specific queue
*/
static void list_task_queue(private_vici_query_t *this, vici_builder_t *b,
ike_sa_t *ike_sa, task_queue_t q, char *name)
{
enumerator_t *enumerator;
bool has = FALSE;
task_t *task;
enumerator = ike_sa->create_task_enumerator(ike_sa, q);
while (enumerator->enumerate(enumerator, &task))
{
if (!has)
{
b->begin_list(b, name);
has = TRUE;
}
b->add_li(b, "%N", task_type_names, task->get_type(task));
}
enumerator->destroy(enumerator);
if (has)
{
b->end_list(b);
}
}
/**
* List details of an IKE_SA
*/
static void list_ike(private_vici_query_t *this, vici_builder_t *b,
ike_sa_t *ike_sa, time_t now)
{
time_t t;
ike_sa_id_t *id;
identification_t *eap;
proposal_t *proposal;
u_int16_t alg, ks;
b->add_kv(b, "uniqueid", "%u", ike_sa->get_unique_id(ike_sa));
b->add_kv(b, "version", "%u", ike_sa->get_version(ike_sa));
b->add_kv(b, "state", "%N", ike_sa_state_names, ike_sa->get_state(ike_sa));
b->add_kv(b, "local-host", "%H", ike_sa->get_my_host(ike_sa));
b->add_kv(b, "local-id", "%Y", ike_sa->get_my_id(ike_sa));
b->add_kv(b, "remote-host", "%H", ike_sa->get_other_host(ike_sa));
b->add_kv(b, "remote-id", "%Y", ike_sa->get_other_id(ike_sa));
eap = ike_sa->get_other_eap_id(ike_sa);
if (!eap->equals(eap, ike_sa->get_other_id(ike_sa)))
{
if (ike_sa->get_version(ike_sa) == IKEV1)
{
b->add_kv(b, "remote-xauth-id", "%Y", eap);
}
else
{
b->add_kv(b, "remote-eap-id", "%Y", eap);
}
}
id = ike_sa->get_id(ike_sa);
if (id->is_initiator(id))
{
b->add_kv(b, "initiator", "yes");
}
b->add_kv(b, "initiator-spi", "%.16"PRIx64, id->get_initiator_spi(id));
b->add_kv(b, "responder-spi", "%.16"PRIx64, id->get_responder_spi(id));
proposal = ike_sa->get_proposal(ike_sa);
if (proposal)
{
if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &ks))
{
b->add_kv(b, "encr-alg", "%N", encryption_algorithm_names, alg);
if (ks)
{
b->add_kv(b, "encr-keysize", "%u", ks);
}
}
if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, &ks))
{
b->add_kv(b, "integ-alg", "%N", integrity_algorithm_names, alg);
if (ks)
{
b->add_kv(b, "integ-keysize", "%u", ks);
}
}
if (proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL))
{
b->add_kv(b, "prf-alg", "%N", pseudo_random_function_names, alg);
}
if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &alg, NULL))
{
b->add_kv(b, "dh-group", "%N", diffie_hellman_group_names, alg);
}
}
if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED)
{
t = ike_sa->get_statistic(ike_sa, STAT_ESTABLISHED);
b->add_kv(b, "established", "%"PRId64, (int64_t)(now - t));
t = ike_sa->get_statistic(ike_sa, STAT_REKEY);
if (t)
{
b->add_kv(b, "rekey-time", "%"PRId64, (int64_t)(t - now));
}
t = ike_sa->get_statistic(ike_sa, STAT_REAUTH);
if (t)
{
b->add_kv(b, "reauth-time", "%"PRId64, (int64_t)(t - now));
}
}
list_task_queue(this, b, ike_sa, TASK_QUEUE_QUEUED, "tasks-queued");
list_task_queue(this, b, ike_sa, TASK_QUEUE_ACTIVE, "tasks-active");
list_task_queue(this, b, ike_sa, TASK_QUEUE_PASSIVE, "tasks-passive");
}
CALLBACK(list_sas, vici_message_t*,
private_vici_query_t *this, char *name, u_int id, vici_message_t *request)
{
vici_builder_t *b;
enumerator_t *isas, *csas;
ike_sa_t *ike_sa;
child_sa_t *child_sa;
time_t now;
char *ike;
u_int ike_id;
bool bl;
bl = request->get_str(request, NULL, "noblock") == NULL;
ike = request->get_str(request, NULL, "ike");
ike_id = request->get_int(request, 0, "ike-id");
isas = charon->controller->create_ike_sa_enumerator(charon->controller, bl);
while (isas->enumerate(isas, &ike_sa))
{
if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
{
continue;
}
if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa))
{
continue;
}
now = time_monotonic(NULL);
b = vici_builder_create();
b->begin_section(b, ike_sa->get_name(ike_sa));
list_ike(this, b, ike_sa, now);
b->begin_section(b, "child-sas");
csas = ike_sa->create_child_sa_enumerator(ike_sa);
while (csas->enumerate(csas, &child_sa))
{
b->begin_section(b, child_sa->get_name(child_sa));
list_child(this, b, child_sa, now);
b->end_section(b);
}
csas->destroy(csas);
b->end_section(b /* child-sas */ );
b->end_section(b);
this->dispatcher->raise_event(this->dispatcher, "list-sa", id,
b->finalize(b));
}
isas->destroy(isas);
b = vici_builder_create();
return b->finalize(b);
}
/**
* Raise a list-policy event for given CHILD_SA
*/
static void raise_policy(private_vici_query_t *this, u_int id, child_sa_t *child)
{
enumerator_t *enumerator;
traffic_selector_t *ts;
vici_builder_t *b;
b = vici_builder_create();
b->begin_section(b, child->get_name(child));
b->add_kv(b, "mode", "%N", ipsec_mode_names, child->get_mode(child));
b->begin_list(b, "local-ts");
enumerator = child->create_ts_enumerator(child, TRUE);
while (enumerator->enumerate(enumerator, &ts))
{
b->add_li(b, "%R", ts);
}
enumerator->destroy(enumerator);
b->end_list(b /* local-ts */);
b->begin_list(b, "remote-ts");
enumerator = child->create_ts_enumerator(child, FALSE);
while (enumerator->enumerate(enumerator, &ts))
{
b->add_li(b, "%R", ts);
}
enumerator->destroy(enumerator);
b->end_list(b /* remote-ts */);
b->end_section(b);
this->dispatcher->raise_event(this->dispatcher, "list-policy", id,
b->finalize(b));
}
/**
* Raise a list-policy event for given CHILD_SA config
*/
static void raise_policy_cfg(private_vici_query_t *this, u_int id,
child_cfg_t *cfg)
{
enumerator_t *enumerator;
linked_list_t *list;
traffic_selector_t *ts;
vici_builder_t *b;
b = vici_builder_create();
b->begin_section(b, cfg->get_name(cfg));
b->add_kv(b, "mode", "%N", ipsec_mode_names, cfg->get_mode(cfg));
b->begin_list(b, "local-ts");
list = cfg->get_traffic_selectors(cfg, TRUE, NULL, NULL);
enumerator = list->create_enumerator(list);
while (enumerator->enumerate(enumerator, &ts))
{
b->add_li(b, "%R", ts);
}
enumerator->destroy(enumerator);
list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
b->end_list(b /* local-ts */);
b->begin_list(b, "remote-ts");
list = cfg->get_traffic_selectors(cfg, FALSE, NULL, NULL);
enumerator = list->create_enumerator(list);
while (enumerator->enumerate(enumerator, &ts))
{
b->add_li(b, "%R", ts);
}
enumerator->destroy(enumerator);
list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
b->end_list(b /* remote-ts */);
b->end_section(b);
this->dispatcher->raise_event(this->dispatcher, "list-policy", id,
b->finalize(b));
}
CALLBACK(list_policies, vici_message_t*,
private_vici_query_t *this, char *name, u_int id, vici_message_t *request)
{
enumerator_t *enumerator;
vici_builder_t *b;
child_sa_t *child_sa;
child_cfg_t *child_cfg;
bool drop, pass, trap;
char *child;
drop = request->get_str(request, NULL, "drop") != NULL;
pass = request->get_str(request, NULL, "pass") != NULL;
trap = request->get_str(request, NULL, "trap") != NULL;
child = request->get_str(request, NULL, "child");
if (trap)
{
enumerator = charon->traps->create_enumerator(charon->traps);
while (enumerator->enumerate(enumerator, NULL, &child_sa))
{
if (child && !streq(child, child_sa->get_name(child_sa)))
{
continue;
}
raise_policy(this, id, child_sa);
}
enumerator->destroy(enumerator);
}
if (drop || pass)
{
enumerator = charon->shunts->create_enumerator(charon->shunts);
while (enumerator->enumerate(enumerator, &child_cfg))
{
if (child && !streq(child, child_cfg->get_name(child_cfg)))
{
continue;
}
switch (child_cfg->get_mode(child_cfg))
{
case MODE_DROP:
if (drop)
{
raise_policy_cfg(this, id, child_cfg);
}
break;
case MODE_PASS:
if (pass)
{
raise_policy_cfg(this, id, child_cfg);
}
break;
default:
break;
}
}
enumerator->destroy(enumerator);
}
b = vici_builder_create();
return b->finalize(b);
}
/**
* Build sections for auth configs, local or remote
*/
static void build_auth_cfgs(peer_cfg_t *peer_cfg, bool local, vici_builder_t *b)
{
enumerator_t *enumerator, *rules;
auth_rule_t rule;
auth_cfg_t *auth;
union {
uintptr_t u;
identification_t *id;
certificate_t *cert;
char *str;
} v;
enumerator = peer_cfg->create_auth_cfg_enumerator(peer_cfg, local);
while (enumerator->enumerate(enumerator, &auth))
{
b->begin_section(b, local ? "local" : "remote");
rules = auth->create_enumerator(auth);
while (rules->enumerate(rules, &rule, &v))
{
switch (rule)
{
case AUTH_RULE_AUTH_CLASS:
b->add_kv(b, "class", "%N", auth_class_names, v.u);
break;
case AUTH_RULE_EAP_TYPE:
b->add_kv(b, "eap-type", "%N", eap_type_names, v.u);
break;
case AUTH_RULE_EAP_VENDOR:
b->add_kv(b, "eap-vendor", "%u", v.u);
break;
case AUTH_RULE_XAUTH_BACKEND:
b->add_kv(b, "xauth", "%s", v.str);
break;
case AUTH_RULE_CRL_VALIDATION:
b->add_kv(b, "revocation", "%N", cert_validation_names, v.u);
break;
case AUTH_RULE_IDENTITY:
b->add_kv(b, "id", "%Y", v.id);
break;
case AUTH_RULE_AAA_IDENTITY:
b->add_kv(b, "aaa_id", "%Y", v.id);
break;
case AUTH_RULE_EAP_IDENTITY:
b->add_kv(b, "eap_id", "%Y", v.id);
break;
case AUTH_RULE_XAUTH_IDENTITY:
b->add_kv(b, "xauth_id", "%Y", v.id);
break;
default:
break;
}
}
rules->destroy(rules);
b->begin_list(b, "groups");
rules = auth->create_enumerator(auth);
while (rules->enumerate(rules, &rule, &v))
{
if (rule == AUTH_RULE_GROUP)
{
b->add_li(b, "%Y", v.id);
}
}
rules->destroy(rules);
b->end_list(b);
b->begin_list(b, "certs");
rules = auth->create_enumerator(auth);
while (rules->enumerate(rules, &rule, &v))
{
if (rule == AUTH_RULE_SUBJECT_CERT)
{
b->add_li(b, "%Y", v.cert->get_subject(v.cert));
}
}
rules->destroy(rules);
b->end_list(b);
b->begin_list(b, "cacerts");
rules = auth->create_enumerator(auth);
while (rules->enumerate(rules, &rule, &v))
{
if (rule == AUTH_RULE_CA_CERT)
{
b->add_li(b, "%Y", v.cert->get_subject(v.cert));
}
}
rules->destroy(rules);
b->end_list(b);
b->end_section(b);
}
enumerator->destroy(enumerator);
}
CALLBACK(list_conns, vici_message_t*,
private_vici_query_t *this, char *name, u_int id, vici_message_t *request)
{
enumerator_t *enumerator, *tokens, *selectors, *children;
peer_cfg_t *peer_cfg;
ike_cfg_t *ike_cfg;
child_cfg_t *child_cfg;
char *ike, *str;
linked_list_t *list;
traffic_selector_t *ts;
vici_builder_t *b;
ike = request->get_str(request, NULL, "ike");
enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
NULL, NULL, NULL, NULL, IKE_ANY);
while (enumerator->enumerate(enumerator, &peer_cfg))
{
if (ike && !streq(ike, peer_cfg->get_name(peer_cfg)))
{
continue;
}
b = vici_builder_create();
b->begin_section(b, peer_cfg->get_name(peer_cfg));
ike_cfg = peer_cfg->get_ike_cfg(peer_cfg);
b->begin_list(b, "local_addrs");
str = ike_cfg->get_my_addr(ike_cfg);
tokens = enumerator_create_token(str, ",", " ");
while (tokens->enumerate(tokens, &str))
{
b->add_li(b, "%s", str);
}
tokens->destroy(tokens);
b->end_list(b);
b->begin_list(b, "remote_addrs");
str = ike_cfg->get_other_addr(ike_cfg);
tokens = enumerator_create_token(str, ",", " ");
while (tokens->enumerate(tokens, &str))
{
b->add_li(b, "%s", str);
}
tokens->destroy(tokens);
b->end_list(b);
b->add_kv(b, "version", "%N", ike_version_names,
peer_cfg->get_ike_version(peer_cfg));
build_auth_cfgs(peer_cfg, TRUE, b);
build_auth_cfgs(peer_cfg, FALSE, b);
b->begin_section(b, "children");
children = peer_cfg->create_child_cfg_enumerator(peer_cfg);
while (children->enumerate(children, &child_cfg))
{
b->begin_section(b, child_cfg->get_name(child_cfg));
b->add_kv(b, "mode", "%N", ipsec_mode_names,
child_cfg->get_mode(child_cfg));
b->begin_list(b, "local-ts");
list = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL);
selectors = list->create_enumerator(list);
while (selectors->enumerate(selectors, &ts))
{
b->add_li(b, "%R", ts);
}
selectors->destroy(selectors);
list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
b->end_list(b /* local-ts */);
b->begin_list(b, "remote-ts");
list = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, NULL);
selectors = list->create_enumerator(list);
while (selectors->enumerate(selectors, &ts))
{
b->add_li(b, "%R", ts);
}
selectors->destroy(selectors);
list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
b->end_list(b /* remote-ts */);
b->end_section(b);
}
children->destroy(children);
b->end_section(b); /* children */
b->end_section(b); /* name */
this->dispatcher->raise_event(this->dispatcher, "list-conn", id,
b->finalize(b));
}
enumerator->destroy(enumerator);
b = vici_builder_create();
return b->finalize(b);
}
/**
* Do we have a private key for given certificate
*/
static bool has_privkey(private_vici_query_t *this, certificate_t *cert)
{
private_key_t *private;
public_key_t *public;
identification_t *keyid;
chunk_t chunk;
bool found = FALSE;
public = cert->get_public_key(cert);
if (public)
{
if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk))
{
keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
private = lib->credmgr->get_private(lib->credmgr,
public->get_type(public), keyid, NULL);
if (private)
{
found = TRUE;
private->destroy(private);
}
keyid->destroy(keyid);
}
public->destroy(public);
}
return found;
}
CALLBACK(list_certs, vici_message_t*,
private_vici_query_t *this, char *name, u_int id, vici_message_t *request)
{
enumerator_t *enumerator, *added;
linked_list_t *list;
certificate_t *cert, *current;
chunk_t encoding;
identification_t *subject = NULL;
int type;
vici_builder_t *b;
bool found;
char *str;
str = request->get_str(request, "ANY", "type");
type = enum_from_name(certificate_type_names, str);
if (type == -1)
{
b = vici_builder_create();
return b->finalize(b);
}
str = request->get_str(request, NULL, "subject");
if (str)
{
subject = identification_create_from_string(str);
}
list = linked_list_create();
enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
type, KEY_ANY, subject, FALSE);
while (enumerator->enumerate(enumerator, &cert))
{
found = FALSE;
added = list->create_enumerator(list);
while (added->enumerate(added, &current))
{
if (current->equals(current, cert))
{
found = TRUE;
break;
}
}
added->destroy(added);
if (!found && cert->get_encoding(cert, CERT_ASN1_DER, &encoding))
{
b = vici_builder_create();
b->add_kv(b, "type", "%N",
certificate_type_names, cert->get_type(cert));
if (has_privkey(this, cert))
{
b->add_kv(b, "has_privkey", "yes");
}
b->add(b, VICI_KEY_VALUE, "data", encoding);
free(encoding.ptr);
this->dispatcher->raise_event(this->dispatcher, "list-cert", id,
b->finalize(b));
list->insert_last(list, cert->get_ref(cert));
}
}
enumerator->destroy(enumerator);
list->destroy_offset(list, offsetof(certificate_t, destroy));
DESTROY_IF(subject);
b = vici_builder_create();
return b->finalize(b);
}
CALLBACK(version, vici_message_t*,
private_vici_query_t *this, char *name, u_int id, vici_message_t *request)
{
struct utsname utsname;
vici_builder_t *b;
b = vici_builder_create();
b->add_kv(b, "daemon", "%s", lib->ns);
b->add_kv(b, "version", "%s", VERSION);
if (uname(&utsname) == 0)
{
b->add_kv(b, "sysname", "%s", utsname.sysname);
b->add_kv(b, "release", "%s", utsname.release);
b->add_kv(b, "machine", "%s", utsname.machine);
}
return b->finalize(b);
}
static void manage_command(private_vici_query_t *this,
char *name, vici_command_cb_t cb, bool reg)
{
this->dispatcher->manage_command(this->dispatcher, name,
reg ? cb : NULL, this);
}
/**
* (Un-)register dispatcher functions
*/
static void manage_commands(private_vici_query_t *this, bool reg)
{
this->dispatcher->manage_event(this->dispatcher, "list-sa", reg);
this->dispatcher->manage_event(this->dispatcher, "list-policy", reg);
this->dispatcher->manage_event(this->dispatcher, "list-conn", reg);
this->dispatcher->manage_event(this->dispatcher, "list-cert", reg);
manage_command(this, "list-sas", list_sas, reg);
manage_command(this, "list-policies", list_policies, reg);
manage_command(this, "list-conns", list_conns, reg);
manage_command(this, "list-certs", list_certs, reg);
manage_command(this, "version", version, reg);
}
METHOD(vici_query_t, destroy, void,
private_vici_query_t *this)
{
manage_commands(this, FALSE);
free(this);
}
/**
* See header
*/
vici_query_t *vici_query_create(vici_dispatcher_t *dispatcher)
{
private_vici_query_t *this;
INIT(this,
.public = {
.destroy = _destroy,
},
.dispatcher = dispatcher,
);
manage_commands(this, TRUE);
return &this->public;
}
+47
View File
@@ -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_ @}*/
+679
View File
@@ -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;
}
+95
View File
@@ -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_ @}*/
+46
View File
@@ -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);
}
+19
View File
@@ -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)
+73 -3
View File
@@ -307,8 +307,25 @@ METHOD(credential_set_t, create_private_enumerator, enumerator_t*,
METHOD(mem_cred_t, add_key, void,
private_mem_cred_t *this, private_key_t *key)
{
enumerator_t *enumerator;
private_key_t *current;
this->lock->write_lock(this->lock);
enumerator = this->keys->create_enumerator(this->keys);
while (enumerator->enumerate(enumerator, &current))
{
if (current->equals(current, key))
{
this->keys->remove_at(this->keys, enumerator);
current->destroy(current);
break;
}
}
enumerator->destroy(enumerator);
this->keys->insert_first(this->keys, key);
this->lock->unlock(this->lock);
}
@@ -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, &current))
{
if (shared_entry_equals(current, new))
{
this->shared->remove_at(this->shared, enumerator);
shared_entry_destroy(current);
break;
}
}
enumerator->destroy(enumerator);
this->shared->insert_first(this->shared, new);
this->lock->unlock(this->lock);
}
+11 -1
View File
@@ -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);
+16 -2
View File
@@ -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;
+13
View File
@@ -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).
*