chiark / gitweb /
resolved: when there's already somebody listening on the LLMNR ports, simple disable...
[elogind.git] / src / resolve / resolved-dns-scope.c
index 5742f3ef472c3bb98cf7be8fee1dcf465c0dc5b4..0f654a6102bf2efff85de7670d068e43bd34f9ed 100644 (file)
@@ -28,8 +28,6 @@
 #include "resolved-dns-domain.h"
 #include "resolved-dns-scope.h"
 
-#define SEND_TIMEOUT_USEC (2*USEC_PER_SEC)
-
 int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) {
         DnsScope *s;
 
@@ -56,7 +54,7 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
 }
 
 DnsScope* dns_scope_free(DnsScope *s) {
-        DnsQueryTransaction *t;
+        DnsTransaction *t;
 
         if (!s)
                 return NULL;
@@ -71,10 +69,10 @@ DnsScope* dns_scope_free(DnsScope *s) {
                  * freed while we still look at it */
 
                 t->block_gc++;
-                dns_query_transaction_complete(t, DNS_QUERY_ABORTED);
+                dns_transaction_complete(t, DNS_TRANSACTION_ABORTED);
                 t->block_gc--;
 
-                dns_query_transaction_free(t);
+                dns_transaction_free(t);
         }
 
         dns_cache_flush(&s->cache);
@@ -87,7 +85,7 @@ DnsScope* dns_scope_free(DnsScope *s) {
         return NULL;
 }
 
-DnsServer *dns_scope_get_server(DnsScope *s) {
+DnsServer *dns_scope_get_dns_server(DnsScope *s) {
         assert(s);
 
         if (s->protocol != DNS_PROTOCOL_DNS)
@@ -135,7 +133,7 @@ int dns_scope_send(DnsScope *s, DnsPacket *p) {
                 if (DNS_PACKET_QDCOUNT(p) > 1)
                         return -ENOTSUP;
 
-                srv = dns_scope_get_server(s);
+                srv = dns_scope_get_dns_server(s);
                 if (!srv)
                         return -ESRCH;
 
@@ -199,7 +197,7 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add
         if (family == AF_UNSPEC) {
                 DnsServer *srv;
 
-                srv = dns_scope_get_server(s);
+                srv = dns_scope_get_dns_server(s);
                 if (!srv)
                         return -ESRCH;
 
@@ -355,6 +353,12 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) {
                 if (fd < 0)
                         return fd;
 
+                /* Always first try to drop membership before we add
+                 * one. This is necessary on some devices, such as
+                 * veth. */
+                if (b)
+                        setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn));
+
                 if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
                         return -errno;
 
@@ -368,6 +372,9 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) {
                 if (fd < 0)
                         return fd;
 
+                if (b)
+                        setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
+
                 if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
                         return -errno;
         } else
@@ -389,14 +396,25 @@ int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union
                 return !!manager_find_dns_server(s->manager, family, address);
 }
 
-static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQuestion *q, DnsAnswer *a, DnsPacket **ret) {
+static int dns_scope_make_reply_packet(
+                DnsScope *s,
+                uint16_t id,
+                int rcode,
+                DnsQuestion *q,
+                DnsAnswer *answer,
+                DnsAnswer *soa,
+                bool tentative,
+                DnsPacket **ret) {
+
         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
         unsigned i;
         int r;
 
         assert(s);
 
-        if (q->n_keys <= 0 && a->n_rrs <= 0)
+        if ((!q || q->n_keys <= 0)
+            && (!answer || answer->n_rrs <= 0)
+            && (!soa || soa->n_rrs <= 0))
                 return -EINVAL;
 
         r = dns_packet_new(&p, s->protocol, 0);
@@ -409,7 +427,7 @@ static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQ
                                                               0 /* opcode */,
                                                               0 /* c */,
                                                               0 /* tc */,
-                                                              0 /* t */,
+                                                              tentative,
                                                               0 /* (ra) */,
                                                               0 /* (ad) */,
                                                               0 /* (cd) */,
@@ -425,14 +443,24 @@ static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQ
                 DNS_PACKET_HEADER(p)->qdcount = htobe16(q->n_keys);
         }
 
-        if (a) {
-                for (i = 0; i < a->n_rrs; i++) {
-                        r = dns_packet_append_rr(p, a->rrs[i], NULL);
+        if (answer) {
+                for (i = 0; i < answer->n_rrs; i++) {
+                        r = dns_packet_append_rr(p, answer->rrs[i], NULL);
+                        if (r < 0)
+                                return r;
+                }
+
+                DNS_PACKET_HEADER(p)->ancount = htobe16(answer->n_rrs);
+        }
+
+        if (soa) {
+                for (i = 0; i < soa->n_rrs; i++) {
+                        r = dns_packet_append_rr(p, soa->rrs[i], NULL);
                         if (r < 0)
                                 return r;
                 }
 
-                DNS_PACKET_HEADER(p)->ancount = htobe16(a->n_rrs);
+                DNS_PACKET_HEADER(p)->arcount = htobe16(soa->n_rrs);
         }
 
         *ret = p;
@@ -443,7 +471,8 @@ static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQ
 
 void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
         _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
-        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
+        bool tentative = false;
         int r, fd;
 
         assert(s);
@@ -452,6 +481,18 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
         if (p->protocol != DNS_PROTOCOL_LLMNR)
                 return;
 
+        if (p->ipproto == IPPROTO_UDP) {
+                /* Don't accept UDP queries directed to anything but
+                 * the LLMNR multicast addresses. See RFC 4795,
+                 * section 2.5.*/
+
+                if (p->family == AF_INET && !in_addr_equal(AF_INET, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV4_ADDRESS))
+                        return;
+
+                if (p->family == AF_INET6 && !in_addr_equal(AF_INET6, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV6_ADDRESS))
+                        return;
+        }
+
         r = dns_packet_extract(p);
         if (r < 0) {
                 log_debug("Failed to extract resources from incoming packet: %s", strerror(-r));
@@ -463,7 +504,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
                 return;
         }
 
-        r = dns_zone_lookup(&s->zone, p->question, &answer);
+        r = dns_zone_lookup(&s->zone, p->question, &answer, &soa, &tentative);
         if (r < 0) {
                 log_debug("Failed to lookup key: %s", strerror(-r));
                 return;
@@ -471,7 +512,10 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
         if (r == 0)
                 return;
 
-        r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, &reply);
+        if (answer)
+                dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0);
+
+        r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, tentative, &reply);
         if (r < 0) {
                 log_debug("Failed to build reply packet: %s", strerror(-r));
                 return;
@@ -501,3 +545,19 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
                 return;
         }
 }
+
+DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *question) {
+        DnsTransaction *t;
+
+        assert(scope);
+        assert(question);
+
+        /* Try to find an ongoing transaction that is a equal or a
+         * superset of the specified question */
+
+        LIST_FOREACH(transactions_by_scope, t, scope->transactions)
+                if (dns_question_is_superset(t->question, question) > 0)
+                        return t;
+
+        return NULL;
+}