aboutsummaryrefslogtreecommitdiffstats
path: root/src/libstrongswan/networking
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/networking')
-rw-r--r--src/libstrongswan/networking/host.c578
-rw-r--r--src/libstrongswan/networking/host.h220
-rw-r--r--src/libstrongswan/networking/host_resolver.c391
-rw-r--r--src/libstrongswan/networking/host_resolver.h60
4 files changed, 1249 insertions, 0 deletions
diff --git a/src/libstrongswan/networking/host.c b/src/libstrongswan/networking/host.c
new file mode 100644
index 000000000..1d0614001
--- /dev/null
+++ b/src/libstrongswan/networking/host.c
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2006-2012 Tobias Brunner
+ * Copyright (C) 2006 Daniel Roethlisberger
+ * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 "host.h"
+
+#include <debug.h>
+#include <library.h>
+
+#define IPV4_LEN 4
+#define IPV6_LEN 16
+
+typedef struct private_host_t private_host_t;
+
+/**
+ * Private Data of a host object.
+ */
+struct private_host_t {
+ /**
+ * Public data
+ */
+ host_t public;
+
+ /**
+ * low-lewel structure, which stores the address
+ */
+ union {
+ /** generic type */
+ struct sockaddr address;
+ /** maximum sockaddr size */
+ struct sockaddr_storage address_max;
+ /** IPv4 address */
+ struct sockaddr_in address4;
+ /** IPv6 address */
+ struct sockaddr_in6 address6;
+ };
+ /**
+ * length of address structure
+ */
+ socklen_t socklen;
+};
+
+
+METHOD(host_t, get_sockaddr, sockaddr_t*,
+ private_host_t *this)
+{
+ return &(this->address);
+}
+
+METHOD(host_t, get_sockaddr_len, socklen_t*,
+ private_host_t *this)
+{
+ return &(this->socklen);
+}
+
+METHOD(host_t, is_anyaddr, bool,
+ private_host_t *this)
+{
+ static const u_int8_t zeroes[IPV6_LEN];
+
+ switch (this->address.sa_family)
+ {
+ case AF_INET:
+ {
+ return memeq(zeroes, &(this->address4.sin_addr.s_addr), IPV4_LEN);
+ }
+ case AF_INET6:
+ {
+ return memeq(zeroes, &(this->address6.sin6_addr.s6_addr), IPV6_LEN);
+ }
+ default:
+ {
+ return FALSE;
+ }
+ }
+}
+
+/**
+ * Described in header.
+ */
+int host_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
+ const void *const *args)
+{
+ private_host_t *this = *((private_host_t**)(args[0]));
+ char buffer[INET6_ADDRSTRLEN + 16];
+
+ if (this == NULL)
+ {
+ snprintf(buffer, sizeof(buffer), "(null)");
+ }
+ else if (is_anyaddr(this) && !spec->plus)
+ {
+ snprintf(buffer, sizeof(buffer), "%%any%s",
+ this->address.sa_family == AF_INET6 ? "6" : "");
+ }
+ else
+ {
+ void *address;
+ u_int16_t port;
+ int len;
+
+ address = &this->address6.sin6_addr;
+ port = this->address6.sin6_port;
+
+ switch (this->address.sa_family)
+ {
+ case AF_INET:
+ address = &this->address4.sin_addr;
+ port = this->address4.sin_port;
+ /* fall */
+ case AF_INET6:
+
+ if (inet_ntop(this->address.sa_family, address,
+ buffer, sizeof(buffer)) == NULL)
+ {
+ snprintf(buffer, sizeof(buffer),
+ "(address conversion failed)");
+ }
+ else if (spec->hash)
+ {
+ len = strlen(buffer);
+ snprintf(buffer + len, sizeof(buffer) - len,
+ "[%d]", ntohs(port));
+ }
+ break;
+ default:
+ snprintf(buffer, sizeof(buffer), "(family not supported)");
+ break;
+ }
+ }
+ if (spec->minus)
+ {
+ return print_in_hook(data, "%-*s", spec->width, buffer);
+ }
+ return print_in_hook(data, "%*s", spec->width, buffer);
+}
+
+METHOD(host_t, get_address, chunk_t,
+ private_host_t *this)
+{
+ chunk_t address = chunk_empty;
+
+ switch (this->address.sa_family)
+ {
+ case AF_INET:
+ {
+ address.ptr = (char*)&(this->address4.sin_addr.s_addr);
+ address.len = IPV4_LEN;
+ return address;
+ }
+ case AF_INET6:
+ {
+ address.ptr = (char*)&(this->address6.sin6_addr.s6_addr);
+ address.len = IPV6_LEN;
+ return address;
+ }
+ default:
+ {
+ /* return empty chunk */
+ return address;
+ }
+ }
+}
+
+METHOD(host_t, get_family, int,
+ private_host_t *this)
+{
+ return this->address.sa_family;
+}
+
+METHOD(host_t, get_port, u_int16_t,
+ private_host_t *this)
+{
+ switch (this->address.sa_family)
+ {
+ case AF_INET:
+ {
+ return ntohs(this->address4.sin_port);
+ }
+ case AF_INET6:
+ {
+ return ntohs(this->address6.sin6_port);
+ }
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+METHOD(host_t, set_port, void,
+ private_host_t *this, u_int16_t port)
+{
+ switch (this->address.sa_family)
+ {
+ case AF_INET:
+ {
+ this->address4.sin_port = htons(port);
+ break;
+ }
+ case AF_INET6:
+ {
+ this->address6.sin6_port = htons(port);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+METHOD(host_t, clone_, host_t*,
+ private_host_t *this)
+{
+ private_host_t *new;
+
+ new = malloc_thing(private_host_t);
+ memcpy(new, this, sizeof(private_host_t));
+
+ return &new->public;
+}
+
+/**
+ * Implements host_t.ip_equals
+ */
+static bool ip_equals(private_host_t *this, private_host_t *other)
+{
+ if (this->address.sa_family != other->address.sa_family)
+ {
+ /* 0.0.0.0 and 0::0 are equal */
+ return (is_anyaddr(this) && is_anyaddr(other));
+ }
+
+ switch (this->address.sa_family)
+ {
+ case AF_INET:
+ {
+ return memeq(&this->address4.sin_addr, &other->address4.sin_addr,
+ sizeof(this->address4.sin_addr));
+ }
+ case AF_INET6:
+ {
+ return memeq(&this->address6.sin6_addr, &other->address6.sin6_addr,
+ sizeof(this->address6.sin6_addr));
+ }
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+/**
+ * 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)
+{
+ if (!ip_equals(this, other))
+ {
+ return FALSE;
+ }
+
+ switch (this->address.sa_family)
+ {
+ case AF_INET:
+ {
+ return (this->address4.sin_port == other->address4.sin_port);
+ }
+ case AF_INET6:
+ {
+ return (this->address6.sin6_port == other->address6.sin6_port);
+ }
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+METHOD(host_t, destroy, void,
+ private_host_t *this)
+{
+ free(this);
+}
+
+/**
+ * Creates an empty host_t object
+ */
+static private_host_t *host_create_empty(void)
+{
+ private_host_t *this;
+
+ INIT(this,
+ .public = {
+ .get_sockaddr = _get_sockaddr,
+ .get_sockaddr_len = _get_sockaddr_len,
+ .clone = _clone_,
+ .get_family = _get_family,
+ .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,
+ .destroy = _destroy,
+ },
+ );
+
+ return this;
+}
+
+/*
+ * Create a %any host with port
+ */
+static host_t *host_create_any_port(int family, u_int16_t port)
+{
+ host_t *this;
+
+ this = host_create_any(family);
+ this->set_port(this, port);
+ return this;
+}
+
+/*
+ * Described in header.
+ */
+host_t *host_create_from_string(char *string, u_int16_t port)
+{
+ private_host_t *this;
+
+ if (streq(string, "%any"))
+ {
+ return host_create_any_port(AF_INET, port);
+ }
+ if (streq(string, "%any6"))
+ {
+ return host_create_any_port(AF_INET6, port);
+ }
+
+ this = host_create_empty();
+ if (strchr(string, '.'))
+ {
+ this->address.sa_family = AF_INET;
+ }
+ else
+ {
+ this->address.sa_family = AF_INET6;
+ }
+ switch (this->address.sa_family)
+ {
+ case AF_INET:
+ {
+ if (inet_pton(AF_INET, string, &this->address4.sin_addr) <=0)
+ {
+ break;
+ }
+ this->address4.sin_port = htons(port);
+ this->socklen = sizeof(struct sockaddr_in);
+ return &this->public;
+ }
+ case AF_INET6:
+ {
+ if (inet_pton(AF_INET6, string, &this->address6.sin6_addr) <=0)
+ {
+ break;
+ }
+ this->address6.sin6_port = htons(port);
+ this->socklen = sizeof(struct sockaddr_in6);
+ return &this->public;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ free(this);
+ return NULL;
+}
+
+/*
+ * Described in header.
+ */
+host_t *host_create_from_sockaddr(sockaddr_t *sockaddr)
+{
+ private_host_t *this = host_create_empty();
+
+ switch (sockaddr->sa_family)
+ {
+ case AF_INET:
+ {
+ memcpy(&this->address4, (struct sockaddr_in*)sockaddr,
+ sizeof(struct sockaddr_in));
+ this->socklen = sizeof(struct sockaddr_in);
+ return &this->public;
+ }
+ case AF_INET6:
+ {
+ memcpy(&this->address6, (struct sockaddr_in6*)sockaddr,
+ sizeof(struct sockaddr_in6));
+ this->socklen = sizeof(struct sockaddr_in6);
+ return &this->public;
+ }
+ default:
+ break;
+ }
+ free(this);
+ return NULL;
+}
+
+/*
+ * Described in header.
+ */
+host_t *host_create_from_dns(char *string, int af, u_int16_t port)
+{
+ host_t *this;
+
+ this = lib->hosts->resolve(lib->hosts, string, af);
+ if (this)
+ {
+ this->set_port(this, port);
+ }
+ return this;
+}
+
+/*
+ * Described in header.
+ */
+host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port)
+{
+ private_host_t *this;
+
+ switch (family)
+ {
+ case AF_INET:
+ if (address.len < IPV4_LEN)
+ {
+ return NULL;
+ }
+ address.len = IPV4_LEN;
+ break;
+ case AF_INET6:
+ if (address.len < IPV6_LEN)
+ {
+ return NULL;
+ }
+ address.len = IPV6_LEN;
+ break;
+ case AF_UNSPEC:
+ switch (address.len)
+ {
+ case IPV4_LEN:
+ family = AF_INET;
+ break;
+ case IPV6_LEN:
+ family = AF_INET6;
+ break;
+ default:
+ return NULL;
+ }
+ break;
+ default:
+ return NULL;
+ }
+ this = host_create_empty();
+ this->address.sa_family = family;
+ switch (family)
+ {
+ case AF_INET:
+ memcpy(&this->address4.sin_addr.s_addr, address.ptr, address.len);
+ this->address4.sin_port = htons(port);
+ this->socklen = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ memcpy(&this->address6.sin6_addr.s6_addr, address.ptr, address.len);
+ this->address6.sin6_port = htons(port);
+ this->socklen = sizeof(struct sockaddr_in6);
+ break;
+ }
+ return &this->public;
+}
+
+/*
+ * Described in header.
+ */
+host_t *host_create_from_subnet(char *string, int *bits)
+{
+ char *pos, buf[64];
+ host_t *net;
+
+ pos = strchr(string, '/');
+ if (pos)
+ {
+ if (pos - string >= sizeof(buf))
+ {
+ return NULL;
+ }
+ strncpy(buf, string, pos - string);
+ buf[pos - string] = '\0';
+ *bits = atoi(pos + 1);
+ return host_create_from_string(buf, 0);
+ }
+ net = host_create_from_string(string, 0);
+ if (net)
+ {
+ if (net->get_family(net) == AF_INET)
+ {
+ *bits = 32;
+ }
+ else
+ {
+ *bits = 128;
+ }
+ }
+ return net;
+}
+
+/*
+ * Described in header.
+ */
+host_t *host_create_any(int family)
+{
+ private_host_t *this = host_create_empty();
+
+ memset(&this->address_max, 0, sizeof(struct sockaddr_storage));
+ this->address.sa_family = family;
+
+ switch (family)
+ {
+ case AF_INET:
+ {
+ this->socklen = sizeof(struct sockaddr_in);
+ return &(this->public);
+ }
+ case AF_INET6:
+ {
+ this->socklen = sizeof(struct sockaddr_in6);
+ return &this->public;
+ }
+ default:
+ break;
+ }
+ free(this);
+ return NULL;
+}
diff --git a/src/libstrongswan/networking/host.h b/src/libstrongswan/networking/host.h
new file mode 100644
index 000000000..cae2a4ff0
--- /dev/null
+++ b/src/libstrongswan/networking/host.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2006-2009 Tobias Brunner
+ * Copyright (C) 2006 Daniel Roethlisberger
+ * Copyright (C) 2005-2008 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 host host
+ * @{ @ingroup networking
+ */
+
+#ifndef HOST_H_
+#define HOST_H_
+
+typedef enum host_diff_t host_diff_t;
+typedef struct host_t host_t;
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <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
+ * useful functions on it.
+ */
+struct host_t {
+
+ /**
+ * Build a clone of this host object.
+ *
+ * @return cloned host
+ */
+ host_t *(*clone) (host_t *this);
+
+ /**
+ * Get a pointer to the internal sockaddr struct.
+ *
+ * This is used for sending and receiving via sockets.
+ *
+ * @return pointer to the internal sockaddr structure
+ */
+ sockaddr_t *(*get_sockaddr) (host_t *this);
+
+ /**
+ * Get the length of the sockaddr struct.
+ *
+ * Depending on the family, the length of the sockaddr struct
+ * is different. Use this function to get the length of the sockaddr
+ * struct returned by get_sock_addr.
+ *
+ * This is used for sending and receiving via sockets.
+ *
+ * @return length of the sockaddr struct
+ */
+ socklen_t *(*get_sockaddr_len) (host_t *this);
+
+ /**
+ * Gets the family of the address
+ *
+ * @return family
+ */
+ int (*get_family) (host_t *this);
+
+ /**
+ * Checks if the ip address of host is set to default route.
+ *
+ * @return TRUE if host is 0.0.0.0 or 0::0, FALSE otherwise
+ */
+ bool (*is_anyaddr) (host_t *this);
+
+ /**
+ * Get the address of this host as chunk_t
+ *
+ * Returned chunk points to internal data.
+ *
+ * @return address string,
+ */
+ chunk_t (*get_address) (host_t *this);
+
+ /**
+ * Get the port of this host
+ *
+ * @return port number
+ */
+ u_int16_t (*get_port) (host_t *this);
+
+ /**
+ * Set the port of this host
+ *
+ * @param port port numer
+ */
+ void (*set_port) (host_t *this, u_int16_t port);
+
+ /**
+ * Compare the ips of two hosts hosts.
+ *
+ * @param other the other to compare
+ * @return TRUE if addresses are equal.
+ */
+ bool (*ip_equals) (host_t *this, host_t *other);
+
+ /**
+ * Compare two hosts, with port.
+ *
+ * @param other the other to compare
+ * @return TRUE if addresses and ports are equal.
+ */
+ 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);
+};
+
+/**
+ * Constructor to create a host_t object from an address string.
+ *
+ * @param string string of an address, such as "152.96.193.130"
+ * @param port port number
+ * @return host_t, NULL if string not an address.
+ */
+host_t *host_create_from_string(char *string, u_int16_t port);
+
+/**
+ * Constructor to create a host_t from a DNS name.
+ *
+ * @param string hostname to resolve
+ * @param family family to prefer, 0 for first match
+ * @param port port number
+ * @return host_t, NULL lookup failed
+ */
+host_t *host_create_from_dns(char *string, int family, u_int16_t port);
+
+/**
+ * Constructor to create a host_t object from an address chunk.
+ *
+ * If family is AF_UNSPEC, it is guessed using address.len.
+ *
+ * @param family Address family, such as AF_INET or AF_INET6
+ * @param address address as chunk_t in network order
+ * @param port port number
+ * @return host_t, NULL if family not supported/chunk invalid
+ */
+host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port);
+
+/**
+ * Constructor to create a host_t object from a sockaddr struct
+ *
+ * @param sockaddr sockaddr struct which contains family, address and port
+ * @return host_t, NULL if family not supported
+ */
+host_t *host_create_from_sockaddr(sockaddr_t *sockaddr);
+
+/**
+ * Create a host from a CIDR subnet definition (1.2.3.0/24), return bits.
+ *
+ * @param string string to parse
+ * @param bits gets the number of network bits in CIDR notation
+ * @return network start address, NULL on error
+ */
+host_t *host_create_from_subnet(char *string, int *bits);
+
+/**
+ * Create a host without an address, a "any" host.
+ *
+ * @param family family of the any host
+ * @return host_t, NULL if family not supported
+ */
+host_t *host_create_any(int family);
+
+/**
+ * printf hook function for host_t.
+ *
+ * Arguments are:
+ * host_t *host
+ * Use #-modifier to include port number
+ * Use +-modifier to force numeric representation (instead of e.g. %any)
+ */
+int host_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
+ const void *const *args);
+
+#endif /** HOST_H_ @}*/
diff --git a/src/libstrongswan/networking/host_resolver.c b/src/libstrongswan/networking/host_resolver.c
new file mode 100644
index 000000000..55b07d318
--- /dev/null
+++ b/src/libstrongswan/networking/host_resolver.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "host_resolver.h"
+
+#include <debug.h>
+#include <library.h>
+#include <threading/condvar.h>
+#include <threading/mutex.h>
+#include <threading/thread.h>
+#include <utils/hashtable.h>
+#include <utils/linked_list.h>
+
+/**
+ * Default minimum and maximum number of threads
+ */
+#define MIN_THREADS_DEFAULT 0
+#define MAX_THREADS_DEFAULT 3
+
+/**
+ * Timeout in seconds to wait for new queries until a thread may be stopped
+ */
+#define NEW_QUERY_WAIT_TIMEOUT 30
+
+typedef struct private_host_resolver_t private_host_resolver_t;
+
+/**
+ * Private data of host_resolver_t
+ */
+struct private_host_resolver_t {
+
+ /**
+ * Public interface
+ */
+ host_resolver_t public;
+
+ /**
+ * Hashtable to check for queued queries, query_t*
+ */
+ hashtable_t *queries;
+
+ /**
+ * Queue for queries, query_t*
+ */
+ linked_list_t *queue;
+
+ /**
+ * Mutex to safely access private data
+ */
+ mutex_t *mutex;
+
+ /**
+ * Condvar to signal arrival of new queries
+ */
+ condvar_t *new_query;
+
+ /**
+ * Minimum number of resolver threads
+ */
+ u_int min_threads;
+
+ /**
+ * Maximum number of resolver threads
+ */
+ u_int max_threads;
+
+ /**
+ * Current number of threads
+ */
+ u_int threads;
+
+ /**
+ * Current number of busy threads
+ */
+ u_int busy_threads;
+
+ /**
+ * Pool of threads, thread_t*
+ */
+ linked_list_t *pool;
+
+ /**
+ * TRUE if no new queries are accepted
+ */
+ bool disabled;
+
+};
+
+typedef struct {
+ /** DNS name we are looking for */
+ char *name;
+ /** address family we request */
+ int family;
+ /** Condvar to signal completion of a query */
+ condvar_t *done;
+ /** refcount */
+ refcount_t refcount;
+ /** the result if successful */
+ host_t *result;
+} query_t;
+
+/**
+ * Destroy the given query_t object if refcount is zero
+ */
+static void query_destroy(query_t *this)
+{
+ if (ref_put(&this->refcount))
+ {
+ DESTROY_IF(this->result);
+ this->done->destroy(this->done);
+ free(this->name);
+ free(this);
+ }
+}
+
+/**
+ * Signals all waiting threads and destroys the query
+ */
+static void query_signal_and_destroy(query_t *this)
+{
+ this->done->broadcast(this->done);
+ query_destroy(this);
+}
+
+/**
+ * Hash a queued query
+ */
+static u_int query_hash(query_t *this)
+{
+ return chunk_hash_inc(chunk_create(this->name, strlen(this->name)),
+ chunk_hash(chunk_from_thing(this->family)));
+}
+
+/**
+ * Compare two queued queries
+ */
+static bool query_equals(query_t *this, query_t *other)
+{
+ return this->family == other->family && streq(this->name, other->name);
+}
+
+/**
+ * Main function of resolver threads
+ */
+static void *resolve_hosts(private_host_resolver_t *this)
+{
+ struct addrinfo hints, *result;
+ query_t *query;
+ int error;
+ bool old, timed_out;
+
+ while (TRUE)
+ {
+ this->mutex->lock(this->mutex);
+ thread_cleanup_push((thread_cleanup_t)this->mutex->unlock, this->mutex);
+ while (this->queue->remove_first(this->queue,
+ (void**)&query) != SUCCESS)
+ {
+ old = thread_cancelability(TRUE);
+ timed_out = this->new_query->timed_wait(this->new_query,
+ this->mutex, NEW_QUERY_WAIT_TIMEOUT * 1000);
+ thread_cancelability(old);
+ if (this->disabled)
+ {
+ thread_cleanup_pop(TRUE);
+ return NULL;
+ }
+ else if (timed_out && (this->threads > this->min_threads))
+ { /* terminate this thread by detaching it */
+ thread_t *thread = thread_current();
+
+ this->threads--;
+ this->pool->remove(this->pool, thread, NULL);
+ thread_cleanup_pop(TRUE);
+ thread->detach(thread);
+ return NULL;
+ }
+ }
+ this->busy_threads++;
+ thread_cleanup_pop(TRUE);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = query->family;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ thread_cleanup_push((thread_cleanup_t)query_signal_and_destroy, query);
+ old = thread_cancelability(TRUE);
+ error = getaddrinfo(query->name, NULL, &hints, &result);
+ thread_cancelability(old);
+ thread_cleanup_pop(FALSE);
+
+ this->mutex->lock(this->mutex);
+ this->busy_threads--;
+ if (error != 0)
+ {
+ DBG1(DBG_LIB, "resolving '%s' failed: %s", query->name,
+ gai_strerror(error));
+ }
+ else
+ { /* result is a linked list, but we use only the first address */
+ query->result = host_create_from_sockaddr(result->ai_addr);
+ freeaddrinfo(result);
+ }
+ this->queries->remove(this->queries, query);
+ query->done->broadcast(query->done);
+ this->mutex->unlock(this->mutex);
+ query_destroy(query);
+ }
+ return NULL;
+}
+
+/**
+ * Try to convert IP addresses directly
+ */
+static host_t *try_numeric_lookup(char *name, int family)
+{
+ struct addrinfo hints, *result;
+ int error;
+ host_t *host;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_NUMERICHOST;
+
+ error = getaddrinfo(name, NULL, &hints, &result);
+ if (error != 0)
+ { /* not an IP address */
+ return NULL;
+ }
+ else
+ { /* result is a linked list, but we use only the first address */
+ host = host_create_from_sockaddr(result->ai_addr);
+ freeaddrinfo(result);
+ }
+ return host;
+}
+
+METHOD(host_resolver_t, resolve, host_t*,
+ private_host_resolver_t *this, char *name, int family)
+{
+ query_t *query, lookup = {
+ .name = name,
+ .family = family,
+ };
+ host_t *result;
+
+ if (streq(name, "%any") || streq(name, "0.0.0.0"))
+ {
+ return host_create_any(family ? family : AF_INET);
+ }
+ if (streq(name, "%any6") || streq(name, "::"))
+ {
+ return host_create_any(family ? family : AF_INET6);
+ }
+ if (family == AF_INET && strchr(name, ':'))
+ { /* do not try to convert v6 addresses for v4 family */
+ return NULL;
+ }
+ result = try_numeric_lookup(name, family);
+ if (result)
+ { /* shortcut for numeric IP addresses */
+ return result;
+ }
+ this->mutex->lock(this->mutex);
+ if (this->disabled)
+ {
+ this->mutex->unlock(this->mutex);
+ return NULL;
+ }
+ query = this->queries->get(this->queries, &lookup);
+ if (!query)
+ {
+ INIT(query,
+ .name = strdup(name),
+ .family = family,
+ .done = condvar_create(CONDVAR_TYPE_DEFAULT),
+ .refcount = 1,
+ );
+ this->queries->put(this->queries, query, query);
+ this->queue->insert_last(this->queue, query);
+ this->new_query->signal(this->new_query);
+ }
+ ref_get(&query->refcount);
+ if (this->busy_threads == this->threads &&
+ this->threads < this->max_threads)
+ {
+ thread_t *thread;
+
+ thread = thread_create((thread_main_t)resolve_hosts, this);
+ if (thread)
+ {
+ this->threads++;
+ this->pool->insert_last(this->pool, thread);
+ }
+ }
+ query->done->wait(query->done, this->mutex);
+ this->mutex->unlock(this->mutex);
+
+ result = query->result ? query->result->clone(query->result) : NULL;
+ query_destroy(query);
+ return result;
+}
+
+METHOD(host_resolver_t, flush, void,
+ private_host_resolver_t *this)
+{
+ enumerator_t *enumerator;
+ query_t *query;
+
+ this->mutex->lock(this->mutex);
+ enumerator = this->queries->create_enumerator(this->queries);
+ while (enumerator->enumerate(enumerator, &query, NULL))
+ { /* use the hashtable here as we also want to signal dequeued queries */
+ this->queries->remove_at(this->queries, enumerator);
+ query->done->broadcast(query->done);
+ }
+ enumerator->destroy(enumerator);
+ this->queue->destroy_function(this->queue, (void*)query_destroy);
+ this->queue = linked_list_create();
+ this->disabled = TRUE;
+ /* this will already terminate most idle threads */
+ this->new_query->broadcast(this->new_query);
+ this->mutex->unlock(this->mutex);
+}
+
+METHOD(host_resolver_t, destroy, void,
+ private_host_resolver_t *this)
+{
+ thread_t *thread;
+
+ flush(this);
+ this->pool->invoke_offset(this->pool, offsetof(thread_t, cancel));
+ while (this->pool->remove_first(this->pool, (void**)&thread) == SUCCESS)
+ {
+ thread->join(thread);
+ }
+ this->pool->destroy(this->pool);
+ this->queue->destroy(this->queue);
+ this->queries->destroy(this->queries);
+ this->new_query->destroy(this->new_query);
+ this->mutex->destroy(this->mutex);
+ free(this);
+}
+
+/*
+ * Described in header
+ */
+host_resolver_t *host_resolver_create()
+{
+ private_host_resolver_t *this;
+
+ INIT(this,
+ .public = {
+ .resolve = _resolve,
+ .flush = _flush,
+ .destroy = _destroy,
+ },
+ .queries = hashtable_create((hashtable_hash_t)query_hash,
+ (hashtable_equals_t)query_equals, 8),
+ .queue = linked_list_create(),
+ .pool = linked_list_create(),
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ .new_query = condvar_create(CONDVAR_TYPE_DEFAULT),
+ );
+
+ this->min_threads = max(0, lib->settings->get_int(lib->settings,
+ "libstrongswan.host_resolver.min_threads",
+ MIN_THREADS_DEFAULT));
+ this->max_threads = max(this->min_threads ?: 1,
+ lib->settings->get_int(lib->settings,
+ "libstrongswan.host_resolver.max_threads",
+ MAX_THREADS_DEFAULT));
+ return &this->public;
+}
diff --git a/src/libstrongswan/networking/host_resolver.h b/src/libstrongswan/networking/host_resolver.h
new file mode 100644
index 000000000..f944a9cdf
--- /dev/null
+++ b/src/libstrongswan/networking/host_resolver.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 host_resolver host_resolver
+ * @{ @ingroup networking
+ */
+
+#ifndef HOST_RESOLVER_H_
+#define HOST_RESOLVER_H_
+
+#include "host.h"
+
+typedef struct host_resolver_t host_resolver_t;
+
+/**
+ * Resolve hosts by DNS name but do so in a separate thread (calling
+ * getaddrinfo(3) directly might block indefinitely, or at least a very long
+ * time if no DNS servers are reachable).
+ */
+struct host_resolver_t {
+
+ /**
+ * Resolve host from the given DNS name.
+ *
+ * @param name name to lookup
+ * @param family requested address family
+ * @return resolved host or NULL if failed or canceled
+ */
+ host_t *(*resolve)(host_resolver_t *this, char *name, int family);
+
+ /**
+ * Flush the queue of queries. No new queries will be accepted afterwards.
+ */
+ void (*flush)(host_resolver_t *this);
+
+ /**
+ * Destroy a host_resolver_t.
+ */
+ void (*destroy)(host_resolver_t *this);
+};
+
+/**
+ * Create a host_resolver_t instance.
+ */
+host_resolver_t *host_resolver_create();
+
+#endif /** HOST_RESOLVER_H_ @}*/