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>