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>