Annotation of embedaddon/strongswan/src/libstrongswan/networking/host_resolver.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2012 Tobias Brunner
! 3: * HSR Hochschule fuer Technik Rapperswil
! 4: *
! 5: * This program is free software; you can redistribute it and/or modify it
! 6: * under the terms of the GNU General Public License as published by the
! 7: * Free Software Foundation; either version 2 of the License, or (at your
! 8: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 9: *
! 10: * This program is distributed in the hope that it will be useful, but
! 11: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 12: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 13: * for more details.
! 14: */
! 15:
! 16: #include <sys/types.h>
! 17:
! 18: #include "host_resolver.h"
! 19:
! 20: #include <library.h>
! 21: #include <utils/debug.h>
! 22: #include <threading/condvar.h>
! 23: #include <threading/mutex.h>
! 24: #include <threading/thread.h>
! 25: #include <collections/hashtable.h>
! 26: #include <collections/linked_list.h>
! 27:
! 28: /**
! 29: * Default minimum and maximum number of threads
! 30: */
! 31: #define MIN_THREADS_DEFAULT 0
! 32: #define MAX_THREADS_DEFAULT 3
! 33:
! 34: /**
! 35: * Timeout in seconds to wait for new queries until a thread may be stopped
! 36: */
! 37: #define NEW_QUERY_WAIT_TIMEOUT 30
! 38:
! 39: typedef struct private_host_resolver_t private_host_resolver_t;
! 40:
! 41: /**
! 42: * Private data of host_resolver_t
! 43: */
! 44: struct private_host_resolver_t {
! 45:
! 46: /**
! 47: * Public interface
! 48: */
! 49: host_resolver_t public;
! 50:
! 51: /**
! 52: * Hashtable to check for queued queries, query_t*
! 53: */
! 54: hashtable_t *queries;
! 55:
! 56: /**
! 57: * Queue for queries, query_t*
! 58: */
! 59: linked_list_t *queue;
! 60:
! 61: /**
! 62: * Mutex to safely access private data
! 63: */
! 64: mutex_t *mutex;
! 65:
! 66: /**
! 67: * Condvar to signal arrival of new queries
! 68: */
! 69: condvar_t *new_query;
! 70:
! 71: /**
! 72: * Minimum number of resolver threads
! 73: */
! 74: u_int min_threads;
! 75:
! 76: /**
! 77: * Maximum number of resolver threads
! 78: */
! 79: u_int max_threads;
! 80:
! 81: /**
! 82: * Current number of threads
! 83: */
! 84: u_int threads;
! 85:
! 86: /**
! 87: * Current number of busy threads
! 88: */
! 89: u_int busy_threads;
! 90:
! 91: /**
! 92: * Pool of threads, thread_t*
! 93: */
! 94: linked_list_t *pool;
! 95:
! 96: /**
! 97: * TRUE if no new queries are accepted
! 98: */
! 99: bool disabled;
! 100:
! 101: };
! 102:
! 103: typedef struct {
! 104: /** DNS name we are looking for */
! 105: char *name;
! 106: /** address family we request */
! 107: int family;
! 108: /** Condvar to signal completion of a query */
! 109: condvar_t *done;
! 110: /** refcount */
! 111: refcount_t refcount;
! 112: /** the result if successful */
! 113: host_t *result;
! 114: } query_t;
! 115:
! 116: /**
! 117: * Destroy the given query_t object if refcount is zero
! 118: */
! 119: static void query_destroy(query_t *this)
! 120: {
! 121: if (ref_put(&this->refcount))
! 122: {
! 123: DESTROY_IF(this->result);
! 124: this->done->destroy(this->done);
! 125: free(this->name);
! 126: free(this);
! 127: }
! 128: }
! 129:
! 130: /**
! 131: * Signals all waiting threads and destroys the query
! 132: */
! 133: static void query_signal_and_destroy(query_t *this)
! 134: {
! 135: this->done->broadcast(this->done);
! 136: query_destroy(this);
! 137: }
! 138:
! 139: /**
! 140: * Hash a queued query
! 141: */
! 142: static u_int query_hash(query_t *this)
! 143: {
! 144: return chunk_hash_inc(chunk_create(this->name, strlen(this->name)),
! 145: chunk_hash(chunk_from_thing(this->family)));
! 146: }
! 147:
! 148: /**
! 149: * Compare two queued queries
! 150: */
! 151: static bool query_equals(query_t *this, query_t *other)
! 152: {
! 153: return this->family == other->family && streq(this->name, other->name);
! 154: }
! 155:
! 156: /**
! 157: * Main function of resolver threads
! 158: */
! 159: static void *resolve_hosts(private_host_resolver_t *this)
! 160: {
! 161: struct addrinfo hints, *result;
! 162: query_t *query;
! 163: int error;
! 164: bool old, timed_out;
! 165:
! 166: /* default resolver threads to non-cancellable */
! 167: thread_cancelability(FALSE);
! 168:
! 169: while (TRUE)
! 170: {
! 171: this->mutex->lock(this->mutex);
! 172: while (this->queue->remove_first(this->queue,
! 173: (void**)&query) != SUCCESS)
! 174: {
! 175: if (this->disabled)
! 176: {
! 177: this->mutex->unlock(this->mutex);
! 178: return NULL;
! 179: }
! 180: timed_out = this->new_query->timed_wait(this->new_query,
! 181: this->mutex, NEW_QUERY_WAIT_TIMEOUT * 1000);
! 182: if (this->disabled)
! 183: {
! 184: this->mutex->unlock(this->mutex);
! 185: return NULL;
! 186: }
! 187: else if (timed_out && (this->threads > this->min_threads))
! 188: { /* terminate this thread by detaching it */
! 189: thread_t *thread = thread_current();
! 190:
! 191: this->threads--;
! 192: this->pool->remove(this->pool, thread, NULL);
! 193: this->mutex->unlock(this->mutex);
! 194: thread->detach(thread);
! 195: return NULL;
! 196: }
! 197: }
! 198: this->busy_threads++;
! 199: this->mutex->unlock(this->mutex);
! 200:
! 201: memset(&hints, 0, sizeof(hints));
! 202: hints.ai_family = query->family;
! 203: hints.ai_socktype = SOCK_DGRAM;
! 204:
! 205: thread_cleanup_push((thread_cleanup_t)query_signal_and_destroy, query);
! 206: old = thread_cancelability(TRUE);
! 207: error = getaddrinfo(query->name, NULL, &hints, &result);
! 208: thread_cancelability(old);
! 209: thread_cleanup_pop(FALSE);
! 210:
! 211: this->mutex->lock(this->mutex);
! 212: this->busy_threads--;
! 213: if (error != 0)
! 214: {
! 215: DBG1(DBG_LIB, "resolving '%s' failed: %s", query->name,
! 216: gai_strerror(error));
! 217: }
! 218: else
! 219: { /* result is a linked list, but we use only the first address */
! 220: query->result = host_create_from_sockaddr(result->ai_addr);
! 221: freeaddrinfo(result);
! 222: }
! 223: this->queries->remove(this->queries, query);
! 224: query->done->broadcast(query->done);
! 225: this->mutex->unlock(this->mutex);
! 226: query_destroy(query);
! 227: }
! 228: return NULL;
! 229: }
! 230:
! 231: METHOD(host_resolver_t, resolve, host_t*,
! 232: private_host_resolver_t *this, char *name, int family)
! 233: {
! 234: query_t *query, lookup = {
! 235: .name = name,
! 236: .family = family,
! 237: };
! 238: host_t *result;
! 239: struct in_addr addr;
! 240:
! 241: switch (family)
! 242: {
! 243: case AF_INET:
! 244: /* do not try to convert v6 addresses for v4 family */
! 245: if (strchr(name, ':'))
! 246: {
! 247: return NULL;
! 248: }
! 249: break;
! 250: case AF_INET6:
! 251: /* do not try to convert v4 addresses for v6 family */
! 252: if (inet_pton(AF_INET, name, &addr) == 1)
! 253: {
! 254: return NULL;
! 255: }
! 256: break;
! 257: }
! 258: this->mutex->lock(this->mutex);
! 259: if (this->disabled)
! 260: {
! 261: this->mutex->unlock(this->mutex);
! 262: return NULL;
! 263: }
! 264: query = this->queries->get(this->queries, &lookup);
! 265: if (!query)
! 266: {
! 267: INIT(query,
! 268: .name = strdup(name),
! 269: .family = family,
! 270: .done = condvar_create(CONDVAR_TYPE_DEFAULT),
! 271: .refcount = 1,
! 272: );
! 273: this->queries->put(this->queries, query, query);
! 274: this->queue->insert_last(this->queue, query);
! 275: this->new_query->signal(this->new_query);
! 276: }
! 277: ref_get(&query->refcount);
! 278: if (this->busy_threads == this->threads &&
! 279: this->threads < this->max_threads)
! 280: {
! 281: thread_t *thread;
! 282:
! 283: thread = thread_create((thread_main_t)resolve_hosts, this);
! 284: if (thread)
! 285: {
! 286: this->threads++;
! 287: this->pool->insert_last(this->pool, thread);
! 288: }
! 289: }
! 290: query->done->wait(query->done, this->mutex);
! 291: this->mutex->unlock(this->mutex);
! 292:
! 293: result = query->result ? query->result->clone(query->result) : NULL;
! 294: query_destroy(query);
! 295: return result;
! 296: }
! 297:
! 298: METHOD(host_resolver_t, flush, void,
! 299: private_host_resolver_t *this)
! 300: {
! 301: enumerator_t *enumerator;
! 302: query_t *query;
! 303:
! 304: this->mutex->lock(this->mutex);
! 305: enumerator = this->queries->create_enumerator(this->queries);
! 306: while (enumerator->enumerate(enumerator, &query, NULL))
! 307: { /* use the hashtable here as we also want to signal dequeued queries */
! 308: this->queries->remove_at(this->queries, enumerator);
! 309: query->done->broadcast(query->done);
! 310: }
! 311: enumerator->destroy(enumerator);
! 312: this->queue->destroy_function(this->queue, (void*)query_destroy);
! 313: this->queue = linked_list_create();
! 314: this->disabled = TRUE;
! 315: /* this will already terminate most idle threads */
! 316: this->new_query->broadcast(this->new_query);
! 317: this->mutex->unlock(this->mutex);
! 318: }
! 319:
! 320: METHOD(host_resolver_t, destroy, void,
! 321: private_host_resolver_t *this)
! 322: {
! 323: thread_t *thread;
! 324:
! 325: flush(this);
! 326: this->pool->invoke_offset(this->pool, offsetof(thread_t, cancel));
! 327: while (this->pool->remove_first(this->pool, (void**)&thread) == SUCCESS)
! 328: {
! 329: thread->join(thread);
! 330: }
! 331: this->pool->destroy(this->pool);
! 332: this->queue->destroy(this->queue);
! 333: this->queries->destroy(this->queries);
! 334: this->new_query->destroy(this->new_query);
! 335: this->mutex->destroy(this->mutex);
! 336: free(this);
! 337: }
! 338:
! 339: /*
! 340: * Described in header
! 341: */
! 342: host_resolver_t *host_resolver_create()
! 343: {
! 344: private_host_resolver_t *this;
! 345:
! 346: INIT(this,
! 347: .public = {
! 348: .resolve = _resolve,
! 349: .flush = _flush,
! 350: .destroy = _destroy,
! 351: },
! 352: .queries = hashtable_create((hashtable_hash_t)query_hash,
! 353: (hashtable_equals_t)query_equals, 8),
! 354: .queue = linked_list_create(),
! 355: .pool = linked_list_create(),
! 356: .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
! 357: .new_query = condvar_create(CONDVAR_TYPE_DEFAULT),
! 358: );
! 359:
! 360: this->min_threads = max(0, lib->settings->get_int(lib->settings,
! 361: "%s.host_resolver.min_threads",
! 362: MIN_THREADS_DEFAULT, lib->ns));
! 363: this->max_threads = max(this->min_threads ?: 1,
! 364: lib->settings->get_int(lib->settings,
! 365: "%s.host_resolver.max_threads",
! 366: MAX_THREADS_DEFAULT, lib->ns));
! 367: return &this->public;
! 368: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>