summaryrefslogtreecommitdiff
path: root/src/libcharon/plugins/resolve/resolve_handler.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/plugins/resolve/resolve_handler.c')
-rw-r--r--src/libcharon/plugins/resolve/resolve_handler.c203
1 files changed, 159 insertions, 44 deletions
diff --git a/src/libcharon/plugins/resolve/resolve_handler.c b/src/libcharon/plugins/resolve/resolve_handler.c
index ec3decc4d..9077b51d4 100644
--- a/src/libcharon/plugins/resolve/resolve_handler.c
+++ b/src/libcharon/plugins/resolve/resolve_handler.c
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2016 Tobias Brunner
* Copyright (C) 2009 Martin Willi
- * Hochschule fuer Technik Rapperswil
+ * HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -21,6 +21,8 @@
#include <unistd.h>
#include <utils/debug.h>
+#include <utils/process.h>
+#include <collections/array.h>
#include <threading/mutex.h>
/* path to resolvconf executable */
@@ -47,12 +49,12 @@ struct private_resolve_handler_t {
char *file;
/**
- * use resolvconf instead of writing directly to resolv.conf
+ * Use resolvconf instead of writing directly to resolv.conf
*/
bool use_resolvconf;
/**
- * prefix to be used for interface names sent to resolvconf
+ * Prefix to be used for interface names sent to resolvconf
*/
char *iface_prefix;
@@ -60,13 +62,55 @@ struct private_resolve_handler_t {
* Mutex to access file exclusively
*/
mutex_t *mutex;
+
+ /**
+ * Reference counting for DNS servers dns_server_t
+ */
+ array_t *servers;
};
/**
+ * Reference counting for DNS servers
+ */
+typedef struct {
+
+ /**
+ * DNS server address
+ */
+ host_t *server;
+
+ /**
+ * Reference count
+ */
+ u_int refcount;
+
+} dns_server_t;
+
+/**
+ * Compare a server and a stored reference
+ */
+static int dns_server_find(const void *a, const void *b)
+{
+ host_t *server = (host_t*)a;
+ dns_server_t *item = (dns_server_t*)b;
+ return chunk_compare(server->get_address(server),
+ item->server->get_address(item->server));
+}
+
+/**
+ * Sort references by DNS server
+ */
+static int dns_server_sort(const void *a, const void *b, void *user)
+{
+ const dns_server_t *da = a, *db = b;
+ return chunk_compare(da->server->get_address(da->server),
+ db->server->get_address(db->server));
+}
+
+/**
* Writes the given nameserver to resolv.conf
*/
-static bool write_nameserver(private_resolve_handler_t *this,
- identification_t *server, host_t *addr)
+static bool write_nameserver(private_resolve_handler_t *this, host_t *addr)
{
FILE *in, *out;
char buf[1024];
@@ -79,8 +123,7 @@ static bool write_nameserver(private_resolve_handler_t *this,
out = fopen(this->file, "w");
if (out)
{
- fprintf(out, "nameserver %H # by strongSwan, from %Y\n", addr,
- server);
+ fprintf(out, "nameserver %H # by strongSwan\n", addr);
DBG1(DBG_IKE, "installing DNS server %H to %s", addr, this->file);
handled = TRUE;
@@ -104,8 +147,7 @@ static bool write_nameserver(private_resolve_handler_t *this,
/**
* Removes the given nameserver from resolv.conf
*/
-static void remove_nameserver(private_resolve_handler_t *this,
- identification_t *server, host_t *addr)
+static void remove_nameserver(private_resolve_handler_t *this, host_t *addr)
{
FILE *in, *out;
char line[1024], matcher[512];
@@ -119,8 +161,7 @@ static void remove_nameserver(private_resolve_handler_t *this,
if (out)
{
snprintf(matcher, sizeof(matcher),
- "nameserver %H # by strongSwan, from %Y\n",
- addr, server);
+ "nameserver %H # by strongSwan\n", addr);
/* copy all, but matching line */
while (fgets(line, sizeof(line), in))
@@ -144,50 +185,91 @@ static void remove_nameserver(private_resolve_handler_t *this,
/**
* Add or remove the given nameserver by invoking resolvconf.
*/
-static bool invoke_resolvconf(private_resolve_handler_t *this,
- identification_t *server, host_t *addr,
+static bool invoke_resolvconf(private_resolve_handler_t *this, host_t *addr,
bool install)
{
- char cmd[128];
- bool success = TRUE;
+ process_t *process;
+ FILE *shell;
+ int in, out, retval;
/* we use the nameserver's IP address as part of the interface name to
* make them unique */
- if (snprintf(cmd, sizeof(cmd), "%s %s %s%H", RESOLVCONF_EXEC,
- install ? "-a" : "-d", this->iface_prefix, addr) >= sizeof(cmd))
+ process = process_start_shell(NULL, install ? &in : NULL, &out, NULL,
+ "2>&1 %s %s %s%H", RESOLVCONF_EXEC,
+ install ? "-a" : "-d", this->iface_prefix, addr);
+
+ if (!process)
{
return FALSE;
}
-
if (install)
{
- FILE *out;
-
- out = popen(cmd, "w");
- if (!out)
+ shell = fdopen(in, "w");
+ if (shell)
{
- return FALSE;
+ DBG1(DBG_IKE, "installing DNS server %H via resolvconf", addr);
+ fprintf(shell, "nameserver %H\n", addr);
+ fclose(shell);
}
- DBG1(DBG_IKE, "installing DNS server %H via resolvconf", addr);
- fprintf(out, "nameserver %H\n", addr);
- success = !ferror(out);
- if (pclose(out))
+ else
{
+ close(in);
+ close(out);
+ process->wait(process, NULL);
return FALSE;
}
}
else
{
- ignore_result(system(cmd));
+ DBG1(DBG_IKE, "removing DNS server %H via resolvconf", addr);
+ }
+ shell = fdopen(out, "r");
+ if (shell)
+ {
+ while (TRUE)
+ {
+ char resp[128], *e;
+
+ if (fgets(resp, sizeof(resp), shell) == NULL)
+ {
+ if (ferror(shell))
+ {
+ DBG1(DBG_IKE, "error reading from resolvconf");
+ }
+ break;
+ }
+ else
+ {
+ e = resp + strlen(resp);
+ if (e > resp && e[-1] == '\n')
+ {
+ e[-1] = '\0';
+ }
+ DBG1(DBG_IKE, "resolvconf: %s", resp);
+ }
+ }
+ fclose(shell);
+ }
+ else
+ {
+ close(out);
+ }
+ if (!process->wait(process, &retval) || retval != EXIT_SUCCESS)
+ {
+ if (install)
+ { /* revert changes when installing fails */
+ invoke_resolvconf(this, addr, FALSE);
+ return FALSE;
+ }
}
- return success;
+ return TRUE;
}
METHOD(attribute_handler_t, handle, bool,
private_resolve_handler_t *this, ike_sa_t *ike_sa,
configuration_attribute_type_t type, chunk_t data)
{
- identification_t *server;
+ dns_server_t *found = NULL;
host_t *addr;
bool handled;
@@ -208,16 +290,34 @@ METHOD(attribute_handler_t, handle, bool,
DESTROY_IF(addr);
return FALSE;
}
- server = ike_sa->get_other_id(ike_sa);
this->mutex->lock(this->mutex);
- if (this->use_resolvconf)
+ if (array_bsearch(this->servers, addr, dns_server_find, &found) == -1)
{
- handled = invoke_resolvconf(this, server, addr, TRUE);
+ if (this->use_resolvconf)
+ {
+ handled = invoke_resolvconf(this, addr, TRUE);
+ }
+ else
+ {
+ handled = write_nameserver(this, addr);
+ }
+ if (handled)
+ {
+ INIT(found,
+ .server = addr->clone(addr),
+ .refcount = 1,
+ );
+ array_insert_create(&this->servers, ARRAY_TAIL, found);
+ array_sort(this->servers, dns_server_sort, NULL);
+ }
}
else
{
- handled = write_nameserver(this, server, addr);
+ DBG1(DBG_IKE, "DNS server %H already installed, increasing refcount",
+ addr);
+ found->refcount++;
+ handled = TRUE;
}
this->mutex->unlock(this->mutex);
addr->destroy(addr);
@@ -233,9 +333,9 @@ METHOD(attribute_handler_t, release, void,
private_resolve_handler_t *this, ike_sa_t *ike_sa,
configuration_attribute_type_t type, chunk_t data)
{
- identification_t *server;
+ dns_server_t *found = NULL;
host_t *addr;
- int family;
+ int family, idx;
switch (type)
{
@@ -249,16 +349,30 @@ METHOD(attribute_handler_t, release, void,
return;
}
addr = host_create_from_chunk(family, data, 0);
- server = ike_sa->get_other_id(ike_sa);
this->mutex->lock(this->mutex);
- if (this->use_resolvconf)
- {
- invoke_resolvconf(this, server, addr, FALSE);
- }
- else
+ idx = array_bsearch(this->servers, addr, dns_server_find, &found);
+ if (idx != -1)
{
- remove_nameserver(this, server, addr);
+ if (--found->refcount > 0)
+ {
+ DBG1(DBG_IKE, "DNS server %H still used, decreasing refcount",
+ addr);
+ }
+ else
+ {
+ if (this->use_resolvconf)
+ {
+ invoke_resolvconf(this, addr, FALSE);
+ }
+ else
+ {
+ remove_nameserver(this, addr);
+ }
+ array_remove(this->servers, idx, NULL);
+ found->server->destroy(found->server);
+ free(found);
+ }
}
this->mutex->unlock(this->mutex);
@@ -341,6 +455,7 @@ METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*,
METHOD(resolve_handler_t, destroy, void,
private_resolve_handler_t *this)
{
+ array_destroy(this->servers);
this->mutex->destroy(this->mutex);
free(this);
}