summaryrefslogtreecommitdiff
path: root/src/libstrongswan/networking
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/networking')
-rw-r--r--src/libstrongswan/networking/host.c90
-rw-r--r--src/libstrongswan/networking/host.h31
-rw-r--r--src/libstrongswan/networking/host_resolver.c20
-rw-r--r--src/libstrongswan/networking/streams/stream.c426
-rw-r--r--src/libstrongswan/networking/streams/stream.h199
-rw-r--r--src/libstrongswan/networking/streams/stream_manager.c235
-rw-r--r--src/libstrongswan/networking/streams/stream_manager.h96
-rw-r--r--src/libstrongswan/networking/streams/stream_service.c332
-rw-r--r--src/libstrongswan/networking/streams/stream_service.h104
-rw-r--r--src/libstrongswan/networking/tun_device.c109
-rw-r--r--src/libstrongswan/networking/tun_device.h15
11 files changed, 1562 insertions, 95 deletions
diff --git a/src/libstrongswan/networking/host.c b/src/libstrongswan/networking/host.c
index bffa96064..8d04a4ec9 100644
--- a/src/libstrongswan/networking/host.c
+++ b/src/libstrongswan/networking/host.c
@@ -54,6 +54,15 @@ struct private_host_t {
socklen_t socklen;
};
+/**
+ * Update the sockaddr internal sa_len option, if available
+ */
+static inline void update_sa_len(private_host_t *this)
+{
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ this->address.sa_len = this->socklen;
+#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
+}
METHOD(host_t, get_sockaddr, sockaddr_t*,
private_host_t *this)
@@ -102,7 +111,7 @@ int host_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
{
snprintf(buffer, sizeof(buffer), "(null)");
}
- else if (is_anyaddr(this) && !spec->plus)
+ else if (is_anyaddr(this) && !spec->plus && !spec->hash)
{
snprintf(buffer, sizeof(buffer), "%%any%s",
this->address.sa_family == AF_INET6 ? "6" : "");
@@ -265,26 +274,6 @@ static bool ip_equals(private_host_t *this, private_host_t *other)
}
/**
- * Implements host_t.get_differences
- */
-static host_diff_t get_differences(host_t *this, host_t *other)
-{
- host_diff_t ret = HOST_DIFF_NONE;
-
- if (!this->ip_equals(this, other))
- {
- ret |= HOST_DIFF_ADDR;
- }
-
- if (this->get_port(this) != other->get_port(other))
- {
- ret |= HOST_DIFF_PORT;
- }
-
- return ret;
-}
-
-/**
* Implements host_t.equals
*/
static bool equals(private_host_t *this, private_host_t *other)
@@ -332,7 +321,6 @@ static private_host_t *host_create_empty(void)
.get_address = _get_address,
.get_port = _get_port,
.set_port = _set_port,
- .get_differences = get_differences,
.ip_equals = (bool (*)(host_t *,host_t *))ip_equals,
.equals = (bool (*)(host_t *,host_t *)) equals,
.is_anyaddr = _is_anyaddr,
@@ -393,6 +381,7 @@ host_t *host_create_from_string_and_family(char *string, int family,
}
/* FALL */
case AF_INET6:
+ memset(&addr.v6, 0, sizeof(addr.v6));
if (inet_pton(AF_INET6, string, &addr.v6.sin6_addr) != 1)
{
return NULL;
@@ -406,6 +395,7 @@ host_t *host_create_from_string_and_family(char *string, int family,
return NULL;
}
af_inet:
+ memset(&addr.v4, 0, sizeof(addr.v4));
if (inet_pton(AF_INET, string, &addr.v4.sin_addr) != 1)
{
return NULL;
@@ -440,6 +430,7 @@ host_t *host_create_from_sockaddr(sockaddr_t *sockaddr)
memcpy(&this->address4, (struct sockaddr_in*)sockaddr,
sizeof(struct sockaddr_in));
this->socklen = sizeof(struct sockaddr_in);
+ update_sa_len(this);
return &this->public;
}
case AF_INET6:
@@ -447,6 +438,7 @@ host_t *host_create_from_sockaddr(sockaddr_t *sockaddr)
memcpy(&this->address6, (struct sockaddr_in6*)sockaddr,
sizeof(struct sockaddr_in6));
this->socklen = sizeof(struct sockaddr_in6);
+ update_sa_len(this);
return &this->public;
}
default:
@@ -529,6 +521,7 @@ host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port)
this->socklen = sizeof(struct sockaddr_in6);
break;
}
+ update_sa_len(this);
return &this->public;
}
@@ -568,6 +561,57 @@ host_t *host_create_from_subnet(char *string, int *bits)
}
/*
+ * See header.
+ */
+host_t *host_create_netmask(int family, int netbits)
+{
+ private_host_t *this;
+ int bits, bytes, len = 0;
+ char *target;
+
+ switch (family)
+ {
+ case AF_INET:
+ if (netbits < 0 || netbits > 32)
+ {
+ return NULL;
+ }
+ this = host_create_empty();
+ this->socklen = sizeof(struct sockaddr_in);
+ target = (char*)&this->address4.sin_addr;
+ len = 4;
+ break;
+ case AF_INET6:
+ if (netbits < 0 || netbits > 128)
+ {
+ return NULL;
+ }
+ this = host_create_empty();
+ this->socklen = sizeof(struct sockaddr_in6);
+ target = (char*)&this->address6.sin6_addr;
+ len = 16;
+ break;
+ default:
+ return NULL;
+ }
+
+ memset(&this->address_max, 0, sizeof(struct sockaddr_storage));
+ this->address.sa_family = family;
+ update_sa_len(this);
+
+ bytes = netbits / 8;
+ bits = 8 - (netbits & 0x07);
+
+ memset(target, 0xff, bytes);
+ if (bytes < len)
+ {
+ memset(target + bytes, 0x00, len - bytes);
+ target[bytes] = (u_int8_t)(0xff << bits);
+ }
+ return &this->public;
+}
+
+/*
* Described in header.
*/
host_t *host_create_any(int family)
@@ -582,11 +626,13 @@ host_t *host_create_any(int family)
case AF_INET:
{
this->socklen = sizeof(struct sockaddr_in);
+ update_sa_len(this);
return &(this->public);
}
case AF_INET6:
{
this->socklen = sizeof(struct sockaddr_in6);
+ update_sa_len(this);
return &this->public;
}
default:
diff --git a/src/libstrongswan/networking/host.h b/src/libstrongswan/networking/host.h
index 25f334779..4fc6cf35c 100644
--- a/src/libstrongswan/networking/host.h
+++ b/src/libstrongswan/networking/host.h
@@ -37,16 +37,6 @@ typedef struct host_t host_t;
#include <utils/chunk.h>
/**
- * Differences between two hosts. They differ in
- * address, port, or both.
- */
-enum host_diff_t {
- HOST_DIFF_NONE = 0,
- HOST_DIFF_ADDR = 1,
- HOST_DIFF_PORT = 2,
-};
-
-/**
* Representates a Host
*
* Host object, identifies a address:port pair and defines some
@@ -102,7 +92,7 @@ struct host_t {
*
* Returned chunk points to internal data.
*
- * @return address string,
+ * @return address blob
*/
chunk_t (*get_address) (host_t *this);
@@ -116,7 +106,7 @@ struct host_t {
/**
* Set the port of this host
*
- * @param port port numer
+ * @param port port number
*/
void (*set_port) (host_t *this, u_int16_t port);
@@ -137,14 +127,6 @@ struct host_t {
bool (*equals) (host_t *this, host_t *other);
/**
- * Compare two hosts and return the differences.
- *
- * @param other the other to compare
- * @return differences in a combination of host_diff_t's
- */
- host_diff_t (*get_differences) (host_t *this, host_t *other);
-
- /**
* Destroy this host object.
*/
void (*destroy) (host_t *this);
@@ -210,6 +192,15 @@ host_t *host_create_from_sockaddr(sockaddr_t *sockaddr);
host_t *host_create_from_subnet(char *string, int *bits);
/**
+ * Create a netmask host having the first netbits bits set.
+ *
+ * @param family family of the netmask host
+ * @param netbits number of leading bits set in the host
+ * @return netmask host
+ */
+host_t *host_create_netmask(int family, int netbits);
+
+/**
* Create a host without an address, a "any" host.
*
* @param family family of the any host
diff --git a/src/libstrongswan/networking/host_resolver.c b/src/libstrongswan/networking/host_resolver.c
index 5e244f114..99a17d17c 100644
--- a/src/libstrongswan/networking/host_resolver.c
+++ b/src/libstrongswan/networking/host_resolver.c
@@ -233,10 +233,24 @@ METHOD(host_resolver_t, resolve, host_t*,
.family = family,
};
host_t *result;
+ struct in_addr addr;
- if (family == AF_INET && strchr(name, ':'))
- { /* do not try to convert v6 addresses for v4 family */
- return NULL;
+ switch (family)
+ {
+ case AF_INET:
+ /* do not try to convert v6 addresses for v4 family */
+ if (strchr(name, ':'))
+ {
+ return NULL;
+ }
+ break;
+ case AF_INET6:
+ /* do not try to convert v4 addresses for v6 family */
+ if (inet_pton(AF_INET, name, &addr) == 1)
+ {
+ return NULL;
+ }
+ break;
}
this->mutex->lock(this->mutex);
if (this->disabled)
diff --git a/src/libstrongswan/networking/streams/stream.c b/src/libstrongswan/networking/streams/stream.c
new file mode 100644
index 000000000..8ecb89fc9
--- /dev/null
+++ b/src/libstrongswan/networking/streams/stream.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <library.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+
+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;
+
+ /**
+ * 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,
+ 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, 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)
+{
+ 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, 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
+ */
+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;
+ stream_cb_t cb;
+
+ switch (event)
+ {
+ case WATCHER_READ:
+ cb = this->read_cb;
+ this->read_cb = NULL;
+ keep = cb(this->read_data, &this->public);
+ if (keep)
+ {
+ this->read_cb = cb;
+ }
+ break;
+ case WATCHER_WRITE:
+ cb = this->write_cb;
+ this->write_cb = NULL;
+ keep = cb(this->write_data, &this->public);
+ if (keep)
+ {
+ this->write_cb = cb;
+ }
+ 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, get_file, FILE*,
+ private_stream_t *this)
+{
+ FILE *file;
+ int fd;
+
+ /* fclose() closes the FD passed to fdopen(), so dup() it */
+ fd = dup(this->fd);
+ if (fd == -1)
+ {
+ return NULL;
+ }
+ file = fdopen(fd, "w+");
+ if (!file)
+ {
+ close(fd);
+ }
+ return file;
+}
+
+METHOD(stream_t, destroy, void,
+ private_stream_t *this)
+{
+ remove_watcher(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_,
+ .read_all = _read_all,
+ .on_read = _on_read,
+ .write = _write_,
+ .write_all = _write_all,
+ .on_write = _on_write,
+ .get_file = _get_file,
+ .destroy = _destroy,
+ },
+ .fd = 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));
+ addr->sun_path[sizeof(addr->sun_path)-1] = '\0';
+
+ 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);
+}
+
+/**
+ * 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
new file mode 100644
index 000000000..810514da9
--- /dev/null
+++ b/src/libstrongswan/networking/streams/stream.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup stream stream
+ * @{ @ingroup streams
+ */
+
+#ifndef STREAM_H_
+#define STREAM_H_
+
+typedef struct stream_t stream_t;
+
+#include <library.h>
+
+#include <sys/un.h>
+#include <sys/socket.h>
+
+/**
+ * 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);
+
+/**
+ * Callback function prototype, called when stream is ready.
+ *
+ * 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
+ * appropriately.
+ *
+ * @param data data passed during callback registration
+ * @param stream associated stream
+ * @return FALSE unregisters the invoked callback, TRUE keeps it
+ */
+typedef bool (*stream_cb_t)(void *data, stream_t *stream);
+
+/**
+ * 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);
+
+ /**
+ * 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.
+ *
+ * @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.
+ *
+ * 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);
+
+ /**
+ * 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.
+ *
+ * @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);
+
+ /**
+ * Get a FILE reference for this stream.
+ *
+ * @return FILE*, must be fclose()d, NULL on error
+ */
+ FILE* (*get_file)(stream_t *this);
+
+ /**
+ * Destroy a 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 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.
+ *
+ * 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_ @}*/
diff --git a/src/libstrongswan/networking/streams/stream_manager.c b/src/libstrongswan/networking/streams/stream_manager.c
new file mode 100644
index 000000000..2cbd6127e
--- /dev/null
+++ b/src/libstrongswan/networking/streams/stream_manager.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "stream_manager.h"
+
+#include <threading/rwlock.h>
+
+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;
+
+ /**
+ * 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;
+
+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, create_service, stream_service_t*,
+ private_stream_manager_t *this, char *uri, int backlog)
+{
+ 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, backlog);
+ if (service)
+ {
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
+
+ return service;
+}
+
+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)
+{
+ 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);
+ 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_,
+ .create_service = _create_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(),
+ .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+ );
+
+ 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_manager.h b/src/libstrongswan/networking/streams/stream_manager.h
new file mode 100644
index 000000000..352d93e2b
--- /dev/null
+++ b/src/libstrongswan/networking/streams/stream_manager.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup stream_manager stream_manager
+ * @{ @ingroup streams
+ */
+
+#ifndef STREAM_MANAGER_H_
+#define STREAM_MANAGER_H_
+
+typedef struct stream_manager_t stream_manager_t;
+
+#include <library.h>
+#include <networking/streams/stream_service.h>
+
+/**
+ * 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);
+
+ /**
+ * 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()
+ * @return service, NULL on error
+ */
+ stream_service_t* (*create_service)(stream_manager_t *this, char *uri,
+ int backlog);
+
+ /**
+ * 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_ @}*/
diff --git a/src/libstrongswan/networking/streams/stream_service.c b/src/libstrongswan/networking/streams/stream_service.c
new file mode 100644
index 000000000..ece17b41f
--- /dev/null
+++ b/src/libstrongswan/networking/streams/stream_service.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <library.h>
+#include <threading/thread.h>
+#include <threading/mutex.h>
+#include <threading/condvar.h>
+#include <processing/jobs/callback_job.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+
+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;
+
+ /**
+ * 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;
+};
+
+/**
+ * Data to pass to async accept job
+ */
+typedef struct {
+ /** callback function */
+ stream_service_cb_t cb;
+ /** callback data */
+ void *data;
+ /** accepted connection */
+ int fd;
+ /** reference to stream service */
+ private_stream_service_t *this;
+} async_data_t;
+
+/**
+ * Clean up accept data
+ */
+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);
+
+ if (data->fd != -1)
+ {
+ 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)
+ {
+ /* 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));
+ }
+ 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;
+ 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, (callback_job_cancel_t)return_false,
+ this->prio));
+ }
+ else
+ {
+ free(data);
+ }
+ 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, 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);
+ }
+
+ this->cb = cb;
+ this->data = data;
+ if (prio <= JOB_PRIO_MAX)
+ {
+ 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, this->cncrncy);
+ close(this->fd);
+ this->mutex->destroy(this->mutex);
+ this->condvar->destroy(this->condvar);
+ 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,
+ .prio = JOB_PRIO_MEDIUM,
+ .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
+ .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
+ );
+
+ return &this->public;
+}
+
+/**
+ * See header
+ */
+stream_service_t *stream_service_create_unix(char *uri, int backlog)
+{
+ 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;
+ }
+ 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)
+ {
+ 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, backlog) < 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);
+}
+
+/**
+ * See header
+ */
+stream_service_t *stream_service_create_tcp(char *uri, int backlog)
+{
+ 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, backlog) < 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
new file mode 100644
index 000000000..c8faba323
--- /dev/null
+++ b/src/libstrongswan/networking/streams/stream_service.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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup stream_service stream_service
+ * @{ @ingroup streams
+ */
+
+#ifndef STREAM_SERVICE_H_
+#define STREAM_SERVICE_H_
+
+typedef struct stream_service_t stream_service_t;
+
+#include <library.h>
+#include <processing/jobs/job.h>
+#include <networking/streams/stream.h>
+
+/**
+ * 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, int backlog);
+
+/**
+ * Service callback routine for accepting client connections.
+ *
+ * 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 bool (*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
+ * @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, u_int cncrncy);
+
+ /**
+ * 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);
+
+/**
+ * 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, 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, int backlog);
+
+#endif /** STREAM_SERVICE_H_ @}*/
diff --git a/src/libstrongswan/networking/tun_device.c b/src/libstrongswan/networking/tun_device.c
index 1da87df05..af7e57140 100644
--- a/src/libstrongswan/networking/tun_device.c
+++ b/src/libstrongswan/networking/tun_device.c
@@ -73,63 +73,28 @@ struct private_tun_device_t {
* The current MTU
*/
int mtu;
-};
-
-/**
- * Set the sockaddr_t from the given netmask
- */
-static void set_netmask(struct ifreq *ifr, int family, u_int8_t netmask)
-{
- int len, bytes, bits;
- char *target;
- switch (family)
- {
- case AF_INET:
- {
- struct sockaddr_in *addr = (struct sockaddr_in*)&ifr->ifr_addr;
- target = (char*)&addr->sin_addr;
- len = 4;
- break;
- }
- case AF_INET6:
- {
- struct sockaddr_in6 *addr = (struct sockaddr_in6*)&ifr->ifr_addr;
- target = (char*)&addr->sin6_addr;
- len = 16;
- break;
- }
- default:
- return;
- }
-
- ifr->ifr_addr.sa_family = family;
-
- bytes = (netmask + 7) / 8;
- bits = (bytes * 8) - netmask;
+ /**
+ * Associated address
+ */
+ host_t *address;
- memset(target, 0xff, bytes);
- memset(target + bytes, 0x00, len - bytes);
- target[bytes - 1] = bits ? (u_int8_t)(0xff << bits) : 0xff;
-}
+ /**
+ * Netmask for address
+ */
+ u_int8_t netmask;
+};
METHOD(tun_device_t, set_address, bool,
private_tun_device_t *this, host_t *addr, u_int8_t netmask)
{
struct ifreq ifr;
- int family;
-
- family = addr->get_family(addr);
- if ((netmask > 32 && family == AF_INET) || netmask > 128)
- {
- DBG1(DBG_LIB, "failed to set address on %s: invalid netmask",
- this->if_name);
- return FALSE;
- }
+ host_t *mask;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
- memcpy(&ifr.ifr_addr, addr->get_sockaddr(addr), sizeof(sockaddr_t));
+ memcpy(&ifr.ifr_addr, addr->get_sockaddr(addr),
+ *addr->get_sockaddr_len(addr));
if (ioctl(this->sock, SIOCSIFADDR, &ifr) < 0)
{
@@ -146,7 +111,15 @@ METHOD(tun_device_t, set_address, bool,
}
#endif /* __APPLE__ */
- set_netmask(&ifr, family, netmask);
+ mask = host_create_netmask(addr->get_family(addr), netmask);
+ if (!mask)
+ {
+ DBG1(DBG_LIB, "invalid netmask: %d", netmask);
+ return FALSE;
+ }
+ memcpy(&ifr.ifr_addr, mask->get_sockaddr(mask),
+ *mask->get_sockaddr_len(mask));
+ mask->destroy(mask);
if (ioctl(this->sock, SIOCSIFNETMASK, &ifr) < 0)
{
@@ -154,9 +127,21 @@ METHOD(tun_device_t, set_address, bool,
this->if_name, strerror(errno));
return FALSE;
}
+ this->address = addr->clone(addr);
+ this->netmask = netmask;
return TRUE;
}
+METHOD(tun_device_t, get_address, host_t*,
+ private_tun_device_t *this, u_int8_t *netmask)
+{
+ if (netmask && this->address)
+ {
+ *netmask = this->netmask;
+ }
+ return this->address;
+}
+
METHOD(tun_device_t, up, bool,
private_tun_device_t *this)
{
@@ -229,11 +214,23 @@ METHOD(tun_device_t, get_name, char*,
return this->if_name;
}
+METHOD(tun_device_t, get_fd, int,
+ private_tun_device_t *this)
+{
+ return this->tunfd;
+}
+
METHOD(tun_device_t, write_packet, bool,
private_tun_device_t *this, chunk_t packet)
{
ssize_t s;
+#ifdef __APPLE__
+ /* UTUN's expect the packets to be prepended by a 32-bit protocol number
+ * instead of parsing the packet again, we assume IPv4 for now */
+ u_int32_t proto = htonl(AF_INET);
+ packet = chunk_cata("cc", chunk_from_thing(proto), packet);
+#endif
s = write(this->tunfd, packet.ptr, packet.len);
if (s < 0)
{
@@ -280,6 +277,11 @@ METHOD(tun_device_t, read_packet, bool,
return FALSE;
}
packet->len = len;
+#ifdef __APPLE__
+ /* UTUN's prepend packets with a 32-bit protocol number */
+ packet->len -= sizeof(u_int32_t);
+ memmove(packet->ptr, packet->ptr + sizeof(u_int32_t), packet->len);
+#endif
return TRUE;
}
@@ -308,6 +310,7 @@ METHOD(tun_device_t, destroy, void,
{
close(this->sock);
}
+ DESTROY_IF(this->address);
free(this);
}
@@ -398,14 +401,18 @@ static bool init_tun(private_tun_device_t *this, const char *name_tmpl)
/* this works on FreeBSD and might also work on Linux with older TUN
* driver versions (no IFF_TUN) */
char devname[IFNAMSIZ];
- int i;
+ /* the same process is allowed to open a device again, but that's not what
+ * we want (unless we previously closed a device, which we don't know at
+ * this point). therefore, this counter is static so we don't accidentally
+ * open a device twice */
+ static int i = -1;
if (name_tmpl)
{
DBG1(DBG_LIB, "arbitrary naming of TUN devices is not supported");
}
- for (i = 0; i < 256; i++)
+ for (; ++i < 256; )
{
snprintf(devname, IFNAMSIZ, "/dev/tun%d", i);
this->tunfd = open(devname, O_RDWR);
@@ -435,7 +442,9 @@ tun_device_t *tun_device_create(const char *name_tmpl)
.get_mtu = _get_mtu,
.set_mtu = _set_mtu,
.get_name = _get_name,
+ .get_fd = _get_fd,
.set_address = _set_address,
+ .get_address = _get_address,
.up = _up,
.destroy = _destroy,
},
diff --git a/src/libstrongswan/networking/tun_device.h b/src/libstrongswan/networking/tun_device.h
index b22a5d170..1d330f133 100644
--- a/src/libstrongswan/networking/tun_device.h
+++ b/src/libstrongswan/networking/tun_device.h
@@ -66,6 +66,14 @@ struct tun_device_t {
bool (*set_address)(tun_device_t *this, host_t *addr, u_int8_t netmask);
/**
+ * Get the IP address previously assigned to using set_address().
+ *
+ * @param netmask pointer receiving the configured netmask, or NULL
+ * @return address previously set, NULL if none
+ */
+ host_t* (*get_address)(tun_device_t *this, u_int8_t *netmask);
+
+ /**
* Bring the TUN device up
*
* @return TRUE if operation successful
@@ -95,6 +103,13 @@ struct tun_device_t {
char *(*get_name)(tun_device_t *this);
/**
+ * Get the underlying tun file descriptor.
+ *
+ * @return file descriptor of this tun device
+ */
+ int (*get_fd)(tun_device_t *this);
+
+ /**
* Destroy a tun_device_t
*/
void (*destroy)(tun_device_t *this);