Annotation of embedaddon/strongswan/src/libstrongswan/networking/host_resolver.c, revision 1.1

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:        }
        !           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: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>