Return to resolve_handler.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libcharon / plugins / resolve |
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: }