Annotation of embedaddon/strongswan/src/libcharon/plugins/resolve/resolve_handler.c, revision 1.1.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>