2 * Copyright (C) 2012-2016 Tobias Brunner
3 * Copyright (C) 2009 Martin Willi
5 * Copyright (C) secunet Security Networks AG
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 #include "resolve_handler.h"
20 #include <sys/types.h>
24 #include <utils/debug.h>
25 #include <utils/process.h>
26 #include <collections/array.h>
27 #include <threading/mutex.h>
29 /* path to resolvconf executable */
30 #define RESOLVCONF_EXEC "/sbin/resolvconf"
32 /* default prefix used for resolvconf interfaces (should have high prio) */
33 #define RESOLVCONF_PREFIX "lo.inet.ipsec."
35 typedef struct private_resolve_handler_t private_resolve_handler_t
;
38 * Private data of an resolve_handler_t object.
40 struct private_resolve_handler_t
{
43 * Public resolve_handler_t interface.
45 resolve_handler_t
public;
48 * resolv.conf file to use
53 * Use resolvconf instead of writing directly to resolv.conf
58 * Prefix to be used for interface names sent to resolvconf
63 * Mutex to access file exclusively
68 * Reference counting for DNS servers dns_server_t
74 * Reference counting for DNS servers
91 * Compare a server and a stored reference
93 static int dns_server_find(const void *a
, const void *b
)
95 host_t
*server
= (host_t
*)a
;
96 dns_server_t
*item
= (dns_server_t
*)b
;
97 return chunk_compare(server
->get_address(server
),
98 item
->server
->get_address(item
->server
));
102 * Sort references by DNS server
104 static int dns_server_sort(const void *a
, const void *b
, void *user
)
106 const dns_server_t
*da
= a
, *db
= b
;
107 return chunk_compare(da
->server
->get_address(da
->server
),
108 db
->server
->get_address(db
->server
));
112 * Writes the given nameserver to resolv.conf
114 static bool write_nameserver(private_resolve_handler_t
*this, host_t
*addr
)
119 bool handled
= FALSE
;
121 in
= fopen(this->file
, "r");
122 /* allows us to stream from in to out */
124 out
= fopen(this->file
, "w");
127 fprintf(out
, "nameserver %H # by strongSwan\n", addr
);
128 DBG1(DBG_IKE
, "installing DNS server %H to %s", addr
, this->file
);
131 /* copy rest of the file */
134 while ((len
= fread(buf
, 1, sizeof(buf
), in
)))
136 ignore_result(fwrite(buf
, 1, len
, out
));
149 * Removes the given nameserver from resolv.conf
151 static void remove_nameserver(private_resolve_handler_t
*this, host_t
*addr
)
154 char line
[1024], matcher
[512];
156 in
= fopen(this->file
, "r");
159 /* allows us to stream from in to out */
161 out
= fopen(this->file
, "w");
164 snprintf(matcher
, sizeof(matcher
),
165 "nameserver %H # by strongSwan\n", addr
);
167 /* copy all, but matching line */
168 while (fgets(line
, sizeof(line
), in
))
170 if (strpfx(line
, matcher
))
172 DBG1(DBG_IKE
, "removing DNS server %H from %s",
187 * Add or remove the given nameserver by invoking resolvconf.
189 static bool invoke_resolvconf(private_resolve_handler_t
*this, host_t
*addr
,
196 /* we use the nameserver's IP address as part of the interface name to
197 * make them unique */
198 process
= process_start_shell(NULL
, install
? &in
: NULL
, &out
, NULL
,
199 "2>&1 %s %s %s%H", RESOLVCONF_EXEC
,
200 install
? "-a" : "-d", this->iface_prefix
, addr
);
208 shell
= fdopen(in
, "w");
211 DBG1(DBG_IKE
, "installing DNS server %H via resolvconf", addr
);
212 fprintf(shell
, "nameserver %H\n", addr
);
219 process
->wait(process
, NULL
);
225 DBG1(DBG_IKE
, "removing DNS server %H via resolvconf", addr
);
227 shell
= fdopen(out
, "r");
234 if (fgets(resp
, sizeof(resp
), shell
) == NULL
)
238 DBG1(DBG_IKE
, "error reading from resolvconf");
244 e
= resp
+ strlen(resp
);
245 if (e
> resp
&& e
[-1] == '\n')
249 DBG1(DBG_IKE
, "resolvconf: %s", resp
);
258 if (!process
->wait(process
, &retval
) || retval
!= EXIT_SUCCESS
)
261 { /* revert changes when installing fails */
262 invoke_resolvconf(this, addr
, FALSE
);
269 METHOD(attribute_handler_t
, handle
, bool,
270 private_resolve_handler_t
*this, ike_sa_t
*ike_sa
,
271 configuration_attribute_type_t type
, chunk_t data
)
273 dns_server_t
*found
= NULL
;
279 case INTERNAL_IP4_DNS
:
280 addr
= host_create_from_chunk(AF_INET
, data
, 0);
282 case INTERNAL_IP6_DNS
:
283 addr
= host_create_from_chunk(AF_INET6
, data
, 0);
289 if (!addr
|| addr
->is_anyaddr(addr
))
295 this->mutex
->lock(this->mutex
);
296 if (array_bsearch(this->servers
, addr
, dns_server_find
, &found
) == -1)
298 if (this->use_resolvconf
)
300 handled
= invoke_resolvconf(this, addr
, TRUE
);
304 handled
= write_nameserver(this, addr
);
309 .server
= addr
->clone(addr
),
312 array_insert_create(&this->servers
, ARRAY_TAIL
, found
);
313 array_sort(this->servers
, dns_server_sort
, NULL
);
318 DBG1(DBG_IKE
, "DNS server %H already installed, increasing refcount",
323 this->mutex
->unlock(this->mutex
);
328 DBG1(DBG_IKE
, "adding DNS server failed");
333 METHOD(attribute_handler_t
, release
, void,
334 private_resolve_handler_t
*this, ike_sa_t
*ike_sa
,
335 configuration_attribute_type_t type
, chunk_t data
)
337 dns_server_t
*found
= NULL
;
343 case INTERNAL_IP4_DNS
:
346 case INTERNAL_IP6_DNS
:
352 addr
= host_create_from_chunk(family
, data
, 0);
354 this->mutex
->lock(this->mutex
);
355 idx
= array_bsearch(this->servers
, addr
, dns_server_find
, &found
);
358 if (--found
->refcount
> 0)
360 DBG1(DBG_IKE
, "DNS server %H still used, decreasing refcount",
365 if (this->use_resolvconf
)
367 invoke_resolvconf(this, addr
, FALSE
);
371 remove_nameserver(this, addr
);
373 array_remove(this->servers
, idx
, NULL
);
374 found
->server
->destroy(found
->server
);
378 this->mutex
->unlock(this->mutex
);
384 * Attribute enumerator implementation
387 /** implements enumerator_t interface */
389 /** request IPv4 DNS? */
391 /** request IPv6 DNS? */
393 } attribute_enumerator_t
;
395 METHOD(enumerator_t
, attribute_enumerate
, bool,
396 attribute_enumerator_t
*this, va_list args
)
398 configuration_attribute_type_t
*type
;
401 VA_ARGS_VGET(args
, type
, data
);
404 *type
= INTERNAL_IP4_DNS
;
411 *type
= INTERNAL_IP6_DNS
;
420 * Check if a list has a host of given family
422 static bool has_host_family(linked_list_t
*list
, int family
)
424 enumerator_t
*enumerator
;
428 enumerator
= list
->create_enumerator(list
);
429 while (enumerator
->enumerate(enumerator
, &host
))
431 if (host
->get_family(host
) == family
)
437 enumerator
->destroy(enumerator
);
442 METHOD(attribute_handler_t
, create_attribute_enumerator
, enumerator_t
*,
443 private_resolve_handler_t
*this, ike_sa_t
*ike_sa
,
446 attribute_enumerator_t
*enumerator
;
450 .enumerate
= enumerator_enumerate_default
,
451 .venumerate
= _attribute_enumerate
,
452 .destroy
= (void*)free
,
454 .v4
= has_host_family(vips
, AF_INET
),
455 .v6
= has_host_family(vips
, AF_INET6
),
457 return &enumerator
->public;
460 METHOD(resolve_handler_t
, destroy
, void,
461 private_resolve_handler_t
*this)
463 array_destroy(this->servers
);
464 this->mutex
->destroy(this->mutex
);
471 resolve_handler_t
*resolve_handler_create()
473 private_resolve_handler_t
*this;
481 .create_attribute_enumerator
= _create_attribute_enumerator
,
485 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
486 .file
= lib
->settings
->get_str(lib
->settings
, "%s.plugins.resolve.file",
487 RESOLV_CONF
, lib
->ns
),
490 if (stat(RESOLVCONF_EXEC
, &st
) == 0)
492 this->use_resolvconf
= TRUE
;
493 this->iface_prefix
= lib
->settings
->get_str(lib
->settings
,
494 "%s.plugins.resolve.resolvconf.iface_prefix",
495 RESOLVCONF_PREFIX
, lib
->ns
);
498 return &this->public;