diff --git a/src/libhydra/plugins/resolve/resolve_handler.c b/src/libhydra/plugins/resolve/resolve_handler.c index 62376dcc6f..18e46f1967 100644 --- a/src/libhydra/plugins/resolve/resolve_handler.c +++ b/src/libhydra/plugins/resolve/resolve_handler.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -15,12 +16,20 @@ #include "resolve_handler.h" +#include +#include #include #include #include #include +/* path to resolvconf executable */ +#define RESOLVCONF_EXEC "/sbin/resolvconf" + +/* prefix used for resolvconf interfaces */ +#define RESOLVCONF_PREFIX "lo.inet.ipsec." + typedef struct private_resolve_handler_t private_resolve_handler_t; /** @@ -38,21 +47,143 @@ struct private_resolve_handler_t { */ char *file; + /** + * use resolvconf instead of writing directly to resolv.conf + */ + bool use_resolvconf; + /** * Mutex to access file exclusively */ mutex_t *mutex; }; +/** + * Writes the given nameserver to resolv.conf + */ +static bool write_nameserver(private_resolve_handler_t *this, + identification_t *server, host_t *addr) +{ + FILE *in, *out; + char buf[1024]; + size_t len; + bool handled = FALSE; + + in = fopen(this->file, "r"); + /* allows us to stream from in to out */ + unlink(this->file); + out = fopen(this->file, "w"); + if (out) + { + fprintf(out, "nameserver %H # by strongSwan, from %Y\n", addr, + server); + DBG1(DBG_IKE, "installing DNS server %H to %s", addr, this->file); + handled = TRUE; + + /* copy rest of the file */ + if (in) + { + while ((len = fread(buf, 1, sizeof(buf), in))) + { + ignore_result(fwrite(buf, 1, len, out)); + } + } + fclose(out); + } + if (in) + { + fclose(in); + } + return handled; +} + +/** + * Removes the given nameserver from resolv.conf + */ +static void remove_nameserver(private_resolve_handler_t *this, + identification_t *server, host_t *addr) +{ + FILE *in, *out; + char line[1024], matcher[512]; + + in = fopen(this->file, "r"); + if (in) + { + /* allows us to stream from in to out */ + unlink(this->file); + out = fopen(this->file, "w"); + if (out) + { + snprintf(matcher, sizeof(matcher), + "nameserver %H # by strongSwan, from %Y\n", + addr, server); + + /* copy all, but matching line */ + while (fgets(line, sizeof(line), in)) + { + if (strneq(line, matcher, strlen(matcher))) + { + DBG1(DBG_IKE, "removing DNS server %H from %s", + addr, this->file); + } + else + { + fputs(line, out); + } + } + fclose(out); + } + fclose(in); + } +} + +/** + * Add or remove the given nameserver by invoking resolvconf. + */ +static bool invoke_resolvconf(private_resolve_handler_t *this, + identification_t *server, host_t *addr, + bool install) +{ + char cmd[128]; + + /* 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", RESOLVCONF_PREFIX, addr) >= sizeof(cmd)) + { + return FALSE; + } + + if (install) + { + FILE *out; + + out = popen(cmd, "w"); + if (!out) + { + return FALSE; + } + DBG1(DBG_IKE, "installing DNS server %H via resolvconf", addr); + fprintf(out, "nameserver %H # by strongSwan, from %Y\n", addr, + server); + if (ferror(out) || pclose(out)) + { + return FALSE; + } + } + else + { + ignore_result(system(cmd)); + } + return TRUE; +} + METHOD(attribute_handler_t, handle, bool, private_resolve_handler_t *this, identification_t *server, configuration_attribute_type_t type, chunk_t data) { - FILE *in, *out; - char buf[1024]; host_t *addr; - size_t len; - bool handled = FALSE; + bool handled; switch (type) { @@ -71,48 +202,30 @@ METHOD(attribute_handler_t, handle, bool, DESTROY_IF(addr); return FALSE; } + this->mutex->lock(this->mutex); - - in = fopen(this->file, "r"); - /* allows us to stream from in to out */ - unlink(this->file); - out = fopen(this->file, "w"); - if (out) + if (this->use_resolvconf) { - fprintf(out, "nameserver %H # by strongSwan, from %Y\n", addr, server); - DBG1(DBG_IKE, "installing DNS server %H to %s", addr, this->file); - handled = TRUE; - - /* copy rest of the file */ - if (in) - { - while ((len = fread(buf, 1, sizeof(buf), in))) - { - ignore_result(fwrite(buf, 1, len, out)); - } - } - fclose(out); + handled = invoke_resolvconf(this, server, addr, TRUE); } - if (in) + else { - fclose(in); + handled = write_nameserver(this, server, addr); } this->mutex->unlock(this->mutex); addr->destroy(addr); if (!handled) { - DBG1(DBG_IKE, "adding DNS server failed", this->file); + DBG1(DBG_IKE, "adding DNS server failed"); } return handled; } -METHOD(attribute_handler_t, release,void, +METHOD(attribute_handler_t, release, void, private_resolve_handler_t *this, identification_t *server, configuration_attribute_type_t type, chunk_t data) { - FILE *in, *out; - char line[1024], matcher[512]; host_t *addr; int family; @@ -127,42 +240,20 @@ METHOD(attribute_handler_t, release,void, default: return; } + addr = host_create_from_chunk(family, data, 0); this->mutex->lock(this->mutex); - - in = fopen(this->file, "r"); - if (in) + if (this->use_resolvconf) { - /* allows us to stream from in to out */ - unlink(this->file); - out = fopen(this->file, "w"); - if (out) - { - addr = host_create_from_chunk(family, data, 0); - snprintf(matcher, sizeof(matcher), - "nameserver %H # by strongSwan, from %Y\n", - addr, server); - - /* copy all, but matching line */ - while (fgets(line, sizeof(line), in)) - { - if (strneq(line, matcher, strlen(matcher))) - { - DBG1(DBG_IKE, "removing DNS server %H from %s", - addr, this->file); - } - else - { - fputs(line, out); - } - } - addr->destroy(addr); - fclose(out); - } - fclose(in); + invoke_resolvconf(this, server, addr, FALSE); + } + else + { + remove_nameserver(this, server, addr); } - this->mutex->unlock(this->mutex); + + addr->destroy(addr); } /** @@ -226,6 +317,7 @@ METHOD(resolve_handler_t, destroy, void, resolve_handler_t *resolve_handler_create() { private_resolve_handler_t *this; + struct stat st; INIT(this, .public = { @@ -241,6 +333,11 @@ resolve_handler_t *resolve_handler_create() RESOLV_CONF, hydra->daemon), ); + if (stat(RESOLVCONF_EXEC, &st) == 0) + { + this->use_resolvconf = TRUE; + } + return &this->public; }