diff options
-rw-r--r-- | src/libcharon/daemon.c | 3 | ||||
-rw-r--r-- | src/libstrongswan/Android.mk | 4 | ||||
-rw-r--r-- | src/libstrongswan/Makefile.am | 6 | ||||
-rw-r--r-- | src/libstrongswan/host_resolver.c | 289 | ||||
-rw-r--r-- | src/libstrongswan/host_resolver.h | 62 | ||||
-rw-r--r-- | src/libstrongswan/library.c | 3 | ||||
-rw-r--r-- | src/libstrongswan/library.h | 6 | ||||
-rw-r--r-- | src/libstrongswan/utils/host.c | 50 |
8 files changed, 373 insertions, 50 deletions
diff --git a/src/libcharon/daemon.c b/src/libcharon/daemon.c index 6e977efc4..9ae56a91d 100644 --- a/src/libcharon/daemon.c +++ b/src/libcharon/daemon.c @@ -84,7 +84,8 @@ static void destroy(private_daemon_t *this) { /* terminate all idle threads */ lib->processor->set_threads(lib->processor, 0); - + /* make sure nobody waits for a DNS query */ + lib->hosts->flush(lib->hosts); /* close all IKE_SAs */ if (this->public.ike_sa_manager) { diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index 4912576df..9c7ef1d0a 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -3,8 +3,8 @@ include $(CLEAR_VARS) # copy-n-paste from Makefile.am LOCAL_SRC_FILES := \ -library.c chunk.c debug.c enum.c settings.c printf_hook.c asn1/asn1.c \ -asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \ +library.c chunk.c debug.c enum.c host_resolver.c settings.c printf_hook.c \ +asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \ crypto/crypters/crypter.c crypto/hashers/hasher.c crypto/pkcs7.c crypto/pkcs9.c \ crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \ crypto/prfs/prf.c crypto/prfs/mac_prf.c \ diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 463d57d95..4017bfcc2 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -1,8 +1,8 @@ ipseclib_LTLIBRARIES = libstrongswan.la libstrongswan_la_SOURCES = \ -library.c chunk.c debug.c enum.c settings.c printf_hook.c asn1/asn1.c \ -asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \ +library.c chunk.c debug.c enum.c host_resolver.c settings.c printf_hook.c \ +asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \ crypto/crypters/crypter.c crypto/hashers/hasher.c crypto/pkcs7.c crypto/pkcs9.c \ crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \ crypto/prfs/prf.c crypto/prfs/mac_prf.c \ @@ -31,7 +31,7 @@ utils/optionsfrom.c utils/capabilities.c utils/backtrace.c utils/tun_device.c if USE_DEV_HEADERS strongswan_includedir = ${dev_headers} nobase_strongswan_include_HEADERS = \ -library.h chunk.h debug.h enum.h settings.h printf_hook.h \ +library.h chunk.h debug.h enum.h host_resolver.h settings.h printf_hook.h \ asn1/asn1.h asn1/asn1_parser.h asn1/oid.h bio/bio_reader.h bio/bio_writer.h \ crypto/crypters/crypter.h crypto/hashers/hasher.h crypto/mac.h \ crypto/pkcs7.h crypto/pkcs9.h crypto/proposal/proposal_keywords.h \ diff --git a/src/libstrongswan/host_resolver.c b/src/libstrongswan/host_resolver.c new file mode 100644 index 000000000..8a9711825 --- /dev/null +++ b/src/libstrongswan/host_resolver.c @@ -0,0 +1,289 @@ +/* + * 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 <processing/jobs/callback_job.h> +#include <threading/condvar.h> +#include <threading/mutex.h> +#include <threading/thread.h> +#include <utils/hashtable.h> +#include <utils/linked_list.h> + +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; + + /** + * Maximum number of resolver threads + */ + u_int max_threads; + + /** + * Current number of threads + */ + u_int threads; + + /** + * 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 job_requeue_t resolve_hosts(private_host_resolver_t *this) +{ + struct addrinfo hints, *result; + query_t *query; + int error; + bool old; + + this->mutex->lock(this->mutex); + while (this->queue->remove_first(this->queue, (void**)&query) != SUCCESS) + { + thread_cleanup_push((thread_cleanup_t)this->mutex->unlock, this->mutex); + old = thread_cancelability(TRUE); + this->new_query->wait(this->new_query, this->mutex); + thread_cancelability(old); + thread_cleanup_pop(FALSE); + } + this->mutex->unlock(this->mutex); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = query->family; + + 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); + 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 JOB_REQUEUE_DIRECT; +} + +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; + } + 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->threads < this->max_threads) + { + this->threads++; + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create_with_prio( + (callback_job_cb_t)resolve_hosts, this, NULL, + (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + } + 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->mutex->unlock(this->mutex); +} + +METHOD(host_resolver_t, destroy, void, + private_host_resolver_t *this) +{ + this->queue->destroy_function(this->queue, (void*)query_signal_and_destroy); + 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(u_int max_threads) +{ + 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(), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .new_query = condvar_create(CONDVAR_TYPE_DEFAULT), + .max_threads = max_threads, + ); + + return &this->public; +} diff --git a/src/libstrongswan/host_resolver.h b/src/libstrongswan/host_resolver.h new file mode 100644 index 000000000..855e3fe03 --- /dev/null +++ b/src/libstrongswan/host_resolver.h @@ -0,0 +1,62 @@ +/* + * 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 libstrongswan + */ + +#ifndef HOST_RESOLVER_H_ +#define HOST_RESOLVER_H_ + +#include "utils/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. + * + * @param max_threads maximum number of resolver threads to use + */ +host_resolver_t *host_resolver_create(u_int max_threads); + +#endif /** HOST_RESOLVER_H_ @}*/ diff --git a/src/libstrongswan/library.c b/src/libstrongswan/library.c index 1179b468c..b85ebeacb 100644 --- a/src/libstrongswan/library.c +++ b/src/libstrongswan/library.c @@ -27,6 +27,7 @@ #include <selectors/traffic_selector.h> #define CHECKSUM_LIBRARY IPSEC_LIB_DIR"/libchecksum.so" +#define HOST_RESOLVER_MAX_THREADS 2 typedef struct private_library_t private_library_t; @@ -68,6 +69,7 @@ void library_deinit() this->public.scheduler->destroy(this->public.scheduler); this->public.processor->destroy(this->public.processor); this->public.plugins->destroy(this->public.plugins); + this->public.hosts->destroy(this->public.hosts); this->public.settings->destroy(this->public.settings); this->public.credmgr->destroy(this->public.credmgr); this->public.creds->destroy(this->public.creds); @@ -182,6 +184,7 @@ bool library_init(char *settings) this->objects = hashtable_create((hashtable_hash_t)hash, (hashtable_equals_t)equals, 4); + this->public.hosts = host_resolver_create(HOST_RESOLVER_MAX_THREADS); this->public.settings = settings_create(settings); this->public.proposal = proposal_keywords_create(); this->public.crypto = crypto_factory_create(); diff --git a/src/libstrongswan/library.h b/src/libstrongswan/library.h index b79bd91be..5bd0d67eb 100644 --- a/src/libstrongswan/library.h +++ b/src/libstrongswan/library.h @@ -77,6 +77,7 @@ #include "printf_hook.h" #include "utils.h" #include "chunk.h" +#include "host_resolver.h" #include "settings.h" #include "integrity_checker.h" #include "processing/processor.h" @@ -171,6 +172,11 @@ struct library_t { scheduler_t *scheduler; /** + * resolve hosts by DNS name + */ + host_resolver_t *hosts; + + /** * various settings loaded from settings file */ settings_t *settings; diff --git a/src/libstrongswan/utils/host.c b/src/libstrongswan/utils/host.c index e17b6ad02..1d0614001 100644 --- a/src/libstrongswan/utils/host.c +++ b/src/libstrongswan/utils/host.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Tobias Brunner + * Copyright (C) 2006-2012 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -16,14 +16,10 @@ * for more details. */ -#define _GNU_SOURCE -#include <sys/socket.h> -#include <netdb.h> -#include <string.h> - #include "host.h" #include <debug.h> +#include <library.h> #define IPV4_LEN 4 #define IPV6_LEN 16 @@ -450,48 +446,14 @@ host_t *host_create_from_sockaddr(sockaddr_t *sockaddr) */ host_t *host_create_from_dns(char *string, int af, u_int16_t port) { - private_host_t *this; - struct addrinfo hints, *result; - int error; - - if (streq(string, "%any")) - { - return host_create_any_port(af ? af : AF_INET, port); - } - if (streq(string, "%any6")) - { - return host_create_any_port(af ? af : AF_INET6, port); - } - if (af == AF_INET && strchr(string, ':')) - { /* do not try to convert v6 addresses for v4 family */ - return NULL; - } + host_t *this; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = af; - error = getaddrinfo(string, NULL, &hints, &result); - if (error != 0) - { - DBG1(DBG_LIB, "resolving '%s' failed: %s", string, gai_strerror(error)); - return NULL; - } - /* result is a linked list, but we use only the first address */ - this = (private_host_t*)host_create_from_sockaddr(result->ai_addr); - freeaddrinfo(result); + this = lib->hosts->resolve(lib->hosts, string, af); if (this) { - 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; - } - return &this->public; + this->set_port(this, port); } - return NULL; + return this; } /* |