From 32b2a5e04b075655564f72a902ee67a69c18ef2a Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 24 Jun 2013 14:58:01 +0200 Subject: [PATCH 01/54] watcher: add a centralized an generic facility to monitor file descriptors --- src/libstrongswan/Android.mk | 2 +- src/libstrongswan/Makefile.am | 4 +- src/libstrongswan/library.c | 2 + src/libstrongswan/library.h | 6 + src/libstrongswan/processing/watcher.c | 396 +++++++++++++++++++++++++ src/libstrongswan/processing/watcher.h | 97 ++++++ 6 files changed, 504 insertions(+), 3 deletions(-) create mode 100644 src/libstrongswan/processing/watcher.c create mode 100644 src/libstrongswan/processing/watcher.h diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index dc533a38b..bb5b78646 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -29,7 +29,7 @@ networking/host.c networking/host_resolver.c networking/packet.c \ networking/tun_device.c \ pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \ -resolver/resolver_manager.c resolver/rr_set.c \ +processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \ selectors/traffic_selector.c threading/thread.c threading/thread_value.c \ threading/mutex.c threading/semaphore.c threading/rwlock.c threading/spinlock.c \ utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \ diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index e131f2ef9..d8e3cf90a 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -27,7 +27,7 @@ networking/host.c networking/host_resolver.c networking/packet.c \ networking/tun_device.c \ pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \ -resolver/resolver_manager.c resolver/rr_set.c \ +processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \ selectors/traffic_selector.c threading/thread.c threading/thread_value.c \ threading/mutex.c threading/semaphore.c threading/rwlock.c threading/spinlock.c \ utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \ @@ -70,7 +70,7 @@ resolver/resolver.h resolver/resolver_response.h resolver/rr_set.h \ resolver/rr.h resolver/resolver_manager.h \ plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.h \ processing/jobs/job.h processing/jobs/callback_job.h processing/processor.h \ -processing/scheduler.h selectors/traffic_selector.h \ +processing/scheduler.h processing/watcher.h selectors/traffic_selector.h \ threading/thread.h threading/thread_value.h \ threading/mutex.h threading/condvar.h threading/spinlock.h threading/semaphore.h \ threading/rwlock.h threading/rwlock_condvar.h threading/lock_profiler.h \ diff --git a/src/libstrongswan/library.c b/src/libstrongswan/library.c index 05d984b18..35d74200c 100644 --- a/src/libstrongswan/library.c +++ b/src/libstrongswan/library.c @@ -80,6 +80,7 @@ void library_deinit() /* make sure the cache is clear before unloading plugins */ lib->credmgr->flush_cache(lib->credmgr, CERT_ANY); + this->public.watcher->destroy(this->public.watcher); this->public.scheduler->destroy(this->public.scheduler); this->public.processor->destroy(this->public.processor); this->public.plugins->destroy(this->public.plugins); @@ -266,6 +267,7 @@ bool library_init(char *settings) this->public.db = database_factory_create(); this->public.processor = processor_create(); this->public.scheduler = scheduler_create(); + this->public.watcher = watcher_create(); this->public.plugins = plugin_loader_create(); if (!check_memwipe()) diff --git a/src/libstrongswan/library.h b/src/libstrongswan/library.h index 1168da8fd..d5497258a 100644 --- a/src/libstrongswan/library.h +++ b/src/libstrongswan/library.h @@ -92,6 +92,7 @@ #include "networking/host_resolver.h" #include "processing/processor.h" #include "processing/scheduler.h" +#include "processing/watcher.h" #include "crypto/crypto_factory.h" #include "crypto/proposal/proposal_keywords.h" #include "fetcher/fetcher_manager.h" @@ -196,6 +197,11 @@ struct library_t { */ scheduler_t *scheduler; + /** + * File descriptor monitoring + */ + watcher_t *watcher; + /** * resolve hosts by DNS name */ diff --git a/src/libstrongswan/processing/watcher.c b/src/libstrongswan/processing/watcher.c new file mode 100644 index 000000000..7ccac72bc --- /dev/null +++ b/src/libstrongswan/processing/watcher.c @@ -0,0 +1,396 @@ +/* + * 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 . + * + * 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 "watcher.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +typedef struct private_watcher_t private_watcher_t; + +/** + * Private data of an watcher_t object. + */ +struct private_watcher_t { + + /** + * Public watcher_t interface. + */ + watcher_t public; + + /** + * List of registered FDs, as entry_t + */ + linked_list_t *fds; + + /** + * Lock to access FD list + */ + mutex_t *mutex; + + /** + * Condvar to signal completion of callback + */ + condvar_t *condvar; + + /** + * Notification pipe to signal watcher thread + */ + int notify[2]; +}; + +/** + * Entry for a registered file descriptor + */ +typedef struct { + /** file descriptor */ + int fd; + /** events to watch */ + watcher_event_t events; + /** registered callback function */ + watcher_cb_t cb; + /** user data to pass to callback */ + void *data; + /** callback currently active? */ + bool active; +} entry_t; + +/** + * Data we pass on for an async notification + */ +typedef struct { + /** file descriptor */ + int fd; + /** event type */ + watcher_event_t event; + /** registered callback function */ + watcher_cb_t cb; + /** user data to pass to callback */ + void *data; + /** keep registered? */ + bool keep; + /** reference to watcher */ + private_watcher_t *this; +} notify_data_t; + +/** + * Notify watcher thread about changes + */ +static void update(private_watcher_t *this) +{ + char buf[1] = { 'u' }; + + if (this->notify[1] != -1) + { + ignore_result(write(this->notify[1], buf, sizeof(buf))); + } +} + + /** + * Execute callback of registered FD, asynchronous + */ +static job_requeue_t notify_async(notify_data_t *data) +{ + data->keep = data->cb(data->data, data->fd, data->event); + return JOB_REQUEUE_NONE; +} + +/** + * Clean up notification data, reactivate FD + */ +static void notify_end(notify_data_t *data) +{ + private_watcher_t *this = data->this; + enumerator_t *enumerator; + entry_t *entry; + + /* reactivate the disabled entry */ + this->mutex->lock(this->mutex); + enumerator = this->fds->create_enumerator(this->fds); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->fd == data->fd) + { + if (!data->keep) + { + entry->events &= ~data->event; + if (!entry->events) + { + this->fds->remove_at(this->fds, enumerator); + free(entry); + break; + } + } + entry->active = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + update(this); + this->condvar->broadcast(this->condvar); + this->mutex->unlock(this->mutex); + + free(data); +} + +/** + * Execute the callback for a registered FD + */ +static bool notify(private_watcher_t *this, entry_t *entry, + watcher_event_t event) +{ + notify_data_t *data; + + /* get a copy of entry for async job, but with specific event */ + INIT(data, + .fd = entry->fd, + .event = event, + .cb = entry->cb, + .data = entry->data, + .keep = TRUE, + .this = this, + ); + + /* deactivate entry, so we can select() other FDs even if the async + * processing did not handle the event yet */ + entry->active = FALSE; + + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create_with_prio((void*)notify_async, data, + (void*)notify_end, (callback_job_cancel_t)return_false, + JOB_PRIO_CRITICAL)); + return TRUE; +} + +/** + * Dispatching function + */ +static job_requeue_t watch(private_watcher_t *this) +{ + enumerator_t *enumerator; + entry_t *entry; + fd_set rd, wr, ex; + int maxfd = 0, res; + + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + + this->mutex->lock(this->mutex); + if (this->fds->get_count(this->fds) == 0) + { + this->mutex->unlock(this->mutex); + return JOB_REQUEUE_NONE; + } + + if (this->notify[0] != -1) + { + FD_SET(this->notify[0], &rd); + maxfd = this->notify[0]; + } + + enumerator = this->fds->create_enumerator(this->fds); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->active) + { + if (entry->events & WATCHER_READ) + { + FD_SET(entry->fd, &rd); + } + if (entry->events & WATCHER_WRITE) + { + FD_SET(entry->fd, &wr); + } + if (entry->events & WATCHER_EXCEPT) + { + FD_SET(entry->fd, &ex); + } + maxfd = max(maxfd, entry->fd); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); + + while (TRUE) + { + char buf[1]; + bool old, notified = FALSE; + + old = thread_cancelability(TRUE); + res = select(maxfd + 1, &rd, &wr, &ex, NULL); + thread_cancelability(old); + if (res > 0) + { + if (this->notify[0] != -1 && FD_ISSET(this->notify[0], &rd)) + { + ignore_result(read(this->notify[0], buf, sizeof(buf))); + return JOB_REQUEUE_DIRECT; + } + + this->mutex->lock(this->mutex); + enumerator = this->fds->create_enumerator(this->fds); + while (enumerator->enumerate(enumerator, &entry)) + { + if (FD_ISSET(entry->fd, &rd)) + { + notified = notify(this, entry, WATCHER_READ); + break; + } + if (FD_ISSET(entry->fd, &wr)) + { + notified = notify(this, entry, WATCHER_WRITE); + break; + } + if (FD_ISSET(entry->fd, &ex)) + { + notified = notify(this, entry, WATCHER_EXCEPT); + break; + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); + + if (notified) + { + /* we temporarily disable a notified FD, rebuild FDSET */ + return JOB_REQUEUE_DIRECT; + } + } + } +} + +METHOD(watcher_t, add, void, + private_watcher_t *this, int fd, watcher_event_t events, + watcher_cb_t cb, void *data) +{ + entry_t *entry; + + INIT(entry, + .fd = fd, + .events = events, + .cb = cb, + .data = data, + .active = TRUE, + ); + + this->mutex->lock(this->mutex); + this->fds->insert_last(this->fds, entry); + if (this->fds->get_count(this->fds) == 1) + { + 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)); + } + else + { + update(this); + } + this->mutex->unlock(this->mutex); +} + +METHOD(watcher_t, remove_, void, + private_watcher_t *this, int fd) +{ + enumerator_t *enumerator; + entry_t *entry; + + this->mutex->lock(this->mutex); + while (TRUE) + { + bool is_in_callback = FALSE; + + enumerator = this->fds->create_enumerator(this->fds); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->fd == fd) + { + if (entry->active) + { + this->fds->remove_at(this->fds, enumerator); + free(entry); + } + else + { + is_in_callback = TRUE; + break; + } + } + } + enumerator->destroy(enumerator); + if (!is_in_callback) + { + break; + } + this->condvar->wait(this->condvar, this->mutex); + } + + update(this); + this->mutex->unlock(this->mutex); +} + +METHOD(watcher_t, destroy, void, + private_watcher_t *this) +{ + this->mutex->destroy(this->mutex); + this->condvar->destroy(this->condvar); + this->fds->destroy(this->fds); + if (this->notify[0] != -1) + { + close(this->notify[0]); + } + if (this->notify[1] != -1) + { + close(this->notify[1]); + } + free(this); +} + +/** + * See header + */ +watcher_t *watcher_create() +{ + private_watcher_t *this; + + INIT(this, + .public = { + .add = _add, + .remove = _remove_, + .destroy = _destroy, + }, + .fds = linked_list_create(), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), + .notify[0] = -1, + .notify[1] = -1, + ); + + if (pipe(this->notify) != 0) + { + DBG1(DBG_LIB, "creating watcher notify pipe failed: %s", + strerror(errno)); + } + return &this->public; +} diff --git a/src/libstrongswan/processing/watcher.h b/src/libstrongswan/processing/watcher.h new file mode 100644 index 000000000..db7dd4fa8 --- /dev/null +++ b/src/libstrongswan/processing/watcher.h @@ -0,0 +1,97 @@ +/* + * 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 . + * + * 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 watcher watcher + * @{ @ingroup processor + */ + +#ifndef WATCHER_H_ +#define WATCHER_H_ + +typedef struct watcher_t watcher_t; +typedef enum watcher_event_t watcher_event_t; + +#include + +/** + * Callback function to register for file descriptor events. + * + * The callback is executed asynchronously using a thread from the pool. + * Monitoring of fd is temporarily suspended to avoid additional events while + * it is processed asynchronously. To allow concurrent events, one can quickly + * process it (using a read/write) and return from the callback. This will + * re-enable the event, while the data read can be processed in another + * asynchronous job. + * + * On Linux, even if select() marks an FD as "ready", a subsequent read/write + * can block. It is therefore highly recommended to use non-blocking I/O + * and handle EAGAIN/EWOULDBLOCK gracefully. + * + * @param data user data passed during registration + * @param fd file descriptor the event occured on + * @param event type of event + * @return TRUE to keep watching event, FALSE to unregister fd for event + */ +typedef bool (*watcher_cb_t)(void *data, int fd, watcher_event_t event); + +/** + * What events to watch for a file descriptor. + */ +enum watcher_event_t { + WATCHER_READ = (1<<0), + WATCHER_WRITE = (1<<1), + WATCHER_EXCEPT = (1<<2), +}; + +/** + * Watch multiple file descriptors using select(). + */ +struct watcher_t { + + /** + * Start watching a new file descriptor. + * + * @param fd file descriptor to start watching + * @param events ORed set of events to watch + * @param cb callback function to invoke on events + * @param data data to pass to cb() + */ + void (*add)(watcher_t *this, int fd, watcher_event_t events, + watcher_cb_t cb, void *data); + + /** + * Stop watching a previously registered file descriptor. + * + * This call blocks until any active callback for this FD returns. + * + * @param fd file descriptor to stop watching + */ + void (*remove)(watcher_t *this, int fd); + + /** + * Destroy a watcher_t. + */ + void (*destroy)(watcher_t *this); +}; + +/** + * Create a watcher instance. + * + * @return watcher + */ +watcher_t *watcher_create(); + +#endif /** WATCHER_H_ @}*/ From b6b940001a8c5b0e4c60433e1b377fc97bb17735 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 26 Jun 2013 17:03:19 +0200 Subject: [PATCH 02/54] stream: add a stream class abstracting BSD sockets Currently only synchronous operation is supported, but this will be extended with asynchronous methods using the new watcher. --- src/libstrongswan/Android.mk | 2 +- src/libstrongswan/Makefile.am | 4 +- src/libstrongswan/networking/streams/stream.c | 119 ++++++++++++++++++ src/libstrongswan/networking/streams/stream.h | 83 ++++++++++++ 4 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 src/libstrongswan/networking/streams/stream.c create mode 100644 src/libstrongswan/networking/streams/stream.h diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index bb5b78646..e95a6d479 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -26,7 +26,7 @@ credentials/sets/callback_cred.c credentials/auth_cfg.c database/database.c \ database/database_factory.c fetcher/fetcher.c fetcher/fetcher_manager.c eap/eap.c \ ipsec/ipsec_types.c \ networking/host.c networking/host_resolver.c networking/packet.c \ -networking/tun_device.c \ +networking/tun_device.c networking/streams/stream.c \ pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \ processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \ diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index d8e3cf90a..9f3c3ed2c 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -24,7 +24,7 @@ credentials/sets/callback_cred.c credentials/auth_cfg.c database/database.c \ database/database_factory.c fetcher/fetcher.c fetcher/fetcher_manager.c eap/eap.c \ ipsec/ipsec_types.c \ networking/host.c networking/host_resolver.c networking/packet.c \ -networking/tun_device.c \ +networking/tun_device.c networking/streams/stream.c \ pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \ processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \ @@ -65,7 +65,7 @@ credentials/auth_cfg.h credentials/credential_set.h credentials/cert_validator.h database/database.h database/database_factory.h fetcher/fetcher.h \ fetcher/fetcher_manager.h eap/eap.h pen/pen.h ipsec/ipsec_types.h \ networking/host.h networking/host_resolver.h networking/packet.h \ -networking/tun_device.h \ +networking/tun_device.h networking/streams/stream.h \ resolver/resolver.h resolver/resolver_response.h resolver/rr_set.h \ resolver/rr.h resolver/resolver_manager.h \ plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.h \ diff --git a/src/libstrongswan/networking/streams/stream.c b/src/libstrongswan/networking/streams/stream.c new file mode 100644 index 000000000..c6a73df17 --- /dev/null +++ b/src/libstrongswan/networking/streams/stream.c @@ -0,0 +1,119 @@ +/* + * 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 . + * + * 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 "stream.h" + +#include +#include + +typedef struct private_stream_t private_stream_t; + +/** + * Private data of an stream_t object. + */ +struct private_stream_t { + + /** + * Public stream_t interface. + */ + stream_t public; + + /** + * Underlying socket + */ + int fd; +}; + +METHOD(stream_t, read_, ssize_t, + private_stream_t *this, void *buf, size_t len, bool block) +{ + while (TRUE) + { + ssize_t ret; + + if (block) + { + ret = read(this->fd, buf, len); + } + else + { + ret = recv(this->fd, buf, len, MSG_DONTWAIT); + if (ret == -1 && errno == EAGAIN) + { + /* unify EGAIN and EWOULDBLOCK */ + errno = EWOULDBLOCK; + } + } + if (ret == -1 && errno == EINTR) + { /* interrupted, try again */ + continue; + } + return ret; + } +} + +METHOD(stream_t, write_, ssize_t, + private_stream_t *this, void *buf, size_t len, bool block) +{ + ssize_t ret; + + while (TRUE) + { + if (block) + { + ret = write(this->fd, buf, len); + } + else + { + ret = send(this->fd, buf, len, MSG_DONTWAIT); + if (ret == -1 && errno == EAGAIN) + { + /* unify EGAIN and EWOULDBLOCK */ + errno = EWOULDBLOCK; + } + } + if (ret == -1 && errno == EINTR) + { /* interrupted, try again */ + continue; + } + return ret; + } +} + +METHOD(stream_t, destroy, void, + private_stream_t *this) +{ + close(this->fd); + free(this); +} + +/** + * See header + */ +stream_t *stream_create_from_fd(int fd) +{ + private_stream_t *this; + + INIT(this, + .public = { + .read = _read_, + .write = _write_, + .destroy = _destroy, + }, + .fd = fd, + ); + + return &this->public; +} diff --git a/src/libstrongswan/networking/streams/stream.h b/src/libstrongswan/networking/streams/stream.h new file mode 100644 index 000000000..219e16ade --- /dev/null +++ b/src/libstrongswan/networking/streams/stream.h @@ -0,0 +1,83 @@ +/* + * 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 . + * + * 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 stream stream + * @{ @ingroup streams + */ + +#ifndef STREAM_H_ +#define STREAM_H_ + +typedef struct stream_t stream_t; + +#include + +/** + * Constructor function prototype for stream_t. + * + * @param uri URI to create a stream for + * @return stream instance, NULL on error + */ +typedef stream_t*(*stream_constructor_t)(char *uri); + +/** + * Abstraction of a Berkley socket using stream semantics. + */ +struct stream_t { + + /** + * Read data from the stream. + * + * If "block" is FALSE and no data is available, the function returns -1 + * and sets errno to EWOULDBLOCK. + * + * @param buf data buffer to read into + * @param len number of bytes to read + * @param block TRUE to use a blocking read + * @return number of bytes read, -1 on error + */ + ssize_t (*read)(stream_t *this, void *buf, size_t len, bool block); + + /** + * Write data to the stream. + * + * If "block" is FALSE and the write would block, the function returns -1 + * and sets errno to EWOULDBLOCK. + * + * @param buf data buffer to write + * @param len number of bytes to write + * @param block TRUE to use a blocking write + * @return number of bytes written, -1 on error + */ + ssize_t (*write)(stream_t *this, void *buf, size_t len, bool block); + + /** + * Destroy a stream_t. + */ + void (*destroy)(stream_t *this); +}; + +/** + * Create a stream from a file descriptor. + * + * The file descriptor MUST be a socket for non-blocking operation. + * + * @param fd file descriptor to wrap into a stream_t + * @return stream instance + */ +stream_t *stream_create_from_fd(int fd); + +#endif /** STREAM_H_ @}*/ From daf1880b3947d8c745f8e6290efcf076bf18a5a8 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 26 Jun 2013 17:13:11 +0200 Subject: [PATCH 03/54] stream: add a stream service class abstracting services using BSD sockets --- src/libstrongswan/Android.mk | 1 + src/libstrongswan/Makefile.am | 2 + .../networking/streams/stream_service.c | 157 ++++++++++++++++++ .../networking/streams/stream_service.h | 78 +++++++++ 4 files changed, 238 insertions(+) create mode 100644 src/libstrongswan/networking/streams/stream_service.c create mode 100644 src/libstrongswan/networking/streams/stream_service.h diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index e95a6d479..790105f41 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -27,6 +27,7 @@ database/database_factory.c fetcher/fetcher.c fetcher/fetcher_manager.c eap/eap. ipsec/ipsec_types.c \ networking/host.c networking/host_resolver.c networking/packet.c \ networking/tun_device.c networking/streams/stream.c \ +networking/streams/stream_service.c \ pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \ processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \ diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 9f3c3ed2c..1edd3ffa4 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -25,6 +25,7 @@ database/database_factory.c fetcher/fetcher.c fetcher/fetcher_manager.c eap/eap. ipsec/ipsec_types.c \ networking/host.c networking/host_resolver.c networking/packet.c \ networking/tun_device.c networking/streams/stream.c \ +networking/streams/stream_service.c \ pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \ processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \ @@ -66,6 +67,7 @@ database/database.h database/database_factory.h fetcher/fetcher.h \ fetcher/fetcher_manager.h eap/eap.h pen/pen.h ipsec/ipsec_types.h \ networking/host.h networking/host_resolver.h networking/packet.h \ networking/tun_device.h networking/streams/stream.h \ +networking/streams/stream_service.h \ resolver/resolver.h resolver/resolver_response.h resolver/rr_set.h \ resolver/rr.h resolver/resolver_manager.h \ plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.h \ diff --git a/src/libstrongswan/networking/streams/stream_service.c b/src/libstrongswan/networking/streams/stream_service.c new file mode 100644 index 000000000..4979ed60f --- /dev/null +++ b/src/libstrongswan/networking/streams/stream_service.c @@ -0,0 +1,157 @@ +/* + * 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 . + * + * 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 "stream_service.h" + +#include +#include + +#include + +typedef struct private_stream_service_t private_stream_service_t; + +/** + * Private data of an stream_service_t object. + */ +struct private_stream_service_t { + + /** + * Public stream_service_t interface. + */ + stream_service_t public; + + /** + * Underlying socket + */ + int fd; + + /** + * Accept callback + */ + stream_service_cb_t cb; + + /** + * Accept callback data + */ + void *data; +}; + +/** + * Data to pass to async accept job + */ +typedef struct { + /** callback function */ + stream_service_cb_t cb; + /** callback data */ + void *data; + /** accepted connection */ + int fd; +} async_data_t; + +/** + * Clean up accept data + */ +static void destroy_async_data(async_data_t *data) +{ + close(data->fd); + free(data); +} + +/** + * Async processing of accepted connection + */ +static job_requeue_t accept_async(async_data_t *data) +{ + stream_t *stream; + + stream = stream_create_from_fd(data->fd); + if (stream) + { + thread_cleanup_push((void*)stream->destroy, stream); + data->cb(data->data, stream); + thread_cleanup_pop(TRUE); + } + return JOB_REQUEUE_NONE; +} + +/** + * Watcher callback function + */ +static bool watch(private_stream_service_t *this, int fd, watcher_event_t event) +{ + async_data_t *data; + + INIT(data, + .cb = this->cb, + .data = this->data, + .fd = accept(fd, NULL, NULL), + ); + + if (data->fd != -1) + { + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create_with_prio((void*)accept_async, data, + (void*)destroy_async_data, NULL, JOB_PRIO_HIGH)); + } + else + { + free(data); + } + return TRUE; +} + +METHOD(stream_service_t, on_accept, void, + private_stream_service_t *this, stream_service_cb_t cb, void *data) +{ + if (this->cb) + { + lib->watcher->remove(lib->watcher, this->fd); + } + + this->cb = cb; + this->data = data; + + if (this->cb) + { + lib->watcher->add(lib->watcher, this->fd, + WATCHER_READ, (watcher_cb_t)watch, this); + } +} + +METHOD(stream_service_t, destroy, void, + private_stream_service_t *this) +{ + on_accept(this, NULL, NULL); + close(this->fd); + free(this); +} + +/** + * See header + */ +stream_service_t *stream_service_create_from_fd(int fd) +{ + private_stream_service_t *this; + + INIT(this, + .public = { + .on_accept = _on_accept, + .destroy = _destroy, + }, + .fd = fd, + ); + + return &this->public; +} diff --git a/src/libstrongswan/networking/streams/stream_service.h b/src/libstrongswan/networking/streams/stream_service.h new file mode 100644 index 000000000..f5da92ee7 --- /dev/null +++ b/src/libstrongswan/networking/streams/stream_service.h @@ -0,0 +1,78 @@ +/* + * 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 . + * + * 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 stream_service stream_service + * @{ @ingroup streams + */ + +#ifndef STREAM_SERVICE_H_ +#define STREAM_SERVICE_H_ + +typedef struct stream_service_t stream_service_t; + +#include + +/** + * Constructor function prototype for stream_servicet. + * + * @param uri URI to create a stream for + * @return stream instance, NULL on error + */ +typedef stream_service_t*(*stream_service_constructor_t)(char *uri); + +/** + * Service callback routine for accepting client connections. + * + * The passed stream_service gets closed/destroyed by the callback caller. + * + * @param data user data, as passed during registration + * @param stream accept()ed client connection + */ +typedef void (*stream_service_cb_t)(void *data, stream_t *stream); + +/** + * A service accepting client connection streams. + */ +struct stream_service_t { + + /** + * Start accepting client connections on this stream service. + * + * To stop accepting connections, pass a NULL callback function. + * + * @param cb callback function to call for accepted client streams + * @param data data to pass to callback function + */ + void (*on_accept)(stream_service_t *this, + stream_service_cb_t cb, void *data); + + /** + * Destroy a stream_service_t. + */ + void (*destroy)(stream_service_t *this); +}; + +/** + * Create a service from a file descriptor. + * + * The file descriptor MUST be a socket. + * + * @param fd file descriptor to wrap into a stream_service_t + * @return stream_service instance + */ +stream_service_t *stream_service_create_from_fd(int fd); + +#endif /** STREAM_SERVICE_H_ @}*/ From d6ff53940f1ad3e0ca8183af68618b1f365579f2 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 26 Jun 2013 17:28:19 +0200 Subject: [PATCH 04/54] stream: add a manager to dynamically register streams and services --- src/libstrongswan/Android.mk | 2 +- src/libstrongswan/Makefile.am | 4 +- .../networking/streams/stream_manager.c | 282 ++++++++++++++++++ .../networking/streams/stream_manager.h | 104 +++++++ 4 files changed, 389 insertions(+), 3 deletions(-) create mode 100644 src/libstrongswan/networking/streams/stream_manager.c create mode 100644 src/libstrongswan/networking/streams/stream_manager.h diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index 790105f41..3811ed083 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -27,7 +27,7 @@ database/database_factory.c fetcher/fetcher.c fetcher/fetcher_manager.c eap/eap. ipsec/ipsec_types.c \ networking/host.c networking/host_resolver.c networking/packet.c \ networking/tun_device.c networking/streams/stream.c \ -networking/streams/stream_service.c \ +networking/streams/stream_service.c networking/streams/stream_manager.c \ pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \ processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \ diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 1edd3ffa4..dfe6e7e00 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -25,7 +25,7 @@ database/database_factory.c fetcher/fetcher.c fetcher/fetcher_manager.c eap/eap. ipsec/ipsec_types.c \ networking/host.c networking/host_resolver.c networking/packet.c \ networking/tun_device.c networking/streams/stream.c \ -networking/streams/stream_service.c \ +networking/streams/stream_service.c networking/streams/stream_manager.c \ pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \ processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \ @@ -67,7 +67,7 @@ database/database.h database/database_factory.h fetcher/fetcher.h \ fetcher/fetcher_manager.h eap/eap.h pen/pen.h ipsec/ipsec_types.h \ networking/host.h networking/host_resolver.h networking/packet.h \ networking/tun_device.h networking/streams/stream.h \ -networking/streams/stream_service.h \ +networking/streams/stream_service.h networking/streams/stream_manager.h \ resolver/resolver.h resolver/resolver_response.h resolver/rr_set.h \ resolver/rr.h resolver/resolver_manager.h \ plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.h \ diff --git a/src/libstrongswan/networking/streams/stream_manager.c b/src/libstrongswan/networking/streams/stream_manager.c new file mode 100644 index 000000000..d28cb70e2 --- /dev/null +++ b/src/libstrongswan/networking/streams/stream_manager.c @@ -0,0 +1,282 @@ +/* + * 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 . + * + * 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 "stream_manager.h" + +#include + +typedef struct private_stream_manager_t private_stream_manager_t; + +/** + * Private data of an stream_manager_t object. + */ +struct private_stream_manager_t { + + /** + * Public stream_manager_t interface. + */ + stream_manager_t public; + + /** + * List of registered stream constructors, as stream_entry_t + */ + linked_list_t *streams; + + /** + * List of registered service constructors, as service_entry_t + */ + linked_list_t *services; + + /** + * List of registered running services, as running_entry_t + */ + linked_list_t *running; + + /** + * Lock for all lists + */ + rwlock_t *lock; +}; + +/** + * Registered stream backend + */ +typedef struct { + /** URI prefix */ + char *prefix; + /** constructor function */ + stream_constructor_t create; +} stream_entry_t; + +/** + * Registered service backend + */ +typedef struct { + /** URI prefix */ + char *prefix; + /** constructor function */ + stream_service_constructor_t create; +} service_entry_t; + +/** + * Running service + */ +typedef struct { + /** URI of service */ + char *uri; + /** stream accept()ing connections */ + stream_service_t *service; +} running_entry_t; + +METHOD(stream_manager_t, connect_, stream_t*, + private_stream_manager_t *this, char *uri) +{ + enumerator_t *enumerator; + stream_entry_t *entry; + stream_t *stream = NULL; + + this->lock->read_lock(this->lock); + enumerator = this->streams->create_enumerator(this->streams); + while (enumerator->enumerate(enumerator, &entry)) + { + if (strpfx(uri, entry->prefix)) + { + stream = entry->create(uri); + if (stream) + { + break; + } + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + return stream; +} + +METHOD(stream_manager_t, start_service, bool, + private_stream_manager_t *this, char *uri, + stream_service_cb_t cb, void *data) +{ + running_entry_t *running; + enumerator_t *enumerator; + service_entry_t *entry; + stream_service_t *service = NULL; + + this->lock->read_lock(this->lock); + enumerator = this->services->create_enumerator(this->services); + while (enumerator->enumerate(enumerator, &entry)) + { + if (strpfx(uri, entry->prefix)) + { + service = entry->create(uri); + if (service) + { + break; + } + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + if (!service) + { + return FALSE; + } + + INIT(running, + .uri = strdup(uri), + .service = service, + ); + service->on_accept(service, cb, data); + + this->lock->write_lock(this->lock); + this->running->insert_last(this->running, running); + this->lock->unlock(this->lock); + + return TRUE; +} + +METHOD(stream_manager_t, stop_service, void, + private_stream_manager_t *this, char *uri) +{ + enumerator_t *enumerator; + running_entry_t *entry; + + this->lock->write_lock(this->lock); + enumerator = this->running->create_enumerator(this->running); + while (enumerator->enumerate(enumerator, &entry)) + { + if (streq(entry->uri, uri)) + { + this->running->remove_at(this->running, enumerator); + entry->service->destroy(entry->service); + free(entry->uri); + free(entry); + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); +} + +METHOD(stream_manager_t, add_stream, void, + private_stream_manager_t *this, char *prefix, stream_constructor_t create) +{ + stream_entry_t *entry; + + INIT(entry, + .prefix = strdup(prefix), + .create = create, + ); + + this->lock->write_lock(this->lock); + this->streams->insert_last(this->streams, entry); + this->lock->unlock(this->lock); +} + +METHOD(stream_manager_t, remove_stream, void, + private_stream_manager_t *this, stream_constructor_t create) +{ + enumerator_t *enumerator; + stream_entry_t *entry; + + this->lock->write_lock(this->lock); + enumerator = this->streams->create_enumerator(this->streams); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->create == create) + { + this->streams->remove_at(this->streams, enumerator); + free(entry->prefix); + free(entry); + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); +} + +METHOD(stream_manager_t, add_service, void, + private_stream_manager_t *this, char *prefix, + stream_service_constructor_t create) +{ + service_entry_t *entry; + + INIT(entry, + .prefix = strdup(prefix), + .create = create, + ); + + this->lock->write_lock(this->lock); + this->services->insert_last(this->services, entry); + this->lock->unlock(this->lock); +} + +METHOD(stream_manager_t, remove_service, void, + private_stream_manager_t *this, stream_service_constructor_t create) +{ + enumerator_t *enumerator; + service_entry_t *entry; + + this->lock->write_lock(this->lock); + enumerator = this->services->create_enumerator(this->services); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->create == create) + { + this->services->remove_at(this->services, enumerator); + free(entry->prefix); + free(entry); + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); +} + +METHOD(stream_manager_t, destroy, void, + private_stream_manager_t *this) +{ + this->streams->destroy(this->streams); + this->services->destroy(this->services); + this->running->destroy(this->running); + this->lock->destroy(this->lock); + free(this); +} + +/** + * See header + */ +stream_manager_t *stream_manager_create() +{ + private_stream_manager_t *this; + + INIT(this, + .public = { + .connect = _connect_, + .start_service = _start_service, + .stop_service = _stop_service, + .add_stream = _add_stream, + .remove_stream = _remove_stream, + .add_service = _add_service, + .remove_service = _remove_service, + .destroy = _destroy, + }, + .streams = linked_list_create(), + .services = linked_list_create(), + .running = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + return &this->public; +} diff --git a/src/libstrongswan/networking/streams/stream_manager.h b/src/libstrongswan/networking/streams/stream_manager.h new file mode 100644 index 000000000..347596f5c --- /dev/null +++ b/src/libstrongswan/networking/streams/stream_manager.h @@ -0,0 +1,104 @@ +/* + * 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 . + * + * 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 stream_manager stream_manager + * @{ @ingroup streams + */ + +#ifndef STREAM_MANAGER_H_ +#define STREAM_MANAGER_H_ + +typedef struct stream_manager_t stream_manager_t; + +#include +#include + +/** + * Manages client-server connections and services using stream_t backends. + */ +struct stream_manager_t { + + /** + * Create a client-server connection to a service. + * + * @param uri URI of service to connect to + * @return stream instance, NULL on error + */ + stream_t* (*connect)(stream_manager_t *this, char *uri); + + /** + * Start a new service under an URI, accept()ing client connections. + * + * @param uri URI of service to provide + * @param cb callback function invoked for each client connection + * @param data user data to pass to callback + * @return TRUE if service started, FALSE on failure + */ + bool (*start_service)(stream_manager_t *this, char *uri, + stream_service_cb_t cb, void *data); + + /** + * Stop a service previously create with start_service(). + * + * @param uri URI of service to stop + */ + void (*stop_service)(stream_manager_t *this, char *uri); + + /** + * Register a stream backend to the manager. + * + * @param prefix prefix of URIs to use the backend for + * @param create constructor function for the stream + */ + void (*add_stream)(stream_manager_t *this, char *prefix, + stream_constructor_t create); + + /** + * Unregister stream backends from the manager. + * + * @param create constructor function passed to add_stream() + */ + void (*remove_stream)(stream_manager_t *this, stream_constructor_t create); + + /** + * Register a stream service backend to the manager. + * + * @param prefix prefix of URIs to use the backend for + * @param create constructor function for the stream service + */ + void (*add_service)(stream_manager_t *this, char *prefix, + stream_service_constructor_t create); + + /** + * Unregister stream service backends from the manager. + * + * @param create constructor function passed to add_service() + */ + void (*remove_service)(stream_manager_t *this, + stream_service_constructor_t create); + + /** + * Destroy a stream_manager_t. + */ + void (*destroy)(stream_manager_t *this); +}; + +/** + * Create a stream_manager instance. + */ +stream_manager_t *stream_manager_create(); + +#endif /** STREAM_MANAGER_H_ @}*/ From 2ba276017d3f755e679bb6a3462f1c806d00ab13 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 27 Jun 2013 10:16:00 +0200 Subject: [PATCH 05/54] stream: create library instance of stream-manager --- src/libstrongswan/library.c | 2 ++ src/libstrongswan/library.h | 9 +++++++++ src/libstrongswan/networking/streams/stream.c | 3 +-- src/libstrongswan/networking/streams/stream_manager.h | 1 - src/libstrongswan/networking/streams/stream_service.c | 3 +-- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/libstrongswan/library.c b/src/libstrongswan/library.c index 35d74200c..f2fa3e0aa 100644 --- a/src/libstrongswan/library.c +++ b/src/libstrongswan/library.c @@ -80,6 +80,7 @@ void library_deinit() /* make sure the cache is clear before unloading plugins */ lib->credmgr->flush_cache(lib->credmgr, CERT_ANY); + this->public.streams->destroy(this->public.streams); this->public.watcher->destroy(this->public.watcher); this->public.scheduler->destroy(this->public.scheduler); this->public.processor->destroy(this->public.processor); @@ -268,6 +269,7 @@ bool library_init(char *settings) this->public.processor = processor_create(); this->public.scheduler = scheduler_create(); this->public.watcher = watcher_create(); + this->public.streams = stream_manager_create(); this->public.plugins = plugin_loader_create(); if (!check_memwipe()) diff --git a/src/libstrongswan/library.h b/src/libstrongswan/library.h index d5497258a..560da27f9 100644 --- a/src/libstrongswan/library.h +++ b/src/libstrongswan/library.h @@ -58,6 +58,9 @@ * @defgroup networking networking * @ingroup libstrongswan * + * @defgroup streams streams + * @ingroup networking + * * @defgroup plugins plugins * @ingroup libstrongswan * @@ -90,6 +93,7 @@ #include "utils/printf_hook.h" #include "utils/utils.h" #include "networking/host_resolver.h" +#include "networking/streams/stream_manager.h" #include "processing/processor.h" #include "processing/scheduler.h" #include "processing/watcher.h" @@ -202,6 +206,11 @@ struct library_t { */ watcher_t *watcher; + /** + * Streams and Services + */ + stream_manager_t *streams; + /** * resolve hosts by DNS name */ diff --git a/src/libstrongswan/networking/streams/stream.c b/src/libstrongswan/networking/streams/stream.c index c6a73df17..43a6bd47e 100644 --- a/src/libstrongswan/networking/streams/stream.c +++ b/src/libstrongswan/networking/streams/stream.c @@ -13,8 +13,7 @@ * for more details. */ -#include "stream.h" - +#include #include #include diff --git a/src/libstrongswan/networking/streams/stream_manager.h b/src/libstrongswan/networking/streams/stream_manager.h index 347596f5c..ab014175c 100644 --- a/src/libstrongswan/networking/streams/stream_manager.h +++ b/src/libstrongswan/networking/streams/stream_manager.h @@ -23,7 +23,6 @@ typedef struct stream_manager_t stream_manager_t; -#include #include /** diff --git a/src/libstrongswan/networking/streams/stream_service.c b/src/libstrongswan/networking/streams/stream_service.c index 4979ed60f..489edaef4 100644 --- a/src/libstrongswan/networking/streams/stream_service.c +++ b/src/libstrongswan/networking/streams/stream_service.c @@ -13,8 +13,7 @@ * for more details. */ -#include "stream_service.h" - +#include #include #include From 7a23588195443f6b70ba47fc068835c268432fbb Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 27 Jun 2013 11:46:41 +0200 Subject: [PATCH 06/54] stream: add printf()-style covenience functions --- src/libstrongswan/networking/streams/stream.c | 43 ++++++++++++++++++- src/libstrongswan/networking/streams/stream.h | 18 ++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/libstrongswan/networking/streams/stream.c b/src/libstrongswan/networking/streams/stream.c index 43a6bd47e..144792e08 100644 --- a/src/libstrongswan/networking/streams/stream.c +++ b/src/libstrongswan/networking/streams/stream.c @@ -33,6 +33,11 @@ struct private_stream_t { * Underlying socket */ int fd; + + /** + * FILE* for convenience functions, or NULL + */ + FILE *file; }; METHOD(stream_t, read_, ssize_t, @@ -91,10 +96,44 @@ METHOD(stream_t, write_, ssize_t, } } +METHOD(stream_t, vprint, int, + private_stream_t *this, char *format, va_list ap) +{ + if (!this->file) + { + this->file = fdopen(this->fd, "w+"); + if (!this->file) + { + return -1; + } + } + return vfprintf(this->file, format, ap); +} + +METHOD(stream_t, print, int, + private_stream_t *this, char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = vprint(this, format, ap); + va_end(ap); + + return ret; +} + METHOD(stream_t, destroy, void, private_stream_t *this) { - close(this->fd); + if (this->file) + { + fclose(this->file); + } + else + { + close(this->fd); + } free(this); } @@ -109,6 +148,8 @@ stream_t *stream_create_from_fd(int fd) .public = { .read = _read_, .write = _write_, + .print = _print, + .vprint = _vprint, .destroy = _destroy, }, .fd = fd, diff --git a/src/libstrongswan/networking/streams/stream.h b/src/libstrongswan/networking/streams/stream.h index 219e16ade..bcf7fb414 100644 --- a/src/libstrongswan/networking/streams/stream.h +++ b/src/libstrongswan/networking/streams/stream.h @@ -64,6 +64,24 @@ struct stream_t { */ ssize_t (*write)(stream_t *this, void *buf, size_t len, bool block); + /** + * printf() convenience function for this stream. + * + * @param format printf format string + * @param ... argument list for format string + * @return number of characters written, negative on error + */ + int (*print)(stream_t *this, char *format, ...); + + /** + * vprintf() convenience function for this stream. + * + * @param format printf format string + * @param ap argument list for format string + * @return number of characters written, negative on error + */ + int (*vprint)(stream_t *this, char *format, va_list ap); + /** * Destroy a stream_t. */ From c1fd8c22ce26b0204d979352e292815d6361c8b4 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 27 Jun 2013 15:49:11 +0200 Subject: [PATCH 07/54] stream: support async operation using watcher --- src/libstrongswan/networking/streams/stream.c | 109 ++++++++++++++++++ src/libstrongswan/networking/streams/stream.h | 33 ++++++ 2 files changed, 142 insertions(+) diff --git a/src/libstrongswan/networking/streams/stream.c b/src/libstrongswan/networking/streams/stream.c index 144792e08..d3b67761e 100644 --- a/src/libstrongswan/networking/streams/stream.c +++ b/src/libstrongswan/networking/streams/stream.c @@ -38,6 +38,28 @@ struct private_stream_t { * FILE* for convenience functions, or NULL */ FILE *file; + + /** + * Callback if data is ready to read + */ + stream_cb_t read_cb; + + /** + * Data for read-ready callback + */ + void *read_data; + + /** + * Callback if write is non-blocking + */ + stream_cb_t write_cb; + + /** + * Data for write-ready callback + */ + void *write_data; + + }; METHOD(stream_t, read_, ssize_t, @@ -96,6 +118,90 @@ METHOD(stream_t, write_, ssize_t, } } +/** + * Remove a registered watcher + */ +static void remove_watcher(private_stream_t *this) +{ + if (this->read_cb || this->write_cb) + { + lib->watcher->remove(lib->watcher, this->fd); + } +} + +/** + * Watcher callback + */ +static bool watch(private_stream_t *this, int fd, watcher_event_t event) +{ + bool keep = FALSE; + + switch (event) + { + case WATCHER_READ: + keep = this->read_cb(this->read_data, &this->public); + if (!keep) + { + this->read_cb = NULL; + } + break; + case WATCHER_WRITE: + keep = this->write_cb(this->write_data, &this->public); + if (!keep) + { + this->write_cb = NULL; + } + break; + case WATCHER_EXCEPT: + break; + } + return keep; +} + +/** + * Register watcher for stream callbacks + */ +static void add_watcher(private_stream_t *this) +{ + watcher_event_t events = 0; + + if (this->read_cb) + { + events |= WATCHER_READ; + } + if (this->write_cb) + { + events |= WATCHER_WRITE; + } + if (events) + { + lib->watcher->add(lib->watcher, this->fd, events, + (watcher_cb_t)watch, this); + } +} + +METHOD(stream_t, on_read, void, + private_stream_t *this, stream_cb_t cb, void *data) +{ + remove_watcher(this); + + this->read_cb = cb; + this->read_data = data; + + add_watcher(this); +} + +METHOD(stream_t, on_write, void, + private_stream_t *this, stream_cb_t cb, void *data) +{ + remove_watcher(this); + + this->write_cb = cb; + this->write_data = data; + + add_watcher(this); +} + METHOD(stream_t, vprint, int, private_stream_t *this, char *format, va_list ap) { @@ -126,6 +232,7 @@ METHOD(stream_t, print, int, METHOD(stream_t, destroy, void, private_stream_t *this) { + remove_watcher(this); if (this->file) { fclose(this->file); @@ -147,7 +254,9 @@ stream_t *stream_create_from_fd(int fd) INIT(this, .public = { .read = _read_, + .on_read = _on_read, .write = _write_, + .on_write = _on_write, .print = _print, .vprint = _vprint, .destroy = _destroy, diff --git a/src/libstrongswan/networking/streams/stream.h b/src/libstrongswan/networking/streams/stream.h index bcf7fb414..4e0a67a07 100644 --- a/src/libstrongswan/networking/streams/stream.h +++ b/src/libstrongswan/networking/streams/stream.h @@ -33,6 +33,23 @@ typedef struct stream_t stream_t; */ typedef stream_t*(*stream_constructor_t)(char *uri); +/** + * Callback function prototype, called when stream is ready. + * + * It is not allowed to destroy the stream during the callback, this would + * deadlock. Instead, return FALSE to destroy the stream. It is not allowed + * to call on_read()/on_write() during this callback. + * + * As select() may return even if a read()/write() would actually block, it is + * recommended to use the non-blocking calls and handle return values + * appropriately. + * + * @param data data passed during callback registration + * @param stream associated stream + * @return FALSE to destroy the stream + */ +typedef bool (*stream_cb_t)(void *data, stream_t *stream); + /** * Abstraction of a Berkley socket using stream semantics. */ @@ -51,6 +68,14 @@ struct stream_t { */ ssize_t (*read)(stream_t *this, void *buf, size_t len, bool block); + /** + * Register a callback to invoke when stream has data to read. + * + * @param cb callback function, NULL to unregister + * @param data data to pass to callback + */ + void (*on_read)(stream_t *this, stream_cb_t cb, void *data); + /** * Write data to the stream. * @@ -64,6 +89,14 @@ struct stream_t { */ ssize_t (*write)(stream_t *this, void *buf, size_t len, bool block); + /** + * Register a callback to invoke when a write would not block. + * + * @param cb callback function, NULL to unregister + * @param data data to pass to callback + */ + void (*on_write)(stream_t *this, stream_cb_t cb, void *data); + /** * printf() convenience function for this stream. * From b785cfe05beaa35be96d57a4f1dcc748b91e50b6 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 26 Jun 2013 17:08:14 +0200 Subject: [PATCH 08/54] stream: add support for UNIX streams --- src/libstrongswan/networking/streams/stream.c | 49 +++++++++++++++++++ src/libstrongswan/networking/streams/stream.h | 24 +++++++++ .../networking/streams/stream_manager.c | 4 ++ 3 files changed, 77 insertions(+) diff --git a/src/libstrongswan/networking/streams/stream.c b/src/libstrongswan/networking/streams/stream.c index d3b67761e..3c782cce0 100644 --- a/src/libstrongswan/networking/streams/stream.c +++ b/src/libstrongswan/networking/streams/stream.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include typedef struct private_stream_t private_stream_t; @@ -266,3 +268,50 @@ stream_t *stream_create_from_fd(int fd) return &this->public; } + +/** + * See header + */ +int stream_parse_uri_unix(char *uri, struct sockaddr_un *addr) +{ + if (!strpfx(uri, "unix://")) + { + return -1; + } + uri += strlen("unix://"); + + memset(addr, 0, sizeof(*addr)); + addr->sun_family = AF_UNIX; + strncpy(addr->sun_path, uri, sizeof(addr->sun_path)); + + return offsetof(struct sockaddr_un, sun_path) + strlen(addr->sun_path); +} + +/** + * See header + */ +stream_t *stream_create_unix(char *uri) +{ + struct sockaddr_un addr; + int len, fd; + + len = stream_parse_uri_unix(uri, &addr); + if (len == -1) + { + DBG1(DBG_NET, "invalid stream URI: '%s'", uri); + return NULL; + } + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + { + DBG1(DBG_NET, "opening socket '%s' failed: %s", uri, strerror(errno)); + return NULL; + } + if (connect(fd, (struct sockaddr*)&addr, len) < 0) + { + DBG1(DBG_NET, "connecting to '%s' failed: %s", uri, strerror(errno)); + close(fd); + return NULL; + } + return stream_create_from_fd(fd); +} diff --git a/src/libstrongswan/networking/streams/stream.h b/src/libstrongswan/networking/streams/stream.h index 4e0a67a07..842ad8e67 100644 --- a/src/libstrongswan/networking/streams/stream.h +++ b/src/libstrongswan/networking/streams/stream.h @@ -25,6 +25,8 @@ typedef struct stream_t stream_t; #include +#include + /** * Constructor function prototype for stream_t. * @@ -121,6 +123,28 @@ struct stream_t { void (*destroy)(stream_t *this); }; +/** + * Create a stream for UNIX sockets. + * + * UNIX URIs start with unix://, followed by the socket path. For absolute + * paths, an URI looks something like: + * + * unix:///path/to/socket + * + * @param uri UNIX socket specific URI, must start with "unix://" + * @return stream instance, NULL on failure + */ +stream_t *stream_create_unix(char *uri); + +/** + * Helper function to parse a unix:// URI to a sockaddr + * + * @param uri URI + * @param addr sockaddr + * @return length of sockaddr, -1 on error + */ +int stream_parse_uri_unix(char *uri, struct sockaddr_un *addr); + /** * Create a stream from a file descriptor. * diff --git a/src/libstrongswan/networking/streams/stream_manager.c b/src/libstrongswan/networking/streams/stream_manager.c index d28cb70e2..38aaf9af9 100644 --- a/src/libstrongswan/networking/streams/stream_manager.c +++ b/src/libstrongswan/networking/streams/stream_manager.c @@ -247,6 +247,8 @@ METHOD(stream_manager_t, remove_service, void, METHOD(stream_manager_t, destroy, void, private_stream_manager_t *this) { + remove_stream(this, stream_create_unix); + this->streams->destroy(this->streams); this->services->destroy(this->services); this->running->destroy(this->running); @@ -278,5 +280,7 @@ stream_manager_t *stream_manager_create() .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), ); + add_stream(this, "unix://", stream_create_unix); + return &this->public; } From f04746d9b43311f5b62f98b987d3f0dcb406daa1 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 26 Jun 2013 17:16:33 +0200 Subject: [PATCH 09/54] stream: add support for UNIX stream services --- .../networking/streams/stream_manager.c | 2 + .../networking/streams/stream_service.c | 51 +++++++++++++++++++ .../networking/streams/stream_service.h | 8 +++ 3 files changed, 61 insertions(+) diff --git a/src/libstrongswan/networking/streams/stream_manager.c b/src/libstrongswan/networking/streams/stream_manager.c index 38aaf9af9..dbd221e2a 100644 --- a/src/libstrongswan/networking/streams/stream_manager.c +++ b/src/libstrongswan/networking/streams/stream_manager.c @@ -248,6 +248,7 @@ METHOD(stream_manager_t, destroy, void, private_stream_manager_t *this) { remove_stream(this, stream_create_unix); + remove_service(this, stream_service_create_unix); this->streams->destroy(this->streams); this->services->destroy(this->services); @@ -281,6 +282,7 @@ stream_manager_t *stream_manager_create() ); add_stream(this, "unix://", stream_create_unix); + add_service(this, "unix://", stream_service_create_unix); return &this->public; } diff --git a/src/libstrongswan/networking/streams/stream_service.c b/src/libstrongswan/networking/streams/stream_service.c index 489edaef4..609ff3cac 100644 --- a/src/libstrongswan/networking/streams/stream_service.c +++ b/src/libstrongswan/networking/streams/stream_service.c @@ -17,7 +17,11 @@ #include #include +#include #include +#include +#include +#include typedef struct private_stream_service_t private_stream_service_t; @@ -154,3 +158,50 @@ stream_service_t *stream_service_create_from_fd(int fd) return &this->public; } + +/** + * See header + */ +stream_service_t *stream_service_create_unix(char *uri) +{ + struct sockaddr_un addr; + mode_t old; + int fd, len; + + len = stream_parse_uri_unix(uri, &addr); + if (len == -1) + { + DBG1(DBG_NET, "invalid stream URI: '%s'", uri); + return NULL; + } + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) + { + DBG1(DBG_NET, "opening socket '%s' failed: %s", uri, strerror(errno)); + return NULL; + } + unlink(addr.sun_path); + + old = umask(~(S_IRWXU | S_IRWXG)); + if (bind(fd, (struct sockaddr*)&addr, len) < 0) + { + DBG1(DBG_NET, "binding socket '%s' failed: %s", uri, strerror(errno)); + close(fd); + return NULL; + } + umask(old); + if (chown(addr.sun_path, lib->caps->get_uid(lib->caps), + lib->caps->get_gid(lib->caps)) != 0) + { + DBG1(DBG_NET, "changing socket permissions for '%s' failed: %s", + uri, strerror(errno)); + } + if (listen(fd, 5) < 0) + { + DBG1(DBG_NET, "listen on socket '%s' failed: %s", uri, strerror(errno)); + unlink(addr.sun_path); + close(fd); + return NULL; + } + return stream_service_create_from_fd(fd); +} diff --git a/src/libstrongswan/networking/streams/stream_service.h b/src/libstrongswan/networking/streams/stream_service.h index f5da92ee7..f864a7aeb 100644 --- a/src/libstrongswan/networking/streams/stream_service.h +++ b/src/libstrongswan/networking/streams/stream_service.h @@ -75,4 +75,12 @@ struct stream_service_t { */ stream_service_t *stream_service_create_from_fd(int fd); +/** + * Create a service instance for UNIX sockets. + * + * @param uri UNIX socket specific URI, must start with "unix://" + * @return stream_service instance, NULL on failure + */ +stream_service_t *stream_service_create_unix(char *uri); + #endif /** STREAM_SERVICE_H_ @}*/ From db1c8aa460ce4b6cc053857aa765322f2233b8ee Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 27 Jun 2013 17:25:21 +0200 Subject: [PATCH 10/54] stream: add support for TCP streams --- src/libstrongswan/networking/streams/stream.c | 81 ++++++++++++++++++- src/libstrongswan/networking/streams/stream.h | 27 +++++++ .../networking/streams/stream_manager.c | 2 + 3 files changed, 108 insertions(+), 2 deletions(-) diff --git a/src/libstrongswan/networking/streams/stream.c b/src/libstrongswan/networking/streams/stream.c index 3c782cce0..bc6bbc210 100644 --- a/src/libstrongswan/networking/streams/stream.c +++ b/src/libstrongswan/networking/streams/stream.c @@ -16,8 +16,7 @@ #include #include #include -#include -#include +#include typedef struct private_stream_t private_stream_t; @@ -315,3 +314,81 @@ stream_t *stream_create_unix(char *uri) } return stream_create_from_fd(fd); } + +/** + * See header. + */ +int stream_parse_uri_tcp(char *uri, struct sockaddr *addr) +{ + char *pos, buf[128]; + host_t *host; + u_long port; + int len; + + if (!strpfx(uri, "tcp://")) + { + return -1; + } + uri += strlen("tcp://"); + pos = strrchr(uri, ':'); + if (!pos) + { + return -1; + } + if (*uri == '[' && pos > uri && *(pos - 1) == ']') + { + /* IPv6 URI */ + snprintf(buf, sizeof(buf), "%.*s", (int)(pos - uri - 2), uri + 1); + } + else + { + snprintf(buf, sizeof(buf), "%.*s", (int)(pos - uri), uri); + } + port = strtoul(pos + 1, &pos, 10); + if (port == ULONG_MAX || *pos || port > 65535) + { + return -1; + } + host = host_create_from_dns(buf, AF_UNSPEC, port); + if (!host) + { + return -1; + } + len = *host->get_sockaddr_len(host); + memcpy(addr, host->get_sockaddr(host), len); + host->destroy(host); + return len; +} + +/** + * See header + */ +stream_t *stream_create_tcp(char *uri) +{ + union { + struct sockaddr_in in; + struct sockaddr_in6 in6; + struct sockaddr sa; + } addr; + int fd, len; + + len = stream_parse_uri_tcp(uri, &addr.sa); + if (len == -1) + { + DBG1(DBG_NET, "invalid stream URI: '%s'", uri); + return NULL; + } + fd = socket(addr.sa.sa_family, SOCK_STREAM, 0); + if (fd < 0) + { + DBG1(DBG_NET, "opening socket '%s' failed: %s", uri, strerror(errno)); + return NULL; + } + if (connect(fd, &addr.sa, len)) + { + DBG1(DBG_NET, "connecting to '%s' failed: %s", uri, strerror(errno)); + close(fd); + return NULL; + } + return stream_create_from_fd(fd); +} diff --git a/src/libstrongswan/networking/streams/stream.h b/src/libstrongswan/networking/streams/stream.h index 842ad8e67..87685f888 100644 --- a/src/libstrongswan/networking/streams/stream.h +++ b/src/libstrongswan/networking/streams/stream.h @@ -26,6 +26,7 @@ typedef struct stream_t stream_t; #include #include +#include /** * Constructor function prototype for stream_t. @@ -145,6 +146,32 @@ stream_t *stream_create_unix(char *uri); */ int stream_parse_uri_unix(char *uri, struct sockaddr_un *addr); +/** + * Create a stream for TCP sockets. + * + * TCP URIs start with tcp://, followed by a hostname (FQDN or IP), followed + * by a colon separated port. A full TCP uri looks something like: + * + * tcp://srv.example.com:5555 + * tcp://0.0.0.0:1234 + * tcp://[fec2::1]:7654 + * + * There is no default port, so a colon after tcp:// is mandatory. + * + * @param uri TCP socket specific URI, must start with "tcp://" + * @return stream instance, NULL on failure + */ +stream_t *stream_create_tcp(char *uri); + +/** + * Helper function to parse a tcp:// URI to a sockaddr + * + * @param uri URI + * @param addr sockaddr, large enough for URI + * @return length of sockaddr, -1 on error + */ +int stream_parse_uri_tcp(char *uri, struct sockaddr *addr); + /** * Create a stream from a file descriptor. * diff --git a/src/libstrongswan/networking/streams/stream_manager.c b/src/libstrongswan/networking/streams/stream_manager.c index dbd221e2a..0141e1d8d 100644 --- a/src/libstrongswan/networking/streams/stream_manager.c +++ b/src/libstrongswan/networking/streams/stream_manager.c @@ -248,6 +248,7 @@ METHOD(stream_manager_t, destroy, void, private_stream_manager_t *this) { remove_stream(this, stream_create_unix); + remove_stream(this, stream_create_tcp); remove_service(this, stream_service_create_unix); this->streams->destroy(this->streams); @@ -282,6 +283,7 @@ stream_manager_t *stream_manager_create() ); add_stream(this, "unix://", stream_create_unix); + add_stream(this, "tcp://", stream_create_tcp); add_service(this, "unix://", stream_service_create_unix); return &this->public; From c5597a4b5682f05841be7d2b4d2ed578f956cb13 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 27 Jun 2013 17:25:51 +0200 Subject: [PATCH 11/54] stream: add support for TCP stream services --- .../networking/streams/stream_manager.c | 2 + .../networking/streams/stream_service.c | 43 +++++++++++++++++++ .../networking/streams/stream_service.h | 8 ++++ 3 files changed, 53 insertions(+) diff --git a/src/libstrongswan/networking/streams/stream_manager.c b/src/libstrongswan/networking/streams/stream_manager.c index 0141e1d8d..db38977c1 100644 --- a/src/libstrongswan/networking/streams/stream_manager.c +++ b/src/libstrongswan/networking/streams/stream_manager.c @@ -250,6 +250,7 @@ METHOD(stream_manager_t, destroy, void, remove_stream(this, stream_create_unix); remove_stream(this, stream_create_tcp); remove_service(this, stream_service_create_unix); + remove_service(this, stream_service_create_tcp); this->streams->destroy(this->streams); this->services->destroy(this->services); @@ -285,6 +286,7 @@ stream_manager_t *stream_manager_create() add_stream(this, "unix://", stream_create_unix); add_stream(this, "tcp://", stream_create_tcp); add_service(this, "unix://", stream_service_create_unix); + add_service(this, "tcp://", stream_service_create_tcp); return &this->public; } diff --git a/src/libstrongswan/networking/streams/stream_service.c b/src/libstrongswan/networking/streams/stream_service.c index 609ff3cac..ab7a7e38f 100644 --- a/src/libstrongswan/networking/streams/stream_service.c +++ b/src/libstrongswan/networking/streams/stream_service.c @@ -205,3 +205,46 @@ stream_service_t *stream_service_create_unix(char *uri) } return stream_service_create_from_fd(fd); } + +/** + * See header + */ +stream_service_t *stream_service_create_tcp(char *uri) +{ + union { + struct sockaddr_in in; + struct sockaddr_in6 in6; + struct sockaddr sa; + } addr; + int fd, len, on = 1; + + len = stream_parse_uri_tcp(uri, &addr.sa); + if (len == -1) + { + DBG1(DBG_NET, "invalid stream URI: '%s'", uri); + return NULL; + } + fd = socket(addr.sa.sa_family, SOCK_STREAM, 0); + if (fd < 0) + { + DBG1(DBG_NET, "opening socket '%s' failed: %s", uri, strerror(errno)); + return NULL; + } + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) + { + DBG1(DBG_NET, "SO_REUSADDR on '%s' failed: %s", uri, strerror(errno)); + } + if (bind(fd, &addr.sa, len) < 0) + { + DBG1(DBG_NET, "binding socket '%s' failed: %s", uri, strerror(errno)); + close(fd); + return NULL; + } + if (listen(fd, 5) < 0) + { + DBG1(DBG_NET, "listen on socket '%s' failed: %s", uri, strerror(errno)); + close(fd); + return NULL; + } + return stream_service_create_from_fd(fd); +} diff --git a/src/libstrongswan/networking/streams/stream_service.h b/src/libstrongswan/networking/streams/stream_service.h index f864a7aeb..f1dc643f8 100644 --- a/src/libstrongswan/networking/streams/stream_service.h +++ b/src/libstrongswan/networking/streams/stream_service.h @@ -83,4 +83,12 @@ stream_service_t *stream_service_create_from_fd(int fd); */ stream_service_t *stream_service_create_unix(char *uri); +/** + * Create a service instance for TCP sockets. + * + * @param uri TCP socket specific URI, must start with "tcp://" + * @return stream_service instance, NULL on failure + */ +stream_service_t *stream_service_create_tcp(char *uri); + #endif /** STREAM_SERVICE_H_ @}*/ From 441bb9e7b762bf234092d3ed1c64b61149e03ebe Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 28 Jun 2013 10:20:13 +0200 Subject: [PATCH 12/54] stream: add backlog option to stream services, forward to listen() --- src/libstrongswan/networking/streams/stream_manager.c | 4 ++-- src/libstrongswan/networking/streams/stream_manager.h | 3 ++- src/libstrongswan/networking/streams/stream_service.c | 8 ++++---- src/libstrongswan/networking/streams/stream_service.h | 11 +++++++---- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/libstrongswan/networking/streams/stream_manager.c b/src/libstrongswan/networking/streams/stream_manager.c index db38977c1..fb35a8370 100644 --- a/src/libstrongswan/networking/streams/stream_manager.c +++ b/src/libstrongswan/networking/streams/stream_manager.c @@ -107,7 +107,7 @@ METHOD(stream_manager_t, connect_, stream_t*, } METHOD(stream_manager_t, start_service, bool, - private_stream_manager_t *this, char *uri, + private_stream_manager_t *this, char *uri, int backlog, stream_service_cb_t cb, void *data) { running_entry_t *running; @@ -121,7 +121,7 @@ METHOD(stream_manager_t, start_service, bool, { if (strpfx(uri, entry->prefix)) { - service = entry->create(uri); + service = entry->create(uri, backlog); if (service) { break; diff --git a/src/libstrongswan/networking/streams/stream_manager.h b/src/libstrongswan/networking/streams/stream_manager.h index ab014175c..ffb7ef306 100644 --- a/src/libstrongswan/networking/streams/stream_manager.h +++ b/src/libstrongswan/networking/streams/stream_manager.h @@ -42,11 +42,12 @@ struct stream_manager_t { * Start a new service under an URI, accept()ing client connections. * * @param uri URI of service to provide + * @param backlog size of the backlog queue, as passed to listen() * @param cb callback function invoked for each client connection * @param data user data to pass to callback * @return TRUE if service started, FALSE on failure */ - bool (*start_service)(stream_manager_t *this, char *uri, + bool (*start_service)(stream_manager_t *this, char *uri, int backlog, stream_service_cb_t cb, void *data); /** diff --git a/src/libstrongswan/networking/streams/stream_service.c b/src/libstrongswan/networking/streams/stream_service.c index ab7a7e38f..85250390b 100644 --- a/src/libstrongswan/networking/streams/stream_service.c +++ b/src/libstrongswan/networking/streams/stream_service.c @@ -162,7 +162,7 @@ stream_service_t *stream_service_create_from_fd(int fd) /** * See header */ -stream_service_t *stream_service_create_unix(char *uri) +stream_service_t *stream_service_create_unix(char *uri, int backlog) { struct sockaddr_un addr; mode_t old; @@ -196,7 +196,7 @@ stream_service_t *stream_service_create_unix(char *uri) DBG1(DBG_NET, "changing socket permissions for '%s' failed: %s", uri, strerror(errno)); } - if (listen(fd, 5) < 0) + if (listen(fd, backlog) < 0) { DBG1(DBG_NET, "listen on socket '%s' failed: %s", uri, strerror(errno)); unlink(addr.sun_path); @@ -209,7 +209,7 @@ stream_service_t *stream_service_create_unix(char *uri) /** * See header */ -stream_service_t *stream_service_create_tcp(char *uri) +stream_service_t *stream_service_create_tcp(char *uri, int backlog) { union { struct sockaddr_in in; @@ -240,7 +240,7 @@ stream_service_t *stream_service_create_tcp(char *uri) close(fd); return NULL; } - if (listen(fd, 5) < 0) + if (listen(fd, backlog) < 0) { DBG1(DBG_NET, "listen on socket '%s' failed: %s", uri, strerror(errno)); close(fd); diff --git a/src/libstrongswan/networking/streams/stream_service.h b/src/libstrongswan/networking/streams/stream_service.h index f1dc643f8..91e5283ba 100644 --- a/src/libstrongswan/networking/streams/stream_service.h +++ b/src/libstrongswan/networking/streams/stream_service.h @@ -26,12 +26,13 @@ typedef struct stream_service_t stream_service_t; #include /** - * Constructor function prototype for stream_servicet. + * Constructor function prototype for stream_service_t. * * @param uri URI to create a stream for + * @param backlog size of the backlog queue, as passed to listen() * @return stream instance, NULL on error */ -typedef stream_service_t*(*stream_service_constructor_t)(char *uri); +typedef stream_service_t*(*stream_service_constructor_t)(char *uri, int backlog); /** * Service callback routine for accepting client connections. @@ -79,16 +80,18 @@ stream_service_t *stream_service_create_from_fd(int fd); * Create a service instance for UNIX sockets. * * @param uri UNIX socket specific URI, must start with "unix://" + * @param backlog size of the backlog queue, as passed to listen() * @return stream_service instance, NULL on failure */ -stream_service_t *stream_service_create_unix(char *uri); +stream_service_t *stream_service_create_unix(char *uri, int backlog); /** * Create a service instance for TCP sockets. * * @param uri TCP socket specific URI, must start with "tcp://" + * @param backlog size of the backlog queue, as passed to listen() * @return stream_service instance, NULL on failure */ -stream_service_t *stream_service_create_tcp(char *uri); +stream_service_t *stream_service_create_tcp(char *uri, int backlog); #endif /** STREAM_SERVICE_H_ @}*/ From db0e160ba28c0ec355f76f88033b0a3a2277deaa Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 28 Jun 2013 10:32:30 +0200 Subject: [PATCH 13/54] stream: add a job priority option to stream services --- .../networking/streams/stream_manager.c | 4 ++-- .../networking/streams/stream_manager.h | 5 ++++- .../networking/streams/stream_service.c | 17 ++++++++++++++--- .../networking/streams/stream_service.h | 5 ++++- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/libstrongswan/networking/streams/stream_manager.c b/src/libstrongswan/networking/streams/stream_manager.c index fb35a8370..c7e5fd19f 100644 --- a/src/libstrongswan/networking/streams/stream_manager.c +++ b/src/libstrongswan/networking/streams/stream_manager.c @@ -108,7 +108,7 @@ METHOD(stream_manager_t, connect_, stream_t*, METHOD(stream_manager_t, start_service, bool, private_stream_manager_t *this, char *uri, int backlog, - stream_service_cb_t cb, void *data) + stream_service_cb_t cb, void *data, job_priority_t prio) { running_entry_t *running; enumerator_t *enumerator; @@ -140,7 +140,7 @@ METHOD(stream_manager_t, start_service, bool, .uri = strdup(uri), .service = service, ); - service->on_accept(service, cb, data); + service->on_accept(service, cb, data, prio); this->lock->write_lock(this->lock); this->running->insert_last(this->running, running); diff --git a/src/libstrongswan/networking/streams/stream_manager.h b/src/libstrongswan/networking/streams/stream_manager.h index ffb7ef306..8639893f1 100644 --- a/src/libstrongswan/networking/streams/stream_manager.h +++ b/src/libstrongswan/networking/streams/stream_manager.h @@ -23,6 +23,7 @@ typedef struct stream_manager_t stream_manager_t; +#include #include /** @@ -45,10 +46,12 @@ struct stream_manager_t { * @param backlog size of the backlog queue, as passed to listen() * @param cb callback function invoked for each client connection * @param data user data to pass to callback + * @param prio job priority to invoke callback with * @return TRUE if service started, FALSE on failure */ bool (*start_service)(stream_manager_t *this, char *uri, int backlog, - stream_service_cb_t cb, void *data); + stream_service_cb_t cb, void *data, + job_priority_t prio); /** * Stop a service previously create with start_service(). diff --git a/src/libstrongswan/networking/streams/stream_service.c b/src/libstrongswan/networking/streams/stream_service.c index 85250390b..5f2905146 100644 --- a/src/libstrongswan/networking/streams/stream_service.c +++ b/src/libstrongswan/networking/streams/stream_service.c @@ -49,6 +49,11 @@ struct private_stream_service_t { * Accept callback data */ void *data; + + /** + * Job priority to invoke callback with + */ + job_priority_t prio; }; /** @@ -106,7 +111,7 @@ static bool watch(private_stream_service_t *this, int fd, watcher_event_t event) { lib->processor->queue_job(lib->processor, (job_t*)callback_job_create_with_prio((void*)accept_async, data, - (void*)destroy_async_data, NULL, JOB_PRIO_HIGH)); + (void*)destroy_async_data, NULL, this->prio)); } else { @@ -116,7 +121,8 @@ static bool watch(private_stream_service_t *this, int fd, watcher_event_t event) } METHOD(stream_service_t, on_accept, void, - private_stream_service_t *this, stream_service_cb_t cb, void *data) + private_stream_service_t *this, stream_service_cb_t cb, void *data, + job_priority_t prio) { if (this->cb) { @@ -125,6 +131,10 @@ METHOD(stream_service_t, on_accept, void, this->cb = cb; this->data = data; + if (prio <= JOB_PRIO_MAX) + { + this->prio = prio; + } if (this->cb) { @@ -136,7 +146,7 @@ METHOD(stream_service_t, on_accept, void, METHOD(stream_service_t, destroy, void, private_stream_service_t *this) { - on_accept(this, NULL, NULL); + on_accept(this, NULL, NULL, this->prio); close(this->fd); free(this); } @@ -154,6 +164,7 @@ stream_service_t *stream_service_create_from_fd(int fd) .destroy = _destroy, }, .fd = fd, + .prio = JOB_PRIO_MEDIUM, ); return &this->public; diff --git a/src/libstrongswan/networking/streams/stream_service.h b/src/libstrongswan/networking/streams/stream_service.h index 91e5283ba..91a7a1722 100644 --- a/src/libstrongswan/networking/streams/stream_service.h +++ b/src/libstrongswan/networking/streams/stream_service.h @@ -23,6 +23,8 @@ typedef struct stream_service_t stream_service_t; +#include +#include #include /** @@ -56,9 +58,10 @@ struct stream_service_t { * * @param cb callback function to call for accepted client streams * @param data data to pass to callback function + * @param prio job priority to run callback with */ void (*on_accept)(stream_service_t *this, - stream_service_cb_t cb, void *data); + stream_service_cb_t cb, void *data, job_priority_t prio); /** * Destroy a stream_service_t. From 70d1ccec963e14c755683cefe33af90f51035560 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 28 Jun 2013 11:50:59 +0200 Subject: [PATCH 14/54] stream: add a concurrency option to services, limiting parallel callbacks --- .../networking/streams/stream_manager.c | 4 +- .../networking/streams/stream_manager.h | 3 +- .../networking/streams/stream_service.c | 67 ++++++++++++++++++- .../networking/streams/stream_service.h | 4 +- 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/src/libstrongswan/networking/streams/stream_manager.c b/src/libstrongswan/networking/streams/stream_manager.c index c7e5fd19f..e70df316f 100644 --- a/src/libstrongswan/networking/streams/stream_manager.c +++ b/src/libstrongswan/networking/streams/stream_manager.c @@ -108,7 +108,7 @@ METHOD(stream_manager_t, connect_, stream_t*, METHOD(stream_manager_t, start_service, bool, private_stream_manager_t *this, char *uri, int backlog, - stream_service_cb_t cb, void *data, job_priority_t prio) + stream_service_cb_t cb, void *data, job_priority_t prio, u_int cncrncy) { running_entry_t *running; enumerator_t *enumerator; @@ -140,7 +140,7 @@ METHOD(stream_manager_t, start_service, bool, .uri = strdup(uri), .service = service, ); - service->on_accept(service, cb, data, prio); + service->on_accept(service, cb, data, prio, cncrncy); this->lock->write_lock(this->lock); this->running->insert_last(this->running, running); diff --git a/src/libstrongswan/networking/streams/stream_manager.h b/src/libstrongswan/networking/streams/stream_manager.h index 8639893f1..4e798fa06 100644 --- a/src/libstrongswan/networking/streams/stream_manager.h +++ b/src/libstrongswan/networking/streams/stream_manager.h @@ -47,11 +47,12 @@ struct stream_manager_t { * @param cb callback function invoked for each client connection * @param data user data to pass to callback * @param prio job priority to invoke callback with + * @param cncrncy maximum number of parallel callback invocations * @return TRUE if service started, FALSE on failure */ bool (*start_service)(stream_manager_t *this, char *uri, int backlog, stream_service_cb_t cb, void *data, - job_priority_t prio); + job_priority_t prio, u_int cncrncy); /** * Stop a service previously create with start_service(). diff --git a/src/libstrongswan/networking/streams/stream_service.c b/src/libstrongswan/networking/streams/stream_service.c index 5f2905146..34d45a067 100644 --- a/src/libstrongswan/networking/streams/stream_service.c +++ b/src/libstrongswan/networking/streams/stream_service.c @@ -15,6 +15,8 @@ #include #include +#include +#include #include #include @@ -54,6 +56,26 @@ struct private_stream_service_t { * Job priority to invoke callback with */ job_priority_t prio; + + /** + * Maximum number of parallel callback invocations + */ + u_int cncrncy; + + /** + * Currently active jobs + */ + u_int active; + + /** + * mutex to lock active counter + */ + mutex_t *mutex; + + /** + * Condvar to wait for callback termination + */ + condvar_t *condvar; }; /** @@ -66,6 +88,8 @@ typedef struct { void *data; /** accepted connection */ int fd; + /** reference to stream service */ + private_stream_service_t *this; } async_data_t; /** @@ -73,6 +97,18 @@ typedef struct { */ static void destroy_async_data(async_data_t *data) { + private_stream_service_t *this = data->this; + + this->mutex->lock(this->mutex); + if (this->active-- == this->cncrncy) + { + /* leaving concurrency limit, restart accept()ing. */ + this->public.on_accept(&this->public, this->cb, this->data, + this->prio, this->cncrncy); + } + this->condvar->signal(this->condvar); + this->mutex->unlock(this->mutex); + close(data->fd); free(data); } @@ -100,15 +136,25 @@ static job_requeue_t accept_async(async_data_t *data) static bool watch(private_stream_service_t *this, int fd, watcher_event_t event) { async_data_t *data; + bool keep = TRUE; INIT(data, .cb = this->cb, .data = this->data, .fd = accept(fd, NULL, NULL), + .this = this, ); if (data->fd != -1) { + this->mutex->lock(this->mutex); + if (++this->active == this->cncrncy) + { + /* concurrency limit reached, stop accept()ing new connections */ + keep = FALSE; + } + this->mutex->unlock(this->mutex); + lib->processor->queue_job(lib->processor, (job_t*)callback_job_create_with_prio((void*)accept_async, data, (void*)destroy_async_data, NULL, this->prio)); @@ -117,13 +163,21 @@ static bool watch(private_stream_service_t *this, int fd, watcher_event_t event) { free(data); } - return TRUE; + return keep; } METHOD(stream_service_t, on_accept, void, private_stream_service_t *this, stream_service_cb_t cb, void *data, - job_priority_t prio) + job_priority_t prio, u_int cncrncy) { + this->mutex->lock(this->mutex); + + /* wait for all callbacks to return */ + while (this->active) + { + this->condvar->wait(this->condvar, this->mutex); + } + if (this->cb) { lib->watcher->remove(lib->watcher, this->fd); @@ -135,19 +189,24 @@ METHOD(stream_service_t, on_accept, void, { this->prio = prio; } + this->cncrncy = cncrncy; if (this->cb) { lib->watcher->add(lib->watcher, this->fd, WATCHER_READ, (watcher_cb_t)watch, this); } + + this->mutex->unlock(this->mutex); } METHOD(stream_service_t, destroy, void, private_stream_service_t *this) { - on_accept(this, NULL, NULL, this->prio); + on_accept(this, NULL, NULL, this->prio, this->cncrncy); close(this->fd); + this->mutex->destroy(this->mutex); + this->condvar->destroy(this->condvar); free(this); } @@ -165,6 +224,8 @@ stream_service_t *stream_service_create_from_fd(int fd) }, .fd = fd, .prio = JOB_PRIO_MEDIUM, + .mutex = mutex_create(MUTEX_TYPE_RECURSIVE), + .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), ); return &this->public; diff --git a/src/libstrongswan/networking/streams/stream_service.h b/src/libstrongswan/networking/streams/stream_service.h index 91a7a1722..27ef79148 100644 --- a/src/libstrongswan/networking/streams/stream_service.h +++ b/src/libstrongswan/networking/streams/stream_service.h @@ -59,9 +59,11 @@ struct stream_service_t { * @param cb callback function to call for accepted client streams * @param data data to pass to callback function * @param prio job priority to run callback with + * @param cncrncy maximum number of parallel callback invocations */ void (*on_accept)(stream_service_t *this, - stream_service_cb_t cb, void *data, job_priority_t prio); + stream_service_cb_t cb, void *data, + job_priority_t prio, u_int cncrncy); /** * Destroy a stream_service_t. From fbdc65debb356c248cace2b46ad5373b91dc0797 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 28 Jun 2013 14:33:41 +0200 Subject: [PATCH 15/54] stream: replace print/vprint() convenience functions by a FILE* getter While this will complicate the implementation of streams not based on a fd, it allows us to unleash the full power of FILE based convenience functions. --- src/libstrongswan/networking/streams/stream.c | 54 ++++++------------- src/libstrongswan/networking/streams/stream.h | 17 ++---- 2 files changed, 20 insertions(+), 51 deletions(-) diff --git a/src/libstrongswan/networking/streams/stream.c b/src/libstrongswan/networking/streams/stream.c index bc6bbc210..9a4a3d310 100644 --- a/src/libstrongswan/networking/streams/stream.c +++ b/src/libstrongswan/networking/streams/stream.c @@ -35,11 +35,6 @@ struct private_stream_t { */ int fd; - /** - * FILE* for convenience functions, or NULL - */ - FILE *file; - /** * Callback if data is ready to read */ @@ -203,45 +198,31 @@ METHOD(stream_t, on_write, void, add_watcher(this); } -METHOD(stream_t, vprint, int, - private_stream_t *this, char *format, va_list ap) +METHOD(stream_t, get_file, FILE*, + private_stream_t *this) { - if (!this->file) + FILE *file; + int fd; + + /* fclose() closes the FD passed to fdopen(), so dup() it */ + fd = dup(this->fd); + if (fd == -1) { - this->file = fdopen(this->fd, "w+"); - if (!this->file) - { - return -1; - } + return NULL; } - return vfprintf(this->file, format, ap); -} - -METHOD(stream_t, print, int, - private_stream_t *this, char *format, ...) -{ - va_list ap; - int ret; - - va_start(ap, format); - ret = vprint(this, format, ap); - va_end(ap); - - return ret; + file = fdopen(fd, "w+"); + if (!file) + { + close(fd); + } + return file; } METHOD(stream_t, destroy, void, private_stream_t *this) { remove_watcher(this); - if (this->file) - { - fclose(this->file); - } - else - { - close(this->fd); - } + close(this->fd); free(this); } @@ -258,8 +239,7 @@ stream_t *stream_create_from_fd(int fd) .on_read = _on_read, .write = _write_, .on_write = _on_write, - .print = _print, - .vprint = _vprint, + .get_file = _get_file, .destroy = _destroy, }, .fd = fd, diff --git a/src/libstrongswan/networking/streams/stream.h b/src/libstrongswan/networking/streams/stream.h index 87685f888..17e5a94be 100644 --- a/src/libstrongswan/networking/streams/stream.h +++ b/src/libstrongswan/networking/streams/stream.h @@ -101,22 +101,11 @@ struct stream_t { void (*on_write)(stream_t *this, stream_cb_t cb, void *data); /** - * printf() convenience function for this stream. + * Get a FILE reference for this stream. * - * @param format printf format string - * @param ... argument list for format string - * @return number of characters written, negative on error + * @return FILE*, must be fclose()d, NULL on error */ - int (*print)(stream_t *this, char *format, ...); - - /** - * vprintf() convenience function for this stream. - * - * @param format printf format string - * @param ap argument list for format string - * @return number of characters written, negative on error - */ - int (*vprint)(stream_t *this, char *format, va_list ap); + FILE* (*get_file)(stream_t *this); /** * Destroy a stream_t. From 047a1906005dbffa1e4961b9a070183834495241 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 28 Jun 2013 14:55:27 +0200 Subject: [PATCH 16/54] stream: use a service constructor to create services It does not make much sense to reference running services in the manager, especially as unregistration would need the URI (which a user would have to store instead of the service reference). --- .../networking/streams/stream_manager.c | 65 ++----------------- .../networking/streams/stream_manager.h | 20 ++---- 2 files changed, 8 insertions(+), 77 deletions(-) diff --git a/src/libstrongswan/networking/streams/stream_manager.c b/src/libstrongswan/networking/streams/stream_manager.c index e70df316f..2cbd6127e 100644 --- a/src/libstrongswan/networking/streams/stream_manager.c +++ b/src/libstrongswan/networking/streams/stream_manager.c @@ -39,11 +39,6 @@ struct private_stream_manager_t { */ linked_list_t *services; - /** - * List of registered running services, as running_entry_t - */ - linked_list_t *running; - /** * Lock for all lists */ @@ -70,16 +65,6 @@ typedef struct { stream_service_constructor_t create; } service_entry_t; -/** - * Running service - */ -typedef struct { - /** URI of service */ - char *uri; - /** stream accept()ing connections */ - stream_service_t *service; -} running_entry_t; - METHOD(stream_manager_t, connect_, stream_t*, private_stream_manager_t *this, char *uri) { @@ -106,11 +91,9 @@ METHOD(stream_manager_t, connect_, stream_t*, return stream; } -METHOD(stream_manager_t, start_service, bool, - private_stream_manager_t *this, char *uri, int backlog, - stream_service_cb_t cb, void *data, job_priority_t prio, u_int cncrncy) +METHOD(stream_manager_t, create_service, stream_service_t*, + private_stream_manager_t *this, char *uri, int backlog) { - running_entry_t *running; enumerator_t *enumerator; service_entry_t *entry; stream_service_t *service = NULL; @@ -131,44 +114,7 @@ METHOD(stream_manager_t, start_service, bool, enumerator->destroy(enumerator); this->lock->unlock(this->lock); - if (!service) - { - return FALSE; - } - - INIT(running, - .uri = strdup(uri), - .service = service, - ); - service->on_accept(service, cb, data, prio, cncrncy); - - this->lock->write_lock(this->lock); - this->running->insert_last(this->running, running); - this->lock->unlock(this->lock); - - return TRUE; -} - -METHOD(stream_manager_t, stop_service, void, - private_stream_manager_t *this, char *uri) -{ - enumerator_t *enumerator; - running_entry_t *entry; - - this->lock->write_lock(this->lock); - enumerator = this->running->create_enumerator(this->running); - while (enumerator->enumerate(enumerator, &entry)) - { - if (streq(entry->uri, uri)) - { - this->running->remove_at(this->running, enumerator); - entry->service->destroy(entry->service); - free(entry->uri); - free(entry); - } - } - enumerator->destroy(enumerator); - this->lock->unlock(this->lock); + return service; } METHOD(stream_manager_t, add_stream, void, @@ -254,7 +200,6 @@ METHOD(stream_manager_t, destroy, void, this->streams->destroy(this->streams); this->services->destroy(this->services); - this->running->destroy(this->running); this->lock->destroy(this->lock); free(this); } @@ -269,8 +214,7 @@ stream_manager_t *stream_manager_create() INIT(this, .public = { .connect = _connect_, - .start_service = _start_service, - .stop_service = _stop_service, + .create_service = _create_service, .add_stream = _add_stream, .remove_stream = _remove_stream, .add_service = _add_service, @@ -279,7 +223,6 @@ stream_manager_t *stream_manager_create() }, .streams = linked_list_create(), .services = linked_list_create(), - .running = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), ); diff --git a/src/libstrongswan/networking/streams/stream_manager.h b/src/libstrongswan/networking/streams/stream_manager.h index 4e798fa06..352d93e2b 100644 --- a/src/libstrongswan/networking/streams/stream_manager.h +++ b/src/libstrongswan/networking/streams/stream_manager.h @@ -40,26 +40,14 @@ struct stream_manager_t { stream_t* (*connect)(stream_manager_t *this, char *uri); /** - * Start a new service under an URI, accept()ing client connections. + * Create a new service under an URI to accept() client connections. * * @param uri URI of service to provide * @param backlog size of the backlog queue, as passed to listen() - * @param cb callback function invoked for each client connection - * @param data user data to pass to callback - * @param prio job priority to invoke callback with - * @param cncrncy maximum number of parallel callback invocations - * @return TRUE if service started, FALSE on failure + * @return service, NULL on error */ - bool (*start_service)(stream_manager_t *this, char *uri, int backlog, - stream_service_cb_t cb, void *data, - job_priority_t prio, u_int cncrncy); - - /** - * Stop a service previously create with start_service(). - * - * @param uri URI of service to stop - */ - void (*stop_service)(stream_manager_t *this, char *uri); + stream_service_t* (*create_service)(stream_manager_t *this, char *uri, + int backlog); /** * Register a stream backend to the manager. From 1d1ef9e7ca7116bbc3e68361ef70772016b50bc5 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 28 Jun 2013 14:33:03 +0200 Subject: [PATCH 17/54] stream: support cancellation of stream service callback --- src/libstrongswan/networking/streams/stream_service.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libstrongswan/networking/streams/stream_service.c b/src/libstrongswan/networking/streams/stream_service.c index 34d45a067..7eaca86be 100644 --- a/src/libstrongswan/networking/streams/stream_service.c +++ b/src/libstrongswan/networking/streams/stream_service.c @@ -156,8 +156,9 @@ static bool watch(private_stream_service_t *this, int fd, watcher_event_t event) this->mutex->unlock(this->mutex); lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio((void*)accept_async, data, - (void*)destroy_async_data, NULL, this->prio)); + (job_t*)callback_job_create_with_prio((void*)accept_async, data, + (void*)destroy_async_data, (callback_job_cancel_t)return_false, + this->prio)); } else { From d57b9e7c822149828ef6ce1467be60c28f011019 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 10:36:52 +0200 Subject: [PATCH 18/54] stream: add read/write_all() methods to stream --- src/libstrongswan/networking/streams/stream.c | 50 ++++++++++++++++++- src/libstrongswan/networking/streams/stream.h | 25 ++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/libstrongswan/networking/streams/stream.c b/src/libstrongswan/networking/streams/stream.c index 9a4a3d310..20379fb41 100644 --- a/src/libstrongswan/networking/streams/stream.c +++ b/src/libstrongswan/networking/streams/stream.c @@ -54,8 +54,6 @@ struct private_stream_t { * Data for write-ready callback */ void *write_data; - - }; METHOD(stream_t, read_, ssize_t, @@ -86,6 +84,29 @@ METHOD(stream_t, read_, ssize_t, } } +METHOD(stream_t, read_all, bool, + private_stream_t *this, void *buf, size_t len) +{ + ssize_t ret; + + while (len) + { + ret = read_(this, buf, len, TRUE); + if (ret < 0) + { + return FALSE; + } + if (ret == 0) + { + errno = ECONNRESET; + return FALSE; + } + len -= ret; + buf += ret; + } + return TRUE; +} + METHOD(stream_t, write_, ssize_t, private_stream_t *this, void *buf, size_t len, bool block) { @@ -114,6 +135,29 @@ METHOD(stream_t, write_, ssize_t, } } +METHOD(stream_t, write_all, bool, + private_stream_t *this, void *buf, size_t len) +{ + ssize_t ret; + + while (len) + { + ret = write_(this, buf, len, TRUE); + if (ret < 0) + { + return FALSE; + } + if (ret == 0) + { + errno = ECONNRESET; + return FALSE; + } + len -= ret; + buf += ret; + } + return TRUE; +} + /** * Remove a registered watcher */ @@ -236,8 +280,10 @@ stream_t *stream_create_from_fd(int fd) INIT(this, .public = { .read = _read_, + .read_all = _read_all, .on_read = _on_read, .write = _write_, + .write_all = _write_all, .on_write = _on_write, .get_file = _get_file, .destroy = _destroy, diff --git a/src/libstrongswan/networking/streams/stream.h b/src/libstrongswan/networking/streams/stream.h index 17e5a94be..8cd8419c3 100644 --- a/src/libstrongswan/networking/streams/stream.h +++ b/src/libstrongswan/networking/streams/stream.h @@ -71,6 +71,19 @@ struct stream_t { */ ssize_t (*read)(stream_t *this, void *buf, size_t len, bool block); + /** + * Read data from the stream, avoiding short reads. + * + * This call is always blocking, and reads until len has been read + * completely. If the connection is closed before enough bytes could be + * returned, errno is set to ECONNRESET. + * + * @param buf data buffer to read into + * @param len number of bytes to read + * @return TRUE if len bytes read, FALSE on error + */ + bool (*read_all)(stream_t *this, void *buf, size_t len); + /** * Register a callback to invoke when stream has data to read. * @@ -92,6 +105,18 @@ struct stream_t { */ ssize_t (*write)(stream_t *this, void *buf, size_t len, bool block); + /** + * Write data to the stream, avoiding short writes. + * + * This call is always blocking, and writes until len bytes has been + * written. + * + * @param buf data buffer to write + * @param len number of bytes to write + * @return TRUE if len bytes written, FALSE on error + */ + bool (*write_all)(stream_t *this, void *buf, size_t len); + /** * Register a callback to invoke when a write would not block. * From e6e8a2b2e01e91f75048fd4a33e04a0a7612bd12 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 14:57:28 +0200 Subject: [PATCH 19/54] stream: support keeping the service alive outside of service callback --- src/libstrongswan/networking/streams/stream_service.c | 3 +-- src/libstrongswan/networking/streams/stream_service.h | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libstrongswan/networking/streams/stream_service.c b/src/libstrongswan/networking/streams/stream_service.c index 7eaca86be..1b06e1eb1 100644 --- a/src/libstrongswan/networking/streams/stream_service.c +++ b/src/libstrongswan/networking/streams/stream_service.c @@ -124,8 +124,7 @@ static job_requeue_t accept_async(async_data_t *data) if (stream) { thread_cleanup_push((void*)stream->destroy, stream); - data->cb(data->data, stream); - thread_cleanup_pop(TRUE); + thread_cleanup_pop(!data->cb(data->data, stream)); } return JOB_REQUEUE_NONE; } diff --git a/src/libstrongswan/networking/streams/stream_service.h b/src/libstrongswan/networking/streams/stream_service.h index 27ef79148..c8faba323 100644 --- a/src/libstrongswan/networking/streams/stream_service.h +++ b/src/libstrongswan/networking/streams/stream_service.h @@ -39,12 +39,14 @@ typedef stream_service_t*(*stream_service_constructor_t)(char *uri, int backlog) /** * Service callback routine for accepting client connections. * - * The passed stream_service gets closed/destroyed by the callback caller. + * The passed stream gets closed/destroyed by the callback caller, unless + * TRUE is returned. * * @param data user data, as passed during registration * @param stream accept()ed client connection + * @return TRUE to keep stream alive, FALSE to destroy it */ -typedef void (*stream_service_cb_t)(void *data, stream_t *stream); +typedef bool (*stream_service_cb_t)(void *data, stream_t *stream); /** * A service accepting client connection streams. From a558ba16f314d250ef62a593e66e263123a0e9a3 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 18:34:08 +0200 Subject: [PATCH 20/54] watcher: release threads waiting in remove() when watcher thread gets cancelled During daemon shutdown, users might call remove() after processor.set_threads(0) has been called. This gets problematic, as a watch event might be unable to signal completion when no threads are available anymore. Work around this issue by cancelling waiters once processor.cancel() has been called. --- src/libstrongswan/processing/watcher.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/libstrongswan/processing/watcher.c b/src/libstrongswan/processing/watcher.c index 7ccac72bc..ff3e11649 100644 --- a/src/libstrongswan/processing/watcher.c +++ b/src/libstrongswan/processing/watcher.c @@ -183,6 +183,28 @@ static bool notify(private_watcher_t *this, entry_t *entry, return TRUE; } +/** + * Thread cancellation function for watcher thread + */ +static void activate_all(private_watcher_t *this) +{ + enumerator_t *enumerator; + entry_t *entry; + + /* When the watcher thread gets cancelled, we have to reactivate any entry + * and signal threads in remove() to go on. */ + + this->mutex->lock(this->mutex); + enumerator = this->fds->create_enumerator(this->fds); + while (enumerator->enumerate(enumerator, &entry)) + { + entry->active = TRUE; + } + enumerator->destroy(enumerator); + this->condvar->broadcast(this->condvar); + this->mutex->unlock(this->mutex); +} + /** * Dispatching function */ @@ -238,9 +260,11 @@ static job_requeue_t watch(private_watcher_t *this) char buf[1]; bool old, notified = FALSE; + thread_cleanup_push((void*)activate_all, this); old = thread_cancelability(TRUE); res = select(maxfd + 1, &rd, &wr, &ex, NULL); thread_cancelability(old); + thread_cleanup_pop(FALSE); if (res > 0) { if (this->notify[0] != -1 && FD_ISSET(this->notify[0], &rd)) From a0e3a7363f0f0c2f1189358dd6670b05d59ede70 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 18:38:42 +0200 Subject: [PATCH 21/54] watcher: unregister a watcher FD if its thread gets cancelled --- src/libstrongswan/processing/watcher.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libstrongswan/processing/watcher.c b/src/libstrongswan/processing/watcher.c index ff3e11649..84a870f2c 100644 --- a/src/libstrongswan/processing/watcher.c +++ b/src/libstrongswan/processing/watcher.c @@ -106,12 +106,25 @@ static void update(private_watcher_t *this) } } +/** + * Cleanup function if callback gets cancelled + */ +static void unregister(notify_data_t *data) +{ + /* if a thread processing a callback gets cancelled, we mark the entry + * as cancelled, like the callback would return FALSE. This is required + * to not queue this watcher again if all threads have been gone. */ + data->keep = FALSE; +} + /** * Execute callback of registered FD, asynchronous */ static job_requeue_t notify_async(notify_data_t *data) { + thread_cleanup_push((void*)unregister, data); data->keep = data->cb(data->data, data->fd, data->event); + thread_cleanup_pop(FALSE); return JOB_REQUEUE_NONE; } From 50720d7ce91e2c260798052156f056734d5a751f Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 2 Jul 2013 11:00:27 +0200 Subject: [PATCH 22/54] processor: add a getter for the threads passed to set_threads() --- src/libstrongswan/processing/processor.c | 8 +++++++- src/libstrongswan/processing/processor.h | 10 ++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/libstrongswan/processing/processor.c b/src/libstrongswan/processing/processor.c index 605a7af75..c465f0259 100644 --- a/src/libstrongswan/processing/processor.c +++ b/src/libstrongswan/processing/processor.c @@ -437,6 +437,12 @@ METHOD(processor_t, set_threads, void, this->mutex->unlock(this->mutex); } +METHOD(processor_t, get_threads, u_int, + private_processor_t *this) +{ + return this->desired_threads; +} + METHOD(processor_t, cancel, void, private_processor_t *this) { @@ -507,6 +513,7 @@ processor_t *processor_create() .get_job_load = _get_job_load, .queue_job = _queue_job, .set_threads = _set_threads, + .get_threads = _get_threads, .cancel = _cancel, .destroy = _destroy, }, @@ -525,4 +532,3 @@ processor_t *processor_create() return &this->public; } - diff --git a/src/libstrongswan/processing/processor.h b/src/libstrongswan/processing/processor.h index 94860f5d3..bd708fba8 100644 --- a/src/libstrongswan/processing/processor.h +++ b/src/libstrongswan/processing/processor.h @@ -87,6 +87,16 @@ struct processor_t { */ void (*set_threads)(processor_t *this, u_int count); + /** + * Get the number of threads set with set_threads(). + * + * This does not actually reflect the number of threads currently active, + * but the number of threads targeted. + * + * @return number of desired threads + */ + u_int (*get_threads)(processor_t *this); + /** * Sets the number of threads to 0 and cancels all blocking jobs, then waits * for all threads to be terminated. From 91a2ae644c2ab6c2fdeb1870bc04e79c255331be Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 2 Jul 2013 11:01:10 +0200 Subject: [PATCH 23/54] watcher: if the processor has no threads, execute the job with watcher thread This is important during shutdown, where we might need to signal some FDs while all idle threads are gone already. --- src/libstrongswan/processing/watcher.c | 30 ++++++++++++++++---------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/libstrongswan/processing/watcher.c b/src/libstrongswan/processing/watcher.c index 84a870f2c..928a3c05d 100644 --- a/src/libstrongswan/processing/watcher.c +++ b/src/libstrongswan/processing/watcher.c @@ -170,8 +170,8 @@ static void notify_end(notify_data_t *data) /** * Execute the callback for a registered FD */ -static bool notify(private_watcher_t *this, entry_t *entry, - watcher_event_t event) +static job_t* notify(private_watcher_t *this, entry_t *entry, + watcher_event_t event) { notify_data_t *data; @@ -189,11 +189,9 @@ static bool notify(private_watcher_t *this, entry_t *entry, * processing did not handle the event yet */ entry->active = FALSE; - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio((void*)notify_async, data, + return (job_t*)callback_job_create_with_prio((void*)notify_async, data, (void*)notify_end, (callback_job_cancel_t)return_false, - JOB_PRIO_CRITICAL)); - return TRUE; + JOB_PRIO_CRITICAL); } /** @@ -271,7 +269,8 @@ static job_requeue_t watch(private_watcher_t *this) while (TRUE) { char buf[1]; - bool old, notified = FALSE; + bool old; + job_t *job = NULL; thread_cleanup_push((void*)activate_all, this); old = thread_cancelability(TRUE); @@ -292,25 +291,34 @@ static job_requeue_t watch(private_watcher_t *this) { if (FD_ISSET(entry->fd, &rd)) { - notified = notify(this, entry, WATCHER_READ); + job = notify(this, entry, WATCHER_READ); break; } if (FD_ISSET(entry->fd, &wr)) { - notified = notify(this, entry, WATCHER_WRITE); + job = notify(this, entry, WATCHER_WRITE); break; } if (FD_ISSET(entry->fd, &ex)) { - notified = notify(this, entry, WATCHER_EXCEPT); + job = notify(this, entry, WATCHER_EXCEPT); break; } } enumerator->destroy(enumerator); this->mutex->unlock(this->mutex); - if (notified) + if (job) { + if (lib->processor->get_threads(lib->processor)) + { + lib->processor->queue_job(lib->processor, job); + } + else + { + job->execute(job); + job->destroy(job); + } /* we temporarily disable a notified FD, rebuild FDSET */ return JOB_REQUEUE_DIRECT; } From 58d0dadddc44244eb9c5e483cde6641afb54ee49 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 2 Jul 2013 14:03:51 +0200 Subject: [PATCH 24/54] watcher: add some debugging statements --- src/libstrongswan/processing/watcher.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libstrongswan/processing/watcher.c b/src/libstrongswan/processing/watcher.c index 928a3c05d..ee7053396 100644 --- a/src/libstrongswan/processing/watcher.c +++ b/src/libstrongswan/processing/watcher.c @@ -250,14 +250,17 @@ static job_requeue_t watch(private_watcher_t *this) { if (entry->events & WATCHER_READ) { + DBG3(DBG_JOB, " watching %d for reading", entry->fd); FD_SET(entry->fd, &rd); } if (entry->events & WATCHER_WRITE) { + DBG3(DBG_JOB, " watching %d for writing", entry->fd); FD_SET(entry->fd, &wr); } if (entry->events & WATCHER_EXCEPT) { + DBG3(DBG_JOB, " watching %d for exceptions", entry->fd); FD_SET(entry->fd, &ex); } maxfd = max(maxfd, entry->fd); @@ -272,6 +275,7 @@ static job_requeue_t watch(private_watcher_t *this) bool old; job_t *job = NULL; + DBG2(DBG_JOB, "watcher going to select()"); thread_cleanup_push((void*)activate_all, this); old = thread_cancelability(TRUE); res = select(maxfd + 1, &rd, &wr, &ex, NULL); @@ -281,6 +285,7 @@ static job_requeue_t watch(private_watcher_t *this) { if (this->notify[0] != -1 && FD_ISSET(this->notify[0], &rd)) { + DBG2(DBG_JOB, "watcher got notification, rebuilding"); ignore_result(read(this->notify[0], buf, sizeof(buf))); return JOB_REQUEUE_DIRECT; } @@ -291,16 +296,19 @@ static job_requeue_t watch(private_watcher_t *this) { if (FD_ISSET(entry->fd, &rd)) { + DBG2(DBG_JOB, "watched FD %d ready to read", entry->fd); job = notify(this, entry, WATCHER_READ); break; } if (FD_ISSET(entry->fd, &wr)) { + DBG2(DBG_JOB, "watched FD %d ready to write", entry->fd); job = notify(this, entry, WATCHER_WRITE); break; } if (FD_ISSET(entry->fd, &ex)) { + DBG2(DBG_JOB, "watched FD %d has exception", entry->fd); job = notify(this, entry, WATCHER_EXCEPT); break; } @@ -323,6 +331,10 @@ static job_requeue_t watch(private_watcher_t *this) return JOB_REQUEUE_DIRECT; } } + else + { + DBG1(DBG_JOB, "watcher select() error: %s", strerror(errno)); + } } } From c9d1742b5da37de26ed622e3b1317572da2f4978 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 2 Jul 2013 14:04:51 +0200 Subject: [PATCH 25/54] stream: don't close underlying socket when creating a stream from it --- src/libstrongswan/networking/streams/stream_service.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libstrongswan/networking/streams/stream_service.c b/src/libstrongswan/networking/streams/stream_service.c index 1b06e1eb1..c2681af3a 100644 --- a/src/libstrongswan/networking/streams/stream_service.c +++ b/src/libstrongswan/networking/streams/stream_service.c @@ -109,7 +109,10 @@ static void destroy_async_data(async_data_t *data) this->condvar->signal(this->condvar); this->mutex->unlock(this->mutex); - close(data->fd); + if (data->fd != -1) + { + close(data->fd); + } free(data); } @@ -123,6 +126,8 @@ static job_requeue_t accept_async(async_data_t *data) stream = stream_create_from_fd(data->fd); if (stream) { + /* FD is now owned by stream, don't close it during cleanup */ + data->fd = -1; thread_cleanup_push((void*)stream->destroy, stream); thread_cleanup_pop(!data->cb(data->data, stream)); } From 4701929266aa5e2896d24c7b8c3d16d416413686 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 2 Jul 2013 14:09:45 +0200 Subject: [PATCH 26/54] stream: allow async read/write callback to destroy the stream explicitly --- src/libstrongswan/networking/streams/stream.c | 17 +++++++++++------ src/libstrongswan/networking/streams/stream.h | 8 ++++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/libstrongswan/networking/streams/stream.c b/src/libstrongswan/networking/streams/stream.c index 20379fb41..b3dd7680b 100644 --- a/src/libstrongswan/networking/streams/stream.c +++ b/src/libstrongswan/networking/streams/stream.c @@ -175,21 +175,26 @@ static void remove_watcher(private_stream_t *this) static bool watch(private_stream_t *this, int fd, watcher_event_t event) { bool keep = FALSE; + stream_cb_t cb; switch (event) { case WATCHER_READ: - keep = this->read_cb(this->read_data, &this->public); - if (!keep) + cb = this->read_cb; + this->read_cb = NULL; + keep = cb(this->read_data, &this->public); + if (keep) { - this->read_cb = NULL; + this->read_cb = cb; } break; case WATCHER_WRITE: - keep = this->write_cb(this->write_data, &this->public); - if (!keep) + cb = this->write_cb; + this->write_cb = NULL; + keep = cb(this->write_data, &this->public); + if (keep) { - this->write_cb = NULL; + this->write_cb = cb; } break; case WATCHER_EXCEPT: diff --git a/src/libstrongswan/networking/streams/stream.h b/src/libstrongswan/networking/streams/stream.h index 8cd8419c3..810514da9 100644 --- a/src/libstrongswan/networking/streams/stream.h +++ b/src/libstrongswan/networking/streams/stream.h @@ -39,9 +39,9 @@ typedef stream_t*(*stream_constructor_t)(char *uri); /** * Callback function prototype, called when stream is ready. * - * It is not allowed to destroy the stream during the callback, this would - * deadlock. Instead, return FALSE to destroy the stream. It is not allowed - * to call on_read()/on_write() during this callback. + * It is allowed to destroy the stream during the callback, but only if it has + * no other active on_read()/on_write() callback and returns FALSE. It is not + * allowed to to call on_read()/on_write/() during the callback. * * As select() may return even if a read()/write() would actually block, it is * recommended to use the non-blocking calls and handle return values @@ -49,7 +49,7 @@ typedef stream_t*(*stream_constructor_t)(char *uri); * * @param data data passed during callback registration * @param stream associated stream - * @return FALSE to destroy the stream + * @return FALSE unregisters the invoked callback, TRUE keeps it */ typedef bool (*stream_cb_t)(void *data, stream_t *stream); From 065907b99d27f1ad02e6af890ee7698dc3bb42a6 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 28 Jun 2013 14:35:12 +0200 Subject: [PATCH 27/54] stroke: use a stream service to handle stroke requests --- src/libcharon/plugins/stroke/stroke_socket.c | 275 ++++--------------- 1 file changed, 48 insertions(+), 227 deletions(-) diff --git a/src/libcharon/plugins/stroke/stroke_socket.c b/src/libcharon/plugins/stroke/stroke_socket.c index 931dba1f4..88f73f3b0 100644 --- a/src/libcharon/plugins/stroke/stroke_socket.c +++ b/src/libcharon/plugins/stroke/stroke_socket.c @@ -26,11 +26,6 @@ #include #include -#include -#include -#include -#include -#include #include "stroke_config.h" #include "stroke_control.h" @@ -61,34 +56,9 @@ struct private_stroke_socket_t { stroke_socket_t public; /** - * Unix socket to listen for strokes + * Service accepting stroke connections */ - int socket; - - /** - * queued stroke commands - */ - linked_list_t *commands; - - /** - * lock for command list - */ - mutex_t *mutex; - - /** - * condvar to signal the arrival or completion of commands - */ - condvar_t *condvar; - - /** - * the number of currently handled commands - */ - u_int handling; - - /** - * the maximum number of concurrently handled commands - */ - u_int max_concurrent; + stream_service_t *service; /** * configuration backend @@ -131,22 +101,6 @@ struct private_stroke_socket_t { stroke_counter_t *counter; }; -/** - * job context to pass to processing thread - */ -struct stroke_job_context_t { - - /** - * file descriptor to read from - */ - int fd; - - /** - * global stroke interface - */ - private_stroke_socket_t *this; -}; - /** * Helper function which corrects the string pointers * in a stroke_msg_t. Strings in a stroke_msg sent over "wire" @@ -616,68 +570,47 @@ static void stroke_config(private_stroke_socket_t *this, } /** - * destroy a job context + * process a stroke request */ -static void stroke_job_context_destroy(stroke_job_context_t *this) -{ - if (this->fd) - { - close(this->fd); - } - free(this); -} - -/** - * called to signal the completion of a command - */ -static inline job_requeue_t job_processed(private_stroke_socket_t *this) -{ - this->mutex->lock(this->mutex); - this->handling--; - this->condvar->signal(this->condvar); - this->mutex->unlock(this->mutex); - return JOB_REQUEUE_NONE; -} - -/** - * process a stroke request from the socket pointed by "fd" - */ -static job_requeue_t process(stroke_job_context_t *ctx) +static bool on_accept(private_stroke_socket_t *this, stream_t *stream) { stroke_msg_t *msg; - u_int16_t msg_length; - ssize_t bytes_read; + u_int16_t len; FILE *out; - private_stroke_socket_t *this = ctx->this; - int strokefd = ctx->fd; - /* peek the length */ - bytes_read = recv(strokefd, &msg_length, sizeof(msg_length), MSG_PEEK); - if (bytes_read != sizeof(msg_length)) + /* read length */ + if (!stream->read_all(stream, &len, sizeof(len))) { - DBG1(DBG_CFG, "reading length of stroke message failed: %s", - strerror(errno)); - return job_processed(this); + if (errno != EWOULDBLOCK) + { + DBG1(DBG_CFG, "reading length of stroke message failed: %s", + strerror(errno)); + } + return FALSE; } /* read message */ - msg = alloca(msg_length); - bytes_read = recv(strokefd, msg, msg_length, 0); - if (bytes_read != msg_length) + msg = malloc(len); + msg->length = len; + if (!stream->read_all(stream, (char*)msg + sizeof(len), len - sizeof(len))) { - DBG1(DBG_CFG, "reading stroke message failed: %s", strerror(errno)); - return job_processed(this); + if (errno != EWOULDBLOCK) + { + DBG1(DBG_CFG, "reading stroke message failed: %s", strerror(errno)); + } + free(msg); + return FALSE; } - out = fdopen(strokefd, "w+"); - if (out == NULL) + DBG3(DBG_CFG, "stroke message %b", (void*)msg, len); + + out = stream->get_file(stream); + if (!out) { - DBG1(DBG_CFG, "opening stroke output channel failed: %s", strerror(errno)); - return job_processed(this); + DBG1(DBG_CFG, "creating stroke output stream failed"); + free(msg); + return FALSE; } - - DBG3(DBG_CFG, "stroke message %b", (void*)msg, msg_length); - switch (msg->type) { case STR_INITIATE: @@ -753,123 +686,15 @@ static job_requeue_t process(stroke_job_context_t *ctx) DBG1(DBG_CFG, "received unknown stroke"); break; } + free(msg); fclose(out); - /* fclose() closes underlying FD */ - ctx->fd = 0; - return job_processed(this); -} - -/** - * Handle queued stroke commands - */ -static job_requeue_t handle(private_stroke_socket_t *this) -{ - stroke_job_context_t *ctx; - callback_job_t *job; - bool oldstate; - - this->mutex->lock(this->mutex); - thread_cleanup_push((thread_cleanup_t)this->mutex->unlock, this->mutex); - oldstate = thread_cancelability(TRUE); - while (this->commands->get_count(this->commands) == 0 || - this->handling >= this->max_concurrent) - { - this->condvar->wait(this->condvar, this->mutex); - } - thread_cancelability(oldstate); - this->commands->remove_first(this->commands, (void**)&ctx); - this->handling++; - thread_cleanup_pop(TRUE); - job = callback_job_create_with_prio((callback_job_cb_t)process, ctx, - (void*)stroke_job_context_destroy, NULL, JOB_PRIO_HIGH); - lib->processor->queue_job(lib->processor, (job_t*)job); - return JOB_REQUEUE_DIRECT; -} - -/** - * Accept stroke commands and queue them to be handled - */ -static job_requeue_t receive(private_stroke_socket_t *this) -{ - struct sockaddr_un strokeaddr; - int strokeaddrlen = sizeof(strokeaddr); - int strokefd; - bool oldstate; - stroke_job_context_t *ctx; - - oldstate = thread_cancelability(TRUE); - strokefd = accept(this->socket, (struct sockaddr *)&strokeaddr, &strokeaddrlen); - thread_cancelability(oldstate); - - if (strokefd < 0) - { - DBG1(DBG_CFG, "accepting stroke connection failed: %s", strerror(errno)); - return JOB_REQUEUE_FAIR; - } - - INIT(ctx, - .fd = strokefd, - .this = this, - ); - this->mutex->lock(this->mutex); - this->commands->insert_last(this->commands, ctx); - this->condvar->signal(this->condvar); - this->mutex->unlock(this->mutex); - - return JOB_REQUEUE_FAIR; -} - -/** - * initialize and open stroke socket - */ -static bool open_socket(private_stroke_socket_t *this) -{ - struct sockaddr_un socket_addr; - mode_t old; - - socket_addr.sun_family = AF_UNIX; - strcpy(socket_addr.sun_path, STROKE_SOCKET); - - /* set up unix socket */ - this->socket = socket(AF_UNIX, SOCK_STREAM, 0); - if (this->socket == -1) - { - DBG1(DBG_CFG, "could not create stroke socket"); - return FALSE; - } - - unlink(socket_addr.sun_path); - old = umask(~(S_IRWXU | S_IRWXG)); - if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0) - { - DBG1(DBG_CFG, "could not bind stroke socket: %s", strerror(errno)); - close(this->socket); - return FALSE; - } - umask(old); - if (chown(socket_addr.sun_path, lib->caps->get_uid(lib->caps), - lib->caps->get_gid(lib->caps)) != 0) - { - DBG1(DBG_CFG, "changing stroke socket permissions failed: %s", - strerror(errno)); - } - - if (listen(this->socket, 10) < 0) - { - DBG1(DBG_CFG, "could not listen on stroke socket: %s", strerror(errno)); - close(this->socket); - unlink(socket_addr.sun_path); - return FALSE; - } - return TRUE; + return FALSE; } METHOD(stroke_socket_t, destroy, void, private_stroke_socket_t *this) { - this->commands->destroy_function(this->commands, (void*)stroke_job_context_destroy); - this->condvar->destroy(this->condvar); - this->mutex->destroy(this->mutex); + DESTROY_IF(this->service); lib->credmgr->remove_set(lib->credmgr, &this->ca->set); lib->credmgr->remove_set(lib->credmgr, &this->cred->set); charon->backends->remove_backend(charon->backends, &this->config->backend); @@ -893,6 +718,8 @@ METHOD(stroke_socket_t, destroy, void, stroke_socket_t *stroke_socket_create() { private_stroke_socket_t *this; + int max_concurrent; + char *uri; INIT(this, .public = { @@ -900,12 +727,6 @@ stroke_socket_t *stroke_socket_create() }, ); - if (!open_socket(this)) - { - free(this); - return NULL; - } - this->cred = stroke_cred_create(); this->attribute = stroke_attribute_create(); this->handler = stroke_handler_create(); @@ -915,13 +736,6 @@ stroke_socket_t *stroke_socket_create() this->list = stroke_list_create(this->attribute); this->counter = stroke_counter_create(); - this->mutex = mutex_create(MUTEX_TYPE_DEFAULT); - this->condvar = condvar_create(CONDVAR_TYPE_DEFAULT); - this->commands = linked_list_create(); - this->max_concurrent = lib->settings->get_int(lib->settings, - "%s.plugins.stroke.max_concurrent", MAX_CONCURRENT_DEFAULT, - charon->name); - lib->credmgr->add_set(lib->credmgr, &this->ca->set); lib->credmgr->add_set(lib->credmgr, &this->cred->set); charon->backends->add_backend(charon->backends, &this->config->backend); @@ -929,13 +743,20 @@ stroke_socket_t *stroke_socket_create() hydra->attributes->add_handler(hydra->attributes, &this->handler->handler); charon->bus->add_listener(charon->bus, &this->counter->listener); - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive, this, - NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); - - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio((callback_job_cb_t)handle, this, - NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + max_concurrent = lib->settings->get_int(lib->settings, + "%s.plugins.stroke.max_concurrent", MAX_CONCURRENT_DEFAULT, + charon->name); + uri = lib->settings->get_str(lib->settings, + "%s.plugins.stroke.socket", "unix://" STROKE_SOCKET, charon->name); + this->service = lib->streams->create_service(lib->streams, uri, 10); + if (!this->service) + { + DBG1(DBG_CFG, "creating stroke socket failed"); + destroy(this); + return NULL; + } + this->service->on_accept(this->service, (stream_service_cb_t)on_accept, + this, JOB_PRIO_CRITICAL, max_concurrent); return &this->public; } From 83faec5abe30796c9226819fe47cbabf3b375235 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 11:19:01 +0200 Subject: [PATCH 28/54] duplicheck: use a stream service to accept client connections As we can't use SOCK_SEQPACKET over TCP, we now have to provide message boundaries ourselves. We do this by appending a 16-bit length header to each sent duplicate identity. --- src/libcharon/plugins/duplicheck/Makefile.am | 3 +- src/libcharon/plugins/duplicheck/duplicheck.c | 89 +++++++++++--- .../plugins/duplicheck/duplicheck_msg.h | 43 +++++++ .../plugins/duplicheck/duplicheck_notify.c | 116 +++++------------- 4 files changed, 146 insertions(+), 105 deletions(-) create mode 100644 src/libcharon/plugins/duplicheck/duplicheck_msg.h diff --git a/src/libcharon/plugins/duplicheck/Makefile.am b/src/libcharon/plugins/duplicheck/Makefile.am index 4de9dba9f..4ea2becf3 100644 --- a/src/libcharon/plugins/duplicheck/Makefile.am +++ b/src/libcharon/plugins/duplicheck/Makefile.am @@ -15,7 +15,8 @@ endif libstrongswan_duplicheck_la_SOURCES = duplicheck_plugin.h duplicheck_plugin.c \ duplicheck_listener.h duplicheck_listener.c \ - duplicheck_notify.h duplicheck_notify.c + duplicheck_notify.h duplicheck_notify.c \ + duplicheck_msg.h libstrongswan_duplicheck_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/duplicheck/duplicheck.c b/src/libcharon/plugins/duplicheck/duplicheck.c index 99731a22b..508e8e386 100644 --- a/src/libcharon/plugins/duplicheck/duplicheck.c +++ b/src/libcharon/plugins/duplicheck/duplicheck.c @@ -16,44 +16,99 @@ #include #include #include +#include #include #include #include +#include -#define DUPLICHECK_SOCKET IPSEC_PIDDIR "/charon.dck" +#include "duplicheck_msg.h" -int main(int argc, char *argv[]) +/** + * Connect to the daemon, return FD + */ +static int make_connection() { - struct sockaddr_un addr; - char buf[128]; + union { + struct sockaddr_un un; + struct sockaddr_in in; + struct sockaddr sa; + } addr; int fd, len; - addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, DUPLICHECK_SOCKET); + if (getenv("TCP_PORT")) + { + addr.in.sin_family = AF_INET; + addr.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.in.sin_port = htons(atoi(getenv("TCP_PORT"))); + len = sizeof(addr.in); + } + else + { + addr.un.sun_family = AF_UNIX; + strcpy(addr.un.sun_path, DUPLICHECK_SOCKET); - fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + len = offsetof(struct sockaddr_un, sun_path) + strlen(addr.un.sun_path); + } + fd = socket(addr.sa.sa_family, SOCK_STREAM, 0); if (fd < 0) { fprintf(stderr, "opening socket failed: %s\n", strerror(errno)); - return 1; + return -1; } - if (connect(fd, (struct sockaddr *)&addr, - offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path)) < 0) + if (connect(fd, &addr.sa, len) < 0) { - fprintf(stderr, "connecting to %s failed: %s\n", - DUPLICHECK_SOCKET, strerror(errno)); + fprintf(stderr, "connecting failed: %s\n", strerror(errno)); close(fd); + return -1; + } + return fd; +} + +int main(int argc, char *argv[]) +{ + char buf[128]; + int fd, len; + u_int16_t msglen; + + fd = make_connection(); + if (fd < 0) + { return 1; } while (1) { - len = recv(fd, &buf, sizeof(buf) - 1, 0); + len = recv(fd, &msglen, sizeof(msglen), 0); + if (len != sizeof(msglen)) + { + break; + } + msglen = ntohs(msglen); + while (msglen) + { + if (sizeof(buf) > msglen) + { + len = msglen; + } + else + { + len = sizeof(buf); + } + len = recv(fd, &buf, len, 0); + if (len < 0) + { + break; + } + msglen -= len; + printf("%.*s", len, buf); + } + printf("\n"); if (len < 0) { - fprintf(stderr, "reading from socket failed: %s\n", strerror(errno)); - close(fd); - return 1; + break; } - printf("%.*s\n", len, buf); } + fprintf(stderr, "reading from socket failed: %s\n", strerror(errno)); + close(fd); + return 1; } diff --git a/src/libcharon/plugins/duplicheck/duplicheck_msg.h b/src/libcharon/plugins/duplicheck/duplicheck_msg.h new file mode 100644 index 000000000..99e297104 --- /dev/null +++ b/src/libcharon/plugins/duplicheck/duplicheck_msg.h @@ -0,0 +1,43 @@ +/* + * 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 . + * + * 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 duplicheck_msg duplicheck_msg + * @{ @ingroup duplicheck + */ + +#ifndef DUPLICHECK_MSG_H_ +#define DUPLICHECK_MSG_H_ + +#include + +/** + * Default Unix socket to connect to + */ +#define DUPLICHECK_SOCKET IPSEC_PIDDIR "/charon.dck" + +typedef struct duplicheck_msg_t duplicheck_msg_t; + +/** + * Message exchanged over duplicheck socket + */ +struct duplicheck_msg_t { + /** length of the identity following, in network order (excluding len). */ + u_int16_t len; + /** identity string, not null terminated */ + char identity[]; +} __attribute__((__packed__)); + +#endif /** DUPLICHECK_MSG_H_ @}*/ diff --git a/src/libcharon/plugins/duplicheck/duplicheck_notify.c b/src/libcharon/plugins/duplicheck/duplicheck_notify.c index 1091258da..e3a4e17b7 100644 --- a/src/libcharon/plugins/duplicheck/duplicheck_notify.c +++ b/src/libcharon/plugins/duplicheck/duplicheck_notify.c @@ -14,6 +14,7 @@ */ #include "duplicheck_notify.h" +#include "duplicheck_msg.h" #include #include @@ -28,7 +29,6 @@ #include #include -#define DUPLICHECK_SOCKET IPSEC_PIDDIR "/charon.dck" typedef struct private_duplicheck_notify_t private_duplicheck_notify_t; @@ -48,108 +48,53 @@ struct private_duplicheck_notify_t { mutex_t *mutex; /** - * List of connected sockets + * List of connected clients, as stream_t */ linked_list_t *connected; /** - * Socket dispatching connections + * stream service accepting connections */ - int socket; + stream_service_t *service; }; -/** - * Open duplicheck unix socket - */ -static bool open_socket(private_duplicheck_notify_t *this) -{ - struct sockaddr_un addr; - mode_t old; - - addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, DUPLICHECK_SOCKET); - - this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0); - if (this->socket == -1) - { - DBG1(DBG_CFG, "creating duplicheck socket failed"); - return FALSE; - } - unlink(addr.sun_path); - old = umask(~(S_IRWXU | S_IRWXG)); - if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) - { - DBG1(DBG_CFG, "binding duplicheck socket failed: %s", strerror(errno)); - close(this->socket); - return FALSE; - } - umask(old); - if (chown(addr.sun_path, lib->caps->get_uid(lib->caps), - lib->caps->get_gid(lib->caps)) != 0) - { - DBG1(DBG_CFG, "changing duplicheck socket permissions failed: %s", - strerror(errno)); - } - if (listen(this->socket, 3) < 0) - { - DBG1(DBG_CFG, "listening on duplicheck socket failed: %s", - strerror(errno)); - close(this->socket); - unlink(addr.sun_path); - return FALSE; - } - return TRUE; -} - /** * Accept duplicheck notification connections */ -static job_requeue_t receive(private_duplicheck_notify_t *this) +static bool on_accept(private_duplicheck_notify_t *this, stream_t *stream) { - struct sockaddr_un addr; - int len = sizeof(addr); - uintptr_t fd; - bool oldstate; + this->mutex->lock(this->mutex); + this->connected->insert_last(this->connected, stream); + this->mutex->unlock(this->mutex); - oldstate = thread_cancelability(TRUE); - fd = accept(this->socket, (struct sockaddr*)&addr, &len); - thread_cancelability(oldstate); - - if (fd != -1) - { - this->mutex->lock(this->mutex); - this->connected->insert_last(this->connected, (void*)fd); - this->mutex->unlock(this->mutex); - } - else - { - DBG1(DBG_CFG, "accepting duplicheck connection failed: %s", - strerror(errno)); - } - return JOB_REQUEUE_FAIR; + return TRUE; } METHOD(duplicheck_notify_t, send_, void, private_duplicheck_notify_t *this, identification_t *id) { - char buf[128]; enumerator_t *enumerator; - uintptr_t fd; + stream_t *stream; + u_int16_t nlen; + char buf[512]; int len; len = snprintf(buf, sizeof(buf), "%Y", id); if (len > 0 && len < sizeof(buf)) { + nlen = htons(len); + this->mutex->lock(this->mutex); enumerator = this->connected->create_enumerator(this->connected); - while (enumerator->enumerate(enumerator, &fd)) + while (enumerator->enumerate(enumerator, &stream)) { - if (send(fd, &buf, len + 1, 0) != len + 1) + if (!stream->write_all(stream, &nlen, sizeof(nlen)) || + !stream->write_all(stream, buf, len)) { DBG1(DBG_CFG, "sending duplicheck notify failed: %s", strerror(errno)); this->connected->remove_at(this->connected, enumerator); - close(fd); + stream->destroy(stream); } } enumerator->destroy(enumerator); @@ -160,16 +105,8 @@ METHOD(duplicheck_notify_t, send_, void, METHOD(duplicheck_notify_t, destroy, void, private_duplicheck_notify_t *this) { - enumerator_t *enumerator; - uintptr_t fd; - - enumerator = this->connected->create_enumerator(this->connected); - while (enumerator->enumerate(enumerator, &fd)) - { - close(fd); - } - enumerator->destroy(enumerator); - this->connected->destroy(this->connected); + DESTROY_IF(this->service); + this->connected->destroy_offset(this->connected, offsetof(stream_t, destroy)); this->mutex->destroy(this->mutex); free(this); } @@ -180,6 +117,7 @@ METHOD(duplicheck_notify_t, destroy, void, duplicheck_notify_t *duplicheck_notify_create() { private_duplicheck_notify_t *this; + char *uri; INIT(this, .public = { @@ -190,14 +128,18 @@ duplicheck_notify_t *duplicheck_notify_create() .mutex = mutex_create(MUTEX_TYPE_DEFAULT), ); - if (!open_socket(this)) + uri = lib->settings->get_str(lib->settings, + "%s.plugins.duplicheck.socket", "unix://" DUPLICHECK_SOCKET, + charon->name); + this->service = lib->streams->create_service(lib->streams, uri, 3); + if (!this->service) { + DBG1(DBG_CFG, "creating duplicheck socket failed"); destroy(this); return NULL; } - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive, this, - NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + this->service->on_accept(this->service, (stream_service_cb_t)on_accept, + this, JOB_PRIO_CRITICAL, 1); return &this->public; } From c2a6fdf286dc96765670b7ef7b2f40cc91dfa53d Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 11:42:18 +0200 Subject: [PATCH 29/54] error-notify: use a stream service to accept client connections As TCP does not have SOCK_SEQPACKET, we now use SOCK_STREAM for the error-notify socket. To have network transparency, the message now uses network byte order. --- .../plugins/error_notify/error_notify.c | 79 +++++++++--- .../error_notify/error_notify_listener.c | 32 ++--- .../plugins/error_notify/error_notify_msg.h | 2 +- .../error_notify/error_notify_socket.c | 112 ++++-------------- 4 files changed, 103 insertions(+), 122 deletions(-) diff --git a/src/libcharon/plugins/error_notify/error_notify.c b/src/libcharon/plugins/error_notify/error_notify.c index fec35a45d..e68f8a4a5 100644 --- a/src/libcharon/plugins/error_notify/error_notify.c +++ b/src/libcharon/plugins/error_notify/error_notify.c @@ -16,46 +16,89 @@ #include "error_notify_msg.h" #include +#include +#include #include #include #include #include #include +#include + +/** + * Connect to the daemon, return FD + */ +static int make_connection() +{ + union { + struct sockaddr_un un; + struct sockaddr_in in; + struct sockaddr sa; + } addr; + int fd, len; + + if (getenv("TCP_PORT")) + { + addr.in.sin_family = AF_INET; + addr.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.in.sin_port = htons(atoi(getenv("TCP_PORT"))); + len = sizeof(addr.in); + } + else + { + addr.un.sun_family = AF_UNIX; + strcpy(addr.un.sun_path, ERROR_NOTIFY_SOCKET); + + len = offsetof(struct sockaddr_un, sun_path) + strlen(addr.un.sun_path); + } + fd = socket(addr.sa.sa_family, SOCK_STREAM, 0); + if (fd < 0) + { + fprintf(stderr, "opening socket failed: %s\n", strerror(errno)); + return -1; + } + if (connect(fd, &addr.sa, len) < 0) + { + fprintf(stderr, "connecting failed: %s\n", strerror(errno)); + close(fd); + return -1; + } + return fd; +} /** * Example of a simple notification listener */ int main(int argc, char *argv[]) { - struct sockaddr_un addr; error_notify_msg_t msg; - int s; + int s, len, total; + void *pos; - addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, ERROR_NOTIFY_SOCKET); - - s = socket(AF_UNIX, SOCK_SEQPACKET, 0); + s = make_connection(); if (s < 0) { - fprintf(stderr, "opening socket failed: %s\n", strerror(errno)); - return 1; - } - if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) - { - fprintf(stderr, "connect failed: %s\n", strerror(errno)); - close(s); return 1; } while (1) { - if (read(s, &msg, sizeof(msg)) != sizeof(msg)) + total = 0; + pos = &msg; + + while (total < sizeof(msg)) { - fprintf(stderr, "read failed: %s\n", strerror(errno)); - close(s); - return 1; + len = read(s, pos, sizeof(msg) - total); + if (len < 0) + { + fprintf(stderr, "read failed: %s\n", strerror(errno)); + close(s); + return 1; + } + total += len; + pos += len; } printf("%d %s %s %s %s\n", - msg.type, msg.name, msg.id, msg.ip, msg.str); + ntohl(msg.type), msg.name, msg.id, msg.ip, msg.str); } close(s); return 0; diff --git a/src/libcharon/plugins/error_notify/error_notify_listener.c b/src/libcharon/plugins/error_notify/error_notify_listener.c index 9a6383cbe..a985cc480 100644 --- a/src/libcharon/plugins/error_notify/error_notify_listener.c +++ b/src/libcharon/plugins/error_notify/error_notify_listener.c @@ -56,80 +56,80 @@ METHOD(listener_t, alert, bool, switch (alert) { case ALERT_RADIUS_NOT_RESPONDING: - msg.type = ERROR_NOTIFY_RADIUS_NOT_RESPONDING; + msg.type = htonl(ERROR_NOTIFY_RADIUS_NOT_RESPONDING); snprintf(msg.str, sizeof(msg.str), "a RADIUS request message timed out"); break; case ALERT_LOCAL_AUTH_FAILED: - msg.type = ERROR_NOTIFY_LOCAL_AUTH_FAILED; + msg.type = htonl(ERROR_NOTIFY_LOCAL_AUTH_FAILED); snprintf(msg.str, sizeof(msg.str), "creating local authentication data failed"); break; case ALERT_PEER_AUTH_FAILED: - msg.type = ERROR_NOTIFY_PEER_AUTH_FAILED; + msg.type = htonl(ERROR_NOTIFY_PEER_AUTH_FAILED); snprintf(msg.str, sizeof(msg.str), "peer authentication failed"); break; case ALERT_PARSE_ERROR_HEADER: - msg.type = ERROR_NOTIFY_PARSE_ERROR_HEADER; + msg.type = htonl(ERROR_NOTIFY_PARSE_ERROR_HEADER); message = va_arg(args, message_t*); snprintf(msg.str, sizeof(msg.str), "parsing IKE header from " "%#H failed", message->get_source(message)); break; case ALERT_PARSE_ERROR_BODY: - msg.type = ERROR_NOTIFY_PARSE_ERROR_BODY; + msg.type = htonl(ERROR_NOTIFY_PARSE_ERROR_BODY); message = va_arg(args, message_t*); snprintf(msg.str, sizeof(msg.str), "parsing IKE message from " "%#H failed", message->get_source(message)); break; case ALERT_RETRANSMIT_SEND_TIMEOUT: - msg.type = ERROR_NOTIFY_RETRANSMIT_SEND_TIMEOUT; + msg.type = htonl(ERROR_NOTIFY_RETRANSMIT_SEND_TIMEOUT); snprintf(msg.str, sizeof(msg.str), "IKE message retransmission timed out"); break; case ALERT_HALF_OPEN_TIMEOUT: - msg.type = ERROR_NOTIFY_HALF_OPEN_TIMEOUT; + msg.type = htonl(ERROR_NOTIFY_HALF_OPEN_TIMEOUT); snprintf(msg.str, sizeof(msg.str), "IKE_SA timed out before it " "could be established"); break; case ALERT_PROPOSAL_MISMATCH_IKE: - msg.type = ERROR_NOTIFY_PROPOSAL_MISMATCH_IKE; + msg.type = htonl(ERROR_NOTIFY_PROPOSAL_MISMATCH_IKE); list = va_arg(args, linked_list_t*); snprintf(msg.str, sizeof(msg.str), "the received IKE_SA poposals " "did not match: %#P", list); break; case ALERT_PROPOSAL_MISMATCH_CHILD: - msg.type = ERROR_NOTIFY_PROPOSAL_MISMATCH_CHILD; + msg.type = htonl(ERROR_NOTIFY_PROPOSAL_MISMATCH_CHILD); list = va_arg(args, linked_list_t*); snprintf(msg.str, sizeof(msg.str), "the received CHILD_SA poposals " "did not match: %#P", list); break; case ALERT_TS_MISMATCH: - msg.type = ERROR_NOTIFY_TS_MISMATCH; + msg.type = htonl(ERROR_NOTIFY_TS_MISMATCH); list = va_arg(args, linked_list_t*); list2 = va_arg(args, linked_list_t*); snprintf(msg.str, sizeof(msg.str), "the received traffic selectors " "did not match: %#R=== %#R", list, list2); break; case ALERT_INSTALL_CHILD_SA_FAILED: - msg.type = ERROR_NOTIFY_INSTALL_CHILD_SA_FAILED; + msg.type = htonl(ERROR_NOTIFY_INSTALL_CHILD_SA_FAILED); snprintf(msg.str, sizeof(msg.str), "installing IPsec SA failed"); break; case ALERT_INSTALL_CHILD_POLICY_FAILED: - msg.type = ERROR_NOTIFY_INSTALL_CHILD_POLICY_FAILED; + msg.type = htonl(ERROR_NOTIFY_INSTALL_CHILD_POLICY_FAILED); snprintf(msg.str, sizeof(msg.str), "installing IPsec policy failed"); break; case ALERT_UNIQUE_REPLACE: - msg.type = ERROR_NOTIFY_UNIQUE_REPLACE; + msg.type = htonl(ERROR_NOTIFY_UNIQUE_REPLACE); snprintf(msg.str, sizeof(msg.str), "replaced old IKE_SA due to uniqueness policy"); break; case ALERT_UNIQUE_KEEP: - msg.type = ERROR_NOTIFY_UNIQUE_KEEP; + msg.type = htonl(ERROR_NOTIFY_UNIQUE_KEEP); snprintf(msg.str, sizeof(msg.str), "keep existing in favor of " "rejected new IKE_SA due to uniqueness policy"); break; case ALERT_VIP_FAILURE: - msg.type = ERROR_NOTIFY_VIP_FAILURE; + msg.type = htonl(ERROR_NOTIFY_VIP_FAILURE); list = va_arg(args, linked_list_t*); if (list->get_first(list, (void**)&host) == SUCCESS) { @@ -143,7 +143,7 @@ METHOD(listener_t, alert, bool, } break; case ALERT_AUTHORIZATION_FAILED: - msg.type = ERROR_NOTIFY_AUTHORIZATION_FAILED; + msg.type = htonl(ERROR_NOTIFY_AUTHORIZATION_FAILED); snprintf(msg.str, sizeof(msg.str), "an authorization plugin " "prevented establishment of an IKE_SA"); break; diff --git a/src/libcharon/plugins/error_notify/error_notify_msg.h b/src/libcharon/plugins/error_notify/error_notify_msg.h index e3cdd67e9..d031fc4c3 100644 --- a/src/libcharon/plugins/error_notify/error_notify_msg.h +++ b/src/libcharon/plugins/error_notify/error_notify_msg.h @@ -61,6 +61,6 @@ struct error_notify_msg_t { char id[128]; /** peer address and port, if known */ char ip[60]; -}; +} __attribute__((packed)); #endif /** ERROR_NOTIFY_MSG_H_ @}*/ diff --git a/src/libcharon/plugins/error_notify/error_notify_socket.c b/src/libcharon/plugins/error_notify/error_notify_socket.c index 2fc74202b..aafd0a4cd 100644 --- a/src/libcharon/plugins/error_notify/error_notify_socket.c +++ b/src/libcharon/plugins/error_notify/error_notify_socket.c @@ -43,12 +43,12 @@ struct private_error_notify_socket_t { error_notify_socket_t public; /** - * Unix socket file descriptor + * Service accepting connections */ - int socket; + stream_service_t *service; /** - * List of connected clients, as uintptr_t FD + * List of connected clients, as stream_t */ linked_list_t *connected; @@ -58,48 +58,6 @@ struct private_error_notify_socket_t { mutex_t *mutex; }; -/** - * Open error notify unix socket - */ -static bool open_socket(private_error_notify_socket_t *this) -{ - struct sockaddr_un addr; - mode_t old; - - addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, ERROR_NOTIFY_SOCKET); - - this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0); - if (this->socket == -1) - { - DBG1(DBG_CFG, "creating notify socket failed"); - return FALSE; - } - unlink(addr.sun_path); - old = umask(~(S_IRWXU | S_IRWXG)); - if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) - { - DBG1(DBG_CFG, "binding notify socket failed: %s", strerror(errno)); - close(this->socket); - return FALSE; - } - umask(old); - if (chown(addr.sun_path, lib->caps->get_uid(lib->caps), - lib->caps->get_gid(lib->caps)) != 0) - { - DBG1(DBG_CFG, "changing notify socket permissions failed: %s", - strerror(errno)); - } - if (listen(this->socket, 10) < 0) - { - DBG1(DBG_CFG, "listening on notify socket failed: %s", strerror(errno)); - close(this->socket); - unlink(addr.sun_path); - return FALSE; - } - return TRUE; -} - METHOD(error_notify_socket_t, has_listeners, bool, private_error_notify_socket_t *this) { @@ -116,23 +74,21 @@ METHOD(error_notify_socket_t, notify, void, private_error_notify_socket_t *this, error_notify_msg_t *msg) { enumerator_t *enumerator; - uintptr_t fd; + stream_t *stream; this->mutex->lock(this->mutex); enumerator = this->connected->create_enumerator(this->connected); - while (enumerator->enumerate(enumerator, (void*)&fd)) + while (enumerator->enumerate(enumerator, &stream)) { - while (send(fd, msg, sizeof(*msg), 0) <= 0) + if (!stream->write_all(stream, msg, sizeof(*msg))) { switch (errno) { - case EINTR: - continue; case ECONNRESET: case EPIPE: /* disconnect, remove this listener */ this->connected->remove_at(this->connected, enumerator); - close(fd); + stream->destroy(stream); break; default: DBG1(DBG_CFG, "sending notify failed: %s", strerror(errno)); @@ -146,45 +102,23 @@ METHOD(error_notify_socket_t, notify, void, } /** - * Accept client connections, dispatch + * Accept client connections */ -static job_requeue_t accept_(private_error_notify_socket_t *this) +static bool on_accept(private_error_notify_socket_t *this, stream_t *stream) { - struct sockaddr_un addr; - int fd, len; - bool oldstate; + this->mutex->lock(this->mutex); + this->connected->insert_last(this->connected, stream); + this->mutex->unlock(this->mutex); - len = sizeof(addr); - oldstate = thread_cancelability(TRUE); - fd = accept(this->socket, (struct sockaddr*)&addr, &len); - thread_cancelability(oldstate); - - if (fd != -1) - { - this->mutex->lock(this->mutex); - this->connected->insert_last(this->connected, (void*)(uintptr_t)fd); - this->mutex->unlock(this->mutex); - } - else - { - DBG1(DBG_CFG, "accepting notify connection failed: %s", - strerror(errno)); - } - return JOB_REQUEUE_DIRECT; + return TRUE; } METHOD(error_notify_socket_t, destroy, void, private_error_notify_socket_t *this) { - uintptr_t fd; - - while (this->connected->remove_last(this->connected, (void*)&fd) == SUCCESS) - { - close(fd); - } - this->connected->destroy(this->connected); + DESTROY_IF(this->service); + this->connected->destroy_offset(this->connected, offsetof(stream_t, destroy)); this->mutex->destroy(this->mutex); - close(this->socket); free(this); } @@ -194,6 +128,7 @@ METHOD(error_notify_socket_t, destroy, void, error_notify_socket_t *error_notify_socket_create() { private_error_notify_socket_t *this; + char *uri; INIT(this, .public = { @@ -205,15 +140,18 @@ error_notify_socket_t *error_notify_socket_create() .mutex = mutex_create(MUTEX_TYPE_DEFAULT), ); - if (!open_socket(this)) + uri = lib->settings->get_str(lib->settings, + "%s.plugins.error-notify.socket", "unix://" ERROR_NOTIFY_SOCKET, + charon->name); + this->service = lib->streams->create_service(lib->streams, uri, 10); + if (!this->service) { - free(this); + DBG1(DBG_CFG, "creating duplicheck socket failed"); + destroy(this); return NULL; } - - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio((callback_job_cb_t)accept_, this, - NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + this->service->on_accept(this->service, (stream_service_cb_t)on_accept, + this, JOB_PRIO_CRITICAL, 1); return &this->public; } From 091d0afa2163a6aba79bf4802330f0099e857e38 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 12:47:45 +0200 Subject: [PATCH 30/54] lookip: use stream service with async I/O dispatching Now uses SOCK_STREAM, as SOCK_SEQPACKET is not available over TCP. To have network transparency, the message now uses network byte order. --- src/libcharon/plugins/lookip/lookip.c | 89 +++- .../plugins/lookip/lookip_listener.c | 21 + .../plugins/lookip/lookip_listener.h | 7 + src/libcharon/plugins/lookip/lookip_msg.h | 4 +- src/libcharon/plugins/lookip/lookip_socket.c | 439 ++++++++---------- 5 files changed, 299 insertions(+), 261 deletions(-) diff --git a/src/libcharon/plugins/lookip/lookip.c b/src/libcharon/plugins/lookip/lookip.c index 9887a3a92..d473c7022 100644 --- a/src/libcharon/plugins/lookip/lookip.c +++ b/src/libcharon/plugins/lookip/lookip.c @@ -20,51 +20,112 @@ #include #include #include +#include #include #include +#include /** * Connect to the daemon, return FD */ static int make_connection() { - struct sockaddr_un addr; - int fd; + union { + struct sockaddr_un un; + struct sockaddr_in in; + struct sockaddr sa; + } addr; + int fd, len; - addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, LOOKIP_SOCKET); + if (getenv("TCP_PORT")) + { + addr.in.sin_family = AF_INET; + addr.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.in.sin_port = htons(atoi(getenv("TCP_PORT"))); + len = sizeof(addr.in); + } + else + { + addr.un.sun_family = AF_UNIX; + strcpy(addr.un.sun_path, LOOKIP_SOCKET); - fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + len = offsetof(struct sockaddr_un, sun_path) + strlen(addr.un.sun_path); + } + fd = socket(addr.sa.sa_family, SOCK_STREAM, 0); if (fd < 0) { fprintf(stderr, "opening socket failed: %s\n", strerror(errno)); return -1; } - if (connect(fd, (struct sockaddr *)&addr, - offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path)) < 0) + if (connect(fd, &addr.sa, len) < 0) { - fprintf(stderr, "connecting to %s failed: %s\n", - LOOKIP_SOCKET, strerror(errno)); + fprintf(stderr, "connecting failed: %s\n", strerror(errno)); close(fd); return -1; } return fd; } +static int read_all(int fd, void *buf, size_t len, int flags) +{ + ssize_t ret, done = 0; + + while (done < len) + { + ret = recv(fd, buf, len - done, flags); + if (ret == -1 && errno == EINTR) + { /* interrupted, try again */ + continue; + } + if (ret == 0) + { + return 0; + } + if (ret < 0) + { + return -1; + } + done += ret; + buf += ret; + } + return len; +} + +static int write_all(int fd, void *buf, size_t len) +{ + ssize_t ret, done = 0; + + while (done < len) + { + ret = write(fd, buf, len - done); + if (ret == -1 && errno == EINTR) + { /* interrupted, try again */ + continue; + } + if (ret < 0) + { + return -1; + } + done += ret; + buf += ret; + } + return len; +} + /** * Send a request message */ static int send_request(int fd, int type, char *vip) { lookip_request_t req = { - .type = type, + .type = htonl(type), }; if (vip) { snprintf(req.vip, sizeof(req.vip), "%s", vip); } - if (send(fd, &req, sizeof(req), 0) != sizeof(req)) + if (write_all(fd, &req, sizeof(req)) != sizeof(req)) { fprintf(stderr, "writing to socket failed: %s\n", strerror(errno)); return 2; @@ -83,7 +144,7 @@ static int receive(int fd, int block, int loop) do { - res = recv(fd, &resp, sizeof(resp), block ? 0 : MSG_DONTWAIT); + res = read_all(fd, &resp, sizeof(resp), block ? 0 : MSG_DONTWAIT); if (res == 0) { /* closed by server */ return 0; @@ -97,7 +158,7 @@ static int receive(int fd, int block, int loop) fprintf(stderr, "reading from socket failed: %s\n", strerror(errno)); return 1; } - switch (resp.type) + switch (ntohl(resp.type)) { case LOOKIP_ENTRY: label = "lookup:"; @@ -120,7 +181,7 @@ static int receive(int fd, int block, int loop) resp.id[sizeof(resp.id) - 1] = '\0'; resp.name[sizeof(resp.name) - 1] = '\0'; - snprintf(name, sizeof(name), "%s[%u]", resp.name, resp.unique_id); + snprintf(name, sizeof(name), "%s[%u]", resp.name, ntohl(resp.unique_id)); printf("%-12s %16s %16s %20s %s\n", label, resp.vip, resp.ip, name, resp.id); } diff --git a/src/libcharon/plugins/lookip/lookip_listener.c b/src/libcharon/plugins/lookip/lookip_listener.c index caf336a2e..d5eab1f6c 100644 --- a/src/libcharon/plugins/lookip/lookip_listener.c +++ b/src/libcharon/plugins/lookip/lookip_listener.c @@ -290,6 +290,26 @@ METHOD(lookip_listener_t, add_listener, void, this->lock->unlock(this->lock); } +METHOD(lookip_listener_t, remove_listener, void, + private_lookip_listener_t *this, void *user) +{ + listener_entry_t *listener; + enumerator_t *enumerator; + + this->lock->write_lock(this->lock); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &listener)) + { + if (listener->user == user) + { + this->listeners->remove_at(this->listeners, enumerator); + free(listener); + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); +} + METHOD(lookip_listener_t, destroy, void, private_lookip_listener_t *this) { @@ -315,6 +335,7 @@ lookip_listener_t *lookip_listener_create() }, .lookup = _lookup, .add_listener = _add_listener, + .remove_listener = _remove_listener, .destroy = _destroy, }, .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), diff --git a/src/libcharon/plugins/lookip/lookip_listener.h b/src/libcharon/plugins/lookip/lookip_listener.h index 56f74ed48..f6612b324 100644 --- a/src/libcharon/plugins/lookip/lookip_listener.h +++ b/src/libcharon/plugins/lookip/lookip_listener.h @@ -74,6 +74,13 @@ struct lookip_listener_t { void (*add_listener)(lookip_listener_t *this, lookip_callback_t cb, void *user); + /** + * Unregister a listener by the user data. + * + * @param user user data, as passed during add_listener() + */ + void (*remove_listener)(lookip_listener_t *this, void *user); + /** * Destroy a lookip_listener_t. */ diff --git a/src/libcharon/plugins/lookip/lookip_msg.h b/src/libcharon/plugins/lookip/lookip_msg.h index d5789c29f..28c02d0de 100644 --- a/src/libcharon/plugins/lookip/lookip_msg.h +++ b/src/libcharon/plugins/lookip/lookip_msg.h @@ -69,7 +69,7 @@ struct lookip_request_t { int type; /** null terminated string representation of virtual IP */ char vip[40]; -}; +} __attribute__((packed)); /** * Response message sent to client. @@ -91,6 +91,6 @@ struct lookip_response_t { char name[40]; /** unique connection id */ unsigned int unique_id; -}; +} __attribute__((packed)); #endif /** LOOKIP_MSG_H_ @}*/ diff --git a/src/libcharon/plugins/lookip/lookip_socket.c b/src/libcharon/plugins/lookip/lookip_socket.c index b1a46f46a..d25573bf4 100644 --- a/src/libcharon/plugins/lookip/lookip_socket.c +++ b/src/libcharon/plugins/lookip/lookip_socket.c @@ -48,17 +48,12 @@ struct private_lookip_socket_t { lookip_listener_t *listener; /** - * lookip unix socket file descriptor + * stream service accepting connections */ - int socket; + stream_service_t *service; /** - * List of registered listeners, as entry_t - */ - linked_list_t *registered; - - /** - * List of connected clients, as uintptr_t FD + * List of connected clients, as entry_t */ linked_list_t *connected; @@ -69,88 +64,80 @@ struct private_lookip_socket_t { }; /** - * Open lookip unix socket - */ -static bool open_socket(private_lookip_socket_t *this) -{ - struct sockaddr_un addr; - mode_t old; - - addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, LOOKIP_SOCKET); - - this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0); - if (this->socket == -1) - { - DBG1(DBG_CFG, "creating lookip socket failed"); - return FALSE; - } - unlink(addr.sun_path); - old = umask(~(S_IRWXU | S_IRWXG)); - if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) - { - DBG1(DBG_CFG, "binding lookip socket failed: %s", strerror(errno)); - close(this->socket); - return FALSE; - } - umask(old); - if (chown(addr.sun_path, lib->caps->get_uid(lib->caps), - lib->caps->get_gid(lib->caps)) != 0) - { - DBG1(DBG_CFG, "changing lookip socket permissions failed: %s", - strerror(errno)); - } - if (listen(this->socket, 10) < 0) - { - DBG1(DBG_CFG, "listening on lookip socket failed: %s", strerror(errno)); - close(this->socket); - unlink(addr.sun_path); - return FALSE; - } - return TRUE; -} - -/** - * Listener callback entry + * List entry for a connected stream */ typedef struct { - /* FD to write to */ - int fd; - /* message type to send */ - int type; - /* back pointer to socket, only for subscriptions */ + /* stream to write to */ + stream_t *stream; + /* registered for up events? */ + bool up; + /* registered for down events? */ + bool down; + /** backref to this for unregistration */ private_lookip_socket_t *this; } entry_t; /** - * Destroy entry + * Clean up a connection entry */ -static void entry_destroy(entry_t *this) +static void entry_destroy(entry_t *entry) { - close(this->fd); - free(this); + entry->stream->destroy(entry->stream); + free(entry); } /** - * Callback function for listener + * Disconnect a stream, remove connection entry */ -static bool listener_cb(entry_t *entry, bool up, host_t *vip, - host_t *other, identification_t *id, - char *name, u_int unique_id) +static void disconnect(private_lookip_socket_t *this, stream_t *stream) +{ + enumerator_t *enumerator; + entry_t *entry; + + this->mutex->lock(this->mutex); + enumerator = this->connected->create_enumerator(this->connected); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->stream == stream) + { + this->connected->remove_at(this->connected, enumerator); + if (entry->up || entry->down) + { + this->listener->remove_listener(this->listener, entry); + } + entry_destroy(entry); + break; + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +/** + * Callback function for listener up/down events + */ +static bool event_cb(entry_t *entry, bool up, host_t *vip, host_t *other, + identification_t *id, char *name, u_int unique_id) { lookip_response_t resp = { - .type = entry->type, - .unique_id = unique_id, + .unique_id = htonl(unique_id), }; - /* filter events */ - if (up && entry->type == LOOKIP_NOTIFY_DOWN) + if (up) { - return TRUE; + if (!entry->up) + { + return TRUE; + } + resp.type = htonl(LOOKIP_NOTIFY_UP); } - if (!up && entry->type == LOOKIP_NOTIFY_UP) + else { - return TRUE; + if (!entry->down) + { + return TRUE; + } + resp.type = htonl(LOOKIP_NOTIFY_DOWN); } snprintf(resp.vip, sizeof(resp.vip), "%H", vip); @@ -158,37 +145,66 @@ static bool listener_cb(entry_t *entry, bool up, host_t *vip, snprintf(resp.id, sizeof(resp.id), "%Y", id); snprintf(resp.name, sizeof(resp.name), "%s", name); - switch (send(entry->fd, &resp, sizeof(resp), 0)) + if (entry->stream->write_all(entry->stream, &resp, sizeof(resp))) { - case sizeof(resp): - return TRUE; - case 0: + return TRUE; + } + switch (errno) + { + case ECONNRESET: + case EPIPE: + /* client disconnected, adios */ + break; + default: + DBG1(DBG_CFG, "sending lookip event failed: %s", strerror(errno)); + break; + } + /* don't unregister, as we return FALSE */ + entry->up = entry->down = FALSE; + disconnect(entry->this, entry->stream); + return FALSE; +} + +/** + * Callback function for queries + */ +static bool query_cb(stream_t *stream, bool up, host_t *vip, host_t *other, + identification_t *id, char *name, u_int unique_id) +{ + lookip_response_t resp = { + .type = htonl(LOOKIP_ENTRY), + .unique_id = htonl(unique_id), + }; + + snprintf(resp.vip, sizeof(resp.vip), "%H", vip); + snprintf(resp.ip, sizeof(resp.ip), "%H", other); + snprintf(resp.id, sizeof(resp.id), "%Y", id); + snprintf(resp.name, sizeof(resp.name), "%s", name); + + if (stream->write_all(stream, &resp, sizeof(resp))) + { + return TRUE; + } + switch (errno) + { + case ECONNRESET: + case EPIPE: /* client disconnected, adios */ break; default: DBG1(DBG_CFG, "sending lookip response failed: %s", strerror(errno)); break; } - if (entry->this) - { /* unregister listener */ - entry->this->mutex->lock(entry->this->mutex); - entry->this->registered->remove(entry->this->registered, entry, NULL); - entry->this->mutex->unlock(entry->this->mutex); - - entry_destroy(entry); - } return FALSE; } /** - * Perform a entry lookup + * Perform a lookup */ -static void query(private_lookip_socket_t *this, int fd, lookip_request_t *req) +static void query(private_lookip_socket_t *this, stream_t *stream, + lookip_request_t *req) { - entry_t entry = { - .fd = fd, - .type = LOOKIP_ENTRY, - }; + host_t *vip = NULL; int matches = 0; @@ -199,17 +215,17 @@ static void query(private_lookip_socket_t *this, int fd, lookip_request_t *req) if (vip) { matches = this->listener->lookup(this->listener, vip, - (void*)listener_cb, &entry); + (void*)query_cb, stream); vip->destroy(vip); } if (matches == 0) { lookip_response_t resp = { - .type = LOOKIP_NOT_FOUND, + .type = htonl(LOOKIP_NOT_FOUND), }; snprintf(resp.vip, sizeof(resp.vip), "%s", req->vip); - if (send(fd, &resp, sizeof(resp), 0) < 0) + if (!stream->write_all(stream, &resp, sizeof(resp))) { DBG1(DBG_CFG, "sending lookip not-found failed: %s", strerror(errno)); @@ -219,46 +235,59 @@ static void query(private_lookip_socket_t *this, int fd, lookip_request_t *req) else { /* dump */ this->listener->lookup(this->listener, NULL, - (void*)listener_cb, &entry); + (void*)query_cb, stream); } } /** * Subscribe to virtual IP events */ -static void subscribe(private_lookip_socket_t *this, int fd, int type) +static void subscribe(private_lookip_socket_t *this, stream_t *stream, bool up) { + enumerator_t *enumerator; entry_t *entry; - INIT(entry, - .fd = fd, - .type = type, - .this = this, - ); - this->mutex->lock(this->mutex); - this->registered->insert_last(this->registered, entry); + enumerator = this->connected->create_enumerator(this->connected); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->stream == stream) + { + if (!entry->up && !entry->down) + { /* newly registered */ + this->listener->add_listener(this->listener, + (void*)event_cb, entry); + } + if (up) + { + entry->up = TRUE; + } + else + { + entry->down = TRUE; + } + } + } + enumerator->destroy(enumerator); this->mutex->unlock(this->mutex); - - this->listener->add_listener(this->listener, (void*)listener_cb, entry); } /** * Check if a client is subscribed for notifications */ -static bool subscribed(private_lookip_socket_t *this, int fd) +static bool subscribed(private_lookip_socket_t *this, stream_t *stream) { enumerator_t *enumerator; bool subscribed = FALSE; entry_t *entry; this->mutex->lock(this->mutex); - enumerator = this->registered->create_enumerator(this->registered); + enumerator = this->connected->create_enumerator(this->connected); while (enumerator->enumerate(enumerator, &entry)) { - if (entry->fd == fd) + if (entry->stream == stream) { - subscribed = TRUE; + subscribed = entry->up || entry->down; break; } } @@ -269,164 +298,80 @@ static bool subscribed(private_lookip_socket_t *this, int fd) } /** - * Create a fd_set from all bound sockets + * Dispatch from a socket, on-read callback */ -static int build_fds(private_lookip_socket_t *this, fd_set *fds) -{ - enumerator_t *enumerator; - uintptr_t fd; - int maxfd; - - FD_ZERO(fds); - FD_SET(this->socket, fds); - maxfd = this->socket; - - this->mutex->lock(this->mutex); - enumerator = this->connected->create_enumerator(this->connected); - while (enumerator->enumerate(enumerator, &fd)) - { - FD_SET(fd, fds); - maxfd = max(maxfd, fd); - } - enumerator->destroy(enumerator); - this->mutex->unlock(this->mutex); - - return maxfd + 1; -} - -/** - * Find the socket select()ed - */ -static int scan_fds(private_lookip_socket_t *this, fd_set *fds) -{ - enumerator_t *enumerator; - uintptr_t fd; - int selected = -1; - - this->mutex->lock(this->mutex); - enumerator = this->connected->create_enumerator(this->connected); - while (enumerator->enumerate(enumerator, &fd)) - { - if (FD_ISSET(fd, fds)) - { - selected = fd; - break; - } - } - enumerator->destroy(enumerator); - this->mutex->unlock(this->mutex); - - return selected; -} - -/** - * Dispatch from a socket, return TRUE to end communication - */ -static bool dispatch(private_lookip_socket_t *this, int fd) +static bool on_read(private_lookip_socket_t *this, stream_t *stream) { lookip_request_t req; - int len; - len = recv(fd, &req, sizeof(req), 0); - if (len != sizeof(req)) + if (stream->read_all(stream, &req, sizeof(req))) { - if (len != 0) + switch (ntohl(req.type)) + { + case LOOKIP_LOOKUP: + query(this, stream, &req); + return TRUE; + case LOOKIP_DUMP: + query(this, stream, NULL); + return TRUE; + case LOOKIP_REGISTER_UP: + subscribe(this, stream, TRUE); + return TRUE; + case LOOKIP_REGISTER_DOWN: + subscribe(this, stream, FALSE); + return TRUE; + case LOOKIP_END: + break; + default: + DBG1(DBG_CFG, "received unknown lookip command"); + break; + } + } + else + { + if (errno != ECONNRESET) { DBG1(DBG_CFG, "receiving lookip request failed: %s", strerror(errno)); } + disconnect(this, stream); + return FALSE; + } + if (subscribed(this, stream)) + { return TRUE; } - switch (req.type) - { - case LOOKIP_LOOKUP: - query(this, fd, &req); - return FALSE; - case LOOKIP_DUMP: - query(this, fd, NULL); - return FALSE; - case LOOKIP_REGISTER_UP: - subscribe(this, fd, LOOKIP_NOTIFY_UP); - return FALSE; - case LOOKIP_REGISTER_DOWN: - subscribe(this, fd, LOOKIP_NOTIFY_DOWN); - return FALSE; - case LOOKIP_END: - return TRUE; - default: - DBG1(DBG_CFG, "received unknown lookip command"); - return TRUE; - } + disconnect(this, stream); + return FALSE; } /** * Accept client connections, dispatch */ -static job_requeue_t receive(private_lookip_socket_t *this) +static bool on_accept(private_lookip_socket_t *this, stream_t *stream) { - struct sockaddr_un addr; - int fd, maxfd, len; - bool oldstate; - fd_set fds; + entry_t *entry; - while (TRUE) - { - maxfd = build_fds(this, &fds); - oldstate = thread_cancelability(TRUE); - if (select(maxfd, &fds, NULL, NULL, NULL) <= 0) - { - thread_cancelability(oldstate); - DBG1(DBG_CFG, "selecting lookip sockets failed: %s", - strerror(errno)); - break; - } - thread_cancelability(oldstate); + INIT(entry, + .stream = stream, + .this = this, + ); - if (FD_ISSET(this->socket, &fds)) - { /* new connection, accept() */ - len = sizeof(addr); - fd = accept(this->socket, (struct sockaddr*)&addr, &len); - if (fd != -1) - { - this->mutex->lock(this->mutex); - this->connected->insert_last(this->connected, - (void*)(uintptr_t)fd); - this->mutex->unlock(this->mutex); - } - else - { - DBG1(DBG_CFG, "accepting lookip connection failed: %s", - strerror(errno)); - } - continue; - } + this->mutex->lock(this->mutex); + this->connected->insert_last(this->connected, entry); + this->mutex->unlock(this->mutex); - fd = scan_fds(this, &fds); - if (fd == -1) - { - continue; - } - if (dispatch(this, fd)) - { - this->mutex->lock(this->mutex); - this->connected->remove(this->connected, (void*)(uintptr_t)fd, NULL); - this->mutex->unlock(this->mutex); - if (!subscribed(this, fd)) - { - close(fd); - } - } - } - return JOB_REQUEUE_FAIR; + stream->on_read(stream, (void*)on_read, this); + + return TRUE; } METHOD(lookip_socket_t, destroy, void, private_lookip_socket_t *this) { - this->registered->destroy_function(this->registered, (void*)entry_destroy); - this->connected->destroy(this->connected); + DESTROY_IF(this->service); + this->connected->destroy_function(this->connected, (void*)entry_destroy); this->mutex->destroy(this->mutex); - close(this->socket); free(this); } @@ -436,26 +381,30 @@ METHOD(lookip_socket_t, destroy, void, lookip_socket_t *lookip_socket_create(lookip_listener_t *listener) { private_lookip_socket_t *this; + char *uri; INIT(this, .public = { .destroy = _destroy, }, .listener = listener, - .registered = linked_list_create(), .connected = linked_list_create(), .mutex = mutex_create(MUTEX_TYPE_DEFAULT), ); - if (!open_socket(this)) + uri = lib->settings->get_str(lib->settings, + "%s.plugins.lookip.socket", "unix://" LOOKIP_SOCKET, + charon->name); + this->service = lib->streams->create_service(lib->streams, uri, 10); + if (!this->service) { - free(this); + DBG1(DBG_CFG, "creating lookip socket failed"); + destroy(this); return NULL; } - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive, this, - NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + this->service->on_accept(this->service, (stream_service_cb_t)on_accept, + this, JOB_PRIO_CRITICAL, 1); return &this->public; } From e11c02c8f1591fef64200137c24598fba9d488a9 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 14:47:11 +0200 Subject: [PATCH 31/54] whitelist: use a stream service to accept client connections Use SOCK_STREAM, as we don't have SOCK_SEQPACKET on TCP. To have network transparency, the message now uses network byte order. --- src/libcharon/plugins/whitelist/whitelist.c | 93 ++++++++++-- .../plugins/whitelist/whitelist_control.c | 134 ++++-------------- .../plugins/whitelist/whitelist_msg.h | 2 +- 3 files changed, 107 insertions(+), 122 deletions(-) diff --git a/src/libcharon/plugins/whitelist/whitelist.c b/src/libcharon/plugins/whitelist/whitelist.c index 0a3a34459..f5fa6f60f 100644 --- a/src/libcharon/plugins/whitelist/whitelist.c +++ b/src/libcharon/plugins/whitelist/whitelist.c @@ -18,45 +18,102 @@ #include #include #include +#include #include #include #include +#include /** * Connect to the daemon, return FD */ static int make_connection() { - struct sockaddr_un addr; - int fd; + union { + struct sockaddr_un un; + struct sockaddr_in in; + struct sockaddr sa; + } addr; + int fd, len; - addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, WHITELIST_SOCKET); + if (getenv("TCP_PORT")) + { + addr.in.sin_family = AF_INET; + addr.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.in.sin_port = htons(atoi(getenv("TCP_PORT"))); + len = sizeof(addr.in); + } + else + { + addr.un.sun_family = AF_UNIX; + strcpy(addr.un.sun_path, WHITELIST_SOCKET); - fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + len = offsetof(struct sockaddr_un, sun_path) + strlen(addr.un.sun_path); + } + fd = socket(addr.sa.sa_family, SOCK_STREAM, 0); if (fd < 0) { fprintf(stderr, "opening socket failed: %s\n", strerror(errno)); return -1; } - if (connect(fd, (struct sockaddr *)&addr, - offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path)) < 0) + if (connect(fd, &addr.sa, len) < 0) { - fprintf(stderr, "connecting to %s failed: %s\n", - WHITELIST_SOCKET, strerror(errno)); + fprintf(stderr, "connecting failed: %s\n", strerror(errno)); close(fd); return -1; } return fd; } +static int read_all(int fd, void *buf, size_t len) +{ + ssize_t ret, done = 0; + + while (done < len) + { + ret = read(fd, buf, len - done); + if (ret == -1 && errno == EINTR) + { /* interrupted, try again */ + continue; + } + if (ret < 0) + { + return -1; + } + done += ret; + buf += ret; + } + return len; +} + +static int write_all(int fd, void *buf, size_t len) +{ + ssize_t ret, done = 0; + + while (done < len) + { + ret = write(fd, buf, len - done); + if (ret == -1 && errno == EINTR) + { /* interrupted, try again */ + continue; + } + if (ret < 0) + { + return -1; + } + done += ret; + buf += ret; + } + return len; +} + /** * Send a single message */ static int send_msg(int type, char *id) { whitelist_msg_t msg = { - .type = type, + .type = htonl(type), }; int fd; @@ -66,7 +123,7 @@ static int send_msg(int type, char *id) return 2; } snprintf(msg.id, sizeof(msg.id), "%s", id); - if (send(fd, &msg, sizeof(msg), 0) != sizeof(msg)) + if (write_all(fd, &msg, sizeof(msg)) != sizeof(msg)) { fprintf(stderr, "writing to socket failed: %s\n", strerror(errno)); close(fd); @@ -74,9 +131,15 @@ static int send_msg(int type, char *id) } if (type == WHITELIST_LIST) { - while (recv(fd, &msg, sizeof(msg), 0) == sizeof(msg)) + while (1) { - if (msg.type != WHITELIST_LIST) + if (read_all(fd, &msg, sizeof(msg)) != sizeof(msg)) + { + fprintf(stderr, "reading failed: %s\n", strerror(errno)); + close(fd); + return 2; + } + if (ntohl(msg.type) != WHITELIST_LIST) { break; } @@ -94,7 +157,7 @@ static int send_msg(int type, char *id) static int send_batch(int type, char *file) { whitelist_msg_t msg = { - .type = type, + .type = htonl(type), }; FILE *f = stdin; int fd, len; @@ -125,7 +188,7 @@ static int send_batch(int type, char *file) { msg.id[len-1] = '\0'; } - if (send(fd, &msg, sizeof(msg), 0) != sizeof(msg)) + if (write_all(fd, &msg, sizeof(msg)) != sizeof(msg)) { fprintf(stderr, "writing to socket failed: %s\n", strerror(errno)); if (f != stdin) diff --git a/src/libcharon/plugins/whitelist/whitelist_control.c b/src/libcharon/plugins/whitelist/whitelist_control.c index b90b62ac1..c3f7ac40e 100644 --- a/src/libcharon/plugins/whitelist/whitelist_control.c +++ b/src/libcharon/plugins/whitelist/whitelist_control.c @@ -23,8 +23,6 @@ #include #include -#include -#include #include "whitelist_msg.h" @@ -46,65 +44,28 @@ struct private_whitelist_control_t { whitelist_listener_t *listener; /** - * Whitelist unix socket file descriptor + * Whitelist stream service */ - int socket; + stream_service_t *service; }; -/** - * Open whitelist unix socket - */ -static bool open_socket(private_whitelist_control_t *this) -{ - struct sockaddr_un addr; - mode_t old; - - addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, WHITELIST_SOCKET); - - this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0); - if (this->socket == -1) - { - DBG1(DBG_CFG, "creating whitelist socket failed"); - return FALSE; - } - unlink(addr.sun_path); - old = umask(~(S_IRWXU | S_IRWXG)); - if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) - { - DBG1(DBG_CFG, "binding whitelist socket failed: %s", strerror(errno)); - close(this->socket); - return FALSE; - } - umask(old); - if (chown(addr.sun_path, lib->caps->get_uid(lib->caps), - lib->caps->get_gid(lib->caps)) != 0) - { - DBG1(DBG_CFG, "changing whitelist socket permissions failed: %s", - strerror(errno)); - } - if (listen(this->socket, 10) < 0) - { - DBG1(DBG_CFG, "listening on whitelist socket failed: %s", strerror(errno)); - close(this->socket); - unlink(addr.sun_path); - return FALSE; - } - return TRUE; -} - /** * Dispatch a received message */ -static void dispatch(private_whitelist_control_t *this, - int fd, whitelist_msg_t *msg) +static bool on_accept(private_whitelist_control_t *this, stream_t *stream) { identification_t *id, *current; enumerator_t *enumerator; + whitelist_msg_t msg; - msg->id[sizeof(msg->id)-1] = 0; - id = identification_create_from_string(msg->id); - switch (msg->type) + if (!stream->read_all(stream, &msg, sizeof(msg))) + { + return FALSE; + } + + msg.id[sizeof(msg.id) - 1] = 0; + id = identification_create_from_string(msg.id); + switch (ntohl(msg.type)) { case WHITELIST_ADD: this->listener->add(this->listener, id); @@ -118,8 +79,8 @@ static void dispatch(private_whitelist_control_t *this, { if (current->matches(current, id)) { - snprintf(msg->id, sizeof(msg->id), "%Y", current); - if (send(fd, msg, sizeof(*msg), 0) != sizeof(*msg)) + snprintf(msg.id, sizeof(msg.id), "%Y", current); + if (!stream->write_all(stream, &msg, sizeof(msg))) { DBG1(DBG_CFG, "listing whitelist failed"); break; @@ -127,9 +88,9 @@ static void dispatch(private_whitelist_control_t *this, } } enumerator->destroy(enumerator); - msg->type = WHITELIST_END; - memset(msg->id, 0, sizeof(msg->id)); - send(fd, msg, sizeof(*msg), 0); + msg.type = htonl(WHITELIST_END); + memset(msg.id, 0, sizeof(msg.id)); + stream->write_all(stream, &msg, sizeof(msg)); break; case WHITELIST_FLUSH: this->listener->flush(this->listener, id); @@ -145,58 +106,14 @@ static void dispatch(private_whitelist_control_t *this, break; } id->destroy(id); -} -/** - * Accept whitelist control connections, dispatch - */ -static job_requeue_t receive(private_whitelist_control_t *this) -{ - struct sockaddr_un addr; - int fd, len = sizeof(addr); - whitelist_msg_t msg; - bool oldstate; - - oldstate = thread_cancelability(TRUE); - fd = accept(this->socket, (struct sockaddr*)&addr, &len); - thread_cancelability(oldstate); - - if (fd != -1) - { - while (TRUE) - { - oldstate = thread_cancelability(TRUE); - len = recv(fd, &msg, sizeof(msg), 0); - thread_cancelability(oldstate); - - if (len == sizeof(msg)) - { - dispatch(this, fd, &msg); - } - else - { - if (len != 0) - { - DBG1(DBG_CFG, "receiving whitelist msg failed: %s", - strerror(errno)); - } - break; - } - } - close(fd); - } - else - { - DBG1(DBG_CFG, "accepting whitelist connection failed: %s", - strerror(errno)); - } - return JOB_REQUEUE_FAIR; + return FALSE; } METHOD(whitelist_control_t, destroy, void, private_whitelist_control_t *this) { - close(this->socket); + this->service->destroy(this->service); free(this); } @@ -206,6 +123,7 @@ METHOD(whitelist_control_t, destroy, void, whitelist_control_t *whitelist_control_create(whitelist_listener_t *listener) { private_whitelist_control_t *this; + char *uri; INIT(this, .public = { @@ -214,15 +132,19 @@ whitelist_control_t *whitelist_control_create(whitelist_listener_t *listener) .listener = listener, ); - if (!open_socket(this)) + uri = lib->settings->get_str(lib->settings, + "%s.plugins.whitelist.socket", "unix://" WHITELIST_SOCKET, + charon->name); + this->service = lib->streams->create_service(lib->streams, uri, 10); + if (!this->service) { + DBG1(DBG_CFG, "creating whitelist socket failed"); free(this); return NULL; } - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive, this, - NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + this->service->on_accept(this->service, (stream_service_cb_t)on_accept, + this, JOB_PRIO_CRITICAL, 0); return &this->public; } diff --git a/src/libcharon/plugins/whitelist/whitelist_msg.h b/src/libcharon/plugins/whitelist/whitelist_msg.h index 65b922996..595fb6ffb 100644 --- a/src/libcharon/plugins/whitelist/whitelist_msg.h +++ b/src/libcharon/plugins/whitelist/whitelist_msg.h @@ -53,6 +53,6 @@ struct whitelist_msg_t { int type; /** null terminated identity */ char id[128]; -}; +} __attribute__((packed)); #endif /** WHITELIST_MSG_H_ @}*/ From 73da4ed849afca1aa3c0b8f13d703796a98184d4 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 12:18:15 +0200 Subject: [PATCH 32/54] load-tester: use a stream service to dispatch control connections --- .../plugins/load_tester/load_tester.c | 2 +- .../plugins/load_tester/load_tester_control.c | 118 ++++-------------- 2 files changed, 27 insertions(+), 93 deletions(-) diff --git a/src/libcharon/plugins/load_tester/load_tester.c b/src/libcharon/plugins/load_tester/load_tester.c index f7361e606..b7b971ee8 100644 --- a/src/libcharon/plugins/load_tester/load_tester.c +++ b/src/libcharon/plugins/load_tester/load_tester.c @@ -35,7 +35,7 @@ static FILE* make_connection() addr.sun_family = AF_UNIX; strcpy(addr.sun_path, LOAD_TESTER_SOCKET); - fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { fprintf(stderr, "opening socket failed: %s\n", strerror(errno)); diff --git a/src/libcharon/plugins/load_tester/load_tester_control.c b/src/libcharon/plugins/load_tester/load_tester_control.c index 3c82b5c30..f9ec9142f 100644 --- a/src/libcharon/plugins/load_tester/load_tester_control.c +++ b/src/libcharon/plugins/load_tester/load_tester_control.c @@ -43,9 +43,9 @@ struct private_load_tester_control_t { load_tester_control_t public; /** - * Load tester unix socket file descriptor + * Load tester control stream service */ - int socket; + stream_service_t *service; }; /** @@ -84,48 +84,6 @@ struct init_listener_t { condvar_t *condvar; }; -/** - * Open load-tester listening socket - */ -static bool open_socket(private_load_tester_control_t *this) -{ - struct sockaddr_un addr; - mode_t old; - - addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, LOAD_TESTER_SOCKET); - - this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0); - if (this->socket == -1) - { - DBG1(DBG_CFG, "creating load-tester socket failed"); - return FALSE; - } - unlink(addr.sun_path); - old = umask(~(S_IRWXU | S_IRWXG)); - if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) - { - DBG1(DBG_CFG, "binding load-tester socket failed: %s", strerror(errno)); - close(this->socket); - return FALSE; - } - umask(old); - if (chown(addr.sun_path, lib->caps->get_uid(lib->caps), - lib->caps->get_gid(lib->caps)) != 0) - { - DBG1(DBG_CFG, "changing load-tester socket permissions failed: %s", - strerror(errno)); - } - if (listen(this->socket, 10) < 0) - { - DBG1(DBG_CFG, "listening on load-tester socket failed: %s", strerror(errno)); - close(this->socket); - unlink(addr.sun_path); - return FALSE; - } - return TRUE; -} - /** * Hashtable hash function */ @@ -215,9 +173,9 @@ static bool initiate_cb(init_listener_t *this, debug_t group, level_t level, } /** - * Initiate load-test, write progress to stream + * Accept connections, initiate load-test, write progress to stream */ -static job_requeue_t initiate(FILE *stream) +static bool on_accept(private_load_tester_control_t *this, stream_t *io) { init_listener_t *listener; enumerator_t *enumerator; @@ -225,15 +183,23 @@ static job_requeue_t initiate(FILE *stream) child_cfg_t *child_cfg; u_int i, count, failed = 0, delay = 0; char buf[16] = ""; + FILE *stream; + stream = io->get_file(io); + if (!stream) + { + return FALSE; + } fflush(stream); if (fgets(buf, sizeof(buf), stream) == NULL) { - return JOB_REQUEUE_NONE; + fclose(stream); + return FALSE; } if (sscanf(buf, "%u %u", &count, &delay) < 1) { - return JOB_REQUEUE_NONE; + fclose(stream); + return FALSE; } INIT(listener, @@ -308,50 +274,15 @@ static job_requeue_t initiate(FILE *stream) free(listener); fprintf(stream, "\n"); + fclose(stream); - return JOB_REQUEUE_NONE; -} - -/** - * Accept load-tester control connections, dispatch - */ -static job_requeue_t receive(private_load_tester_control_t *this) -{ - struct sockaddr_un addr; - int fd, len = sizeof(addr); - bool oldstate; - FILE *stream; - - oldstate = thread_cancelability(TRUE); - fd = accept(this->socket, (struct sockaddr*)&addr, &len); - thread_cancelability(oldstate); - - if (fd != -1) - { - stream = fdopen(fd, "r+"); - if (stream) - { - DBG1(DBG_CFG, "client connected"); - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio( - (callback_job_cb_t)initiate, stream, (void*)fclose, - (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); - } - else - { - close(fd); - } - } - return JOB_REQUEUE_FAIR; + return FALSE; } METHOD(load_tester_control_t, destroy, void, private_load_tester_control_t *this) { - if (this->socket != -1) - { - close(this->socket); - } + DESTROY_IF(this->service); free(this); } @@ -361,6 +292,7 @@ METHOD(load_tester_control_t, destroy, void, load_tester_control_t *load_tester_control_create() { private_load_tester_control_t *this; + char *uri; INIT(this, .public = { @@ -368,16 +300,18 @@ load_tester_control_t *load_tester_control_create() }, ); - if (open_socket(this)) + uri = lib->settings->get_str(lib->settings, + "%s.plugins.load-tester.socket", "unix://" LOAD_TESTER_SOCKET, + charon->name); + this->service = lib->streams->create_service(lib->streams, uri, 10); + if (this->service) { - lib->processor->queue_job(lib->processor, (job_t*) - callback_job_create_with_prio((callback_job_cb_t)receive, this, NULL, - (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + this->service->on_accept(this->service, (stream_service_cb_t)on_accept, + this, JOB_PRIO_CRITICAL, 0); } else { - this->socket = -1; + DBG1(DBG_CFG, "creating load-tester control socket failed"); } - return &this->public; } From 17028e29c290b95d286c5b263e5cab528a20eb1f Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 11:59:56 +0200 Subject: [PATCH 33/54] farp: use watcher instead of dedicated receiver thread --- src/libcharon/plugins/farp/farp_spoofer.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/libcharon/plugins/farp/farp_spoofer.c b/src/libcharon/plugins/farp/farp_spoofer.c index 52b037c19..9f66d7407 100644 --- a/src/libcharon/plugins/farp/farp_spoofer.c +++ b/src/libcharon/plugins/farp/farp_spoofer.c @@ -96,20 +96,16 @@ static void send_arp(private_farp_spoofer_t *this, /** * ARP request receiving */ -static job_requeue_t receive_arp(private_farp_spoofer_t *this) +static bool receive_arp(private_farp_spoofer_t *this) { struct sockaddr_ll addr; socklen_t addr_len = sizeof(addr); arp_t arp; - int oldstate; ssize_t len; host_t *local, *remote; - oldstate = thread_cancelability(TRUE); - len = recvfrom(this->skt, &arp, sizeof(arp), 0, + len = recvfrom(this->skt, &arp, sizeof(arp), MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); - thread_cancelability(oldstate); - if (len == sizeof(arp)) { local = host_create_from_chunk(AF_INET, @@ -124,12 +120,13 @@ static job_requeue_t receive_arp(private_farp_spoofer_t *this) remote->destroy(remote); } - return JOB_REQUEUE_DIRECT; + return TRUE; } METHOD(farp_spoofer_t, destroy, void, private_farp_spoofer_t *this) { + lib->watcher->remove(lib->watcher, this->skt); close(this->skt); free(this); } @@ -183,10 +180,8 @@ farp_spoofer_t *farp_spoofer_create(farp_listener_t *listener) return NULL; } - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive_arp, - this, NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + lib->watcher->add(lib->watcher, this->skt, WATCHER_READ, + (watcher_cb_t)receive_arp, this); return &this->public; } - From c0db5d3845049a1bde0834dcf63099bc35135e77 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 09:47:28 +0200 Subject: [PATCH 34/54] dhcp: use watcher instead of dedicated receiver thread --- src/libcharon/plugins/dhcp/dhcp_socket.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/libcharon/plugins/dhcp/dhcp_socket.c b/src/libcharon/plugins/dhcp/dhcp_socket.c index 72e6ff4da..044c8a819 100644 --- a/src/libcharon/plugins/dhcp/dhcp_socket.c +++ b/src/libcharon/plugins/dhcp/dhcp_socket.c @@ -562,7 +562,8 @@ static void handle_ack(private_dhcp_socket_t *this, dhcp_t *dhcp, int optlen) /** * Receive DHCP responses */ -static job_requeue_t receive_dhcp(private_dhcp_socket_t *this) +static bool receive_dhcp(private_dhcp_socket_t *this, int fd, + watcher_event_t event) { struct sockaddr_ll addr; socklen_t addr_len = sizeof(addr); @@ -571,14 +572,12 @@ static job_requeue_t receive_dhcp(private_dhcp_socket_t *this) struct udphdr udp; dhcp_t dhcp; } packet; - int oldstate, optlen, origoptlen, optsize, optpos = 0; + int optlen, origoptlen, optsize, optpos = 0; ssize_t len; dhcp_option_t *option; - oldstate = thread_cancelability(TRUE); - len = recvfrom(this->receive, &packet, sizeof(packet), 0, + len = recvfrom(fd, &packet, sizeof(packet), MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); - thread_cancelability(oldstate); if (len >= sizeof(struct iphdr) + sizeof(struct udphdr) + offsetof(dhcp_t, options)) @@ -611,7 +610,7 @@ static job_requeue_t receive_dhcp(private_dhcp_socket_t *this) optpos += optsize; } } - return JOB_REQUEUE_DIRECT; + return TRUE; } METHOD(dhcp_socket_t, destroy, void, @@ -627,6 +626,7 @@ METHOD(dhcp_socket_t, destroy, void, } if (this->receive > 0) { + lib->watcher->remove(lib->watcher, this->receive); close(this->receive); } this->mutex->destroy(this->mutex); @@ -767,10 +767,8 @@ dhcp_socket_t *dhcp_socket_create() return NULL; } - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive_dhcp, - this, NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + lib->watcher->add(lib->watcher, this->receive, WATCHER_READ, + (watcher_cb_t)receive_dhcp, this); return &this->public; } - From 5f755cef462176cf8bd2399926de8a97a5debc20 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 11:52:42 +0200 Subject: [PATCH 35/54] eap-radius: use watcher instead of receiver thread on DAE socket --- .../plugins/eap_radius/eap_radius_dae.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/libcharon/plugins/eap_radius/eap_radius_dae.c b/src/libcharon/plugins/eap_radius/eap_radius_dae.c index 2ea2b059c..f22ddc56f 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_dae.c +++ b/src/libcharon/plugins/eap_radius/eap_radius_dae.c @@ -379,21 +379,17 @@ static void process_coa(private_eap_radius_dae_t *this, /** * Receive RADIUS DAE requests */ -static job_requeue_t receive(private_eap_radius_dae_t *this) +static bool receive(private_eap_radius_dae_t *this) { struct sockaddr_storage addr; socklen_t addr_len = sizeof(addr); radius_message_t *request; char buf[2048]; ssize_t len; - bool oldstate; host_t *client; - oldstate = thread_cancelability(TRUE); - len = recvfrom(this->fd, buf, sizeof(buf), 0, + len = recvfrom(this->fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); - thread_cancelability(oldstate); - if (len > 0) { request = radius_message_parse(chunk_create(buf, len)); @@ -433,11 +429,11 @@ static job_requeue_t receive(private_eap_radius_dae_t *this) DBG1(DBG_NET, "ignoring invalid RADIUS DAE request"); } } - else + else if (errno != EWOULDBLOCK) { DBG1(DBG_NET, "receiving RADIUS DAE request failed: %s", strerror(errno)); } - return JOB_REQUEUE_DIRECT; + return TRUE; } /** @@ -483,6 +479,7 @@ METHOD(eap_radius_dae_t, destroy, void, { if (this->fd != -1) { + lib->watcher->remove(lib->watcher, this->fd); close(this->fd); } DESTROY_IF(this->signer); @@ -533,9 +530,8 @@ eap_radius_dae_t *eap_radius_dae_create(eap_radius_accounting_t *accounting) return NULL; } - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive, - this, NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + lib->watcher->add(lib->watcher, this->fd, WATCHER_READ, + (watcher_cb_t)receive, this); return &this->public; } From f4f77d746739c871a3fc7728cdc5fdc8aa931c77 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 15:42:22 +0200 Subject: [PATCH 36/54] kernel-netlink: use watcher to receive kernel events for net/ipsec --- .../kernel_netlink/kernel_netlink_ipsec.c | 30 ++++++++----------- .../kernel_netlink/kernel_netlink_net.c | 29 ++++++++---------- 2 files changed, 24 insertions(+), 35 deletions(-) diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index 2f8cb6b3e..b34fa149c 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -37,11 +37,9 @@ #include #include -#include #include #include #include -#include /** Required for Linux 2.6.26 kernel and later */ #ifndef XFRM_STATE_AF_UNSPEC @@ -972,40 +970,37 @@ static void process_mapping(private_kernel_netlink_ipsec_t *this, /** * Receives events from kernel */ -static job_requeue_t receive_events(private_kernel_netlink_ipsec_t *this) +static bool receive_events(private_kernel_netlink_ipsec_t *this, int fd, + watcher_event_t event) { char response[1024]; struct nlmsghdr *hdr = (struct nlmsghdr*)response; struct sockaddr_nl addr; socklen_t addr_len = sizeof(addr); int len; - bool oldstate; - - oldstate = thread_cancelability(TRUE); - len = recvfrom(this->socket_xfrm_events, response, sizeof(response), 0, - (struct sockaddr*)&addr, &addr_len); - thread_cancelability(oldstate); + len = recvfrom(this->socket_xfrm_events, response, sizeof(response), + MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); if (len < 0) { switch (errno) { case EINTR: /* interrupted, try again */ - return JOB_REQUEUE_DIRECT; + return TRUE; case EAGAIN: /* no data ready, select again */ - return JOB_REQUEUE_DIRECT; + return TRUE; default: DBG1(DBG_KNL, "unable to receive from xfrm event socket"); sleep(1); - return JOB_REQUEUE_FAIR; + return TRUE; } } if (addr.nl_pid != 0) { /* not from kernel. not interested, try another one */ - return JOB_REQUEUE_DIRECT; + return TRUE; } while (NLMSG_OK(hdr, len)) @@ -1031,7 +1026,7 @@ static job_requeue_t receive_events(private_kernel_netlink_ipsec_t *this) } hdr = NLMSG_NEXT(hdr, len); } - return JOB_REQUEUE_DIRECT; + return TRUE; } METHOD(kernel_ipsec_t, get_features, kernel_feature_t, @@ -2605,6 +2600,7 @@ METHOD(kernel_ipsec_t, destroy, void, if (this->socket_xfrm_events > 0) { + lib->watcher->remove(lib->watcher, this->socket_xfrm_events); close(this->socket_xfrm_events); } DESTROY_IF(this->socket_xfrm); @@ -2707,10 +2703,8 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() destroy(this); return NULL; } - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio( - (callback_job_cb_t)receive_events, this, NULL, - (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + lib->watcher->add(lib->watcher, this->socket_xfrm_events, WATCHER_READ, + (watcher_cb_t)receive_events, this); } return &this->public; diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c index c29aff433..e129ab131 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c @@ -50,7 +50,6 @@ #include #include -#include #include #include #include @@ -1079,40 +1078,37 @@ static void process_route(private_kernel_netlink_net_t *this, struct nlmsghdr *h /** * Receives events from kernel */ -static job_requeue_t receive_events(private_kernel_netlink_net_t *this) +static bool receive_events(private_kernel_netlink_net_t *this, int fd, + watcher_event_t event) { char response[1024]; struct nlmsghdr *hdr = (struct nlmsghdr*)response; struct sockaddr_nl addr; socklen_t addr_len = sizeof(addr); int len; - bool oldstate; - - oldstate = thread_cancelability(TRUE); - len = recvfrom(this->socket_events, response, sizeof(response), 0, - (struct sockaddr*)&addr, &addr_len); - thread_cancelability(oldstate); + len = recvfrom(this->socket_events, response, sizeof(response), + MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); if (len < 0) { switch (errno) { case EINTR: /* interrupted, try again */ - return JOB_REQUEUE_DIRECT; + return TRUE; case EAGAIN: /* no data ready, select again */ - return JOB_REQUEUE_DIRECT; + return TRUE; default: DBG1(DBG_KNL, "unable to receive from rt event socket"); sleep(1); - return JOB_REQUEUE_FAIR; + return TRUE; } } if (addr.nl_pid != 0) { /* not from kernel. not interested, try another one */ - return JOB_REQUEUE_DIRECT; + return TRUE; } while (NLMSG_OK(hdr, len)) @@ -1140,7 +1136,7 @@ static job_requeue_t receive_events(private_kernel_netlink_net_t *this) } hdr = NLMSG_NEXT(hdr, len); } - return JOB_REQUEUE_DIRECT; + return TRUE; } /** enumerator over addresses */ @@ -2175,6 +2171,7 @@ METHOD(kernel_net_t, destroy, void, } if (this->socket_events > 0) { + lib->watcher->remove(lib->watcher, this->socket_events); close(this->socket_events); } enumerator = this->routes->create_enumerator(this->routes); @@ -2314,10 +2311,8 @@ kernel_netlink_net_t *kernel_netlink_net_create() return NULL; } - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio( - (callback_job_cb_t)receive_events, this, NULL, - (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + lib->watcher->add(lib->watcher, this->socket_events, WATCHER_READ, + (watcher_cb_t)receive_events, this); } if (init_address_list(this) != SUCCESS) From 7f698daef9bec8cd8020003dcde590cabe1a80f9 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 15:45:01 +0200 Subject: [PATCH 37/54] kernel-pfkey: use watcher to receive networking events --- .../plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index dd998042c..214feac05 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -62,9 +62,7 @@ #include #include #include -#include #include -#include /** non linux specific */ #ifndef IPPROTO_COMP @@ -1385,31 +1383,28 @@ static void process_mapping(private_kernel_pfkey_ipsec_t *this, /** * Receives events from kernel */ -static job_requeue_t receive_events(private_kernel_pfkey_ipsec_t *this) +static bool receive_events(private_kernel_pfkey_ipsec_t *this, int fd, + watcher_event_t event) { unsigned char buf[PFKEY_BUFFER_SIZE]; struct sadb_msg *msg = (struct sadb_msg*)buf; - bool oldstate; int len; - oldstate = thread_cancelability(TRUE); - len = recvfrom(this->socket_events, buf, sizeof(buf), 0, NULL, 0); - thread_cancelability(oldstate); - + len = recvfrom(this->socket_events, buf, sizeof(buf), MSG_DONTWAIT, NULL, 0); if (len < 0) { switch (errno) { case EINTR: /* interrupted, try again */ - return JOB_REQUEUE_DIRECT; + return TRUE; case EAGAIN: /* no data ready, select again */ - return JOB_REQUEUE_DIRECT; + return TRUE; default: DBG1(DBG_KNL, "unable to receive from PF_KEY event socket"); sleep(1); - return JOB_REQUEUE_FAIR; + return TRUE; } } @@ -1417,17 +1412,17 @@ static job_requeue_t receive_events(private_kernel_pfkey_ipsec_t *this) msg->sadb_msg_len < PFKEY_LEN(sizeof(struct sadb_msg))) { DBG2(DBG_KNL, "received corrupted PF_KEY message"); - return JOB_REQUEUE_DIRECT; + return TRUE; } if (msg->sadb_msg_pid != 0) { /* not from kernel. not interested, try another one */ - return JOB_REQUEUE_DIRECT; + return TRUE; } if (msg->sadb_msg_len > len / PFKEY_ALIGNMENT) { DBG1(DBG_KNL, "buffer was too small to receive the complete " "PF_KEY message"); - return JOB_REQUEUE_DIRECT; + return TRUE; } switch (msg->sadb_msg_type) @@ -1452,7 +1447,7 @@ static job_requeue_t receive_events(private_kernel_pfkey_ipsec_t *this) break; } - return JOB_REQUEUE_DIRECT; + return TRUE; } METHOD(kernel_ipsec_t, get_spi, status_t, @@ -2779,6 +2774,7 @@ METHOD(kernel_ipsec_t, destroy, void, } if (this->socket_events > 0) { + lib->watcher->remove(lib->watcher, this->socket_events); close(this->socket_events); } this->policies->invoke_function(this->policies, @@ -2864,10 +2860,8 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create() return NULL; } - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio( - (callback_job_cb_t)receive_events, this, NULL, - (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + lib->watcher->add(lib->watcher, this->socket_events, WATCHER_READ, + (watcher_cb_t)receive_events, this); } return &this->public; From 46666dd3c1f99a0f94669156c102e727401cf03a Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 15:48:22 +0200 Subject: [PATCH 38/54] kernel-pfroute: use watcher to receive kernel events --- .../kernel_pfroute/kernel_pfroute_net.c | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index a5453d0bb..976170c57 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -866,7 +866,8 @@ static void process_route(private_kernel_pfroute_net_t *this, /** * Receives PF_ROUTE messages from kernel */ -static job_requeue_t receive_events(private_kernel_pfroute_net_t *this) +static bool receive_events(private_kernel_pfroute_net_t *this, int fd, + watcher_event_t event) { struct { union { @@ -877,36 +878,32 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this) char buf[sizeof(struct sockaddr_storage) * RTAX_MAX]; } msg; int len, hdrlen; - bool oldstate; - - oldstate = thread_cancelability(TRUE); - len = recv(this->socket, &msg, sizeof(msg), 0); - thread_cancelability(oldstate); + len = recv(this->socket, &msg, sizeof(msg), MSG_DONTWAIT); if (len < 0) { switch (errno) { case EINTR: case EAGAIN: - return JOB_REQUEUE_DIRECT; + return TRUE; default: DBG1(DBG_KNL, "unable to receive from PF_ROUTE event socket"); sleep(1); - return JOB_REQUEUE_FAIR; + return TRUE; } } if (len < offsetof(struct rt_msghdr, rtm_flags) || len < msg.rtm.rtm_msglen) { DBG1(DBG_KNL, "received invalid PF_ROUTE message"); - return JOB_REQUEUE_DIRECT; + return TRUE; } if (msg.rtm.rtm_version != RTM_VERSION) { DBG1(DBG_KNL, "received PF_ROUTE message with unsupported version: %d", msg.rtm.rtm_version); - return JOB_REQUEUE_DIRECT; + return TRUE; } switch (msg.rtm.rtm_type) { @@ -923,12 +920,12 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this) hdrlen = sizeof(msg.rtm); break; default: - return JOB_REQUEUE_DIRECT; + return TRUE; } if (msg.rtm.rtm_msglen < hdrlen) { DBG1(DBG_KNL, "ignoring short PF_ROUTE message"); - return JOB_REQUEUE_DIRECT; + return TRUE; } switch (msg.rtm.rtm_type) { @@ -958,7 +955,7 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this) this->condvar->broadcast(this->condvar); this->mutex->unlock(this->mutex); - return JOB_REQUEUE_DIRECT; + return TRUE; } @@ -1699,6 +1696,7 @@ METHOD(kernel_net_t, destroy, void, if (this->socket != -1) { + lib->watcher->remove(lib->watcher, this->socket); close(this->socket); } @@ -1786,10 +1784,8 @@ kernel_pfroute_net_t *kernel_pfroute_net_create() } else { - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio( - (callback_job_cb_t)receive_events, this, NULL, - (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + lib->watcher->add(lib->watcher, this->socket, WATCHER_READ, + (watcher_cb_t)receive_events, this); } if (init_address_list(this) != SUCCESS) { From cfdb5f48554018a81b5ebcb77cf5c1b463dedd99 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 8 Jul 2013 10:39:23 +0200 Subject: [PATCH 39/54] error-notify: fix error handling when creating the socket fails --- src/libcharon/plugins/error_notify/error_notify_plugin.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libcharon/plugins/error_notify/error_notify_plugin.c b/src/libcharon/plugins/error_notify/error_notify_plugin.c index 9ee3ed69f..ef0ce7bc6 100644 --- a/src/libcharon/plugins/error_notify/error_notify_plugin.c +++ b/src/libcharon/plugins/error_notify/error_notify_plugin.c @@ -109,6 +109,12 @@ plugin_t *error_notify_plugin_create() .socket = error_notify_socket_create(), ); + if (!this->socket) + { + free(this); + return NULL; + } + this->listener = error_notify_listener_create(this->socket); return &this->public.plugin; From d3278c1f7393ce8d60b8069f08cafd03efd93b21 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 8 Jul 2013 10:40:25 +0200 Subject: [PATCH 40/54] lookip: fix error handling when creating the socket fails --- src/libcharon/plugins/lookip/lookip_plugin.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libcharon/plugins/lookip/lookip_plugin.c b/src/libcharon/plugins/lookip/lookip_plugin.c index 4466ad99f..63b138162 100644 --- a/src/libcharon/plugins/lookip/lookip_plugin.c +++ b/src/libcharon/plugins/lookip/lookip_plugin.c @@ -80,7 +80,7 @@ METHOD(plugin_t, get_features, int, METHOD(plugin_t, destroy, void, private_lookip_plugin_t *this) { - this->socket->destroy(this->socket); + DESTROY_IF(this->socket); this->listener->destroy(this->listener); free(this); } @@ -108,7 +108,13 @@ plugin_t *lookip_plugin_create() }, .listener = lookip_listener_create(), ); + this->socket = lookip_socket_create(this->listener); + if (!this->socket) + { + destroy(this); + return NULL; + } return &this->public.plugin; } From 0ccc5bb216752a1cd3617de052ad1e0b49b41fa9 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 8 Jul 2013 10:52:49 +0200 Subject: [PATCH 41/54] whitelist: fix error handling when creating the socket fails --- src/libcharon/plugins/whitelist/whitelist_plugin.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libcharon/plugins/whitelist/whitelist_plugin.c b/src/libcharon/plugins/whitelist/whitelist_plugin.c index e51f02c05..38465aebb 100644 --- a/src/libcharon/plugins/whitelist/whitelist_plugin.c +++ b/src/libcharon/plugins/whitelist/whitelist_plugin.c @@ -108,7 +108,13 @@ plugin_t *whitelist_plugin_create() }, .listener = whitelist_listener_create(), ); + this->control = whitelist_control_create(this->listener); + if (!this->control) + { + destroy(this); + return NULL; + } return &this->public.plugin; } From 0a35ae781d49740c2b5df7057a439f8045e600c4 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 8 Jul 2013 11:44:52 +0200 Subject: [PATCH 42/54] whitelist: use a read-copy when listing entries While this requires a little more overhead, we can free the lock should the stream block, allowing other threads to add/remove entries. --- .../plugins/whitelist/whitelist_control.c | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/src/libcharon/plugins/whitelist/whitelist_control.c b/src/libcharon/plugins/whitelist/whitelist_control.c index c3f7ac40e..e97885c8f 100644 --- a/src/libcharon/plugins/whitelist/whitelist_control.c +++ b/src/libcharon/plugins/whitelist/whitelist_control.c @@ -23,6 +23,7 @@ #include #include +#include #include "whitelist_msg.h" @@ -49,13 +50,53 @@ struct private_whitelist_control_t { stream_service_t *service; }; +/* + * List whitelist entries using a read-copy + */ +static void list(private_whitelist_control_t *this, + stream_t *stream, identification_t *id) +{ + identification_t *current; + enumerator_t *enumerator; + linked_list_t *list; + whitelist_msg_t msg = { + .type = htonl(WHITELIST_LIST), + }; + + list = linked_list_create(); + enumerator = this->listener->create_enumerator(this->listener); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (current->matches(current, id)) + { + list->insert_last(list, current->clone(current)); + } + } + enumerator->destroy(enumerator); + + while (list->remove_first(list, (void**)¤t) == SUCCESS) + { + snprintf(msg.id, sizeof(msg.id), "%Y", current); + current->destroy(current); + if (!stream->write_all(stream, &msg, sizeof(msg))) + { + DBG1(DBG_CFG, "listing whitelist failed: %s", strerror(errno)); + break; + } + } + list->destroy_offset(list, offsetof(identification_t, destroy)); + + msg.type = htonl(WHITELIST_END); + memset(msg.id, 0, sizeof(msg.id)); + stream->write_all(stream, &msg, sizeof(msg)); +} + /** * Dispatch a received message */ static bool on_accept(private_whitelist_control_t *this, stream_t *stream) { - identification_t *id, *current; - enumerator_t *enumerator; + identification_t *id; whitelist_msg_t msg; if (!stream->read_all(stream, &msg, sizeof(msg))) @@ -74,23 +115,7 @@ static bool on_accept(private_whitelist_control_t *this, stream_t *stream) this->listener->remove(this->listener, id); break; case WHITELIST_LIST: - enumerator = this->listener->create_enumerator(this->listener); - while (enumerator->enumerate(enumerator, ¤t)) - { - if (current->matches(current, id)) - { - snprintf(msg.id, sizeof(msg.id), "%Y", current); - if (!stream->write_all(stream, &msg, sizeof(msg))) - { - DBG1(DBG_CFG, "listing whitelist failed"); - break; - } - } - } - enumerator->destroy(enumerator); - msg.type = htonl(WHITELIST_END); - memset(msg.id, 0, sizeof(msg.id)); - stream->write_all(stream, &msg, sizeof(msg)); + list(this, stream, id); break; case WHITELIST_FLUSH: this->listener->flush(this->listener, id); From f33d1d503f15597250c1f15eec5a21103d21e55e Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 4 Feb 2013 09:59:54 +0100 Subject: [PATCH 43/54] error-notify: increase size of string/identity fields in messages --- src/libcharon/plugins/error_notify/error_notify_msg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcharon/plugins/error_notify/error_notify_msg.h b/src/libcharon/plugins/error_notify/error_notify_msg.h index d031fc4c3..759d620c6 100644 --- a/src/libcharon/plugins/error_notify/error_notify_msg.h +++ b/src/libcharon/plugins/error_notify/error_notify_msg.h @@ -54,11 +54,11 @@ struct error_notify_msg_t { /** message type */ int type; /** string with an error description */ - char str[128]; + char str[384]; /** connection name, if known */ char name[64]; /** peer identity, if known */ - char id[128]; + char id[256]; /** peer address and port, if known */ char ip[60]; } __attribute__((packed)); From f7cff7fac45e7914dd742d4348be1b17b9e63e0c Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 4 Feb 2013 10:02:14 +0100 Subject: [PATCH 44/54] lookip: double size of id field in message --- src/libcharon/plugins/lookip/lookip_msg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcharon/plugins/lookip/lookip_msg.h b/src/libcharon/plugins/lookip/lookip_msg.h index 28c02d0de..83b765ece 100644 --- a/src/libcharon/plugins/lookip/lookip_msg.h +++ b/src/libcharon/plugins/lookip/lookip_msg.h @@ -86,7 +86,7 @@ struct lookip_response_t { /** null terminated string representation of outer IP */ char ip[40]; /** null terminated peer identity */ - char id[128]; + char id[256]; /** null terminated connection name */ char name[40]; /** unique connection id */ From 4d7a762871f52dac5c7bd7808edc94a55dd40e1a Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 9 Jul 2013 11:55:32 +0200 Subject: [PATCH 45/54] credmgr: introduce a hook function to catch trust chain validation errors --- .../plugins/addrblock/addrblock_validator.c | 7 ++- .../plugins/coupling/coupling_validator.c | 4 ++ .../credentials/cert_validator.h | 3 ++ .../credentials/credential_manager.c | 45 +++++++++++++--- .../credentials/credential_manager.h | 54 +++++++++++++++++++ .../constraints/constraints_validator.c | 8 +++ .../plugins/revocation/revocation_validator.c | 6 +++ 7 files changed, 120 insertions(+), 7 deletions(-) diff --git a/src/libcharon/plugins/addrblock/addrblock_validator.c b/src/libcharon/plugins/addrblock/addrblock_validator.c index 65f4ed08c..372c978a2 100644 --- a/src/libcharon/plugins/addrblock/addrblock_validator.c +++ b/src/libcharon/plugins/addrblock/addrblock_validator.c @@ -94,7 +94,12 @@ METHOD(cert_validator_t, validate, bool, if (subject->get_type(subject) == CERT_X509 && issuer->get_type(issuer) == CERT_X509) { - return check_addrblock((x509_t*)subject, (x509_t*)issuer); + if (!check_addrblock((x509_t*)subject, (x509_t*)issuer)) + { + lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION, + subject); + return FALSE; + } } return TRUE; } diff --git a/src/libcharon/plugins/coupling/coupling_validator.c b/src/libcharon/plugins/coupling/coupling_validator.c index 539be7548..5a72531fa 100644 --- a/src/libcharon/plugins/coupling/coupling_validator.c +++ b/src/libcharon/plugins/coupling/coupling_validator.c @@ -167,6 +167,8 @@ METHOD(cert_validator_t, validate, bool, { DBG1(DBG_CFG, "coupling new certificate '%Y' failed", subject->get_subject(subject)); + lib->credmgr->call_hook(lib->credmgr + CRED_HOOK_POLICY_VIOLATION, subject); } } else @@ -174,6 +176,8 @@ METHOD(cert_validator_t, validate, bool, DBG1(DBG_CFG, "coupling new certificate '%Y' failed, limit of %d " "couplings reached", subject->get_subject(subject), this->max_couplings); + lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION, + subject); } this->mutex->unlock(this->mutex); } diff --git a/src/libstrongswan/credentials/cert_validator.h b/src/libstrongswan/credentials/cert_validator.h index 325fa0af3..6b28f35c1 100644 --- a/src/libstrongswan/credentials/cert_validator.h +++ b/src/libstrongswan/credentials/cert_validator.h @@ -53,6 +53,9 @@ struct cert_validator_t { /** * Validate a subject certificate in relation to its issuer. * + * If FALSE is returned, the validator should call_hook() on the + * credential manager with an appropriate type and the certificate. + * * @param subject subject certificate to check * @param issuer issuer of subject * @param online whether to do online revocation checking diff --git a/src/libstrongswan/credentials/credential_manager.c b/src/libstrongswan/credentials/credential_manager.c index fa255551b..de19c8d96 100644 --- a/src/libstrongswan/credentials/credential_manager.c +++ b/src/libstrongswan/credentials/credential_manager.c @@ -81,6 +81,16 @@ struct private_credential_manager_t { * mutex for cache queue */ mutex_t *queue_mutex; + + /** + * Registered hook to call on validation errors + */ + credential_hook_t hook; + + /** + * Registered data to pass to hook + */ + void *hook_data; }; /** data to pass to create_private_enumerator */ @@ -126,6 +136,22 @@ typedef struct { enumerator_t *exclusive; } sets_enumerator_t; +METHOD(credential_manager_t, set_hook, void, + private_credential_manager_t *this, credential_hook_t hook, void *data) +{ + this->hook = hook; + this->hook_data = data; +} + +METHOD(credential_manager_t, call_hook, void, + private_credential_manager_t *this, credential_hook_type_t type, + certificate_t *cert) +{ + if (this->hook) + { + this->hook(this->hook_data, type, cert); + } +} METHOD(enumerator_t, sets_enumerate, bool, sets_enumerator_t *this, credential_set_t **set) @@ -553,15 +579,17 @@ static bool check_lifetime(private_credential_manager_t *this, { DBG1(DBG_CFG, "%s certificate invalid (valid from %T to %T)", label, ¬_before, FALSE, ¬_after, FALSE); - return FALSE; + break; } return TRUE; case SUCCESS: return TRUE; case FAILED: default: - return FALSE; + break; } + call_hook(this, CRED_HOOK_EXPIRED, cert); + return FALSE; } /** @@ -722,9 +750,10 @@ static bool verify_trust_chain(private_credential_manager_t *this, { if (current->equals(current, issuer)) { - DBG1(DBG_CFG, " self-signed certificate \"%Y\" is not trusted", - current->get_subject(current)); + DBG1(DBG_CFG, " self-signed certificate \"%Y\" is not " + "trusted", current->get_subject(current)); issuer->destroy(issuer); + call_hook(this, CRED_HOOK_UNTRUSTED_ROOT, current); break; } auth->add(auth, AUTH_RULE_IM_CERT, issuer->get_ref(issuer)); @@ -736,6 +765,7 @@ static bool verify_trust_chain(private_credential_manager_t *this, { DBG1(DBG_CFG, "no issuer certificate found for \"%Y\"", current->get_subject(current)); + call_hook(this, CRED_HOOK_NO_ISSUER, current); break; } } @@ -754,8 +784,8 @@ static bool verify_trust_chain(private_credential_manager_t *this, current = issuer; if (trusted) { - DBG1(DBG_CFG, " reached self-signed root ca with a path length of %d", - pathlen); + DBG1(DBG_CFG, " reached self-signed root ca with a " + "path length of %d", pathlen); break; } } @@ -763,6 +793,7 @@ static bool verify_trust_chain(private_credential_manager_t *this, if (pathlen > MAX_TRUST_PATH_LEN) { DBG1(DBG_CFG, "maximum path length of %d exceeded", MAX_TRUST_PATH_LEN); + call_hook(this, CRED_HOOK_EXCEEDED_PATH_LEN, subject); } if (trusted) { @@ -1305,6 +1336,8 @@ credential_manager_t *credential_manager_create() .remove_local_set = _remove_local_set, .add_validator = _add_validator, .remove_validator = _remove_validator, + .set_hook = _set_hook, + .call_hook = _call_hook, .destroy = _destroy, }, .sets = linked_list_create(), diff --git a/src/libstrongswan/credentials/credential_manager.h b/src/libstrongswan/credentials/credential_manager.h index 73c585734..445ea3f9c 100644 --- a/src/libstrongswan/credentials/credential_manager.h +++ b/src/libstrongswan/credentials/credential_manager.h @@ -22,6 +22,7 @@ #define CREDENTIAL_MANAGER_H_ typedef struct credential_manager_t credential_manager_t; +typedef enum credential_hook_type_t credential_hook_type_t; #include #include @@ -32,6 +33,37 @@ typedef struct credential_manager_t credential_manager_t; #include #include +/** + * Type of a credential hook error/event. + */ +enum credential_hook_type_t { + /** The certificate has expired (or is not yet valid) */ + CRED_HOOK_EXPIRED, + /** The certificate has been revoked */ + CRED_HOOK_REVOKED, + /** Checking certificate revocation failed. This does not necessarily mean + * the certificate is rejected, just that revocation checking failed. */ + CRED_HOOK_VALIDATION_FAILED, + /** No trusted issuer certificate has been found for this certificate */ + CRED_HOOK_NO_ISSUER, + /** Encountered a self-signed (root) certificate, but it is not trusted */ + CRED_HOOK_UNTRUSTED_ROOT, + /** Maximum trust chain length exceeded for certificate */ + CRED_HOOK_EXCEEDED_PATH_LEN, + /** The certificate violates some other kind of policy and gets rejected */ + CRED_HOOK_POLICY_VIOLATION, +}; + +/** + * Hook function to invoke on certificate validation errors. + * + * @param data user data supplied during hook registration + * @param type type of validation error/event + * @param cert associated certificate + */ +typedef void (*credential_hook_t)(void *data, credential_hook_type_t type, + certificate_t *cert); + /** * Manages credentials using credential_sets. * @@ -262,6 +294,28 @@ struct credential_manager_t { */ void (*remove_validator)(credential_manager_t *this, cert_validator_t *vdtr); + /** + * Set a hook to call on certain credential validation errors. + * + * @param hook hook to register, NULL to unregister + * @param data data to pass to hook + */ + void (*set_hook)(credential_manager_t *this, credential_hook_t hook, + void *data); + + /** + * Call the registered credential hook, if any. + * + * While hooks are usually called by the credential manager itself, some + * validator plugins might raise hooks as well if they consider certificates + * invalid. + * + * @param type type of the event + * @param cert associated certificate + */ + void (*call_hook)(credential_manager_t *this, credential_hook_type_t type, + certificate_t *cert); + /** * Destroy a credential_manager instance. */ diff --git a/src/libstrongswan/plugins/constraints/constraints_validator.c b/src/libstrongswan/plugins/constraints/constraints_validator.c index 83a74299a..62ccc7108 100644 --- a/src/libstrongswan/plugins/constraints/constraints_validator.c +++ b/src/libstrongswan/plugins/constraints/constraints_validator.c @@ -533,20 +533,28 @@ METHOD(cert_validator_t, validate, bool, { if (!check_pathlen((x509_t*)issuer, pathlen)) { + lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_EXCEEDED_PATH_LEN, + subject); return FALSE; } if (!check_name_constraints(subject, (x509_t*)issuer)) { + lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION, + subject); return FALSE; } if (!check_policy((x509_t*)subject, (x509_t*)issuer, !pathlen, auth)) { + lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION, + subject); return FALSE; } if (anchor) { if (!check_policy_constraints((x509_t*)issuer, pathlen, auth)) { + lib->credmgr->call_hook(lib->credmgr, + CRED_HOOK_POLICY_VIOLATION, issuer); return FALSE; } } diff --git a/src/libstrongswan/plugins/revocation/revocation_validator.c b/src/libstrongswan/plugins/revocation/revocation_validator.c index 44c234559..c8ec3f723 100644 --- a/src/libstrongswan/plugins/revocation/revocation_validator.c +++ b/src/libstrongswan/plugins/revocation/revocation_validator.c @@ -691,6 +691,8 @@ METHOD(cert_validator_t, validate, bool, case VALIDATION_REVOKED: case VALIDATION_ON_HOLD: /* has already been logged */ + lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_REVOKED, + subject); return FALSE; case VALIDATION_SKIPPED: DBG2(DBG_CFG, "ocsp check skipped, no ocsp found"); @@ -711,6 +713,8 @@ METHOD(cert_validator_t, validate, bool, case VALIDATION_REVOKED: case VALIDATION_ON_HOLD: /* has already been logged */ + lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_REVOKED, + subject); return FALSE; case VALIDATION_FAILED: case VALIDATION_SKIPPED: @@ -720,6 +724,8 @@ METHOD(cert_validator_t, validate, bool, DBG1(DBG_CFG, "certificate status is unknown, crl is stale"); break; } + lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_VALIDATION_FAILED, + subject); } return TRUE; } From 58750670cf03b7e492f769cc8eff8b8bcf2f1572 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 9 Jul 2013 14:21:40 +0200 Subject: [PATCH 46/54] bus: raise certificate validation alerts using credential manager hook --- src/libcharon/bus/bus.c | 29 +++++++++++++++++++++++++++++ src/libcharon/bus/bus.h | 14 ++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/libcharon/bus/bus.c b/src/libcharon/bus/bus.c index 34d4678d3..b46184809 100644 --- a/src/libcharon/bus/bus.c +++ b/src/libcharon/bus/bus.c @@ -833,10 +833,37 @@ METHOD(bus_t, assign_vips, void, this->mutex->unlock(this->mutex); } +/** + * Credential manager hook function to forward bus alerts + */ +static void hook_creds(private_bus_t *this, credential_hook_type_t type, + certificate_t *cert) +{ + switch (type) + { + case CRED_HOOK_EXPIRED: + return alert(this, ALERT_CERT_EXPIRED, cert); + case CRED_HOOK_REVOKED: + return alert(this, ALERT_CERT_REVOKED, cert); + case CRED_HOOK_VALIDATION_FAILED: + return alert(this, ALERT_CERT_VALIDATION_FAILED, cert); + case CRED_HOOK_NO_ISSUER: + return alert(this, ALERT_CERT_NO_ISSUER, cert); + case CRED_HOOK_UNTRUSTED_ROOT: + return alert(this, ALERT_CERT_UNTRUSTED_ROOT, cert); + case CRED_HOOK_EXCEEDED_PATH_LEN: + return alert(this, ALERT_CERT_EXCEEDED_PATH_LEN, cert); + case CRED_HOOK_POLICY_VIOLATION: + return alert(this, ALERT_CERT_POLICY_VIOLATION, cert); + } +} + METHOD(bus_t, destroy, void, private_bus_t *this) { debug_t group; + + lib->credmgr->set_hook(lib->credmgr, NULL, NULL); for (group = 0; group < DBG_MAX; group++) { this->loggers[group]->destroy(this->loggers[group]); @@ -897,5 +924,7 @@ bus_t *bus_create() this->max_vlevel[group] = LEVEL_SILENT; } + lib->credmgr->set_hook(lib->credmgr, (credential_hook_t)hook_creds, this); + return &this->public; } diff --git a/src/libcharon/bus/bus.h b/src/libcharon/bus/bus.h index cc2eb0167..4a0ac68e3 100644 --- a/src/libcharon/bus/bus.h +++ b/src/libcharon/bus/bus.h @@ -136,6 +136,20 @@ enum alert_t { ALERT_AUTHORIZATION_FAILED, /** IKE_SA hit the hard lifetime limit before it could be rekeyed */ ALERT_IKE_SA_EXPIRED, + /** Certificate rejected; it has expired, certificate_t */ + ALERT_CERT_EXPIRED, + /** Certificate rejected; it has been revoked, certificate_t */ + ALERT_CERT_REVOKED, + /** Validating certificate status failed, certificate_t */ + ALERT_CERT_VALIDATION_FAILED, + /** Certificate rejected; no trusted issuer found, certificate_t */ + ALERT_CERT_NO_ISSUER, + /** Certificate rejected; root not trusted, certificate_t */ + ALERT_CERT_UNTRUSTED_ROOT, + /** Certificate rejected; trustchain length exceeds limit, certificate_t */ + ALERT_CERT_EXCEEDED_PATH_LEN, + /** Certificate rejected; other policy violation, certificate_t */ + ALERT_CERT_POLICY_VIOLATION, }; /** From 868abd0626881fb5bd952d89f190d827596feb46 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 9 Jul 2013 14:28:10 +0200 Subject: [PATCH 47/54] error-notify: catch and forward some alerts related to certificate validation --- .../error_notify/error_notify_listener.c | 22 +++++++++++++++++++ .../plugins/error_notify/error_notify_msg.h | 3 +++ 2 files changed, 25 insertions(+) diff --git a/src/libcharon/plugins/error_notify/error_notify_listener.c b/src/libcharon/plugins/error_notify/error_notify_listener.c index a985cc480..13860fe50 100644 --- a/src/libcharon/plugins/error_notify/error_notify_listener.c +++ b/src/libcharon/plugins/error_notify/error_notify_listener.c @@ -45,6 +45,8 @@ METHOD(listener_t, alert, bool, identification_t *id; linked_list_t *list, *list2; peer_cfg_t *peer_cfg; + certificate_t *cert; + time_t not_before, not_after; if (!this->socket->has_listeners(this->socket)) { @@ -147,6 +149,26 @@ METHOD(listener_t, alert, bool, snprintf(msg.str, sizeof(msg.str), "an authorization plugin " "prevented establishment of an IKE_SA"); break; + case ALERT_CERT_EXPIRED: + msg.type = htonl(ERROR_NOTIFY_CERT_EXPIRED); + cert = va_arg(args, certificate_t*); + cert->get_validity(cert, NULL, ¬_before, ¬_after); + snprintf(msg.str, sizeof(msg.str), "certificiate expired: '%Y' " + "(valid from %T to %T)", cert->get_subject(cert), + ¬_before, TRUE, ¬_after, TRUE); + break; + case ALERT_CERT_REVOKED: + msg.type = htonl(ERROR_NOTIFY_CERT_REVOKED); + cert = va_arg(args, certificate_t*); + snprintf(msg.str, sizeof(msg.str), "certificiate revoked: '%Y'", + cert->get_subject(cert)); + break; + case ALERT_CERT_NO_ISSUER: + msg.type = htonl(ERROR_NOTIFY_NO_ISSUER_CERT); + cert = va_arg(args, certificate_t*); + snprintf(msg.str, sizeof(msg.str), "no trusted issuer certificate " + "found: '%Y'", cert->get_issuer(cert)); + break; default: return TRUE; } diff --git a/src/libcharon/plugins/error_notify/error_notify_msg.h b/src/libcharon/plugins/error_notify/error_notify_msg.h index 759d620c6..c66080276 100644 --- a/src/libcharon/plugins/error_notify/error_notify_msg.h +++ b/src/libcharon/plugins/error_notify/error_notify_msg.h @@ -45,6 +45,9 @@ enum { ERROR_NOTIFY_UNIQUE_KEEP = 14, ERROR_NOTIFY_VIP_FAILURE = 15, ERROR_NOTIFY_AUTHORIZATION_FAILED = 16, + ERROR_NOTIFY_CERT_EXPIRED = 17, + ERROR_NOTIFY_CERT_REVOKED = 18, + ERROR_NOTIFY_NO_ISSUER_CERT = 19, }; /** From 8fc89db7b6cd9d0c23dd96d5312ddc92aae16eb4 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 15 Nov 2011 17:13:53 +0000 Subject: [PATCH 48/54] certexpire: add an option to enforce exporting trustchains having a private key --- .../plugins/certexpire/certexpire_export.c | 98 ++++++++++++++++--- 1 file changed, 83 insertions(+), 15 deletions(-) diff --git a/src/libcharon/plugins/certexpire/certexpire_export.c b/src/libcharon/plugins/certexpire/certexpire_export.c index e339b8004..f1205cfd8 100644 --- a/src/libcharon/plugins/certexpire/certexpire_export.c +++ b/src/libcharon/plugins/certexpire/certexpire_export.c @@ -88,6 +88,11 @@ struct private_certexpire_export_t { * String to use in empty fields, if using fixed_fields */ char *empty_string; + + /** + * Force export of all trustchains we have a private key for + */ + bool force; }; /** @@ -184,21 +189,6 @@ static void export_csv(private_certexpire_export_t *this, char *path, } } -/** - * Export cached trustchain expiration dates to CSV files - */ -static void cron_export(private_certexpire_export_t *this) -{ - if (this->local_path) - { - export_csv(this, this->local_path, this->local); - } - if (this->remote_path) - { - export_csv(this, this->remote_path, this->remote); - } -} - METHOD(certexpire_export_t, add, void, private_certexpire_export_t *this, linked_list_t *trustchain, bool local) { @@ -320,6 +310,81 @@ METHOD(certexpire_export_t, add, void, enumerator->destroy(enumerator); } +/** + * Add trustchains we have a private key for to the list + */ +static void add_local_certs(private_certexpire_export_t *this) +{ + enumerator_t *enumerator; + certificate_t *cert; + + enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, + CERT_X509, KEY_ANY, NULL, FALSE); + while (enumerator->enumerate(enumerator, &cert)) + { + linked_list_t *trustchain; + private_key_t *private; + public_key_t *public; + identification_t *keyid; + chunk_t chunk; + x509_t *x509 = (x509_t*)cert; + + trustchain = linked_list_create(); + + public = cert->get_public_key(cert); + if (public) + { + if (public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &chunk)) + { + keyid = identification_create_from_encoding(ID_KEY_ID, chunk); + private = lib->credmgr->get_private(lib->credmgr, + public->get_type(public), keyid, NULL); + keyid->destroy(keyid); + if (private) + { + trustchain->insert_last(trustchain, cert->get_ref(cert)); + + while (!(x509->get_flags(x509) & X509_SELF_SIGNED)) + { + cert = lib->credmgr->get_cert(lib->credmgr, CERT_X509, + KEY_ANY, cert->get_issuer(cert), FALSE); + if (!cert) + { + break; + } + x509 = (x509_t*)cert; + trustchain->insert_last(trustchain, cert); + } + private->destroy(private); + } + } + public->destroy(public); + } + add(this, trustchain, TRUE); + trustchain->destroy_offset(trustchain, offsetof(certificate_t, destroy)); + } + enumerator->destroy(enumerator); +} + +/** + * Export cached trustchain expiration dates to CSV files + */ +static void cron_export(private_certexpire_export_t *this) +{ + if (this->local_path) + { + if (this->force) + { + add_local_certs(this); + } + export_csv(this, this->local_path, this->local); + } + if (this->remote_path) + { + export_csv(this, this->remote_path, this->remote); + } +} + METHOD(certexpire_export_t, destroy, void, private_certexpire_export_t *this) { @@ -382,6 +447,9 @@ certexpire_export_t *certexpire_export_create() .empty_string = lib->settings->get_str(lib->settings, "%s.plugins.certexpire.csv.empty_string", "", charon->name), + .force = lib->settings->get_bool(lib->settings, + "%s.plugins.certexpire.csv.force", + TRUE, charon->name), ); cron = lib->settings->get_str(lib->settings, From d0c25a3f23285a1ed17a82d22459a012e53dbae3 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 17 Jul 2013 16:03:23 +0200 Subject: [PATCH 49/54] watcher: read multiple notifications if available Use non-blocking I/O on the read end of the notify pipe. This also makes sure the read does not block should select() signal data while there is none. --- src/libstrongswan/processing/watcher.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/libstrongswan/processing/watcher.c b/src/libstrongswan/processing/watcher.c index ee7053396..da7ba759a 100644 --- a/src/libstrongswan/processing/watcher.c +++ b/src/libstrongswan/processing/watcher.c @@ -25,6 +25,7 @@ #include #include #include +#include typedef struct private_watcher_t private_watcher_t; @@ -286,7 +287,7 @@ static job_requeue_t watch(private_watcher_t *this) if (this->notify[0] != -1 && FD_ISSET(this->notify[0], &rd)) { DBG2(DBG_JOB, "watcher got notification, rebuilding"); - ignore_result(read(this->notify[0], buf, sizeof(buf))); + while (read(this->notify[0], buf, sizeof(buf)) > 0); return JOB_REQUEUE_DIRECT; } @@ -430,6 +431,7 @@ METHOD(watcher_t, destroy, void, watcher_t *watcher_create() { private_watcher_t *this; + int flags; INIT(this, .public = { @@ -444,7 +446,18 @@ watcher_t *watcher_create() .notify[1] = -1, ); - if (pipe(this->notify) != 0) + if (pipe(this->notify) == 0) + { + /* use non-blocking I/O on read-end of notify pipe */ + flags = fcntl(this->notify[0], F_GETFL); + if (flags == -1 || + fcntl(this->notify[0], F_SETFL, flags | O_NONBLOCK) == -1) + { + DBG1(DBG_LIB, "setting watcher notify pipe read-end non-blocking " + "failed: %s", strerror(errno)); + } + } + else { DBG1(DBG_LIB, "creating watcher notify pipe failed: %s", strerror(errno)); From 55240835b0562a35025d261d85dd2bb04446d350 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 17 Jul 2013 16:07:47 +0200 Subject: [PATCH 50/54] watcher: properly support multiple watch callback types for the same FD --- src/libstrongswan/processing/watcher.c | 75 ++++++++++++++------------ src/libstrongswan/processing/watcher.h | 6 ++- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/src/libstrongswan/processing/watcher.c b/src/libstrongswan/processing/watcher.c index da7ba759a..9e02a1b49 100644 --- a/src/libstrongswan/processing/watcher.c +++ b/src/libstrongswan/processing/watcher.c @@ -58,6 +58,11 @@ struct private_watcher_t { * Notification pipe to signal watcher thread */ int notify[2]; + + /** + * List of callback jobs to process by watcher thread, as job_t + */ + linked_list_t *jobs; }; /** @@ -72,8 +77,8 @@ typedef struct { watcher_cb_t cb; /** user data to pass to callback */ void *data; - /** callback currently active? */ - bool active; + /** callback(s) currently active? */ + int in_callback; } entry_t; /** @@ -155,7 +160,7 @@ static void notify_end(notify_data_t *data) break; } } - entry->active = TRUE; + entry->in_callback--; break; } } @@ -171,8 +176,8 @@ static void notify_end(notify_data_t *data) /** * Execute the callback for a registered FD */ -static job_t* notify(private_watcher_t *this, entry_t *entry, - watcher_event_t event) +static void notify(private_watcher_t *this, entry_t *entry, + watcher_event_t event) { notify_data_t *data; @@ -188,11 +193,12 @@ static job_t* notify(private_watcher_t *this, entry_t *entry, /* deactivate entry, so we can select() other FDs even if the async * processing did not handle the event yet */ - entry->active = FALSE; + entry->in_callback++; - return (job_t*)callback_job_create_with_prio((void*)notify_async, data, + this->jobs->insert_last(this->jobs, + callback_job_create_with_prio((void*)notify_async, data, (void*)notify_end, (callback_job_cancel_t)return_false, - JOB_PRIO_CRITICAL); + JOB_PRIO_CRITICAL)); } /** @@ -210,7 +216,7 @@ static void activate_all(private_watcher_t *this) enumerator = this->fds->create_enumerator(this->fds); while (enumerator->enumerate(enumerator, &entry)) { - entry->active = TRUE; + entry->in_callback = 0; } enumerator->destroy(enumerator); this->condvar->broadcast(this->condvar); @@ -247,7 +253,7 @@ static job_requeue_t watch(private_watcher_t *this) enumerator = this->fds->create_enumerator(this->fds); while (enumerator->enumerate(enumerator, &entry)) { - if (entry->active) + if (!entry->in_callback) { if (entry->events & WATCHER_READ) { @@ -274,7 +280,7 @@ static job_requeue_t watch(private_watcher_t *this) { char buf[1]; bool old; - job_t *job = NULL; + job_t *job; DBG2(DBG_JOB, "watcher going to select()"); thread_cleanup_push((void*)activate_all, this); @@ -295,38 +301,39 @@ static job_requeue_t watch(private_watcher_t *this) enumerator = this->fds->create_enumerator(this->fds); while (enumerator->enumerate(enumerator, &entry)) { - if (FD_ISSET(entry->fd, &rd)) + if (FD_ISSET(entry->fd, &rd) && (entry->events & WATCHER_READ)) { DBG2(DBG_JOB, "watched FD %d ready to read", entry->fd); - job = notify(this, entry, WATCHER_READ); - break; + notify(this, entry, WATCHER_READ); } - if (FD_ISSET(entry->fd, &wr)) + if (FD_ISSET(entry->fd, &wr) && (entry->events & WATCHER_WRITE)) { DBG2(DBG_JOB, "watched FD %d ready to write", entry->fd); - job = notify(this, entry, WATCHER_WRITE); - break; + notify(this, entry, WATCHER_WRITE); } - if (FD_ISSET(entry->fd, &ex)) + if (FD_ISSET(entry->fd, &ex) && (entry->events & WATCHER_EXCEPT)) { DBG2(DBG_JOB, "watched FD %d has exception", entry->fd); - job = notify(this, entry, WATCHER_EXCEPT); - break; + notify(this, entry, WATCHER_EXCEPT); } } enumerator->destroy(enumerator); this->mutex->unlock(this->mutex); - if (job) + if (this->jobs->get_count(this->jobs)) { - if (lib->processor->get_threads(lib->processor)) + while (this->jobs->remove_first(this->jobs, + (void**)&job) == SUCCESS) { - lib->processor->queue_job(lib->processor, job); - } - else - { - job->execute(job); - job->destroy(job); + if (lib->processor->get_threads(lib->processor)) + { + lib->processor->queue_job(lib->processor, job); + } + else + { + job->execute(job); + job->destroy(job); + } } /* we temporarily disable a notified FD, rebuild FDSET */ return JOB_REQUEUE_DIRECT; @@ -350,7 +357,6 @@ METHOD(watcher_t, add, void, .events = events, .cb = cb, .data = data, - .active = TRUE, ); this->mutex->lock(this->mutex); @@ -384,16 +390,13 @@ METHOD(watcher_t, remove_, void, { if (entry->fd == fd) { - if (entry->active) - { - this->fds->remove_at(this->fds, enumerator); - free(entry); - } - else + if (entry->in_callback) { is_in_callback = TRUE; break; } + this->fds->remove_at(this->fds, enumerator); + free(entry); } } enumerator->destroy(enumerator); @@ -422,6 +425,7 @@ METHOD(watcher_t, destroy, void, { close(this->notify[1]); } + this->jobs->destroy(this->jobs); free(this); } @@ -442,6 +446,7 @@ watcher_t *watcher_create() .fds = linked_list_create(), .mutex = mutex_create(MUTEX_TYPE_DEFAULT), .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), + .jobs = linked_list_create(), .notify[0] = -1, .notify[1] = -1, ); diff --git a/src/libstrongswan/processing/watcher.h b/src/libstrongswan/processing/watcher.h index db7dd4fa8..02d9188f0 100644 --- a/src/libstrongswan/processing/watcher.h +++ b/src/libstrongswan/processing/watcher.h @@ -64,6 +64,9 @@ struct watcher_t { /** * Start watching a new file descriptor. * + * Multiple callbacks can be registered for the same file descriptor, and + * all of them get notified. Such callbacks are executed concurrently. + * * @param fd file descriptor to start watching * @param events ORed set of events to watch * @param cb callback function to invoke on events @@ -75,7 +78,8 @@ struct watcher_t { /** * Stop watching a previously registered file descriptor. * - * This call blocks until any active callback for this FD returns. + * This call blocks until any active callback for this FD returns. All + * callbacks registered for that FD get unregistered. * * @param fd file descriptor to stop watching */ From 6653e6c13e61da0753625af13de62c46d1ceeb48 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 18 Jul 2013 11:37:42 +0200 Subject: [PATCH 51/54] processor: add an execute_job() method to directly execute an important job If all worker threads are busy and waiting for an event, we must ensure that a job delivering that event gets executed. This new method has this property for CRITICAL jobs, using a worker if we have one, but executing the job directly if not. --- src/libstrongswan/processing/processor.c | 26 ++++++++++++++++++++++++ src/libstrongswan/processing/processor.h | 10 +++++++++ 2 files changed, 36 insertions(+) diff --git a/src/libstrongswan/processing/processor.c b/src/libstrongswan/processing/processor.c index c465f0259..f193b8d5e 100644 --- a/src/libstrongswan/processing/processor.c +++ b/src/libstrongswan/processing/processor.c @@ -401,6 +401,31 @@ METHOD(processor_t, queue_job, void, this->mutex->unlock(this->mutex); } +METHOD(processor_t, execute_job, void, + private_processor_t *this, job_t *job) +{ + job_priority_t prio; + bool queued = FALSE; + + this->mutex->lock(this->mutex); + if (get_idle_threads_nolock(this)) + { + prio = sane_prio(job->get_priority(job)); + job->status = JOB_STATUS_QUEUED; + /* insert job in front to execute it immediately */ + this->jobs[prio]->insert_first(this->jobs[prio], job); + queued = TRUE; + } + this->job_added->signal(this->job_added); + this->mutex->unlock(this->mutex); + + if (!queued) + { + job->execute(job); + job->destroy(job); + } +} + METHOD(processor_t, set_threads, void, private_processor_t *this, u_int count) { @@ -512,6 +537,7 @@ processor_t *processor_create() .get_working_threads = _get_working_threads, .get_job_load = _get_job_load, .queue_job = _queue_job, + .execute_job = _execute_job, .set_threads = _set_threads, .get_threads = _get_threads, .cancel = _cancel, diff --git a/src/libstrongswan/processing/processor.h b/src/libstrongswan/processing/processor.h index bd708fba8..c691cfb60 100644 --- a/src/libstrongswan/processing/processor.h +++ b/src/libstrongswan/processing/processor.h @@ -74,6 +74,16 @@ struct processor_t { */ void (*queue_job) (processor_t *this, job_t *job); + /** + * Directly execute a job with an idle worker thread. + * + * If no idle thread is available, the job gets executed by the calling + * thread. + * + * @param job job, gets destroyed + */ + void (*execute_job)(processor_t *this, job_t *job); + /** * Set the number of threads to use in the processor. * From ea009869e9e5cc914c5ce98261351029c7ab079e Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 18 Jul 2013 11:40:40 +0200 Subject: [PATCH 52/54] watcher: use processors new execute_job() to notify FDs Just queueing is problematic, as all threads might be busy waiting for events that the queued (but never executed) job delivers. --- src/libstrongswan/processing/watcher.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/libstrongswan/processing/watcher.c b/src/libstrongswan/processing/watcher.c index 9e02a1b49..69cb3c8f5 100644 --- a/src/libstrongswan/processing/watcher.c +++ b/src/libstrongswan/processing/watcher.c @@ -325,15 +325,7 @@ static job_requeue_t watch(private_watcher_t *this) while (this->jobs->remove_first(this->jobs, (void**)&job) == SUCCESS) { - if (lib->processor->get_threads(lib->processor)) - { - lib->processor->queue_job(lib->processor, job); - } - else - { - job->execute(job); - job->destroy(job); - } + lib->processor->execute_job(lib->processor, job); } /* we temporarily disable a notified FD, rebuild FDSET */ return JOB_REQUEUE_DIRECT; From 1897dd730fa5462edbd22601bdb1231fbaab3fa6 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 18 Jul 2013 11:42:59 +0200 Subject: [PATCH 53/54] processor: remove the now unused get_threads() method again --- src/libstrongswan/processing/processor.c | 7 ------- src/libstrongswan/processing/processor.h | 10 ---------- 2 files changed, 17 deletions(-) diff --git a/src/libstrongswan/processing/processor.c b/src/libstrongswan/processing/processor.c index f193b8d5e..e00216e07 100644 --- a/src/libstrongswan/processing/processor.c +++ b/src/libstrongswan/processing/processor.c @@ -462,12 +462,6 @@ METHOD(processor_t, set_threads, void, this->mutex->unlock(this->mutex); } -METHOD(processor_t, get_threads, u_int, - private_processor_t *this) -{ - return this->desired_threads; -} - METHOD(processor_t, cancel, void, private_processor_t *this) { @@ -539,7 +533,6 @@ processor_t *processor_create() .queue_job = _queue_job, .execute_job = _execute_job, .set_threads = _set_threads, - .get_threads = _get_threads, .cancel = _cancel, .destroy = _destroy, }, diff --git a/src/libstrongswan/processing/processor.h b/src/libstrongswan/processing/processor.h index c691cfb60..f96530e54 100644 --- a/src/libstrongswan/processing/processor.h +++ b/src/libstrongswan/processing/processor.h @@ -97,16 +97,6 @@ struct processor_t { */ void (*set_threads)(processor_t *this, u_int count); - /** - * Get the number of threads set with set_threads(). - * - * This does not actually reflect the number of threads currently active, - * but the number of threads targeted. - * - * @return number of desired threads - */ - u_int (*get_threads)(processor_t *this); - /** * Sets the number of threads to 0 and cancels all blocking jobs, then waits * for all threads to be terminated. From b4b3959b2233f881078f866f50d3296cca235438 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 18 Jul 2013 15:46:17 +0200 Subject: [PATCH 54/54] stream-service: move CAP_CHOWN check from plugins to service constructor A plugin service can be a TCP socket now, so it does not make much sense to strictly check for CAP_CHOWN. --- src/libcharon/plugins/duplicheck/duplicheck_plugin.c | 6 ------ .../plugins/error_notify/error_notify_plugin.c | 6 ------ src/libcharon/plugins/load_tester/load_tester_plugin.c | 7 ------- src/libcharon/plugins/lookip/lookip_plugin.c | 6 ------ src/libcharon/plugins/stroke/stroke_plugin.c | 10 ++-------- src/libcharon/plugins/whitelist/whitelist_plugin.c | 6 ------ src/libstrongswan/networking/streams/stream_service.c | 5 +++++ 7 files changed, 7 insertions(+), 39 deletions(-) diff --git a/src/libcharon/plugins/duplicheck/duplicheck_plugin.c b/src/libcharon/plugins/duplicheck/duplicheck_plugin.c index 6b8609ebc..4d018dbef 100644 --- a/src/libcharon/plugins/duplicheck/duplicheck_plugin.c +++ b/src/libcharon/plugins/duplicheck/duplicheck_plugin.c @@ -98,12 +98,6 @@ plugin_t *duplicheck_plugin_create() return NULL; } - if (!lib->caps->check(lib->caps, CAP_CHOWN)) - { /* required to chown(2) notify socket */ - DBG1(DBG_CFG, "duplicheck plugin requires CAP_CHOWN capability"); - return NULL; - } - INIT(this, .public = { .plugin = { diff --git a/src/libcharon/plugins/error_notify/error_notify_plugin.c b/src/libcharon/plugins/error_notify/error_notify_plugin.c index ef0ce7bc6..40ace6014 100644 --- a/src/libcharon/plugins/error_notify/error_notify_plugin.c +++ b/src/libcharon/plugins/error_notify/error_notify_plugin.c @@ -92,12 +92,6 @@ plugin_t *error_notify_plugin_create() { private_error_notify_plugin_t *this; - if (!lib->caps->check(lib->caps, CAP_CHOWN)) - { /* required to chown(2) notify socket */ - DBG1(DBG_CFG, "error-notify plugin requires CAP_CHOWN capability"); - return NULL; - } - INIT(this, .public = { .plugin = { diff --git a/src/libcharon/plugins/load_tester/load_tester_plugin.c b/src/libcharon/plugins/load_tester/load_tester_plugin.c index 7f2d425fd..03557a269 100644 --- a/src/libcharon/plugins/load_tester/load_tester_plugin.c +++ b/src/libcharon/plugins/load_tester/load_tester_plugin.c @@ -269,12 +269,6 @@ plugin_t *load_tester_plugin_create() return NULL; } - if (!lib->caps->check(lib->caps, CAP_CHOWN)) - { /* required to chown(2) control socket */ - DBG1(DBG_CFG, "load-tester plugin requires CAP_CHOWN capability"); - return NULL; - } - INIT(this, .public = { .plugin = { @@ -304,4 +298,3 @@ plugin_t *load_tester_plugin_create() } return &this->public.plugin; } - diff --git a/src/libcharon/plugins/lookip/lookip_plugin.c b/src/libcharon/plugins/lookip/lookip_plugin.c index 63b138162..a6c32d65d 100644 --- a/src/libcharon/plugins/lookip/lookip_plugin.c +++ b/src/libcharon/plugins/lookip/lookip_plugin.c @@ -92,12 +92,6 @@ plugin_t *lookip_plugin_create() { private_lookip_plugin_t *this; - if (!lib->caps->check(lib->caps, CAP_CHOWN)) - { /* required to chown(2) control socket */ - DBG1(DBG_CFG, "lookip plugin requires CAP_CHOWN capability"); - return NULL; - } - INIT(this, .public = { .plugin = { diff --git a/src/libcharon/plugins/stroke/stroke_plugin.c b/src/libcharon/plugins/stroke/stroke_plugin.c index 767bdc64b..31df1f99b 100644 --- a/src/libcharon/plugins/stroke/stroke_plugin.c +++ b/src/libcharon/plugins/stroke/stroke_plugin.c @@ -51,12 +51,13 @@ static bool register_stroke(private_stroke_plugin_t *this, if (reg) { this->socket = stroke_socket_create(); + return this->socket != NULL; } else { DESTROY_IF(this->socket); + return TRUE; } - return TRUE; } METHOD(plugin_t, get_features, int, @@ -91,12 +92,6 @@ plugin_t *stroke_plugin_create() { private_stroke_plugin_t *this; - if (!lib->caps->check(lib->caps, CAP_CHOWN)) - { /* required to chown(2) stroke socket */ - DBG1(DBG_CFG, "stroke plugin requires CAP_CHOWN capability"); - return NULL; - } - INIT(this, .public = { .plugin = { @@ -110,4 +105,3 @@ plugin_t *stroke_plugin_create() return &this->public.plugin; } - diff --git a/src/libcharon/plugins/whitelist/whitelist_plugin.c b/src/libcharon/plugins/whitelist/whitelist_plugin.c index 38465aebb..3ea45723c 100644 --- a/src/libcharon/plugins/whitelist/whitelist_plugin.c +++ b/src/libcharon/plugins/whitelist/whitelist_plugin.c @@ -92,12 +92,6 @@ plugin_t *whitelist_plugin_create() { private_whitelist_plugin_t *this; - if (!lib->caps->check(lib->caps, CAP_CHOWN)) - { /* required to chown(2) control socket */ - DBG1(DBG_CFG, "whitelist plugin requires CAP_CHOWN capability"); - return NULL; - } - INIT(this, .public = { .plugin = { diff --git a/src/libstrongswan/networking/streams/stream_service.c b/src/libstrongswan/networking/streams/stream_service.c index c2681af3a..ece17b41f 100644 --- a/src/libstrongswan/networking/streams/stream_service.c +++ b/src/libstrongswan/networking/streams/stream_service.c @@ -251,6 +251,11 @@ stream_service_t *stream_service_create_unix(char *uri, int backlog) DBG1(DBG_NET, "invalid stream URI: '%s'", uri); return NULL; } + if (!lib->caps->check(lib->caps, CAP_CHOWN)) + { /* required to chown(2) service socket */ + DBG1(DBG_NET, "socket '%s' requires CAP_CHOWN capability", uri); + return NULL; + } fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) {