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>