]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #2115 from dvdhrm/rbtree
authorTom Gundersen <teg@jklm.no>
Tue, 8 Dec 2015 16:31:09 +0000 (17:31 +0100)
committerTom Gundersen <teg@jklm.no>
Tue, 8 Dec 2015 16:31:09 +0000 (17:31 +0100)
basic: add RB-Tree implementation

19 files changed:
.gitignore
Makefile.am
src/resolve/resolved-def.h
src/resolve/resolved-dns-cache.c
src/resolve/resolved-dns-cache.h
src/resolve/resolved-dns-packet.c
src/resolve/resolved-dns-packet.h
src/resolve/resolved-dns-rr.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-transaction.h
src/resolve/resolved-link.c
src/resolve/resolved-link.h
src/resolve/resolved-manager.c
src/resolve/resolved-manager.h
src/resolve/resolved-mdns.c [new file with mode: 0644]
src/resolve/resolved-mdns.h [new file with mode: 0644]
src/test/test-rlimit-util.c [new file with mode: 0644]

index 5acf5f819adc47e61d78b3b8011f14776c6b0731..4d9cbbe4a070786c99b51ead52c7b9d73ee5e783 100644 (file)
 /test-replace-var
 /test-resolve
 /test-ring
+/test-rlimit-util
 /test-sched-prio
 /test-set
 /test-sigbus
index 31e311dba1a46bb086c7cfb1e188123dc9eafdc1..e28edfc8cbaada686ffc2a09056585addb517cfb 100644 (file)
@@ -147,6 +147,7 @@ tests=
 manual_tests =
 TEST_EXTENSIONS = .py
 PY_LOG_COMPILER = $(PYTHON)
+DISABLE_HARD_ERRORS = yes
 if ENABLE_TESTS
 noinst_PROGRAMS = $(manual_tests) $(tests)
 TESTS = $(tests)
@@ -744,8 +745,6 @@ CLEANFILES += \
        man/systemd.index.xml \
        man/systemd.directives.xml
 
-EXTRA_DIST += \
-       tools/make-man-rules.py
 
 endif
 
@@ -754,6 +753,7 @@ endif
 EXTRA_DIST += \
        $(filter-out man/systemd.directives.xml man/systemd.index.xml,$(XML_FILES)) \
        tools/make-man-index.py \
+       tools/make-man-rules.py \
        tools/make-directive-index.py \
        tools/xml_helper.py \
        man/glib-event-glue.c
@@ -1500,7 +1500,8 @@ tests += \
        test-af-list \
        test-arphrd-list \
        test-dns-domain \
-       test-install-root
+       test-install-root \
+       test-rlimit-util
 
 if HAVE_ACL
 tests += \
@@ -1869,6 +1870,12 @@ test_acl_util_LDADD = \
 test_namespace_LDADD = \
        libcore.la
 
+test_rlimit_util_SOURCES = \
+       src/test/test-rlimit-util.c
+
+test_rlimit_util_LDADD = \
+       libshared.la
+
 BUILT_SOURCES += \
        src/test/test-hashmap-ordered.c
 
@@ -5166,6 +5173,8 @@ systemd_resolved_SOURCES = \
        src/resolve/resolved-link.c \
        src/resolve/resolved-llmnr.h \
        src/resolve/resolved-llmnr.c \
+       src/resolve/resolved-mdns.h \
+       src/resolve/resolved-mdns.c \
        src/resolve/resolved-def.h \
        src/resolve/resolved-dns-rr.h \
        src/resolve/resolved-dns-rr.c \
@@ -6218,21 +6227,7 @@ DISTCHECK_CONFIGURE_FLAGS += \
        --disable-split-usr
 endif
 
-#
-# Require python when making dist
-#
-.PHONY: dist-check-python dist-check-compat-libs dist-check-help
-dist-check-python:
-if !HAVE_PYTHON
-       @echo "*** python and python-lxml module must be installed and enabled in order to make dist"
-       @false
-endif
-
-dist-check-compat-libs:
-if !ENABLE_COMPAT_LIBS
-       @echo "*** compat-libs must be enabled in order to make dist"
-       @false
-endif
+.PHONY: dist-check-help
 
 dist-check-help: $(rootbin_PROGRAMS) $(bin_PROGRAMS)
        for i in $(abspath $^); do                                             \
@@ -6242,8 +6237,6 @@ dist-check-help: $(rootbin_PROGRAMS) $(bin_PROGRAMS)
                exit 1;                                                        \
             fi; done
 
-dist: dist-check-python dist-check-compat-libs
-
 .PHONY: hwdb-update
 hwdb-update:
        ( cd $(top_srcdir)/hwdb && \
index db5ee57b5115156d2e99ff2761fd8539538a0692..6014d345f3dff1c06df5493ad9415246185c26af 100644 (file)
@@ -24,6 +24,8 @@
 #define SD_RESOLVED_DNS           (UINT64_C(1) << 0)
 #define SD_RESOLVED_LLMNR_IPV4    (UINT64_C(1) << 1)
 #define SD_RESOLVED_LLMNR_IPV6    (UINT64_C(1) << 2)
+#define SD_RESOLVED_MDNS_IPV4     (UINT64_C(1) << 3)
+#define SD_RESOLVED_MDNS_IPV6     (UINT64_C(1) << 4)
 #define SD_RESOLVED_NO_CNAME      (UINT64_C(1) << 5)
 #define SD_RESOLVED_NO_TXT        (UINT64_C(1) << 6)
 #define SD_RESOLVED_NO_ADDRESS    (UINT64_C(1) << 7)
@@ -31,4 +33,6 @@
 #define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) << 9)
 
 #define SD_RESOLVED_LLMNR         (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
-#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_LLMNR|SD_RESOLVED_DNS)
+#define SD_RESOLVED_MDNS          (SD_RESOLVED_MDNS_IPV4|SD_RESOLVED_MDNS_IPV6)
+
+#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_MDNS|SD_RESOLVED_LLMNR|SD_RESOLVED_DNS)
index bcb9994a8c567f842e6cd99a1f5da97c81606e7e..6124ff659c9679ce74e2336089d7dac1422a1b58 100644 (file)
@@ -459,7 +459,12 @@ int dns_cache_put(
 
         /* Second, add in positive entries for all contained RRs */
         for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) {
-                r = dns_cache_put_positive(c, answer->items[i].rr, authenticated, timestamp, owner_family, owner_address);
+                DnsResourceRecord *rr = answer->items[i].rr;
+
+                if (rr->key->cache_flush)
+                        dns_cache_remove(c, rr->key);
+
+                r = dns_cache_put_positive(c, rr, authenticated, timestamp, owner_family, owner_address);
                 if (r < 0)
                         goto fail;
         }
@@ -726,6 +731,39 @@ int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_
         return 1;
 }
 
+int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
+        unsigned ancount = 0;
+        Iterator iterator;
+        DnsCacheItem *i;
+        int r;
+
+        assert(cache);
+
+        HASHMAP_FOREACH(i, cache->by_key, iterator) {
+                DnsCacheItem *j;
+
+                LIST_FOREACH(by_key, j, i) {
+                        _cleanup_free_ char *t = NULL;
+
+                        if (!j->rr)
+                                continue;
+
+                        if (!dns_key_is_shared(j->rr->key))
+                                continue;
+
+                        r = dns_packet_append_rr(p, j->rr, NULL, NULL);
+                        if (r < 0)
+                                return r;
+
+                        ancount ++;
+                }
+        }
+
+        DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
+
+        return 0;
+}
+
 void dns_cache_dump(DnsCache *cache, FILE *f) {
         Iterator iterator;
         DnsCacheItem *i;
index 5f9116478585e34daa6114360f38bd1541b126ed..0f28bbe543ba581942d610b1937aabf3ec15e443 100644 (file)
@@ -32,6 +32,7 @@ typedef struct DnsCache {
 } DnsCache;
 
 #include "resolved-dns-answer.h"
+#include "resolved-dns-packet.h"
 #include "resolved-dns-question.h"
 #include "resolved-dns-rr.h"
 
@@ -45,3 +46,5 @@ int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_
 
 void dns_cache_dump(DnsCache *cache, FILE *f);
 bool dns_cache_is_empty(DnsCache *cache);
+
+int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p);
index ea776f7916dc71c568febd1ffe8e5c66a40270dd..9bd08eeec2553b52c90993654a7362db91d41ff2 100644 (file)
@@ -88,6 +88,16 @@ int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu, bool
                                                          0 /* ad */,
                                                          0 /* cd */,
                                                          0 /* rcode */));
+        else if (protocol == DNS_PROTOCOL_MDNS)
+                h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */,
+                                                         0 /* opcode */,
+                                                         0 /* aa */,
+                                                         0 /* tc */,
+                                                         0 /* rd (ask for recursion) */,
+                                                         0 /* ra */,
+                                                         0 /* ad */,
+                                                         0 /* cd */,
+                                                         0 /* rcode */));
         else
                 h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */,
                                                          0 /* opcode */,
@@ -182,6 +192,13 @@ int dns_packet_validate_reply(DnsPacket *p) {
 
                 break;
 
+        case DNS_PROTOCOL_MDNS:
+                /* RFC 6762, Section 18 */
+                if (DNS_PACKET_RCODE(p) != 0)
+                        return -EBADMSG;
+
+                break;
+
         default:
                 break;
         }
@@ -223,6 +240,18 @@ int dns_packet_validate_query(DnsPacket *p) {
 
                 break;
 
+        case DNS_PROTOCOL_MDNS:
+                /* RFC 6762, Section 18 */
+                if (DNS_PACKET_AA(p)    != 0 ||
+                    DNS_PACKET_RD(p)    != 0 ||
+                    DNS_PACKET_RA(p)    != 0 ||
+                    DNS_PACKET_AD(p)    != 0 ||
+                    DNS_PACKET_CD(p)    != 0 ||
+                    DNS_PACKET_RCODE(p) != 0)
+                        return -EBADMSG;
+
+                break;
+
         default:
                 break;
         }
@@ -1392,6 +1421,7 @@ fail:
 
 int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
         _cleanup_free_ char *name = NULL;
+        bool cache_flush = false;
         uint16_t class, type;
         DnsResourceKey *key;
         size_t saved_rindex;
@@ -1414,12 +1444,23 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
         if (r < 0)
                 goto fail;
 
+        if (p->protocol == DNS_PROTOCOL_MDNS) {
+                /* See RFC6762, Section 10.2 */
+
+                if (class & MDNS_RR_CACHE_FLUSH) {
+                        class &= ~MDNS_RR_CACHE_FLUSH;
+                        cache_flush = true;
+                }
+        }
+
         key = dns_resource_key_new_consume(class, type, name);
         if (!key) {
                 r = -ENOMEM;
                 goto fail;
         }
 
+        key->cache_flush = cache_flush;
+
         name = NULL;
         *ret = key;
 
@@ -1778,8 +1819,16 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
 
                 break;
 
-        case DNS_TYPE_NSEC:
-                r = dns_packet_read_name(p, &rr->nsec.next_domain_name, false, NULL);
+        case DNS_TYPE_NSEC: {
+
+                /*
+                 * RFC6762, section 18.14 explicly states mDNS should use name compression.
+                 * This contradicts RFC3845, section 2.1.1
+                 */
+
+                bool allow_compressed = p->protocol == DNS_PROTOCOL_MDNS;
+
+                r = dns_packet_read_name(p, &rr->nsec.next_domain_name, allow_compressed, NULL);
                 if (r < 0)
                         goto fail;
 
@@ -1792,7 +1841,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
                  * without the NSEC bit set. */
 
                 break;
-
+        }
         case DNS_TYPE_NSEC3: {
                 uint8_t size;
 
index aa2823cfb9daecec941dfa4397dc6d2b490d2642..48b3572cb4a4e08f4d077ffcda50bc298ef4b3de 100644 (file)
@@ -225,6 +225,9 @@ DnsProtocol dns_protocol_from_string(const char *s) _pure_;
 #define LLMNR_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 252U) })
 #define LLMNR_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03 } })
 
+#define MDNS_MULTICAST_IPV4_ADDRESS  ((struct in_addr) { .s_addr = htobe32(224U << 24 | 251U) })
+#define MDNS_MULTICAST_IPV6_ADDRESS  ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xfb } })
+
 static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, bool authenticated) {
         uint64_t f;
 
@@ -239,6 +242,9 @@ static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family,
         case DNS_PROTOCOL_LLMNR:
                 return f|(family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4);
 
+        case DNS_PROTOCOL_MDNS:
+                return family == AF_INET6 ? SD_RESOLVED_MDNS_IPV6 : SD_RESOLVED_MDNS_IPV4;
+
         default:
                 break;
         }
index b82fa7756221aef4a3596166abb1b50509771940..5c2306ba96ae97b18f3510819da017e93b6e85f4 100644 (file)
@@ -45,6 +45,9 @@ enum {
 #define DNSKEY_FLAG_ZONE_KEY (UINT16_C(1) << 8)
 #define DNSKEY_FLAG_SEP      (UINT16_C(1) << 0)
 
+/* mDNS RR flags */
+#define MDNS_RR_CACHE_FLUSH  (UINT16_C(1) << 15)
+
 /* DNSSEC algorithm identifiers, see
  * http://tools.ietf.org/html/rfc4034#appendix-A.1 and
  * https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
@@ -76,6 +79,7 @@ struct DnsResourceKey {
         unsigned n_ref;
         uint16_t class, type;
         char *_name; /* don't access directy, use DNS_RESOURCE_KEY_NAME()! */
+        bool cache_flush:1;
 };
 
 /* Creates a temporary resource key. This is only useful to quickly
@@ -246,6 +250,10 @@ int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRec
 int dns_resource_key_to_string(const DnsResourceKey *key, char **ret);
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref);
 
+static inline bool dns_key_is_shared(const DnsResourceKey *key) {
+        return IN_SET(key->type, DNS_TYPE_PTR);
+}
+
 DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key);
 DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name);
 DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr);
index a90692cdf40ae8739f8cc8f8a038492425a70960..eae903526b88f6489d7a353874f2894d94b6f2a2 100644 (file)
@@ -30,6 +30,7 @@
 #include "random-util.h"
 #include "resolved-dns-scope.h"
 #include "resolved-llmnr.h"
+#include "resolved-mdns.h"
 #include "socket-util.h"
 #include "strv.h"
 
@@ -59,6 +60,7 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
         LIST_PREPEND(scopes, m->dns_scopes, s);
 
         dns_scope_llmnr_membership(s, true);
+        dns_scope_mdns_membership(s, true);
 
         log_debug("New scope on link %s, protocol %s, family %s", l ? l->name : "*", dns_protocol_to_string(protocol), family == AF_UNSPEC ? "*" : af_to_name(family));
 
@@ -95,6 +97,7 @@ DnsScope* dns_scope_free(DnsScope *s) {
         log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family));
 
         dns_scope_llmnr_membership(s, false);
+        dns_scope_mdns_membership(s, false);
         dns_scope_abort_transactions(s);
 
         while (s->query_candidates)
@@ -162,7 +165,6 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
         union in_addr_union addr;
         int ifindex = 0, r;
         int family;
-        uint16_t port;
         uint32_t mtu;
         size_t saved_size = 0;
 
@@ -228,7 +230,6 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
                         return -EBUSY;
 
                 family = s->family;
-                port = LLMNR_PORT;
 
                 if (family == AF_INET) {
                         addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
@@ -241,7 +242,30 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
                 if (fd < 0)
                         return fd;
 
-                r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
+                r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, p);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case DNS_PROTOCOL_MDNS:
+                if (!ratelimit_test(&s->ratelimit))
+                        return -EBUSY;
+
+                family = s->family;
+
+                if (family == AF_INET) {
+                        addr.in = MDNS_MULTICAST_IPV4_ADDRESS;
+                        fd = manager_mdns_ipv4_fd(s->manager);
+                } else if (family == AF_INET6) {
+                        addr.in6 = MDNS_MULTICAST_IPV6_ADDRESS;
+                        fd = manager_mdns_ipv6_fd(s->manager);
+                } else
+                        return -EAFNOSUPPORT;
+                if (fd < 0)
+                        return fd;
+
+                r = manager_send(s->manager, fd, ifindex, family, &addr, MDNS_PORT, p);
                 if (r < 0)
                         return r;
 
@@ -477,19 +501,15 @@ int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
         return true;
 }
 
-int dns_scope_llmnr_membership(DnsScope *s, bool b) {
+static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in, struct in6_addr in6) {
         int fd;
 
         assert(s);
-
-        if (s->protocol != DNS_PROTOCOL_LLMNR)
-                return 0;
-
         assert(s->link);
 
         if (s->family == AF_INET) {
                 struct ip_mreqn mreqn = {
-                        .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS,
+                        .imr_multiaddr = in,
                         .imr_ifindex = s->link->ifindex,
                 };
 
@@ -508,7 +528,7 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) {
 
         } else if (s->family == AF_INET6) {
                 struct ipv6_mreq mreq = {
-                        .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS,
+                        .ipv6mr_multiaddr = in6,
                         .ipv6mr_interface = s->link->ifindex,
                 };
 
@@ -527,6 +547,22 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) {
         return 0;
 }
 
+int dns_scope_llmnr_membership(DnsScope *s, bool b) {
+
+        if (s->protocol != DNS_PROTOCOL_LLMNR)
+                return 0;
+
+        return dns_scope_multicast_membership(s, b, LLMNR_MULTICAST_IPV4_ADDRESS, LLMNR_MULTICAST_IPV6_ADDRESS);
+}
+
+int dns_scope_mdns_membership(DnsScope *s, bool b) {
+
+        if (s->protocol != DNS_PROTOCOL_MDNS)
+                return 0;
+
+        return dns_scope_multicast_membership(s, b, MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_IPV6_ADDRESS);
+}
+
 static int dns_scope_make_reply_packet(
                 DnsScope *s,
                 uint16_t id,
index 15d9a1fd6fab00e2859153112d4b3f05f90e73b5..2fc2e07debbda89479599ccbaf4da45acf9601c5 100644 (file)
@@ -93,6 +93,7 @@ DnsServer *dns_scope_get_dns_server(DnsScope *s);
 void dns_scope_next_dns_server(DnsScope *s);
 
 int dns_scope_llmnr_membership(DnsScope *s, bool b);
+int dns_scope_mdns_membership(DnsScope *s, bool b);
 
 void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p);
 
index 1103a34c6f66f3e07a151754e998ddb991241b69..90f07e6c4b9f6a6fb773c58f7b3398c42dfe8342 100644 (file)
@@ -24,6 +24,7 @@
 #include "dns-domain.h"
 #include "fd-util.h"
 #include "random-util.h"
+#include "resolved-dns-cache.h"
 #include "resolved-dns-transaction.h"
 #include "resolved-llmnr.h"
 #include "string-table.h"
@@ -243,7 +244,7 @@ static int on_stream_complete(DnsStream *s, int error) {
         }
 
         if (dns_packet_validate_reply(p) <= 0) {
-                log_debug("Invalid LLMNR TCP packet.");
+                log_debug("Invalid TCP reply packet.");
                 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
                 return 0;
         }
@@ -384,6 +385,18 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
 
                 break;
 
+        case DNS_PROTOCOL_MDNS:
+                assert(t->scope->link);
+
+                /* For mDNS we will not accept any packets from other interfaces */
+                if (p->ifindex != t->scope->link->ifindex)
+                        return;
+
+                if (p->family != t->scope->family)
+                        return;
+
+                break;
+
         case DNS_PROTOCOL_DNS:
                 break;
 
@@ -446,6 +459,13 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
         }
 
         if (DNS_PACKET_TC(p)) {
+
+                /* Truncated packets for mDNS are not allowed. Give up immediately. */
+                if (t->scope->protocol == DNS_PROTOCOL_MDNS) {
+                        dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+                        return;
+                }
+
                 /* Response was truncated, let's try again with good old TCP */
                 r = dns_transaction_open_tcp(t);
                 if (r == -ESRCH) {
@@ -454,7 +474,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                         return;
                 }
                 if (r < 0) {
-                        /* On LLMNR, if we cannot connect to the host,
+                        /* On LLMNR and mDNS, if we cannot connect to the host,
                          * we immediately give up */
                         if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
                                 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
@@ -481,29 +501,31 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                 return;
         }
 
-        /* Only consider responses with equivalent query section to the request */
-        if (p->question->n_keys != 1 || dns_resource_key_equal(p->question->keys[0], t->key) <= 0) {
-                dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
-                return;
-        }
+        if (t->scope->protocol == DNS_PROTOCOL_DNS) {
+                /* Only consider responses with equivalent query section to the request */
+                if (p->question->n_keys != 1 || dns_resource_key_equal(p->question->keys[0], t->key) <= 0) {
+                        dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+                        return;
+                }
 
-        /* Install the answer as answer to the transaction */
-        dns_answer_unref(t->answer);
-        t->answer = dns_answer_ref(p->answer);
-        t->answer_rcode = DNS_PACKET_RCODE(p);
-        t->answer_authenticated = t->scope->dnssec_mode == DNSSEC_TRUST && DNS_PACKET_AD(p);
-
-        /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
-        if (DNS_PACKET_SHALL_CACHE(p))
-                dns_cache_put(&t->scope->cache,
-                              t->key,
-                              DNS_PACKET_RCODE(p),
-                              p->answer,
-                              DNS_PACKET_ANCOUNT(p),
-                              t->answer_authenticated,
-                              0,
-                              p->family,
-                              &p->sender);
+                /* Install the answer as answer to the transaction */
+                dns_answer_unref(t->answer);
+                t->answer = dns_answer_ref(p->answer);
+                t->answer_rcode = DNS_PACKET_RCODE(p);
+                t->answer_authenticated = t->scope->dnssec_mode == DNSSEC_TRUST && DNS_PACKET_AD(p);
+
+                /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
+                if (DNS_PACKET_SHALL_CACHE(p))
+                        dns_cache_put(&t->scope->cache,
+                                      t->key,
+                                      DNS_PACKET_RCODE(p),
+                                      p->answer,
+                                      DNS_PACKET_ANCOUNT(p),
+                                      t->answer_authenticated,
+                                      0,
+                                      p->family,
+                                      &p->sender);
+        }
 
         if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
                 dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
@@ -571,21 +593,26 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat
         assert(s);
         assert(t);
 
-        /* Timeout reached? Increase the timeout for the server used */
-        switch (t->scope->protocol) {
-        case DNS_PROTOCOL_DNS:
-                assert(t->server);
+        if (!t->initial_jitter_scheduled || t->initial_jitter_elapsed) {
+                /* Timeout reached? Increase the timeout for the server used */
+                switch (t->scope->protocol) {
+                case DNS_PROTOCOL_DNS:
+                        assert(t->server);
 
-                dns_server_packet_lost(t->server, t->current_features, usec - t->start_usec);
+                        dns_server_packet_lost(t->server, t->current_features, usec - t->start_usec);
 
-                break;
-        case DNS_PROTOCOL_LLMNR:
-        case DNS_PROTOCOL_MDNS:
-                dns_scope_packet_lost(t->scope, usec - t->start_usec);
+                        break;
+                case DNS_PROTOCOL_LLMNR:
+                case DNS_PROTOCOL_MDNS:
+                        dns_scope_packet_lost(t->scope, usec - t->start_usec);
 
-                break;
-        default:
-                assert_not_reached("Invalid DNS protocol.");
+                        break;
+                default:
+                        assert_not_reached("Invalid DNS protocol.");
+                }
+
+                if (t->initial_jitter_scheduled)
+                        t->initial_jitter_elapsed = true;
         }
 
         /* ...and try again with a new server */
@@ -598,38 +625,6 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat
         return 0;
 }
 
-static int dns_transaction_make_packet(DnsTransaction *t) {
-        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
-        int r;
-
-        assert(t);
-
-        if (t->sent)
-                return 0;
-
-        r = dns_packet_new_query(&p, t->scope->protocol, 0, t->scope->dnssec_mode == DNSSEC_YES);
-        if (r < 0)
-                return r;
-
-        r = dns_scope_good_key(t->scope, t->key);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                return -EDOM;
-
-        r = dns_packet_append_key(p, t->key, NULL);
-        if (r < 0)
-                return r;
-
-        DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
-        DNS_PACKET_HEADER(p)->id = t->id;
-
-        t->sent = p;
-        p = NULL;
-
-        return 0;
-}
-
 static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
         assert(t);
         assert(t->scope);
@@ -639,17 +634,18 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
                 assert(t->server);
 
                 return t->server->resend_timeout;
-        case DNS_PROTOCOL_LLMNR:
         case DNS_PROTOCOL_MDNS:
+                assert(t->n_attempts > 0);
+                return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
+        case DNS_PROTOCOL_LLMNR:
                 return t->scope->resend_timeout;
         default:
                 assert_not_reached("Invalid DNS protocol.");
         }
 }
 
-int dns_transaction_go(DnsTransaction *t) {
+static int dns_transaction_prepare_next_attempt(DnsTransaction *t, usec_t ts) {
         bool had_stream;
-        usec_t ts;
         int r;
 
         assert(t);
@@ -658,11 +654,6 @@ int dns_transaction_go(DnsTransaction *t) {
 
         dns_transaction_stop(t);
 
-        log_debug("Excercising transaction on scope %s on %s/%s",
-                  dns_protocol_to_string(t->scope->protocol),
-                  t->scope->link ? t->scope->link->name : "*",
-                  t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
-
         if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
                 dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
                 return 0;
@@ -675,8 +666,6 @@ int dns_transaction_go(DnsTransaction *t) {
                 return 0;
         }
 
-        assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
-
         t->n_attempts++;
         t->start_usec = ts;
         t->received = dns_packet_unref(t->received);
@@ -739,31 +728,203 @@ int dns_transaction_go(DnsTransaction *t) {
                 }
         }
 
-        if (t->scope->protocol == DNS_PROTOCOL_LLMNR && !t->initial_jitter) {
-                usec_t jitter;
+        return 1;
+}
+
+static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
+
+        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+        bool add_known_answers = false;
+        DnsTransaction *other;
+        unsigned qdcount;
+        usec_t ts;
+        int r;
+
+        assert(t);
+        assert(t->scope->protocol == DNS_PROTOCOL_MDNS);
+
+        /* Discard any previously prepared packet, so we can start over and coaleasce again */
+        t->sent = dns_packet_unref(t->sent);
+
+        r = dns_packet_new_query(&p, t->scope->protocol, 0, false);
+        if (r < 0)
+                return r;
+
+        r = dns_packet_append_key(p, t->key, NULL);
+        if (r < 0)
+                return r;
+
+        qdcount = 1;
+
+        if (dns_key_is_shared(t->key))
+                add_known_answers = true;
+
+        /*
+         * For mDNS, we want to coalesce as many open queries in pending transactions into one single
+         * query packet on the wire as possible. To achieve that, we iterate through all pending transactions
+         * in our current scope, and see whether their timing contraints allow them to be sent.
+         */
+
+        assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
+        LIST_FOREACH(transactions_by_scope, other, t->scope->transactions) {
+
+                /* Skip ourselves */
+                if (other == t)
+                        continue;
+
+                if (other->state != DNS_TRANSACTION_PENDING)
+                        continue;
+
+                if (other->next_attempt_after > ts)
+                        continue;
+
+                if (qdcount >= UINT16_MAX)
+                        break;
+
+                r = dns_packet_append_key(p, other->key, NULL);
+
+                /*
+                 * If we can't stuff more questions into the packet, just give up.
+                 * One of the 'other' transactions will fire later and take care of the rest.
+                 */
+                if (r == -EMSGSIZE)
+                        break;
+
+                if (r < 0)
+                        return r;
+
+                r = dns_transaction_prepare_next_attempt(other, ts);
+                if (r <= 0)
+                        continue;
+
+                ts += transaction_get_resend_timeout(other);
+
+                r = sd_event_add_time(
+                                other->scope->manager->event,
+                                &other->timeout_event_source,
+                                clock_boottime_or_monotonic(),
+                                ts, 0,
+                                on_transaction_timeout, other);
+                if (r < 0)
+                        return r;
+
+                other->state = DNS_TRANSACTION_PENDING;
+                other->next_attempt_after = ts;
+
+                qdcount ++;
+
+                if (dns_key_is_shared(other->key))
+                        add_known_answers = true;
+        }
+
+        DNS_PACKET_HEADER(p)->qdcount = htobe16(qdcount);
+        DNS_PACKET_HEADER(p)->id = t->id;
+
+        /* Append known answer section if we're asking for any shared record */
+        if (add_known_answers) {
+                r = dns_cache_export_shared_to_packet(&t->scope->cache, p);
+                if (r < 0)
+                        return r;
+        }
+
+        t->sent = p;
+        p = NULL;
+
+        return 0;
+}
+
+static int dns_transaction_make_packet(DnsTransaction *t) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+        int r;
+
+        assert(t);
+
+        if (t->scope->protocol == DNS_PROTOCOL_MDNS)
+                return dns_transaction_make_packet_mdns(t);
+
+        if (t->sent)
+                return 0;
+
+        r = dns_packet_new_query(&p, t->scope->protocol, 0, t->scope->dnssec_mode == DNSSEC_YES);
+        if (r < 0)
+                return r;
+
+        r = dns_scope_good_key(t->scope, t->key);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EDOM;
+
+        r = dns_packet_append_key(p, t->key, NULL);
+        if (r < 0)
+                return r;
+
+        DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
+        DNS_PACKET_HEADER(p)->id = t->id;
+
+        t->sent = p;
+        p = NULL;
+
+        return 0;
+}
+
+int dns_transaction_go(DnsTransaction *t) {
+        usec_t ts;
+        int r;
+
+        assert(t);
+
+        assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
+        r = dns_transaction_prepare_next_attempt(t, ts);
+        if (r <= 0)
+                return r;
+
+        log_debug("Excercising transaction on scope %s on %s/%s",
+                  dns_protocol_to_string(t->scope->protocol),
+                  t->scope->link ? t->scope->link->name : "*",
+                  t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
+
+        if (!t->initial_jitter_scheduled &&
+            (t->scope->protocol == DNS_PROTOCOL_LLMNR ||
+             t->scope->protocol == DNS_PROTOCOL_MDNS)) {
+                usec_t jitter, accuracy;
 
                 /* RFC 4795 Section 2.7 suggests all queries should be
                  * delayed by a random time from 0 to JITTER_INTERVAL. */
 
-                t->initial_jitter = true;
+                t->initial_jitter_scheduled = true;
 
                 random_bytes(&jitter, sizeof(jitter));
-                jitter %= LLMNR_JITTER_INTERVAL_USEC;
+
+                switch (t->scope->protocol) {
+                case DNS_PROTOCOL_LLMNR:
+                        jitter %= LLMNR_JITTER_INTERVAL_USEC;
+                        accuracy = LLMNR_JITTER_INTERVAL_USEC;
+                        break;
+                case DNS_PROTOCOL_MDNS:
+                        jitter %= MDNS_JITTER_RANGE_USEC;
+                        jitter += MDNS_JITTER_MIN_USEC;
+                        accuracy = MDNS_JITTER_RANGE_USEC;
+                        break;
+                default:
+                        assert_not_reached("bad protocol");
+                }
 
                 r = sd_event_add_time(
                                 t->scope->manager->event,
                                 &t->timeout_event_source,
                                 clock_boottime_or_monotonic(),
-                                ts + jitter,
-                                LLMNR_JITTER_INTERVAL_USEC,
+                                ts + jitter, accuracy,
                                 on_transaction_timeout, t);
                 if (r < 0)
                         return r;
 
                 t->n_attempts = 0;
+                t->next_attempt_after = ts;
                 t->state = DNS_TRANSACTION_PENDING;
 
-                log_debug("Delaying LLMNR transaction for " USEC_FMT "us.", jitter);
+                log_debug("Delaying %s transaction for " USEC_FMT "us.", dns_protocol_to_string(t->scope->protocol), jitter);
                 return 0;
         }
 
@@ -810,16 +971,20 @@ int dns_transaction_go(DnsTransaction *t) {
                 return dns_transaction_go(t);
         }
 
+        ts += transaction_get_resend_timeout(t);
+
         r = sd_event_add_time(
                         t->scope->manager->event,
                         &t->timeout_event_source,
                         clock_boottime_or_monotonic(),
-                        ts + transaction_get_resend_timeout(t), 0,
+                        ts, 0,
                         on_transaction_timeout, t);
         if (r < 0)
                 return r;
 
         t->state = DNS_TRANSACTION_PENDING;
+        t->next_attempt_after = ts;
+
         return 1;
 }
 
index a3058ce6e875173016420cbb3fff8cf347d35225..af08b20e44b381feef57143e3deeafbff242f69a 100644 (file)
@@ -62,7 +62,8 @@ struct DnsTransaction {
         DnsTransactionState state;
         uint16_t id;
 
-        bool initial_jitter;
+        bool initial_jitter_scheduled;
+        bool initial_jitter_elapsed;
 
         DnsPacket *sent, *received;
 
@@ -72,6 +73,7 @@ struct DnsTransaction {
         bool answer_authenticated;
 
         usec_t start_usec;
+        usec_t next_attempt_after;
         sd_event_source *timeout_event_source;
         unsigned n_attempts;
 
@@ -119,6 +121,10 @@ DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_;
 /* LLMNR Jitter interval, see RFC 4795 Section 7 */
 #define LLMNR_JITTER_INTERVAL_USEC (100 * USEC_PER_MSEC)
 
+/* mDNS Jitter interval, see RFC 6762 Section 5.2 */
+#define MDNS_JITTER_MIN_USEC   (20 * USEC_PER_MSEC)
+#define MDNS_JITTER_RANGE_USEC (100 * USEC_PER_MSEC)
+
 /* Maximum attempts to send DNS requests, across all DNS servers */
 #define DNS_TRANSACTION_ATTEMPTS_MAX 16
 
index ddd9427dabdc2e37b2d6fce2af1ab422e97275ec..84100bd988e70bc0ad36320c4259c3a632ec2d4f 100644 (file)
@@ -77,6 +77,8 @@ Link *link_free(Link *l) {
         dns_scope_free(l->unicast_scope);
         dns_scope_free(l->llmnr_ipv4_scope);
         dns_scope_free(l->llmnr_ipv6_scope);
+        dns_scope_free(l->mdns_ipv4_scope);
+        dns_scope_free(l->mdns_ipv6_scope);
 
         free(l);
         return NULL;
@@ -118,6 +120,28 @@ static void link_allocate_scopes(Link *l) {
                 }
         } else
                 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
+
+        if (link_relevant(l, AF_INET) &&
+            l->mdns_support != SUPPORT_NO &&
+            l->manager->mdns_support != SUPPORT_NO) {
+                if (!l->mdns_ipv4_scope) {
+                        r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to allocate mDNS IPv4 scope: %m");
+                }
+        } else
+                l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope);
+
+        if (link_relevant(l, AF_INET6) &&
+            l->mdns_support != SUPPORT_NO &&
+            l->manager->mdns_support != SUPPORT_NO) {
+                if (!l->mdns_ipv6_scope) {
+                        r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to allocate mDNS IPv6 scope: %m");
+                }
+        } else
+                l->mdns_ipv6_scope = dns_scope_free(l->mdns_ipv6_scope);
 }
 
 void link_add_rrs(Link *l, bool force_remove) {
index eb00015bd55ad3e1cd91aade9bf75797ebd56e17..a3b406bbc21134e7d62a9534a3f267ad14767d71 100644 (file)
@@ -67,10 +67,13 @@ struct Link {
         unsigned n_search_domains;
 
         Support llmnr_support;
+        Support mdns_support;
 
         DnsScope *unicast_scope;
         DnsScope *llmnr_ipv4_scope;
         DnsScope *llmnr_ipv6_scope;
+        DnsScope *mdns_ipv4_scope;
+        DnsScope *mdns_ipv6_scope;
 
         char name[IF_NAMESIZE];
         uint32_t mtu;
index 5a3696ccb05f616df961ba0a7a7ccc1f42d53337..a2677f442aec2ae1df726f8fb6cbbed3dc7094d8 100644 (file)
@@ -40,6 +40,7 @@
 #include "resolved-llmnr.h"
 #include "resolved-manager.h"
 #include "resolved-resolv-conf.h"
+#include "resolved-mdns.h"
 #include "socket-util.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -472,6 +473,7 @@ int manager_new(Manager **ret) {
 
         m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1;
         m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1;
+        m->mdns_ipv4_fd = m->mdns_ipv6_fd = -1;
         m->hostname_fd = -1;
 
         m->llmnr_support = SUPPORT_YES;
@@ -528,6 +530,10 @@ int manager_start(Manager *m) {
         if (r < 0)
                 return r;
 
+        r = manager_mdns_start(m);
+        if (r < 0)
+                return r;
+
         return 0;
 }
 
@@ -559,6 +565,7 @@ Manager *manager_free(Manager *m) {
         sd_event_source_unref(m->rtnl_event_source);
 
         manager_llmnr_stop(m);
+        manager_mdns_stop(m);
 
         sd_bus_slot_unref(m->prepare_for_sleep_slot);
         sd_event_source_unref(m->bus_retry_event_source);
@@ -1024,11 +1031,25 @@ DnsScope* manager_find_scope(Manager *m, DnsPacket *p) {
         if (!l)
                 return NULL;
 
-        if (p->protocol == DNS_PROTOCOL_LLMNR) {
+        switch (p->protocol) {
+        case DNS_PROTOCOL_LLMNR:
                 if (p->family == AF_INET)
                         return l->llmnr_ipv4_scope;
                 else if (p->family == AF_INET6)
                         return l->llmnr_ipv6_scope;
+
+                break;
+
+        case DNS_PROTOCOL_MDNS:
+                if (p->family == AF_INET)
+                        return l->mdns_ipv4_scope;
+                else if (p->family == AF_INET6)
+                        return l->mdns_ipv6_scope;
+
+                break;
+
+        default:
+                break;
         }
 
         return NULL;
index 1056f23ab7c017f196d1538c36523979f8af2327..b52273403a5f6d39e1a1786e0deb97c23f0b1845 100644 (file)
@@ -54,6 +54,7 @@ struct Manager {
         sd_event *event;
 
         Support llmnr_support;
+        Support mdns_support;
 
         /* Network */
         Hashmap *links;
@@ -102,6 +103,13 @@ struct Manager {
         sd_event_source *llmnr_ipv4_tcp_event_source;
         sd_event_source *llmnr_ipv6_tcp_event_source;
 
+        /* mDNS */
+        int mdns_ipv4_fd;
+        int mdns_ipv6_fd;
+
+        sd_event_source *mdns_ipv4_event_source;
+        sd_event_source *mdns_ipv6_event_source;
+
         /* dbus */
         sd_bus *bus;
         sd_event_source *bus_retry_event_source;
diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c
new file mode 100644 (file)
index 0000000..096a4b1
--- /dev/null
@@ -0,0 +1,288 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2015 Daniel Mack
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+ ***/
+
+#include <resolv.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "fd-util.h"
+#include "resolved-manager.h"
+#include "resolved-mdns.h"
+
+void manager_mdns_stop(Manager *m) {
+        assert(m);
+
+        m->mdns_ipv4_event_source = sd_event_source_unref(m->mdns_ipv4_event_source);
+        m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
+
+        m->mdns_ipv6_event_source = sd_event_source_unref(m->mdns_ipv6_event_source);
+        m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
+}
+
+int manager_mdns_start(Manager *m) {
+        int r;
+
+        assert(m);
+
+        if (m->mdns_support == SUPPORT_NO)
+                return 0;
+
+        r = manager_mdns_ipv4_fd(m);
+        if (r == -EADDRINUSE)
+                goto eaddrinuse;
+        if (r < 0)
+                return r;
+
+        if (socket_ipv6_is_supported()) {
+                r = manager_mdns_ipv6_fd(m);
+                if (r == -EADDRINUSE)
+                        goto eaddrinuse;
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+
+eaddrinuse:
+        log_warning("There appears to be another mDNS responder running. Turning off mDNS support.");
+        m->mdns_support = SUPPORT_NO;
+        manager_mdns_stop(m);
+
+        return 0;
+}
+
+static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+        Manager *m = userdata;
+        DnsScope *scope;
+        int r;
+
+        r = manager_recv(m, fd, DNS_PROTOCOL_MDNS, &p);
+        if (r <= 0)
+                return r;
+
+        scope = manager_find_scope(m, p);
+        if (!scope) {
+                log_warning("Got mDNS UDP packet on unknown scope. Ignoring.");
+                return 0;
+        }
+
+        if (dns_packet_validate_reply(p) > 0) {
+                unsigned i;
+
+                log_debug("Got mDNS reply packet");
+
+                /*
+                 * mDNS is different from regular DNS and LLMNR with regard to handling responses.
+                 * While on other protocols, we can ignore every answer that doesn't match a question
+                 * we broadcast earlier, RFC6762, section 18.1 recommends looking at and caching all
+                 * incoming information, regardless of the DNS packet ID.
+                 *
+                 * Hence, extract the packet here, and try to find a transaction for answer the we got
+                 * and complete it. Also store the new information in scope's cache.
+                 */
+                r = dns_packet_extract(p);
+                if (r < 0) {
+                        log_debug("mDNS packet extraction failed.");
+                        return 0;
+                }
+
+                dns_scope_check_conflicts(scope, p);
+
+                for (i = 0; i < p->answer->n_rrs; i++) {
+                        DnsResourceRecord *rr;
+                        DnsTransaction *t;
+
+                        rr = p->answer->items[i].rr;
+
+                        t = dns_scope_find_transaction(scope, rr->key, false);
+                        if (t)
+                                dns_transaction_process_reply(t, p);
+                }
+
+                dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer,
+                              p->answer->n_rrs, false, 0, p->family, &p->sender);
+
+        } else if (dns_packet_validate_query(p) > 0)  {
+                log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));
+
+                dns_scope_process_query(scope, NULL, p);
+        } else
+                log_debug("Invalid mDNS UDP packet.");
+
+        return 0;
+}
+
+int manager_mdns_ipv4_fd(Manager *m) {
+        union sockaddr_union sa = {
+                .in.sin_family = AF_INET,
+                .in.sin_port = htobe16(MDNS_PORT),
+        };
+        static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
+        int r;
+
+        assert(m);
+
+        if (m->mdns_ipv4_fd >= 0)
+                return m->mdns_ipv4_fd;
+
+        m->mdns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (m->mdns_ipv4_fd < 0)
+                return -errno;
+
+        r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        /* Disable Don't-Fragment bit in the IP header */
+        r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = bind(m->mdns_ipv4_fd, &sa.sa, sizeof(sa.in));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, m->mdns_ipv4_fd, EPOLLIN, on_mdns_packet, m);
+        if (r < 0)
+                goto fail;
+
+        return m->mdns_ipv4_fd;
+
+fail:
+        m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
+        return r;
+}
+
+int manager_mdns_ipv6_fd(Manager *m) {
+        union sockaddr_union sa = {
+                .in6.sin6_family = AF_INET6,
+                .in6.sin6_port = htobe16(MDNS_PORT),
+        };
+        static const int one = 1, ttl = 255;
+        int r;
+
+        assert(m);
+
+        if (m->mdns_ipv6_fd >= 0)
+                return m->mdns_ipv6_fd;
+
+        m->mdns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (m->mdns_ipv6_fd < 0)
+                return -errno;
+
+        r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
+        r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = bind(m->mdns_ipv6_fd, &sa.sa, sizeof(sa.in6));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, m->mdns_ipv6_fd, EPOLLIN, on_mdns_packet, m);
+        if (r < 0)  {
+                r = -errno;
+                goto fail;
+        }
+
+        return m->mdns_ipv6_fd;
+
+fail:
+        m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
+        return r;
+}
diff --git a/src/resolve/resolved-mdns.h b/src/resolve/resolved-mdns.h
new file mode 100644 (file)
index 0000000..8a84010
--- /dev/null
@@ -0,0 +1,32 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2015 Daniel Mack
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "resolved-manager.h"
+
+#define MDNS_PORT 5353
+
+int manager_mdns_ipv4_fd(Manager *m);
+int manager_mdns_ipv6_fd(Manager *m);
+
+void manager_mdns_stop(Manager *m);
+int manager_mdns_start(Manager *m);
diff --git a/src/test/test-rlimit-util.c b/src/test/test-rlimit-util.c
new file mode 100644 (file)
index 0000000..00d3ecc
--- /dev/null
@@ -0,0 +1,69 @@
+/***
+  This file is part of systemd
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/resource.h>
+
+#include "capability-util.h"
+#include "macro.h"
+#include "rlimit-util.h"
+#include "string-util.h"
+#include "util.h"
+
+int main(int argc, char *argv[]) {
+        struct rlimit old, new, high;
+        struct rlimit err = {
+                .rlim_cur = 10,
+                .rlim_max = 5,
+        };
+
+        log_parse_environment();
+        log_open();
+
+        assert_se(drop_capability(CAP_SYS_RESOURCE) == 0);
+
+        assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0);
+        new.rlim_cur = MIN(5U, old.rlim_max);
+        new.rlim_max = MIN(10U, old.rlim_max);
+        assert_se(setrlimit(RLIMIT_NOFILE, &new) >= 0);
+
+        assert_se(rlimit_from_string("LimitNOFILE") == RLIMIT_NOFILE);
+        assert_se(rlimit_from_string("DefaultLimitNOFILE") == -1);
+
+        assert_se(streq_ptr(rlimit_to_string(RLIMIT_NOFILE), "LimitNOFILE"));
+        assert_se(rlimit_to_string(-1) == NULL);
+
+        assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0);
+        assert_se(setrlimit_closest(RLIMIT_NOFILE, &old) == 0);
+        assert_se(getrlimit(RLIMIT_NOFILE, &new) == 0);
+        assert_se(old.rlim_cur == new.rlim_cur);
+        assert_se(old.rlim_max == new.rlim_max);
+
+        assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0);
+        high = RLIMIT_MAKE_CONST(old.rlim_max + 1);
+        assert_se(setrlimit_closest(RLIMIT_NOFILE, &high) == 0);
+        assert_se(getrlimit(RLIMIT_NOFILE, &new) == 0);
+        assert_se(new.rlim_max == old.rlim_max);
+        assert_se(new.rlim_cur == new.rlim_max);
+
+        assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0);
+        assert_se(setrlimit_closest(RLIMIT_NOFILE, &err) == -EINVAL);
+        assert_se(getrlimit(RLIMIT_NOFILE, &new) == 0);
+        assert_se(old.rlim_cur == new.rlim_cur);
+        assert_se(old.rlim_max == new.rlim_max);
+
+        return 0;
+}