2 * Copyright (C) 2012 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
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>.
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
16 #include <sys/types.h>
17 #include <sys/socket.h>
20 #include "host_resolver.h"
23 #include <utils/debug.h>
24 #include <threading/condvar.h>
25 #include <threading/mutex.h>
26 #include <threading/thread.h>
27 #include <collections/hashtable.h>
28 #include <collections/linked_list.h>
31 * Default minimum and maximum number of threads
33 #define MIN_THREADS_DEFAULT 0
34 #define MAX_THREADS_DEFAULT 3
37 * Timeout in seconds to wait for new queries until a thread may be stopped
39 #define NEW_QUERY_WAIT_TIMEOUT 30
41 typedef struct private_host_resolver_t private_host_resolver_t
;
44 * Private data of host_resolver_t
46 struct private_host_resolver_t
{
51 host_resolver_t
public;
54 * Hashtable to check for queued queries, query_t*
59 * Queue for queries, query_t*
64 * Mutex to safely access private data
69 * Condvar to signal arrival of new queries
74 * Minimum number of resolver threads
79 * Maximum number of resolver threads
84 * Current number of threads
89 * Current number of busy threads
94 * Pool of threads, thread_t*
99 * TRUE if no new queries are accepted
106 /** DNS name we are looking for */
108 /** address family we request */
110 /** Condvar to signal completion of a query */
114 /** the result if successful */
119 * Destroy the given query_t object if refcount is zero
121 static void query_destroy(query_t
*this)
123 if (ref_put(&this->refcount
))
125 DESTROY_IF(this->result
);
126 this->done
->destroy(this->done
);
133 * Signals all waiting threads and destroys the query
135 static void query_signal_and_destroy(query_t
*this)
137 this->done
->broadcast(this->done
);
142 * Hash a queued query
144 static u_int
query_hash(query_t
*this)
146 return chunk_hash_inc(chunk_create(this->name
, strlen(this->name
)),
147 chunk_hash(chunk_from_thing(this->family
)));
151 * Compare two queued queries
153 static bool query_equals(query_t
*this, query_t
*other
)
155 return this->family
== other
->family
&& streq(this->name
, other
->name
);
159 * Main function of resolver threads
161 static void *resolve_hosts(private_host_resolver_t
*this)
163 struct addrinfo hints
, *result
;
170 this->mutex
->lock(this->mutex
);
171 thread_cleanup_push((thread_cleanup_t
)this->mutex
->unlock
, this->mutex
);
172 while (this->queue
->remove_first(this->queue
,
173 (void**)&query
) != SUCCESS
)
175 old
= thread_cancelability(TRUE
);
176 timed_out
= this->new_query
->timed_wait(this->new_query
,
177 this->mutex
, NEW_QUERY_WAIT_TIMEOUT
* 1000);
178 thread_cancelability(old
);
181 thread_cleanup_pop(TRUE
);
184 else if (timed_out
&& (this->threads
> this->min_threads
))
185 { /* terminate this thread by detaching it */
186 thread_t
*thread
= thread_current();
189 this->pool
->remove(this->pool
, thread
, NULL
);
190 thread_cleanup_pop(TRUE
);
191 thread
->detach(thread
);
195 this->busy_threads
++;
196 thread_cleanup_pop(TRUE
);
198 memset(&hints
, 0, sizeof(hints
));
199 hints
.ai_family
= query
->family
;
200 hints
.ai_socktype
= SOCK_DGRAM
;
202 thread_cleanup_push((thread_cleanup_t
)query_signal_and_destroy
, query
);
203 old
= thread_cancelability(TRUE
);
204 error
= getaddrinfo(query
->name
, NULL
, &hints
, &result
);
205 thread_cancelability(old
);
206 thread_cleanup_pop(FALSE
);
208 this->mutex
->lock(this->mutex
);
209 this->busy_threads
--;
212 DBG1(DBG_LIB
, "resolving '%s' failed: %s", query
->name
,
213 gai_strerror(error
));
216 { /* result is a linked list, but we use only the first address */
217 query
->result
= host_create_from_sockaddr(result
->ai_addr
);
218 freeaddrinfo(result
);
220 this->queries
->remove(this->queries
, query
);
221 query
->done
->broadcast(query
->done
);
222 this->mutex
->unlock(this->mutex
);
223 query_destroy(query
);
228 METHOD(host_resolver_t
, resolve
, host_t
*,
229 private_host_resolver_t
*this, char *name
, int family
)
231 query_t
*query
, lookup
= {
237 if (streq(name
, "%any") || streq(name
, "0.0.0.0"))
239 return host_create_any(family ? family
: AF_INET
);
241 if (streq(name
, "%any6") || streq(name
, "::"))
243 return host_create_any(family ? family
: AF_INET6
);
245 if (family
== AF_INET
&& strchr(name
, ':'))
246 { /* do not try to convert v6 addresses for v4 family */
249 this->mutex
->lock(this->mutex
);
252 this->mutex
->unlock(this->mutex
);
255 query
= this->queries
->get(this->queries
, &lookup
);
259 .name
= strdup(name
),
261 .done
= condvar_create(CONDVAR_TYPE_DEFAULT
),
264 this->queries
->put(this->queries
, query
, query
);
265 this->queue
->insert_last(this->queue
, query
);
266 this->new_query
->signal(this->new_query
);
268 ref_get(&query
->refcount
);
269 if (this->busy_threads
== this->threads
&&
270 this->threads
< this->max_threads
)
274 thread
= thread_create((thread_main_t
)resolve_hosts
, this);
278 this->pool
->insert_last(this->pool
, thread
);
281 query
->done
->wait(query
->done
, this->mutex
);
282 this->mutex
->unlock(this->mutex
);
284 result
= query
->result ? query
->result
->clone(query
->result
) : NULL
;
285 query_destroy(query
);
289 METHOD(host_resolver_t
, flush
, void,
290 private_host_resolver_t
*this)
292 enumerator_t
*enumerator
;
295 this->mutex
->lock(this->mutex
);
296 enumerator
= this->queries
->create_enumerator(this->queries
);
297 while (enumerator
->enumerate(enumerator
, &query
, NULL
))
298 { /* use the hashtable here as we also want to signal dequeued queries */
299 this->queries
->remove_at(this->queries
, enumerator
);
300 query
->done
->broadcast(query
->done
);
302 enumerator
->destroy(enumerator
);
303 this->queue
->destroy_function(this->queue
, (void*)query_destroy
);
304 this->queue
= linked_list_create();
305 this->disabled
= TRUE
;
306 /* this will already terminate most idle threads */
307 this->new_query
->broadcast(this->new_query
);
308 this->mutex
->unlock(this->mutex
);
311 METHOD(host_resolver_t
, destroy
, void,
312 private_host_resolver_t
*this)
317 this->pool
->invoke_offset(this->pool
, offsetof(thread_t
, cancel
));
318 while (this->pool
->remove_first(this->pool
, (void**)&thread
) == SUCCESS
)
320 thread
->join(thread
);
322 this->pool
->destroy(this->pool
);
323 this->queue
->destroy(this->queue
);
324 this->queries
->destroy(this->queries
);
325 this->new_query
->destroy(this->new_query
);
326 this->mutex
->destroy(this->mutex
);
331 * Described in header
333 host_resolver_t
*host_resolver_create()
335 private_host_resolver_t
*this;
343 .queries
= hashtable_create((hashtable_hash_t
)query_hash
,
344 (hashtable_equals_t
)query_equals
, 8),
345 .queue
= linked_list_create(),
346 .pool
= linked_list_create(),
347 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
348 .new_query
= condvar_create(CONDVAR_TYPE_DEFAULT
),
351 this->min_threads
= max(0, lib
->settings
->get_int(lib
->settings
,
352 "libstrongswan.host_resolver.min_threads",
353 MIN_THREADS_DEFAULT
));
354 this->max_threads
= max(this->min_threads ?
: 1,
355 lib
->settings
->get_int(lib
->settings
,
356 "libstrongswan.host_resolver.max_threads",
357 MAX_THREADS_DEFAULT
));
358 return &this->public;