chiark / gitweb /
hostnamed: watch system hostname changes and update LLMNR RRs accordingly
authorLennart Poettering <lennart@poettering.net>
Thu, 31 Jul 2014 17:54:43 +0000 (19:54 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 31 Jul 2014 17:54:43 +0000 (19:54 +0200)
src/resolve/resolved-manager.c
src/resolve/resolved.h

index 72034c1f487868fffe12a0a0b4db4c93676a4821..6987c29e072574c6cb087fb70395e932c638ac71 100644 (file)
@@ -34,6 +34,8 @@
 #include "socket-util.h"
 #include "af-list.h"
 #include "resolved.h"
+#include "utf8.h"
+#include "resolved-dns-domain.h"
 
 #define SEND_TIMEOUT_USEC (200 * USEC_PER_MSEC)
 
@@ -384,6 +386,92 @@ int manager_parse_config_file(Manager *m) {
                             false, false, true, m);
 }
 
+static int determine_hostname(char **ret) {
+        _cleanup_free_ char *h = NULL, *n = NULL;
+        int r;
+
+        assert(ret);
+
+        h = gethostname_malloc();
+        if (!h)
+                return log_oom();
+
+        if (!utf8_is_valid(h)) {
+                log_error("System hostname is not UTF-8 clean.");
+                return -EINVAL;
+        }
+
+        r = dns_name_normalize(h, &n);
+        if (r < 0) {
+                log_error("System hostname '%s' cannot be normalized.", h);
+                return r;
+        }
+
+        *ret = n;
+        n = NULL;
+
+        return 0;
+}
+
+static int on_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
+        _cleanup_free_ char *h = NULL;
+        Manager *m = userdata;
+        int r;
+
+        assert(m);
+
+        r = determine_hostname(&h);
+        if (r < 0)
+                return 0; /* ignore invalid hostnames */
+
+        if (streq(h, m->hostname))
+                return 0;
+
+        log_info("System hostname changed to '%s'.", h);
+        free(m->hostname);
+        m->hostname = h;
+        h = NULL;
+
+        manager_refresh_rrs(m);
+
+        return 0;
+}
+
+static int manager_watch_hostname(Manager *m) {
+        _cleanup_free_ char *h = NULL;
+        int r;
+
+        assert(m);
+
+        m->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY);
+        if (m->hostname_fd < 0) {
+                log_warning("Failed to watch hostname: %m");
+                return 0;
+        }
+
+        r = sd_event_add_io(m->event, &m->hostname_event_source, m->hostname_fd, 0, on_hostname_change, m);
+        if (r < 0) {
+                if (r == -EPERM)
+                        /* kernels prior to 3.2 don't support polling this file. Ignore the failure. */
+                        m->hostname_fd = safe_close(m->hostname_fd);
+                else {
+                        log_error("Failed to add hostname event source: %s", strerror(-r));
+                        return r;
+                }
+        }
+
+        r = determine_hostname(&m->hostname);
+        if (r < 0) {
+                log_info("Defaulting to hostname 'linux'.");
+                m->hostname = strdup("linux");
+                if (!m->hostname)
+                        return log_oom();
+        } else
+                log_info("Using system hostname '%s'.", m->hostname);
+
+        return 0;
+}
+
 int manager_new(Manager **ret) {
         _cleanup_(manager_freep) Manager *m = NULL;
         int r;
@@ -397,6 +485,7 @@ int manager_new(Manager **ret) {
         m->dns_ipv4_fd = m->dns_ipv6_fd = -1;
         m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1;
         m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1;
+        m->hostname_fd = -1;
 
         m->use_llmnr = true;
 
@@ -404,10 +493,6 @@ int manager_new(Manager **ret) {
         if (r < 0)
                 return r;
 
-        m->hostname = gethostname_malloc();
-        if (!m->hostname)
-                return -ENOMEM;
-
         r = sd_event_default(&m->event);
         if (r < 0)
                 return r;
@@ -417,6 +502,10 @@ int manager_new(Manager **ret) {
 
         sd_event_set_watchdog(m->event, true);
 
+        r = manager_watch_hostname(m);
+        if (r < 0)
+                return r;
+
         r = dns_scope_new(m, &m->unicast_scope, NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
         if (r < 0)
                 return r;
@@ -497,7 +586,11 @@ Manager *manager_free(Manager *m) {
 
         dns_resource_key_unref(m->host_ipv4_key);
         dns_resource_key_unref(m->host_ipv6_key);
+
+        safe_close(m->hostname_fd);
+        sd_event_source_unref(m->hostname_event_source);
         free(m->hostname);
+
         free(m);
 
         return NULL;
@@ -1408,12 +1501,25 @@ int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_a
         return 0;
 }
 
+void manager_refresh_rrs(Manager *m) {
+        Iterator i;
+        Link *l;
+
+        assert(m);
+
+        m->host_ipv4_key = dns_resource_key_unref(m->host_ipv4_key);
+        m->host_ipv6_key = dns_resource_key_unref(m->host_ipv6_key);
+
+        HASHMAP_FOREACH(l, m->links, i) {
+                link_add_rrs(l, true);
+                link_add_rrs(l, false);
+        }
+}
+
 int manager_next_hostname(Manager *m) {
         const char *p;
-        Iterator i;
         uint64_t u;
         char *h;
-        Link *l;
 
         assert(m);
 
@@ -1435,18 +1541,12 @@ int manager_next_hostname(Manager *m) {
         if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->hostname), m->hostname, u) < 0)
                 return -ENOMEM;
 
-        log_info("Hostname conflict, changing local hostname from '%s' to '%s'.", m->hostname, h);
+        log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->hostname, h);
 
         free(m->hostname);
         m->hostname = h;
 
-        m->host_ipv4_key = dns_resource_key_unref(m->host_ipv4_key);
-        m->host_ipv6_key = dns_resource_key_unref(m->host_ipv6_key);
-
-        HASHMAP_FOREACH(l, m->links, i) {
-                link_add_rrs(l, true);
-                link_add_rrs(l, false);
-        }
+        manager_refresh_rrs(m);
 
         return 0;
 }
index 07560553b98444fd6949edf64e598aa6643c89b5..34eb7b5c5abca5815fcf0963ac636c6860c40180 100644 (file)
@@ -90,6 +90,10 @@ struct Manager {
         char *hostname;
         DnsResourceKey *host_ipv4_key;
         DnsResourceKey *host_ipv6_key;
+
+        /* Watch the system hostname */
+        int hostname_fd;
+        sd_event_source *hostname_event_source;
 };
 
 /* Manager */
@@ -120,6 +124,7 @@ int manager_ifindex_is_loopback(Manager *m, int ifindex);
 int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr);
 LinkAddress* manager_find_address(Manager *m, int family, const union in_addr_union *in_addr);
 
+void manager_refresh_rrs(Manager *m);
 int manager_next_hostname(Manager *m);
 
 int manager_our_packet(Manager *m, DnsPacket *p);