File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libstrongswan / networking / host_resolver.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:20:08 2021 UTC (3 years, 5 months ago) by misho
Branches: strongswan, MAIN
CVS tags: v5_9_2p0, HEAD
strongswan 5.9.2

    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: 	if (this->threads)
  291: 	{
  292: 		query->done->wait(query->done, this->mutex);
  293: 	}
  294: 	else
  295: 	{
  296: 		DBG1(DBG_LIB, "resolving '%s' failed: no resolver threads", query->name);
  297: 		/* this should always be the case if we end up here, but make sure */
  298: 		if (query->refcount == 1)
  299: 		{
  300: 			this->queries->remove(this->queries, query);
  301: 			this->queue->remove_last(this->queue, (void**)&query);
  302: 		}
  303: 	}
  304: 	this->mutex->unlock(this->mutex);
  305: 
  306: 	result = query->result ? query->result->clone(query->result) : NULL;
  307: 	query_destroy(query);
  308: 	return result;
  309: }
  310: 
  311: METHOD(host_resolver_t, flush, void,
  312: 	private_host_resolver_t *this)
  313: {
  314: 	enumerator_t *enumerator;
  315: 	query_t *query;
  316: 
  317: 	this->mutex->lock(this->mutex);
  318: 	enumerator = this->queries->create_enumerator(this->queries);
  319: 	while (enumerator->enumerate(enumerator, &query, NULL))
  320: 	{	/* use the hashtable here as we also want to signal dequeued queries */
  321: 		this->queries->remove_at(this->queries, enumerator);
  322: 		query->done->broadcast(query->done);
  323: 	}
  324: 	enumerator->destroy(enumerator);
  325: 	this->queue->destroy_function(this->queue, (void*)query_destroy);
  326: 	this->queue = linked_list_create();
  327: 	this->disabled = TRUE;
  328: 	/* this will already terminate most idle threads */
  329: 	this->new_query->broadcast(this->new_query);
  330: 	this->mutex->unlock(this->mutex);
  331: }
  332: 
  333: METHOD(host_resolver_t, destroy, void,
  334: 	private_host_resolver_t *this)
  335: {
  336: 	thread_t *thread;
  337: 
  338: 	flush(this);
  339: 	this->pool->invoke_offset(this->pool, offsetof(thread_t, cancel));
  340: 	while (this->pool->remove_first(this->pool, (void**)&thread) == SUCCESS)
  341: 	{
  342: 		thread->join(thread);
  343: 	}
  344: 	this->pool->destroy(this->pool);
  345: 	this->queue->destroy(this->queue);
  346: 	this->queries->destroy(this->queries);
  347: 	this->new_query->destroy(this->new_query);
  348: 	this->mutex->destroy(this->mutex);
  349: 	free(this);
  350: }
  351: 
  352: /*
  353:  * Described in header
  354:  */
  355: host_resolver_t *host_resolver_create()
  356: {
  357: 	private_host_resolver_t *this;
  358: 
  359: 	INIT(this,
  360: 		.public = {
  361: 			.resolve = _resolve,
  362: 			.flush = _flush,
  363: 			.destroy = _destroy,
  364: 		},
  365: 		.queries = hashtable_create((hashtable_hash_t)query_hash,
  366: 									(hashtable_equals_t)query_equals, 8),
  367: 		.queue = linked_list_create(),
  368: 		.pool = linked_list_create(),
  369: 		.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
  370: 		.new_query = condvar_create(CONDVAR_TYPE_DEFAULT),
  371: 	);
  372: 
  373: 	this->min_threads = max(0, lib->settings->get_int(lib->settings,
  374: 												"%s.host_resolver.min_threads",
  375: 												MIN_THREADS_DEFAULT, lib->ns));
  376: 	this->max_threads = max(this->min_threads ?: 1,
  377: 							lib->settings->get_int(lib->settings,
  378: 												"%s.host_resolver.max_threads",
  379: 												MAX_THREADS_DEFAULT, lib->ns));
  380: 	return &this->public;
  381: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>