Annotation of embedaddon/strongswan/src/libstrongswan/networking/host_resolver.c, revision 1.1.1.2
1.1 misho 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: }
1.1.1.2 ! misho 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: }
1.1 misho 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>