Use Botan 2.12.0 for tests
[strongswan.git] / src / libstrongswan / networking / host_resolver.c
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 }