Added support for the resolvconf framework in resolve plugin.
authorTobias Brunner <tobias@strongswan.org>
Mon, 26 Mar 2012 13:00:14 +0000 (15:00 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 27 Mar 2012 08:44:21 +0000 (10:44 +0200)
If /sbin/resolvconf is found nameservers are not written directly to
/etc/resolv.conf but instead resolvconf is invoked.

src/libhydra/plugins/resolve/resolve_handler.c

index 62376dc..18e46f1 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2012 Tobias Brunner
  * Copyright (C) 2009 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
 
 #include "resolve_handler.h"
 
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
 #include <hydra.h>
 #include <debug.h>
 #include <threading/mutex.h>
 
+/* 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;
 
 /**
@@ -39,47 +48,35 @@ 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;
 };
 
-METHOD(attribute_handler_t, handle, bool,
-       private_resolve_handler_t *this, identification_t *server,
-       configuration_attribute_type_t type, chunk_t data)
+/**
+ * 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];
-       host_t *addr;
        size_t len;
        bool handled = FALSE;
 
-       switch (type)
-       {
-               case INTERNAL_IP4_DNS:
-                       addr = host_create_from_chunk(AF_INET, data, 0);
-                       break;
-               case INTERNAL_IP6_DNS:
-                       addr = host_create_from_chunk(AF_INET6, data, 0);
-                       break;
-               default:
-                       return FALSE;
-       }
-
-       if (!addr || addr->is_anyaddr(addr))
-       {
-               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)
        {
-               fprintf(out, "nameserver %H   # by strongSwan, from %Y\n", addr, server);
+               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;
 
@@ -97,38 +94,17 @@ METHOD(attribute_handler_t, handle, bool,
        {
                fclose(in);
        }
-       this->mutex->unlock(this->mutex);
-       addr->destroy(addr);
-
-       if (!handled)
-       {
-               DBG1(DBG_IKE, "adding DNS server failed", this->file);
-       }
        return handled;
 }
 
-METHOD(attribute_handler_t, release,void,
-       private_resolve_handler_t *this, identification_t *server,
-       configuration_attribute_type_t type, chunk_t data)
+/**
+ * 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];
-       host_t *addr;
-       int family;
-
-       switch (type)
-       {
-               case INTERNAL_IP4_DNS:
-                       family = AF_INET;
-                       break;
-               case INTERNAL_IP6_DNS:
-                       family = AF_INET6;
-                       break;
-               default:
-                       return;
-       }
-
-       this->mutex->lock(this->mutex);
 
        in = fopen(this->file, "r");
        if (in)
@@ -138,7 +114,6 @@ METHOD(attribute_handler_t, release,void,
                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);
@@ -156,13 +131,129 @@ METHOD(attribute_handler_t, release,void,
                                        fputs(line, out);
                                }
                        }
-                       addr->destroy(addr);
                        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)
+{
+       host_t *addr;
+       bool handled;
+
+       switch (type)
+       {
+               case INTERNAL_IP4_DNS:
+                       addr = host_create_from_chunk(AF_INET, data, 0);
+                       break;
+               case INTERNAL_IP6_DNS:
+                       addr = host_create_from_chunk(AF_INET6, data, 0);
+                       break;
+               default:
+                       return FALSE;
+       }
+
+       if (!addr || addr->is_anyaddr(addr))
+       {
+               DESTROY_IF(addr);
+               return FALSE;
+       }
+
+       this->mutex->lock(this->mutex);
+       if (this->use_resolvconf)
+       {
+               handled = invoke_resolvconf(this, server, addr, TRUE);
+       }
+       else
+       {
+               handled = write_nameserver(this, server, addr);
+       }
+       this->mutex->unlock(this->mutex);
+       addr->destroy(addr);
+
+       if (!handled)
+       {
+               DBG1(DBG_IKE, "adding DNS server failed");
+       }
+       return handled;
+}
+
+METHOD(attribute_handler_t, release, void,
+       private_resolve_handler_t *this, identification_t *server,
+       configuration_attribute_type_t type, chunk_t data)
+{
+       host_t *addr;
+       int family;
+
+       switch (type)
+       {
+               case INTERNAL_IP4_DNS:
+                       family = AF_INET;
+                       break;
+               case INTERNAL_IP6_DNS:
+                       family = AF_INET6;
+                       break;
+               default:
+                       return;
+       }
+       addr = host_create_from_chunk(family, data, 0);
+
+       this->mutex->lock(this->mutex);
+       if (this->use_resolvconf)
+       {
+               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;
 }