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