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