Annotation of embedaddon/strongswan/src/libcharon/plugins/resolve/resolve_handler.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2012-2016 Tobias Brunner
        !             3:  * Copyright (C) 2009 Martin Willi
        !             4:  * HSR Hochschule fuer Technik Rapperswil
        !             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 "resolve_handler.h"
        !            18: 
        !            19: #include <sys/types.h>
        !            20: #include <sys/stat.h>
        !            21: #include <unistd.h>
        !            22: 
        !            23: #include <utils/debug.h>
        !            24: #include <utils/process.h>
        !            25: #include <collections/array.h>
        !            26: #include <threading/mutex.h>
        !            27: 
        !            28: /* path to resolvconf executable */
        !            29: #define RESOLVCONF_EXEC "/sbin/resolvconf"
        !            30: 
        !            31: /* default prefix used for resolvconf interfaces (should have high prio) */
        !            32: #define RESOLVCONF_PREFIX "lo.inet.ipsec."
        !            33: 
        !            34: typedef struct private_resolve_handler_t private_resolve_handler_t;
        !            35: 
        !            36: /**
        !            37:  * Private data of an resolve_handler_t object.
        !            38:  */
        !            39: struct private_resolve_handler_t {
        !            40: 
        !            41:        /**
        !            42:         * Public resolve_handler_t interface.
        !            43:         */
        !            44:        resolve_handler_t public;
        !            45: 
        !            46:        /**
        !            47:         * resolv.conf file to use
        !            48:         */
        !            49:        char *file;
        !            50: 
        !            51:        /**
        !            52:         * Use resolvconf instead of writing directly to resolv.conf
        !            53:         */
        !            54:        bool use_resolvconf;
        !            55: 
        !            56:        /**
        !            57:         * Prefix to be used for interface names sent to resolvconf
        !            58:         */
        !            59:        char *iface_prefix;
        !            60: 
        !            61:        /**
        !            62:         * Mutex to access file exclusively
        !            63:         */
        !            64:        mutex_t *mutex;
        !            65: 
        !            66:        /**
        !            67:         * Reference counting for DNS servers dns_server_t
        !            68:         */
        !            69:        array_t *servers;
        !            70: };
        !            71: 
        !            72: /**
        !            73:  * Reference counting for DNS servers
        !            74:  */
        !            75: typedef struct {
        !            76: 
        !            77:        /**
        !            78:         * DNS server address
        !            79:         */
        !            80:        host_t *server;
        !            81: 
        !            82:        /**
        !            83:         * Reference count
        !            84:         */
        !            85:        u_int refcount;
        !            86: 
        !            87: } dns_server_t;
        !            88: 
        !            89: /**
        !            90:  * Compare a server and a stored reference
        !            91:  */
        !            92: static int dns_server_find(const void *a, const void *b)
        !            93: {
        !            94:        host_t *server = (host_t*)a;
        !            95:        dns_server_t *item = (dns_server_t*)b;
        !            96:        return chunk_compare(server->get_address(server),
        !            97:                                                 item->server->get_address(item->server));
        !            98: }
        !            99: 
        !           100: /**
        !           101:  * Sort references by DNS server
        !           102:  */
        !           103: static int dns_server_sort(const void *a, const void *b, void *user)
        !           104: {
        !           105:        const dns_server_t *da = a, *db = b;
        !           106:        return chunk_compare(da->server->get_address(da->server),
        !           107:                                                 db->server->get_address(db->server));
        !           108: }
        !           109: 
        !           110: /**
        !           111:  * Writes the given nameserver to resolv.conf
        !           112:  */
        !           113: static bool write_nameserver(private_resolve_handler_t *this, host_t *addr)
        !           114: {
        !           115:        FILE *in, *out;
        !           116:        char buf[1024];
        !           117:        size_t len;
        !           118:        bool handled = FALSE;
        !           119: 
        !           120:        in = fopen(this->file, "r");
        !           121:        /* allows us to stream from in to out */
        !           122:        unlink(this->file);
        !           123:        out = fopen(this->file, "w");
        !           124:        if (out)
        !           125:        {
        !           126:                fprintf(out, "nameserver %H   # by strongSwan\n", addr);
        !           127:                DBG1(DBG_IKE, "installing DNS server %H to %s", addr, this->file);
        !           128:                handled = TRUE;
        !           129: 
        !           130:                /* copy rest of the file */
        !           131:                if (in)
        !           132:                {
        !           133:                        while ((len = fread(buf, 1, sizeof(buf), in)))
        !           134:                        {
        !           135:                                ignore_result(fwrite(buf, 1, len, out));
        !           136:                        }
        !           137:                }
        !           138:                fclose(out);
        !           139:        }
        !           140:        if (in)
        !           141:        {
        !           142:                fclose(in);
        !           143:        }
        !           144:        return handled;
        !           145: }
        !           146: 
        !           147: /**
        !           148:  * Removes the given nameserver from resolv.conf
        !           149:  */
        !           150: static void remove_nameserver(private_resolve_handler_t *this, host_t *addr)
        !           151: {
        !           152:        FILE *in, *out;
        !           153:        char line[1024], matcher[512];
        !           154: 
        !           155:        in = fopen(this->file, "r");
        !           156:        if (in)
        !           157:        {
        !           158:                /* allows us to stream from in to out */
        !           159:                unlink(this->file);
        !           160:                out = fopen(this->file, "w");
        !           161:                if (out)
        !           162:                {
        !           163:                        snprintf(matcher, sizeof(matcher),
        !           164:                                         "nameserver %H   # by strongSwan\n", addr);
        !           165: 
        !           166:                        /* copy all, but matching line */
        !           167:                        while (fgets(line, sizeof(line), in))
        !           168:                        {
        !           169:                                if (strpfx(line, matcher))
        !           170:                                {
        !           171:                                        DBG1(DBG_IKE, "removing DNS server %H from %s",
        !           172:                                                 addr, this->file);
        !           173:                                }
        !           174:                                else
        !           175:                                {
        !           176:                                        fputs(line, out);
        !           177:                                }
        !           178:                        }
        !           179:                        fclose(out);
        !           180:                }
        !           181:                fclose(in);
        !           182:        }
        !           183: }
        !           184: 
        !           185: /**
        !           186:  * Add or remove the given nameserver by invoking resolvconf.
        !           187:  */
        !           188: static bool invoke_resolvconf(private_resolve_handler_t *this, host_t *addr,
        !           189:                                                          bool install)
        !           190: {
        !           191:        process_t *process;
        !           192:        FILE *shell;
        !           193:        int in, out, retval;
        !           194: 
        !           195:        /* we use the nameserver's IP address as part of the interface name to
        !           196:         * make them unique */
        !           197:        process = process_start_shell(NULL, install ? &in : NULL, &out, NULL,
        !           198:                                                        "2>&1 %s %s %s%H", RESOLVCONF_EXEC,
        !           199:                                                        install ? "-a" : "-d", this->iface_prefix, addr);
        !           200: 
        !           201:        if (!process)
        !           202:        {
        !           203:                return FALSE;
        !           204:        }
        !           205:        if (install)
        !           206:        {
        !           207:                shell = fdopen(in, "w");
        !           208:                if (shell)
        !           209:                {
        !           210:                        DBG1(DBG_IKE, "installing DNS server %H via resolvconf", addr);
        !           211:                        fprintf(shell, "nameserver %H\n", addr);
        !           212:                        fclose(shell);
        !           213:                }
        !           214:                else
        !           215:                {
        !           216:                        close(in);
        !           217:                        close(out);
        !           218:                        process->wait(process, NULL);
        !           219:                        return FALSE;
        !           220:                }
        !           221:        }
        !           222:        else
        !           223:        {
        !           224:                DBG1(DBG_IKE, "removing DNS server %H via resolvconf", addr);
        !           225:        }
        !           226:        shell = fdopen(out, "r");
        !           227:        if (shell)
        !           228:        {
        !           229:                while (TRUE)
        !           230:                {
        !           231:                        char resp[128], *e;
        !           232: 
        !           233:                        if (fgets(resp, sizeof(resp), shell) == NULL)
        !           234:                        {
        !           235:                                if (ferror(shell))
        !           236:                                {
        !           237:                                        DBG1(DBG_IKE, "error reading from resolvconf");
        !           238:                                }
        !           239:                                break;
        !           240:                        }
        !           241:                        else
        !           242:                        {
        !           243:                                e = resp + strlen(resp);
        !           244:                                if (e > resp && e[-1] == '\n')
        !           245:                                {
        !           246:                                        e[-1] = '\0';
        !           247:                                }
        !           248:                                DBG1(DBG_IKE, "resolvconf: %s", resp);
        !           249:                        }
        !           250:                }
        !           251:                fclose(shell);
        !           252:        }
        !           253:        else
        !           254:        {
        !           255:                close(out);
        !           256:        }
        !           257:        if (!process->wait(process, &retval) || retval != EXIT_SUCCESS)
        !           258:        {
        !           259:                if (install)
        !           260:                {       /* revert changes when installing fails */
        !           261:                        invoke_resolvconf(this, addr, FALSE);
        !           262:                        return FALSE;
        !           263:                }
        !           264:        }
        !           265:        return TRUE;
        !           266: }
        !           267: 
        !           268: METHOD(attribute_handler_t, handle, bool,
        !           269:        private_resolve_handler_t *this, ike_sa_t *ike_sa,
        !           270:        configuration_attribute_type_t type, chunk_t data)
        !           271: {
        !           272:        dns_server_t *found = NULL;
        !           273:        host_t *addr;
        !           274:        bool handled;
        !           275: 
        !           276:        switch (type)
        !           277:        {
        !           278:                case INTERNAL_IP4_DNS:
        !           279:                        addr = host_create_from_chunk(AF_INET, data, 0);
        !           280:                        break;
        !           281:                case INTERNAL_IP6_DNS:
        !           282:                        addr = host_create_from_chunk(AF_INET6, data, 0);
        !           283:                        break;
        !           284:                default:
        !           285:                        return FALSE;
        !           286:        }
        !           287: 
        !           288:        if (!addr || addr->is_anyaddr(addr))
        !           289:        {
        !           290:                DESTROY_IF(addr);
        !           291:                return FALSE;
        !           292:        }
        !           293: 
        !           294:        this->mutex->lock(this->mutex);
        !           295:        if (array_bsearch(this->servers, addr, dns_server_find, &found) == -1)
        !           296:        {
        !           297:                if (this->use_resolvconf)
        !           298:                {
        !           299:                        handled = invoke_resolvconf(this, addr, TRUE);
        !           300:                }
        !           301:                else
        !           302:                {
        !           303:                        handled = write_nameserver(this, addr);
        !           304:                }
        !           305:                if (handled)
        !           306:                {
        !           307:                        INIT(found,
        !           308:                                .server = addr->clone(addr),
        !           309:                                .refcount = 1,
        !           310:                        );
        !           311:                        array_insert_create(&this->servers, ARRAY_TAIL, found);
        !           312:                        array_sort(this->servers, dns_server_sort, NULL);
        !           313:                }
        !           314:        }
        !           315:        else
        !           316:        {
        !           317:                DBG1(DBG_IKE, "DNS server %H already installed, increasing refcount",
        !           318:                         addr);
        !           319:                found->refcount++;
        !           320:                handled = TRUE;
        !           321:        }
        !           322:        this->mutex->unlock(this->mutex);
        !           323:        addr->destroy(addr);
        !           324: 
        !           325:        if (!handled)
        !           326:        {
        !           327:                DBG1(DBG_IKE, "adding DNS server failed");
        !           328:        }
        !           329:        return handled;
        !           330: }
        !           331: 
        !           332: METHOD(attribute_handler_t, release, void,
        !           333:        private_resolve_handler_t *this, ike_sa_t *ike_sa,
        !           334:        configuration_attribute_type_t type, chunk_t data)
        !           335: {
        !           336:        dns_server_t *found = NULL;
        !           337:        host_t *addr;
        !           338:        int family, idx;
        !           339: 
        !           340:        switch (type)
        !           341:        {
        !           342:                case INTERNAL_IP4_DNS:
        !           343:                        family = AF_INET;
        !           344:                        break;
        !           345:                case INTERNAL_IP6_DNS:
        !           346:                        family = AF_INET6;
        !           347:                        break;
        !           348:                default:
        !           349:                        return;
        !           350:        }
        !           351:        addr = host_create_from_chunk(family, data, 0);
        !           352: 
        !           353:        this->mutex->lock(this->mutex);
        !           354:        idx = array_bsearch(this->servers, addr, dns_server_find, &found);
        !           355:        if (idx != -1)
        !           356:        {
        !           357:                if (--found->refcount > 0)
        !           358:                {
        !           359:                        DBG1(DBG_IKE, "DNS server %H still used, decreasing refcount",
        !           360:                                 addr);
        !           361:                }
        !           362:                else
        !           363:                {
        !           364:                        if (this->use_resolvconf)
        !           365:                        {
        !           366:                                invoke_resolvconf(this, addr, FALSE);
        !           367:                        }
        !           368:                        else
        !           369:                        {
        !           370:                                remove_nameserver(this, addr);
        !           371:                        }
        !           372:                        array_remove(this->servers, idx, NULL);
        !           373:                        found->server->destroy(found->server);
        !           374:                        free(found);
        !           375:                }
        !           376:        }
        !           377:        this->mutex->unlock(this->mutex);
        !           378: 
        !           379:        addr->destroy(addr);
        !           380: }
        !           381: 
        !           382: /**
        !           383:  * Attribute enumerator implementation
        !           384:  */
        !           385: typedef struct {
        !           386:        /** implements enumerator_t interface */
        !           387:        enumerator_t public;
        !           388:        /** request IPv4 DNS? */
        !           389:        bool v4;
        !           390:        /** request IPv6 DNS? */
        !           391:        bool v6;
        !           392: } attribute_enumerator_t;
        !           393: 
        !           394: METHOD(enumerator_t, attribute_enumerate, bool,
        !           395:        attribute_enumerator_t *this, va_list args)
        !           396: {
        !           397:        configuration_attribute_type_t *type;
        !           398:        chunk_t *data;
        !           399: 
        !           400:        VA_ARGS_VGET(args, type, data);
        !           401:        if (this->v4)
        !           402:        {
        !           403:                *type = INTERNAL_IP4_DNS;
        !           404:                *data = chunk_empty;
        !           405:                this->v4 = FALSE;
        !           406:                return TRUE;
        !           407:        }
        !           408:        if (this->v6)
        !           409:        {
        !           410:                *type = INTERNAL_IP6_DNS;
        !           411:                *data = chunk_empty;
        !           412:                this->v6 = FALSE;
        !           413:                return TRUE;
        !           414:        }
        !           415:        return FALSE;
        !           416: }
        !           417: 
        !           418: /**
        !           419:  * Check if a list has a host of given family
        !           420:  */
        !           421: static bool has_host_family(linked_list_t *list, int family)
        !           422: {
        !           423:        enumerator_t *enumerator;
        !           424:        host_t *host;
        !           425:        bool found = FALSE;
        !           426: 
        !           427:        enumerator = list->create_enumerator(list);
        !           428:        while (enumerator->enumerate(enumerator, &host))
        !           429:        {
        !           430:                if (host->get_family(host) == family)
        !           431:                {
        !           432:                        found = TRUE;
        !           433:                        break;
        !           434:                }
        !           435:        }
        !           436:        enumerator->destroy(enumerator);
        !           437: 
        !           438:        return found;
        !           439: }
        !           440: 
        !           441: METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*,
        !           442:        private_resolve_handler_t *this, ike_sa_t *ike_sa,
        !           443:        linked_list_t *vips)
        !           444: {
        !           445:        attribute_enumerator_t *enumerator;
        !           446: 
        !           447:        INIT(enumerator,
        !           448:                .public = {
        !           449:                        .enumerate = enumerator_enumerate_default,
        !           450:                        .venumerate = _attribute_enumerate,
        !           451:                        .destroy = (void*)free,
        !           452:                },
        !           453:                .v4 = has_host_family(vips, AF_INET),
        !           454:                .v6 = has_host_family(vips, AF_INET6),
        !           455:        );
        !           456:        return &enumerator->public;
        !           457: }
        !           458: 
        !           459: METHOD(resolve_handler_t, destroy, void,
        !           460:        private_resolve_handler_t *this)
        !           461: {
        !           462:        array_destroy(this->servers);
        !           463:        this->mutex->destroy(this->mutex);
        !           464:        free(this);
        !           465: }
        !           466: 
        !           467: /**
        !           468:  * See header
        !           469:  */
        !           470: resolve_handler_t *resolve_handler_create()
        !           471: {
        !           472:        private_resolve_handler_t *this;
        !           473:        struct stat st;
        !           474: 
        !           475:        INIT(this,
        !           476:                .public = {
        !           477:                        .handler = {
        !           478:                                .handle = _handle,
        !           479:                                .release = _release,
        !           480:                                .create_attribute_enumerator = _create_attribute_enumerator,
        !           481:                        },
        !           482:                        .destroy = _destroy,
        !           483:                },
        !           484:                .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
        !           485:                .file = lib->settings->get_str(lib->settings, "%s.plugins.resolve.file",
        !           486:                                                                           RESOLV_CONF, lib->ns),
        !           487:        );
        !           488: 
        !           489:        if (stat(RESOLVCONF_EXEC, &st) == 0)
        !           490:        {
        !           491:                this->use_resolvconf = TRUE;
        !           492:                this->iface_prefix = lib->settings->get_str(lib->settings,
        !           493:                                                                "%s.plugins.resolve.resolvconf.iface_prefix",
        !           494:                                                                RESOLVCONF_PREFIX, lib->ns);
        !           495:        }
        !           496: 
        !           497:        return &this->public;
        !           498: }

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