resolve: Add refcounting for installed DNS servers

This fixes DNS server installation if make-before-break reauthentication
is used as there the new SA and DNS server is installed before it then
is removed again when the old IKE_SA is torn down.
This commit is contained in:
Tobias Brunner
2016-06-10 18:40:01 +02:00
parent f1064ca59a
commit b1f7d10800
+99 -27
View File
@@ -22,6 +22,7 @@
#include <utils/debug.h> #include <utils/debug.h>
#include <utils/process.h> #include <utils/process.h>
#include <collections/array.h>
#include <threading/mutex.h> #include <threading/mutex.h>
/* path to resolvconf executable */ /* path to resolvconf executable */
@@ -48,12 +49,12 @@ struct private_resolve_handler_t {
char *file; char *file;
/** /**
* use resolvconf instead of writing directly to resolv.conf * Use resolvconf instead of writing directly to resolv.conf
*/ */
bool use_resolvconf; 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; char *iface_prefix;
@@ -61,13 +62,55 @@ struct private_resolve_handler_t {
* Mutex to access file exclusively * Mutex to access file exclusively
*/ */
mutex_t *mutex; 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 * Writes the given nameserver to resolv.conf
*/ */
static bool write_nameserver(private_resolve_handler_t *this, static bool write_nameserver(private_resolve_handler_t *this, host_t *addr)
identification_t *server, host_t *addr)
{ {
FILE *in, *out; FILE *in, *out;
char buf[1024]; char buf[1024];
@@ -80,8 +123,7 @@ static bool write_nameserver(private_resolve_handler_t *this,
out = fopen(this->file, "w"); out = fopen(this->file, "w");
if (out) if (out)
{ {
fprintf(out, "nameserver %H # by strongSwan, from %Y\n", addr, fprintf(out, "nameserver %H # by strongSwan\n", addr);
server);
DBG1(DBG_IKE, "installing DNS server %H to %s", addr, this->file); DBG1(DBG_IKE, "installing DNS server %H to %s", addr, this->file);
handled = TRUE; handled = TRUE;
@@ -105,8 +147,7 @@ static bool write_nameserver(private_resolve_handler_t *this,
/** /**
* Removes the given nameserver from resolv.conf * Removes the given nameserver from resolv.conf
*/ */
static void remove_nameserver(private_resolve_handler_t *this, static void remove_nameserver(private_resolve_handler_t *this, host_t *addr)
identification_t *server, host_t *addr)
{ {
FILE *in, *out; FILE *in, *out;
char line[1024], matcher[512]; char line[1024], matcher[512];
@@ -120,8 +161,7 @@ static void remove_nameserver(private_resolve_handler_t *this,
if (out) if (out)
{ {
snprintf(matcher, sizeof(matcher), snprintf(matcher, sizeof(matcher),
"nameserver %H # by strongSwan, from %Y\n", "nameserver %H # by strongSwan\n", addr);
addr, server);
/* copy all, but matching line */ /* copy all, but matching line */
while (fgets(line, sizeof(line), in)) while (fgets(line, sizeof(line), in))
@@ -145,8 +185,7 @@ static void remove_nameserver(private_resolve_handler_t *this,
/** /**
* Add or remove the given nameserver by invoking resolvconf. * Add or remove the given nameserver by invoking resolvconf.
*/ */
static bool invoke_resolvconf(private_resolve_handler_t *this, static bool invoke_resolvconf(private_resolve_handler_t *this, host_t *addr,
identification_t *server, host_t *addr,
bool install) bool install)
{ {
process_t *process; process_t *process;
@@ -219,7 +258,7 @@ static bool invoke_resolvconf(private_resolve_handler_t *this,
{ {
if (install) if (install)
{ /* revert changes when installing fails */ { /* revert changes when installing fails */
invoke_resolvconf(this, server, addr, FALSE); invoke_resolvconf(this, addr, FALSE);
return FALSE; return FALSE;
} }
} }
@@ -230,7 +269,7 @@ METHOD(attribute_handler_t, handle, bool,
private_resolve_handler_t *this, ike_sa_t *ike_sa, private_resolve_handler_t *this, ike_sa_t *ike_sa,
configuration_attribute_type_t type, chunk_t data) configuration_attribute_type_t type, chunk_t data)
{ {
identification_t *server; dns_server_t *found = NULL;
host_t *addr; host_t *addr;
bool handled; bool handled;
@@ -251,16 +290,34 @@ METHOD(attribute_handler_t, handle, bool,
DESTROY_IF(addr); DESTROY_IF(addr);
return FALSE; return FALSE;
} }
server = ike_sa->get_other_id(ike_sa);
this->mutex->lock(this->mutex); 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 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); this->mutex->unlock(this->mutex);
addr->destroy(addr); addr->destroy(addr);
@@ -276,9 +333,9 @@ METHOD(attribute_handler_t, release, void,
private_resolve_handler_t *this, ike_sa_t *ike_sa, private_resolve_handler_t *this, ike_sa_t *ike_sa,
configuration_attribute_type_t type, chunk_t data) configuration_attribute_type_t type, chunk_t data)
{ {
identification_t *server; dns_server_t *found = NULL;
host_t *addr; host_t *addr;
int family; int family, idx;
switch (type) switch (type)
{ {
@@ -292,16 +349,30 @@ METHOD(attribute_handler_t, release, void,
return; return;
} }
addr = host_create_from_chunk(family, data, 0); addr = host_create_from_chunk(family, data, 0);
server = ike_sa->get_other_id(ike_sa);
this->mutex->lock(this->mutex); this->mutex->lock(this->mutex);
if (this->use_resolvconf) idx = array_bsearch(this->servers, addr, dns_server_find, &found);
if (idx != -1)
{ {
invoke_resolvconf(this, server, addr, FALSE); if (--found->refcount > 0)
} {
else DBG1(DBG_IKE, "DNS server %H still used, decreasing refcount",
{ addr);
remove_nameserver(this, server, 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); this->mutex->unlock(this->mutex);
@@ -384,6 +455,7 @@ METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*,
METHOD(resolve_handler_t, destroy, void, METHOD(resolve_handler_t, destroy, void,
private_resolve_handler_t *this) private_resolve_handler_t *this)
{ {
array_destroy(this->servers);
this->mutex->destroy(this->mutex); this->mutex->destroy(this->mutex);
free(this); free(this);
} }