chiark / gitweb /
resolve: add llmnr responder side for UDP and TCP
authorLennart Poettering <lennart@poettering.net>
Tue, 29 Jul 2014 12:24:02 +0000 (14:24 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 29 Jul 2014 18:57:58 +0000 (20:57 +0200)
Name defending is still missing.

23 files changed:
Makefile.am
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-query.c
src/resolve/resolved-dns-query.h
src/resolve/resolved-dns-rr.c
src/resolve/resolved-dns-rr.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-dns-server.h
src/resolve/resolved-dns-stream.c [new file with mode: 0644]
src/resolve/resolved-dns-stream.h [new file with mode: 0644]
src/resolve/resolved-dns-zone.c [new file with mode: 0644]
src/resolve/resolved-dns-zone.h [new file with mode: 0644]
src/resolve/resolved-link.c
src/resolve/resolved-link.h
src/resolve/resolved-manager.c
src/resolve/resolved.h
src/shared/in-addr-util.c
src/shared/in-addr-util.h
src/shared/missing.h

index d02a8ca42bdc787e59cfd48d33346e623ddd579d..9a3913b9c1ea212676abd9f65aa404c26f07ccfc 100644 (file)
@@ -4754,7 +4754,11 @@ systemd_resolved_SOURCES = \
        src/resolve/resolved-dns-server.h \
        src/resolve/resolved-dns-server.c \
        src/resolve/resolved-dns-cache.h \
        src/resolve/resolved-dns-server.h \
        src/resolve/resolved-dns-server.c \
        src/resolve/resolved-dns-cache.h \
-       src/resolve/resolved-dns-cache.c
+       src/resolve/resolved-dns-cache.c \
+       src/resolve/resolved-dns-zone.h \
+       src/resolve/resolved-dns-zone.c \
+       src/resolve/resolved-dns-stream.h \
+       src/resolve/resolved-dns-stream.c
 
 nodist_systemd_resolved_SOURCES = \
        src/resolve/resolved-gperf.c
 
 nodist_systemd_resolved_SOURCES = \
        src/resolve/resolved-gperf.c
index 6ea5d49fc49e290f115aa2957c7bf921d2257e40..8c859d19b5fed0c23687cb1cf21bc7e616c6bd36 100644 (file)
 /* We never keep any item longer than 10min in our cache */
 #define CACHE_TTL_MAX_USEC (10 * USEC_PER_MINUTE)
 
 /* We never keep any item longer than 10min in our cache */
 #define CACHE_TTL_MAX_USEC (10 * USEC_PER_MINUTE)
 
+typedef enum DnsCacheItemType DnsCacheItemType;
+typedef struct DnsCacheItem DnsCacheItem;
+
+enum DnsCacheItemType {
+        DNS_CACHE_POSITIVE,
+        DNS_CACHE_NODATA,
+        DNS_CACHE_NXDOMAIN,
+};
+
+struct DnsCacheItem {
+        DnsResourceKey *key;
+        DnsResourceRecord *rr;
+        usec_t until;
+        DnsCacheItemType type;
+        unsigned prioq_idx;
+        LIST_FIELDS(DnsCacheItem, by_key);
+};
+
 static void dns_cache_item_free(DnsCacheItem *i) {
         if (!i)
                 return;
 static void dns_cache_item_free(DnsCacheItem *i) {
         if (!i)
                 return;
@@ -157,9 +175,11 @@ static int dns_cache_item_prioq_compare_func(const void *a, const void *b) {
         return 0;
 }
 
         return 0;
 }
 
-static int init_cache(DnsCache *c) {
+static int dns_cache_init(DnsCache *c) {
         int r;
 
         int r;
 
+        assert(c);
+
         r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func);
         if (r < 0)
                 return r;
         r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func);
         if (r < 0)
                 return r;
@@ -258,7 +278,7 @@ static int dns_cache_put_positive(DnsCache *c, DnsResourceRecord *rr, usec_t tim
         }
 
         /* Otherwise, add the new RR */
         }
 
         /* Otherwise, add the new RR */
-        r = init_cache(c);
+        r = dns_cache_init(c);
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
@@ -294,7 +314,7 @@ static int dns_cache_put_negative(DnsCache *c, DnsResourceKey *key, int rcode, u
         if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
                 return 0;
 
         if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
                 return 0;
 
-        r = init_cache(c);
+        r = dns_cache_init(c);
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
@@ -394,6 +414,7 @@ int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) {
 
         assert(c);
         assert(q);
 
         assert(c);
         assert(q);
+        assert(rcode);
         assert(ret);
 
         if (q->n_keys <= 0) {
         assert(ret);
 
         if (q->n_keys <= 0) {
index 6f5bf45a38c2c802eff0662bd6d7978f71ee132c..590cf691b3e75c2d6b03847fa5037ac3b72a2358 100644 (file)
@@ -28,8 +28,6 @@
 #include "time-util.h"
 #include "list.h"
 
 #include "time-util.h"
 #include "list.h"
 
-typedef struct DnsCacheItem DnsCacheItem;
-
 typedef struct DnsCache {
         Hashmap *by_key;
         Prioq *by_expiry;
 typedef struct DnsCache {
         Hashmap *by_key;
         Prioq *by_expiry;
@@ -39,21 +37,6 @@ typedef struct DnsCache {
 #include "resolved-dns-question.h"
 #include "resolved-dns-answer.h"
 
 #include "resolved-dns-question.h"
 #include "resolved-dns-answer.h"
 
-typedef enum DnsCacheItemType {
-        DNS_CACHE_POSITIVE,
-        DNS_CACHE_NODATA,
-        DNS_CACHE_NXDOMAIN,
-} DnsCacheItemType;
-
-typedef struct DnsCacheItem {
-        DnsResourceKey *key;
-        DnsResourceRecord *rr;
-        usec_t until;
-        DnsCacheItemType type;
-        unsigned prioq_idx;
-        LIST_FIELDS(DnsCacheItem, by_key);
-} DnsCacheItem;
-
 void dns_cache_flush(DnsCache *c);
 void dns_cache_prune(DnsCache *c);
 
 void dns_cache_flush(DnsCache *c);
 void dns_cache_prune(DnsCache *c);
 
index e5a4a403411e332fd220737585de5642921ffd20..1ff56875e64fd10f9c9e185fedb126f77bac38b8 100644 (file)
@@ -130,7 +130,7 @@ int dns_packet_validate(DnsPacket *p) {
         if (p->size > DNS_PACKET_SIZE_MAX)
                 return -EBADMSG;
 
         if (p->size > DNS_PACKET_SIZE_MAX)
                 return -EBADMSG;
 
-        return 0;
+        return 1;
 }
 
 int dns_packet_validate_reply(DnsPacket *p) {
 }
 
 int dns_packet_validate_reply(DnsPacket *p) {
@@ -142,13 +142,44 @@ int dns_packet_validate_reply(DnsPacket *p) {
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        if (DNS_PACKET_QR(p) == 0)
+        if (DNS_PACKET_QR(p) != 1)
+                return 0;
+
+        if (DNS_PACKET_OPCODE(p) != 0)
                 return -EBADMSG;
 
                 return -EBADMSG;
 
+        return 1;
+}
+
+int dns_packet_validate_query(DnsPacket *p) {
+        int r;
+
+        assert(p);
+
+        r = dns_packet_validate(p);
+        if (r < 0)
+                return r;
+
+        if (DNS_PACKET_QR(p) != 0)
+                return 0;
+
         if (DNS_PACKET_OPCODE(p) != 0)
                 return -EBADMSG;
 
         if (DNS_PACKET_OPCODE(p) != 0)
                 return -EBADMSG;
 
-        return 0;
+        if (DNS_PACKET_TC(p))
+                return -EBADMSG;
+
+        if (p->protocol == DNS_PROTOCOL_LLMNR &&
+            DNS_PACKET_QDCOUNT(p) != 1)
+                return -EBADMSG;
+
+        if (DNS_PACKET_ANCOUNT(p) > 0)
+                return -EBADMSG;
+
+        if (DNS_PACKET_NSCOUNT(p) > 0)
+                return -EBADMSG;
+
+        return 1;
 }
 
 static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start) {
 }
 
 static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start) {
@@ -216,6 +247,20 @@ static void dns_packet_truncate(DnsPacket *p, size_t sz) {
         p->size = sz;
 }
 
         p->size = sz;
 }
 
+int dns_packet_append_blob(DnsPacket *p, const void *d, size_t l, size_t *start) {
+        void *q;
+        int r;
+
+        assert(p);
+
+        r = dns_packet_extend(p, l, &q, start);
+        if (r < 0)
+                return r;
+
+        memcpy(q, d, l);
+        return 0;
+}
+
 int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start) {
         void *d;
         int r;
 int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start) {
         void *d;
         int r;
@@ -242,7 +287,25 @@ int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start) {
                 return r;
 
         ((uint8_t*) d)[0] = (uint8_t) (v >> 8);
                 return r;
 
         ((uint8_t*) d)[0] = (uint8_t) (v >> 8);
-        ((uint8_t*) d)[1] = (uint8_t) (v & 255);
+        ((uint8_t*) d)[1] = (uint8_t) v;
+
+        return 0;
+}
+
+int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start) {
+        void *d;
+        int r;
+
+        assert(p);
+
+        r = dns_packet_extend(p, sizeof(uint32_t), &d, start);
+        if (r < 0)
+                return r;
+
+        ((uint8_t*) d)[0] = (uint8_t) (v >> 24);
+        ((uint8_t*) d)[1] = (uint8_t) (v >> 16);
+        ((uint8_t*) d)[2] = (uint8_t) (v >> 8);
+        ((uint8_t*) d)[3] = (uint8_t) v;
 
         return 0;
 }
 
         return 0;
 }
@@ -387,6 +450,114 @@ fail:
         return r;
 }
 
         return r;
 }
 
+int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start) {
+        size_t saved_size, rdlength_offset, end, rdlength;
+        int r;
+
+        assert(p);
+        assert(rr);
+
+        saved_size = p->size;
+
+        r = dns_packet_append_key(p, rr->key, NULL);
+        if (r < 0)
+                goto fail;
+
+        r = dns_packet_append_uint32(p, rr->ttl, NULL);
+        if (r < 0)
+                goto fail;
+
+        /* Initially we write 0 here */
+        r = dns_packet_append_uint16(p, 0, &rdlength_offset);
+        if (r < 0)
+                goto fail;
+
+        switch (rr->key->type) {
+
+        case DNS_TYPE_PTR:
+        case DNS_TYPE_NS:
+        case DNS_TYPE_CNAME:
+                r = dns_packet_append_name(p, rr->ptr.name, NULL);
+                break;
+
+        case DNS_TYPE_HINFO:
+                r = dns_packet_append_string(p, rr->hinfo.cpu, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_string(p, rr->hinfo.os, NULL);
+                break;
+
+        case DNS_TYPE_A:
+                r = dns_packet_append_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL);
+                break;
+
+        case DNS_TYPE_AAAA:
+                r = dns_packet_append_blob(p, &rr->aaaa.in6_addr, sizeof(struct in6_addr), NULL);
+                break;
+
+        case DNS_TYPE_SOA:
+                r = dns_packet_append_name(p, rr->soa.mname, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_name(p, rr->soa.rname, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_uint32(p, rr->soa.serial, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_uint32(p, rr->soa.refresh, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_uint32(p, rr->soa.retry, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_uint32(p, rr->soa.expire, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_uint32(p, rr->soa.minimum, NULL);
+                break;
+
+        case DNS_TYPE_MX:
+        case DNS_TYPE_TXT:
+        case DNS_TYPE_SRV:
+        case DNS_TYPE_DNAME:
+        case DNS_TYPE_SSHFP:
+        default:
+                r = dns_packet_append_blob(p, rr->generic.data, rr->generic.size, NULL);
+                break;
+        }
+        if (r < 0)
+                goto fail;
+
+        /* Let's calculate the actual data size and update the field */
+        rdlength = p->size - rdlength_offset - sizeof(uint16_t);
+        if (rdlength > 0xFFFF) {
+                r = ENOSPC;
+                goto fail;
+        }
+
+        end = p->size;
+        p->size = rdlength_offset;
+        r = dns_packet_append_uint16(p, rdlength, NULL);
+        if (r < 0)
+                goto fail;
+        p->size = end;
+
+        return 0;
+
+fail:
+        dns_packet_truncate(p, saved_size);
+        return r;
+}
+
+
 int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
         assert(p);
 
 int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
         assert(p);
 
@@ -411,6 +582,21 @@ void dns_packet_rewind(DnsPacket *p, size_t idx) {
         p->rindex = idx;
 }
 
         p->rindex = idx;
 }
 
+int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start) {
+        const void *q;
+        int r;
+
+        assert(p);
+        assert(d);
+
+        r = dns_packet_read(p, sz, &q, start);
+        if (r < 0)
+                return r;
+
+        memcpy(d, q, sz);
+        return 0;
+}
+
 int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) {
         const void *d;
         int r;
 int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) {
         const void *d;
         int r;
@@ -696,19 +882,11 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
                 break;
 
         case DNS_TYPE_A:
                 break;
 
         case DNS_TYPE_A:
-                r = dns_packet_read(p, sizeof(struct in_addr), &d, NULL);
-                if (r < 0)
-                        goto fail;
-
-                memcpy(&rr->a.in_addr, d, sizeof(struct in_addr));
+                r = dns_packet_read_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL);
                 break;
 
         case DNS_TYPE_AAAA:
                 break;
 
         case DNS_TYPE_AAAA:
-                r = dns_packet_read(p, sizeof(struct in6_addr), &d, NULL);
-                if (r < 0)
-                        goto fail;
-
-                memcpy(&rr->aaaa.in6_addr, d, sizeof(struct in6_addr));
+                r = dns_packet_read_blob(p, &rr->aaaa.in6_addr, sizeof(struct in6_addr), NULL);
                 break;
 
         case DNS_TYPE_SOA:
                 break;
 
         case DNS_TYPE_SOA:
@@ -739,6 +917,11 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
                 r = dns_packet_read_uint32(p, &rr->soa.minimum, NULL);
                 break;
 
                 r = dns_packet_read_uint32(p, &rr->soa.minimum, NULL);
                 break;
 
+        case DNS_TYPE_MX:
+        case DNS_TYPE_TXT:
+        case DNS_TYPE_SRV:
+        case DNS_TYPE_DNAME:
+        case DNS_TYPE_SSHFP:
         default:
                 r = dns_packet_read(p, rdlength, &d, NULL);
                 if (r < 0)
         default:
                 r = dns_packet_read(p, rdlength, &d, NULL);
                 if (r < 0)
index b8370def32a1d0273022666455bf1d5e3b31fc13..ad4a38e6e42188c3dad8b996ecfbd2e61432650c 100644 (file)
@@ -77,8 +77,9 @@ struct DnsPacket {
 
         /* Packet reception meta data */
         int ifindex;
 
         /* Packet reception meta data */
         int ifindex;
-        int family;
+        int family, ipproto;
         union in_addr_union sender, destination;
         union in_addr_union sender, destination;
+        uint16_t sender_port, destination_port;
         uint32_t ttl;
 };
 
         uint32_t ttl;
 };
 
@@ -131,15 +132,20 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DnsPacket*, dns_packet_unref);
 
 int dns_packet_validate(DnsPacket *p);
 int dns_packet_validate_reply(DnsPacket *p);
 
 int dns_packet_validate(DnsPacket *p);
 int dns_packet_validate_reply(DnsPacket *p);
+int dns_packet_validate_query(DnsPacket *p);
 
 
+int dns_packet_append_blob(DnsPacket *p, const void *d, size_t sz, size_t *start);
 int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start);
 int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start);
 int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start);
 int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start);
+int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start);
 int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start);
 int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, size_t *start);
 int dns_packet_append_name(DnsPacket *p, const char *name, size_t *start);
 int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start);
 int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, size_t *start);
 int dns_packet_append_name(DnsPacket *p, const char *name, size_t *start);
-int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start);
+int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, size_t *start);
+int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start);
 
 int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start);
 
 int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start);
+int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start);
 int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start);
 int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start);
 int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start);
 int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start);
 int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start);
 int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start);
index 8b4aa3bfd7a6f4a4f1839572466ac4700282a6fb..32448c5822f5140b4661afaa7fd9d8e3fb178fd8 100644 (file)
@@ -43,8 +43,7 @@ DnsQueryTransaction* dns_query_transaction_free(DnsQueryTransaction *t) {
         dns_packet_unref(t->received);
         dns_answer_unref(t->cached);
 
         dns_packet_unref(t->received);
         dns_answer_unref(t->cached);
 
-        sd_event_source_unref(t->tcp_event_source);
-        safe_close(t->tcp_fd);
+        dns_stream_free(t->stream);
 
         if (t->scope) {
                 LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
 
         if (t->scope) {
                 LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
@@ -90,7 +89,6 @@ static int dns_query_transaction_new(DnsQueryTransaction **ret, DnsScope *s, Dns
         if (!t)
                 return -ENOMEM;
 
         if (!t)
                 return -ENOMEM;
 
-        t->tcp_fd = -1;
         t->question = dns_question_ref(q);
 
         do
         t->question = dns_question_ref(q);
 
         do
@@ -119,8 +117,7 @@ static void dns_query_transaction_stop(DnsQueryTransaction *t) {
         assert(t);
 
         t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
         assert(t);
 
         t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
-        t->tcp_event_source = sd_event_source_unref(t->tcp_event_source);
-        t->tcp_fd = safe_close(t->tcp_fd);
+        t->stream = dns_stream_free(t->stream);
 }
 
 void dns_query_transaction_complete(DnsQueryTransaction *t, DnsQueryState state) {
 }
 
 void dns_query_transaction_complete(DnsQueryTransaction *t, DnsQueryState state) {
@@ -149,132 +146,66 @@ void dns_query_transaction_complete(DnsQueryTransaction *t, DnsQueryState state)
         dns_query_transaction_gc(t);
 }
 
         dns_query_transaction_gc(t);
 }
 
-static int on_tcp_ready(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        DnsQueryTransaction *t = userdata;
-        int r;
-
-        assert(t);
-
-        if (revents & EPOLLOUT) {
-                struct iovec iov[2];
-                be16_t sz;
-                ssize_t ss;
-
-                sz = htobe16(t->sent->size);
-
-                iov[0].iov_base = &sz;
-                iov[0].iov_len = sizeof(sz);
-                iov[1].iov_base = DNS_PACKET_DATA(t->sent);
-                iov[1].iov_len = t->sent->size;
-
-                IOVEC_INCREMENT(iov, 2, t->tcp_written);
-
-                ss = writev(fd, iov, 2);
-                if (ss < 0) {
-                        if (errno != EINTR && errno != EAGAIN) {
-                                dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
-                                return -errno;
-                        }
-                } else
-                        t->tcp_written += ss;
-
-                /* Are we done? If so, disable the event source for EPOLLOUT */
-                if (t->tcp_written >= sizeof(sz) + t->sent->size) {
-                        r = sd_event_source_set_io_events(s, EPOLLIN);
-                        if (r < 0) {
-                                dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
-                                return r;
-                        }
-                }
-        }
-
-        if (revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) {
-
-                if (t->tcp_read < sizeof(t->tcp_read_size)) {
-                        ssize_t ss;
-
-                        ss = read(fd, (uint8_t*) &t->tcp_read_size + t->tcp_read, sizeof(t->tcp_read_size) - t->tcp_read);
-                        if (ss < 0) {
-                                if (errno != EINTR && errno != EAGAIN) {
-                                        dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
-                                        return -errno;
-                                }
-                        } else if (ss == 0) {
-                                dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
-                                return -EIO;
-                        } else
-                                t->tcp_read += ss;
-                }
-
-                if (t->tcp_read >= sizeof(t->tcp_read_size)) {
-
-                        if (be16toh(t->tcp_read_size) < DNS_PACKET_HEADER_SIZE) {
-                                dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
-                                return -EBADMSG;
-                        }
+static int on_stream_complete(DnsStream *s, int error) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+        DnsQueryTransaction *t;
 
 
-                        if (t->tcp_read < sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
-                                ssize_t ss;
+        assert(s);
+        assert(s->transaction);
 
 
-                                if (!t->received) {
-                                        r = dns_packet_new(&t->received, t->scope->protocol, be16toh(t->tcp_read_size));
-                                        if (r < 0) {
-                                                dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
-                                                return r;
-                                        }
-                                }
+        /* Copy the data we care about out of the stream before we
+         * destroy it. */
+        t = s->transaction;
+        p = dns_packet_ref(s->read_packet);
 
 
-                                ss = read(fd,
-                                          (uint8_t*) DNS_PACKET_DATA(t->received) + t->tcp_read - sizeof(t->tcp_read_size),
-                                          sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size) - t->tcp_read);
-                                if (ss < 0) {
-                                        if (errno != EINTR && errno != EAGAIN) {
-                                                dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
-                                                return -errno;
-                                        }
-                                } else if (ss == 0) {
-                                        dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
-                                        return -EIO;
-                                }  else
-                                        t->tcp_read += ss;
-                        }
+        t->stream = dns_stream_free(t->stream);
 
 
-                        if (t->tcp_read >= sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
-                                t->received->size = be16toh(t->tcp_read_size);
-                                dns_query_transaction_process_reply(t, t->received);
-                                return 0;
-                        }
-                }
+        if (error != 0) {
+                dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
+                return 0;
         }
 
         }
 
+        dns_query_transaction_process_reply(t, p);
         return 0;
 }
 
 static int dns_query_transaction_open_tcp(DnsQueryTransaction *t) {
         return 0;
 }
 
 static int dns_query_transaction_open_tcp(DnsQueryTransaction *t) {
+        _cleanup_close_ int fd = -1;
         int r;
 
         assert(t);
 
         int r;
 
         assert(t);
 
+        if (t->stream)
+                return 0;
+
         if (t->scope->protocol == DNS_PROTOCOL_DNS)
         if (t->scope->protocol == DNS_PROTOCOL_DNS)
-                return -ENOTSUP;
+                fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53);
+        else if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
+                if (!t->received)
+                        return -EINVAL;
 
 
-        if (t->tcp_fd >= 0)
-                return 0;
+                fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port);
+        } else
+                return -EAFNOSUPPORT;
 
 
-        t->tcp_written = 0;
-        t->tcp_read = 0;
-        t->received = dns_packet_unref(t->received);
+        if (fd < 0)
+                return fd;
+
+        r = dns_stream_new(t->scope->manager, &t->stream, t->scope->protocol, fd);
+        if (r < 0)
+                return r;
 
 
-        t->tcp_fd = dns_scope_tcp_socket(t->scope);
-        if (t->tcp_fd < 0)
-                return t->tcp_fd;
+        fd = -1;
 
 
-        r = sd_event_add_io(t->scope->manager->event, &t->tcp_event_source, t->tcp_fd, EPOLLIN|EPOLLOUT, on_tcp_ready, t);
+        r = dns_stream_write_packet(t->stream, t->sent);
         if (r < 0) {
         if (r < 0) {
-                t->tcp_fd = safe_close(t->tcp_fd);
+                t->stream = dns_stream_free(t->stream);
                 return r;
         }
 
                 return r;
         }
 
+        t->received = dns_packet_unref(t->received);
+        t->stream->complete = on_stream_complete;
+
         return 0;
 }
 
         return 0;
 }
 
@@ -289,12 +220,46 @@ void dns_query_transaction_process_reply(DnsQueryTransaction *t, DnsPacket *p) {
          * should hence not attempt to access the query or transaction
          * after calling this function. */
 
          * should hence not attempt to access the query or transaction
          * after calling this function. */
 
+        if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
+                assert(t->scope->link);
+
+                /* For LLMNR we will not accept any packets from other
+                 * interfaces */
+
+                if (p->ifindex != t->scope->link->ifindex)
+                        return;
+
+                if (p->family != t->scope->family)
+                        return;
+
+                if (p->ipproto == IPPROTO_UDP) {
+                        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;
+                }
+        }
+
+        if (t->scope->protocol == DNS_PROTOCOL_DNS) {
+
+                /* For DNS we are fine with accepting packets on any
+                 * interface, but the source IP address must be one of
+                 * a valid DNS server */
+
+                if (!dns_scope_good_dns_server(t->scope, p->family, &p->sender))
+                        return;
+
+                if (p->sender_port != 53)
+                        return;
+        }
+
         if (t->received != p) {
                 dns_packet_unref(t->received);
                 t->received = dns_packet_ref(p);
         }
 
         if (t->received != p) {
                 dns_packet_unref(t->received);
                 t->received = dns_packet_ref(p);
         }
 
-        if (t->tcp_fd >= 0) {
+        if (p->ipproto == IPPROTO_TCP) {
                 if (DNS_PACKET_TC(p)) {
                         /* Truncated via TCP? Somebody must be fucking with us */
                         dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
                 if (DNS_PACKET_TC(p)) {
                         /* Truncated via TCP? Somebody must be fucking with us */
                         dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
@@ -317,7 +282,14 @@ void dns_query_transaction_process_reply(DnsQueryTransaction *t, DnsPacket *p) {
                         return;
                 }
                 if (r < 0) {
                         return;
                 }
                 if (r < 0) {
-                        /* Couldn't send? Try immediately again, with a new server */
+                        /* On LLMNR, if we cannot connect to the host,
+                         * we immediately give up */
+                        if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
+                                dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
+                                return;
+                        }
+
+                        /* On DNS, couldn't send? Try immediately again, with a new server */
                         dns_scope_next_dns_server(t->scope);
 
                         r = dns_query_transaction_go(t);
                         dns_scope_next_dns_server(t->scope);
 
                         r = dns_query_transaction_go(t);
index 0b7656489269c187adc68a205fe0bffdae6d2818..37f50b67c4836e3df6dd74d0483758a29e35ca1d 100644 (file)
@@ -36,6 +36,7 @@ typedef struct DnsQueryTransaction DnsQueryTransaction;
 #include "resolved-dns-packet.h"
 #include "resolved-dns-question.h"
 #include "resolved-dns-answer.h"
 #include "resolved-dns-packet.h"
 #include "resolved-dns-question.h"
 #include "resolved-dns-answer.h"
+#include "resolved-dns-stream.h"
 
 typedef enum DnsQueryState {
         DNS_QUERY_NULL,
 
 typedef enum DnsQueryState {
         DNS_QUERY_NULL,
@@ -65,11 +66,8 @@ struct DnsQueryTransaction {
         sd_event_source *timeout_event_source;
         unsigned n_attempts;
 
         sd_event_source *timeout_event_source;
         unsigned n_attempts;
 
-        /* TCP connection logic */
-        int tcp_fd;
-        sd_event_source *tcp_event_source;
-        size_t tcp_written, tcp_read;
-        be16_t tcp_read_size;
+        /* TCP connection logic, if we need it */
+        DnsStream *stream;
 
         /* Queries this transaction is referenced by and that shall by
          * notified about this specific transaction completing. */
 
         /* Queries this transaction is referenced by and that shall by
          * notified about this specific transaction completing. */
index f68eb18425984f4a513a420bab30be46d73ea1aa..5097eff0838f36c24b46106bc5b4ce1705912916 100644 (file)
@@ -213,6 +213,40 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
         return NULL;
 }
 
         return NULL;
 }
 
+int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
+        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+        _cleanup_free_ char *ptr = NULL;
+        int r;
+
+        assert(ret);
+        assert(address);
+        assert(hostname);
+
+        r = dns_name_reverse(family, address, &ptr);
+        if (r < 0)
+                return r;
+
+        key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
+        if (!key)
+                return -ENOMEM;
+
+        ptr = NULL;
+
+        rr = dns_resource_record_new(key);
+        if (!rr)
+                return -ENOMEM;
+
+        rr->ptr.name = strdup(hostname);
+        if (!rr->ptr.name)
+                return -ENOMEM;
+
+        *ret = rr;
+        rr = NULL;
+
+        return 0;
+}
+
 int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
         int r;
 
 int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
         int r;
 
index 418bbede65d7f872b405201262b8c6d019821541..a9d14fc22f9805aa68d86830b16e09cbc820da45 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "util.h"
 #include "hashmap.h"
 
 #include "util.h"
 #include "hashmap.h"
+#include "in-addr-util.h"
 
 typedef struct DnsResourceKey DnsResourceKey;
 typedef struct DnsResourceRecord DnsResourceRecord;
 
 typedef struct DnsResourceKey DnsResourceKey;
 typedef struct DnsResourceRecord DnsResourceRecord;
@@ -49,8 +50,8 @@ enum {
         DNS_TYPE_TXT   = 0x10,
         DNS_TYPE_AAAA  = 0x1C,
         DNS_TYPE_SRV   = 0x21,
         DNS_TYPE_TXT   = 0x10,
         DNS_TYPE_AAAA  = 0x1C,
         DNS_TYPE_SRV   = 0x21,
-        DNS_TYPE_SSHFP = 0x2C,
         DNS_TYPE_DNAME = 0x27,
         DNS_TYPE_DNAME = 0x27,
+        DNS_TYPE_SSHFP = 0x2C,
 
         /* Special records */
         DNS_TYPE_ANY   = 0xFF,
 
         /* Special records */
         DNS_TYPE_ANY   = 0xFF,
@@ -141,6 +142,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref);
 DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key);
 DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr);
 DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr);
 DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key);
 DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr);
 DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr);
+int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name);
 int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b);
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref);
 
 int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b);
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref);
 
index 96a2ff7fae0fcdbae47af863dbd4775994423e31..9a636b179c82ee3d3ebcae2ebf4e9516375a9458 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <netinet/tcp.h>
 
 
 #include <netinet/tcp.h>
 
+#include "missing.h"
 #include "strv.h"
 #include "socket-util.h"
 #include "af-list.h"
 #include "strv.h"
 #include "socket-util.h"
 #include "af-list.h"
@@ -77,6 +78,7 @@ DnsScope* dns_scope_free(DnsScope *s) {
         }
 
         dns_cache_flush(&s->cache);
         }
 
         dns_cache_flush(&s->cache);
+        dns_zone_flush(&s->zone);
 
         LIST_REMOVE(scopes, s->manager->dns_scopes, s);
         strv_free(s->domains);
 
         LIST_REMOVE(scopes, s->manager->dns_scopes, s);
         strv_free(s->domains);
@@ -130,6 +132,9 @@ int dns_scope_send(DnsScope *s, DnsPacket *p) {
         if (s->protocol == DNS_PROTOCOL_DNS) {
                 DnsServer *srv;
 
         if (s->protocol == DNS_PROTOCOL_DNS) {
                 DnsServer *srv;
 
+                if (DNS_PACKET_QDCOUNT(p) > 1)
+                        return -ENOTSUP;
+
                 srv = dns_scope_get_server(s);
                 if (!srv)
                         return -ESRCH;
                 srv = dns_scope_get_server(s);
                 if (!srv)
                         return -ESRCH;
@@ -163,12 +168,10 @@ int dns_scope_send(DnsScope *s, DnsPacket *p) {
 
                 if (family == AF_INET) {
                         addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
 
                 if (family == AF_INET) {
                         addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
-                        /* fd = manager_dns_ipv4_fd(s->manager); */
                         fd = manager_llmnr_ipv4_udp_fd(s->manager);
                 } else if (family == AF_INET6) {
                         addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS;
                         fd = manager_llmnr_ipv6_udp_fd(s->manager);
                         fd = manager_llmnr_ipv4_udp_fd(s->manager);
                 } else if (family == AF_INET6) {
                         addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS;
                         fd = manager_llmnr_ipv6_udp_fd(s->manager);
-                        /* fd = manager_dns_ipv6_fd(s->manager); */
                 } else
                         return -EAFNOSUPPORT;
                 if (fd < 0)
                 } else
                         return -EAFNOSUPPORT;
                 if (fd < 0)
@@ -183,39 +186,86 @@ int dns_scope_send(DnsScope *s, DnsPacket *p) {
         return 1;
 }
 
         return 1;
 }
 
-int dns_scope_tcp_socket(DnsScope *s) {
+int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port) {
         _cleanup_close_ int fd = -1;
         union sockaddr_union sa = {};
         socklen_t salen;
         _cleanup_close_ int fd = -1;
         union sockaddr_union sa = {};
         socklen_t salen;
-        int one, ret;
-        DnsServer *srv;
-        int r;
+        static const int one = 1;
+        int ret, r;
 
         assert(s);
 
         assert(s);
+        assert((family == AF_UNSPEC) == !address);
 
 
-        srv = dns_scope_get_server(s);
-        if (!srv)
-                return -ESRCH;
-
-        sa.sa.sa_family = srv->family;
-        if (srv->family == AF_INET) {
-                sa.in.sin_port = htobe16(53);
-                sa.in.sin_addr = srv->address.in;
-                salen = sizeof(sa.in);
-        } else if (srv->family == AF_INET6) {
-                sa.in6.sin6_port = htobe16(53);
-                sa.in6.sin6_addr = srv->address.in6;
-                sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
-                salen = sizeof(sa.in6);
-        } else
-                return -EAFNOSUPPORT;
+        if (family == AF_UNSPEC) {
+                DnsServer *srv;
+
+                srv = dns_scope_get_server(s);
+                if (!srv)
+                        return -ESRCH;
+
+                sa.sa.sa_family = srv->family;
+                if (srv->family == AF_INET) {
+                        sa.in.sin_port = htobe16(port);
+                        sa.in.sin_addr = srv->address.in;
+                        salen = sizeof(sa.in);
+                } else if (srv->family == AF_INET6) {
+                        sa.in6.sin6_port = htobe16(port);
+                        sa.in6.sin6_addr = srv->address.in6;
+                        sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
+                        salen = sizeof(sa.in6);
+                } else
+                        return -EAFNOSUPPORT;
+        } else {
+                sa.sa.sa_family = family;
+
+                if (family == AF_INET) {
+                        sa.in.sin_port = htobe16(port);
+                        sa.in.sin_addr = address->in;
+                        salen = sizeof(sa.in);
+                } else if (family == AF_INET6) {
+                        sa.in6.sin6_port = htobe16(port);
+                        sa.in6.sin6_addr = address->in6;
+                        sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
+                        salen = sizeof(sa.in6);
+                } else
+                        return -EAFNOSUPPORT;
+        }
 
 
-        fd = socket(srv->family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        fd = socket(sa.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
         if (fd < 0)
                 return -errno;
 
         if (fd < 0)
                 return -errno;
 
-        one = 1;
-        setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
+        r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
+        if (r < 0)
+                return -errno;
+
+        if (s->link) {
+                uint32_t ifindex = htobe32(s->link->ifindex);
+
+                if (sa.sa.sa_family == AF_INET) {
+                        r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
+                        if (r < 0)
+                                return -errno;
+                } else if (sa.sa.sa_family == AF_INET6) {
+                        r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
+                        if (r < 0)
+                                return -errno;
+                }
+        }
+
+        if (s->protocol == DNS_PROTOCOL_LLMNR) {
+                /* RFC 4795, section 2.5 suggests the TTL to be set to 1 */
+
+                if (sa.sa.sa_family == AF_INET) {
+                        r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
+                        if (r < 0)
+                                return -errno;
+                } else if (sa.sa.sa_family == AF_INET6) {
+                        r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
+                        if (r < 0)
+                                return -errno;
+                }
+        }
 
         r = connect(fd, &sa.sa, salen);
         if (r < 0 && errno != EINPROGRESS)
 
         r = connect(fd, &sa.sa, salen);
         if (r < 0 && errno != EINPROGRESS)
@@ -223,6 +273,7 @@ int dns_scope_tcp_socket(DnsScope *s) {
 
         ret = fd;
         fd = -1;
 
         ret = fd;
         fd = -1;
+
         return ret;
 }
 
         return ret;
 }
 
@@ -324,3 +375,115 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) {
 
         return 0;
 }
 
         return 0;
 }
+
+int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union *address) {
+        assert(s);
+        assert(address);
+
+        if (s->protocol != DNS_PROTOCOL_DNS)
+                return 1;
+
+        if (s->link)
+                return !!link_find_dns_server(s->link,  family, address);
+        else
+                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) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+        unsigned i;
+        int r;
+
+        assert(s);
+
+        if (q->n_keys <= 0 && a->n_rrs <= 0)
+                return -EINVAL;
+
+        r = dns_packet_new(&p, s->protocol, 0);
+        if (r < 0)
+                return r;
+
+        DNS_PACKET_HEADER(p)->id = id;
+        DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(1, 0, 0, 0, 0, 0, 0, 0, rcode));
+
+        if (q) {
+                for (i = 0; i < q->n_keys; i++) {
+                        r = dns_packet_append_key(p, q->keys[i], NULL);
+                        if (r < 0)
+                                return r;
+                }
+
+                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 (r < 0)
+                                return r;
+                }
+
+                DNS_PACKET_HEADER(p)->ancount = htobe16(a->n_rrs);
+        }
+
+        *ret = p;
+        p = NULL;
+
+        return 0;
+}
+
+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;
+        int r, fd;
+
+        assert(s);
+        assert(p);
+
+        if (p->protocol != DNS_PROTOCOL_LLMNR)
+                return;
+
+        r = dns_packet_extract(p);
+        if (r < 0) {
+                log_debug("Failed to extract resources from incoming packet: %s", strerror(-r));
+                return;
+        }
+
+        r = dns_zone_lookup(&s->zone, p->question, &answer);
+        if (r < 0) {
+                log_debug("Failed to lookup key: %s", strerror(-r));
+                return;
+        }
+        if (r == 0)
+                return;
+
+        r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, &reply);
+        if (r < 0) {
+                log_debug("Failed to build reply packet: %s", strerror(-r));
+                return;
+        }
+
+        if (stream)
+                r = dns_stream_write_packet(stream, reply);
+        else {
+                if (p->family == AF_INET)
+                        fd = manager_llmnr_ipv4_udp_fd(s->manager);
+                else if (p->family == AF_INET6)
+                        fd = manager_llmnr_ipv6_udp_fd(s->manager);
+                else {
+                        log_debug("Unknown protocol");
+                        return;
+                }
+                if (fd < 0) {
+                        log_debug("Failed to get reply socket: %s", strerror(-fd));
+                        return;
+                }
+
+                r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, reply);
+        }
+
+        if (r < 0) {
+                log_debug("Failed to send reply packet: %s", strerror(-r));
+                return;
+        }
+}
index 6c93fa80be3a1c4ec3399a6f0ecc263c8f877e07..639c4b4e86c0e54fdf4da977601888c30b8495d2 100644 (file)
@@ -31,6 +31,8 @@ typedef struct DnsScope DnsScope;
 #include "resolved-dns-packet.h"
 #include "resolved-dns-query.h"
 #include "resolved-dns-cache.h"
 #include "resolved-dns-packet.h"
 #include "resolved-dns-query.h"
 #include "resolved-dns-cache.h"
+#include "resolved-dns-zone.h"
+#include "resolved-dns-stream.h"
 
 typedef enum DnsScopeMatch {
         DNS_SCOPE_NO,
 
 typedef enum DnsScopeMatch {
         DNS_SCOPE_NO,
@@ -51,6 +53,7 @@ struct DnsScope {
         char **domains;
 
         DnsCache cache;
         char **domains;
 
         DnsCache cache;
+        DnsZone zone;
 
         LIST_HEAD(DnsQueryTransaction, transactions);
 
 
         LIST_HEAD(DnsQueryTransaction, transactions);
 
@@ -61,12 +64,15 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, int family
 DnsScope* dns_scope_free(DnsScope *s);
 
 int dns_scope_send(DnsScope *s, DnsPacket *p);
 DnsScope* dns_scope_free(DnsScope *s);
 
 int dns_scope_send(DnsScope *s, DnsPacket *p);
-int dns_scope_tcp_socket(DnsScope *s);
+int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port);
 
 DnsScopeMatch dns_scope_good_domain(DnsScope *s, const char *domain);
 int dns_scope_good_key(DnsScope *s, DnsResourceKey *key);
 
 DnsScopeMatch dns_scope_good_domain(DnsScope *s, const char *domain);
 int dns_scope_good_key(DnsScope *s, DnsResourceKey *key);
+int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union *address);
 
 DnsServer *dns_scope_get_server(DnsScope *s);
 void dns_scope_next_dns_server(DnsScope *s);
 
 int dns_scope_llmnr_membership(DnsScope *s, bool b);
 
 DnsServer *dns_scope_get_server(DnsScope *s);
 void dns_scope_next_dns_server(DnsScope *s);
 
 int dns_scope_llmnr_membership(DnsScope *s, bool b);
+
+void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p);
index 45a7c4ff3b7563b7fb0fc1cc776973d33857f364..2736032ea6d4e6ffce258486269e441e2165ad45 100644 (file)
@@ -31,6 +31,7 @@ typedef enum DnsServerSource DnsServerSource;
 #include "resolved-dns-server.h"
 
 enum DnsServerSource {
 #include "resolved-dns-server.h"
 
 enum DnsServerSource {
+        DNS_SERVER_ANY,
         DNS_SERVER_SYSTEM,
         DNS_SERVER_LINK,
         _DNS_SERVER_SOURCE_MAX
         DNS_SERVER_SYSTEM,
         DNS_SERVER_LINK,
         _DNS_SERVER_SOURCE_MAX
diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c
new file mode 100644 (file)
index 0000000..24a2288
--- /dev/null
@@ -0,0 +1,380 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  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 <netinet/tcp.h>
+
+#include "missing.h"
+#include "resolved-dns-stream.h"
+
+#define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC)
+#define DNS_STREAMS_MAX 128
+
+static void dns_stream_stop(DnsStream *s) {
+        assert(s);
+
+        s->io_event_source = sd_event_source_unref(s->io_event_source);
+        s->timeout_event_source = sd_event_source_unref(s->timeout_event_source);
+        s->fd = safe_close(s->fd);
+}
+
+static int dns_stream_update_io(DnsStream *s) {
+        int f = 0;
+
+        assert(s);
+
+        if (s->write_packet && s->n_written < sizeof(s->write_size) + s->write_packet->size)
+                f |= EPOLLOUT;
+        if (!s->read_packet || s->n_read < sizeof(s->read_size) + s->read_packet->size)
+                f |= EPOLLIN;
+
+        return sd_event_source_set_io_events(s->io_event_source, f);
+}
+
+static int stream_complete(DnsStream *s, int error) {
+        assert(s);
+
+        dns_stream_stop(s);
+
+        if (s->complete)
+                s->complete(s, error);
+        else
+                dns_stream_free(s);
+
+        return 0;
+}
+
+static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) {
+        DnsStream *s = userdata;
+
+        assert(s);
+
+        return stream_complete(s, ETIMEDOUT);
+}
+
+static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
+        DnsStream *s = userdata;
+        int r;
+
+        assert(s);
+
+        if ((revents & EPOLLOUT) &&
+            s->write_packet &&
+            s->n_written < sizeof(s->write_size) + s->write_packet->size) {
+
+                struct iovec iov[2];
+                ssize_t ss;
+
+                iov[0].iov_base = &s->write_size;
+                iov[0].iov_len = sizeof(s->write_size);
+                iov[1].iov_base = DNS_PACKET_DATA(s->write_packet);
+                iov[1].iov_len = s->write_packet->size;
+
+                IOVEC_INCREMENT(iov, 2, s->n_written);
+
+                ss = writev(fd, iov, 2);
+                if (ss < 0) {
+                        if (errno != EINTR && errno != EAGAIN)
+                                return stream_complete(s, errno);
+                } else
+                        s->n_written += ss;
+
+                /* Are we done? If so, disable the event source for EPOLLOUT */
+                if (s->n_written >= sizeof(s->write_size) + s->write_packet->size) {
+                        r = dns_stream_update_io(s);
+                        if (r < 0)
+                                return stream_complete(s, -r);
+                }
+        }
+
+        if ((revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) &&
+            (!s->read_packet ||
+             s->n_read < sizeof(s->read_size) + s->read_packet->size)) {
+
+                if (s->n_read < sizeof(s->read_size)) {
+                        ssize_t ss;
+
+                        ss = read(fd, (uint8_t*) &s->read_size + s->n_read, sizeof(s->read_size) - s->n_read);
+                        if (ss < 0) {
+                                if (errno != EINTR && errno != EAGAIN)
+                                        return stream_complete(s, errno);
+                        } else if (ss == 0)
+                                return stream_complete(s, ECONNRESET);
+                        else
+                                s->n_read += ss;
+                }
+
+                if (s->n_read >= sizeof(s->read_size)) {
+
+                        if (be16toh(s->read_size) < DNS_PACKET_HEADER_SIZE)
+                                return stream_complete(s, EBADMSG);
+
+                        if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size)) {
+                                ssize_t ss;
+
+                                if (!s->read_packet) {
+                                        r = dns_packet_new(&s->read_packet, s->protocol, be16toh(s->read_size));
+                                        if (r < 0)
+                                                return stream_complete(s, -r);
+
+                                        s->read_packet->size = be16toh(s->read_size);
+                                        s->read_packet->ipproto = IPPROTO_TCP;
+                                        s->read_packet->family = s->peer.sa.sa_family;
+                                        s->read_packet->ttl = s->ttl;
+                                        s->read_packet->ifindex = s->ifindex;
+
+                                        if (s->read_packet->family == AF_INET) {
+                                                s->read_packet->sender.in = s->peer.in.sin_addr;
+                                                s->read_packet->sender_port = be16toh(s->peer.in.sin_port);
+                                                s->read_packet->destination.in = s->local.in.sin_addr;
+                                                s->read_packet->destination_port = be16toh(s->local.in.sin_port);
+                                        } else {
+                                                assert(s->read_packet->family == AF_INET6);
+                                                s->read_packet->sender.in6 = s->peer.in6.sin6_addr;
+                                                s->read_packet->sender_port = be16toh(s->peer.in6.sin6_port);
+                                                s->read_packet->destination.in6 = s->local.in6.sin6_addr;
+                                                s->read_packet->destination_port = be16toh(s->local.in6.sin6_port);
+
+                                                if (s->read_packet->ifindex == 0)
+                                                        s->read_packet->ifindex = s->peer.in6.sin6_scope_id;
+                                                if (s->read_packet->ifindex == 0)
+                                                        s->read_packet->ifindex = s->local.in6.sin6_scope_id;
+                                        }
+                                }
+
+                                ss = read(fd,
+                                          (uint8_t*) DNS_PACKET_DATA(s->read_packet) + s->n_read - sizeof(s->read_size),
+                                          sizeof(s->read_size) + be16toh(s->read_size) - s->n_read);
+                                if (ss < 0) {
+                                        if (errno != EINTR && errno != EAGAIN)
+                                                return stream_complete(s, errno);
+                                } else if (ss == 0)
+                                        return stream_complete(s, ECONNRESET);
+                                else
+                                        s->n_read += ss;
+                        }
+
+                        /* Are we done? If so, disable the event source for EPOLLIN */
+                        if (s->n_read >= sizeof(s->read_size) + be16toh(s->read_size)) {
+                                r = dns_stream_update_io(s);
+                                if (r < 0)
+                                        return stream_complete(s, -r);
+
+                                /* If there's a packet handler
+                                 * installed, call that. Note that
+                                 * this is optional... */
+                                if (s->on_packet)
+                                        return s->on_packet(s);
+                        }
+                }
+        }
+
+        if ((s->write_packet && s->n_written >= sizeof(s->write_size) + s->write_packet->size) &&
+            (s->read_packet && s->n_read >= sizeof(s->read_size) + s->read_packet->size))
+                return stream_complete(s, 0);
+
+        return 0;
+}
+
+DnsStream *dns_stream_free(DnsStream *s) {
+        if (!s)
+                return NULL;
+
+        dns_stream_stop(s);
+
+        if (s->manager) {
+                LIST_REMOVE(streams, s->manager->dns_streams, s);
+                s->manager->n_dns_streams--;
+        }
+
+        dns_packet_unref(s->write_packet);
+        dns_packet_unref(s->read_packet);
+
+        free(s);
+
+        return 0;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_free);
+
+int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
+        static const int one = 1;
+        union {
+                struct cmsghdr header; /* For alignment */
+                uint8_t buffer[CMSG_SPACE(MAX(sizeof(struct in_pktinfo), sizeof(struct in6_pktinfo)))
+                               + EXTRA_CMSG_SPACE /* kernel appears to require extra space */];
+        } control;
+        struct msghdr mh = {};
+        struct cmsghdr *cmsg;
+        _cleanup_(dns_stream_freep) DnsStream *s = NULL;
+        socklen_t sl;
+        int r;
+
+        assert(m);
+        assert(fd >= 0);
+
+        if (m->n_dns_streams > DNS_STREAMS_MAX)
+                return -EBUSY;
+
+        s = new0(DnsStream, 1);
+        if (!s)
+                return -ENOMEM;
+
+        s->fd = -1;
+        s->protocol = protocol;
+
+        /* Query the remote side */
+        s->peer_salen = sizeof(s->peer);
+        r = getpeername(fd, &s->peer.sa, &s->peer_salen);
+        if (r < 0)
+                return -errno;
+        if (s->peer.sa.sa_family == AF_INET6)
+                s->ifindex = s->peer.in6.sin6_scope_id;
+
+        /* Query the local side */
+        s->local_salen = sizeof(s->local);
+        r = getsockname(fd, &s->local.sa, &s->local_salen);
+        if (r < 0)
+                return -errno;
+        if (s->local.sa.sa_family == AF_INET6 && s->ifindex <= 0)
+                s->ifindex = s->local.in6.sin6_scope_id;
+
+        /* Check consistency */
+        assert(s->peer.sa.sa_family == s->local.sa.sa_family);
+        assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6));
+
+        /* Query connection meta information */
+        sl = sizeof(control);
+        if (s->peer.sa.sa_family == AF_INET) {
+                r = getsockopt(fd, IPPROTO_IP, IP_PKTOPTIONS, &control, &sl);
+                if (r < 0)
+                        return -errno;
+        } else {
+                assert(s->peer.sa.sa_family == AF_INET6);
+
+                r = getsockopt(fd, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, &control, &sl);
+                if (r < 0)
+                        return -errno;
+        }
+
+        mh.msg_control = &control;
+        mh.msg_controllen = sl;
+        for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
+
+                if (cmsg->cmsg_level == IPPROTO_IPV6) {
+                        assert(s->peer.sa.sa_family == AF_INET6);
+
+                        switch (cmsg->cmsg_type) {
+
+                        case IPV6_PKTINFO: {
+                                struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
+
+                                if (s->ifindex <= 0)
+                                        s->ifindex = i->ipi6_ifindex;
+                                break;
+                        }
+
+                        case IPV6_HOPLIMIT:
+                                s->ttl = *(int *) CMSG_DATA(cmsg);
+                                break;
+                        }
+
+                } else if (cmsg->cmsg_level == IPPROTO_IP) {
+                        assert(s->peer.sa.sa_family == AF_INET);
+
+                        switch (cmsg->cmsg_type) {
+
+                        case IP_PKTINFO: {
+                                struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
+
+                                if (s->ifindex <= 0)
+                                        s->ifindex = i->ipi_ifindex;
+                                break;
+                        }
+
+                        case IP_TTL:
+                                s->ttl = *(int *) CMSG_DATA(cmsg);
+                                break;
+                        }
+                }
+        }
+
+        /* The Linux kernel sets the interface index to the loopback
+         * device if the connection came from the local host since it
+         * avoids the routing table in such a case. Let's unset the
+         * interface index in such a case. */
+        if (s->ifindex > 0 && manager_ifindex_is_loopback(m, s->ifindex) != 0)
+                s->ifindex = 0;
+
+        /* If we don't know the interface index still, we look for the
+         * first local interface with a matching address. Yuck! */
+        if (s->ifindex <= 0)
+                s->ifindex = manager_find_ifindex(m, s->local.sa.sa_family, s->local.sa.sa_family == AF_INET ? (union in_addr_union*) &s->local.in.sin_addr : (union in_addr_union*)  &s->local.in6.sin6_addr);
+
+        r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
+        if (r < 0)
+                return -errno;
+
+        if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) {
+                uint32_t ifindex = htobe32(s->ifindex);
+
+                /* Make sure all packets for this connection are sent on the same interface */
+                if (s->local.sa.sa_family == AF_INET) {
+                        r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
+                        if (r < 0)
+                                return -errno;
+                } else if (s->local.sa.sa_family == AF_INET6) {
+                        r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
+                        if (r < 0)
+                                return -errno;
+                }
+        }
+
+        r = sd_event_add_io(m->event, &s->io_event_source, fd, EPOLLIN, on_stream_io, s);
+        if (r < 0)
+                return r;
+
+        r = sd_event_add_time(m->event, &s->timeout_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + DNS_STREAM_TIMEOUT_USEC, 0, on_stream_timeout, s);
+        if (r < 0)
+                return r;
+
+        LIST_PREPEND(streams, m->dns_streams, s);
+        s->manager = m;
+        s->fd = fd;
+        m->n_dns_streams++;
+
+        *ret = s;
+        s = NULL;
+
+        return 0;
+}
+
+int dns_stream_write_packet(DnsStream *s, DnsPacket *p) {
+        assert(s);
+
+        if (s->write_packet)
+                return -EBUSY;
+
+        s->write_packet = dns_packet_ref(p);
+        s->write_size = htobe16(p->size);
+        s->n_written = 0;
+
+        return dns_stream_update_io(s);
+}
diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h
new file mode 100644 (file)
index 0000000..db45658
--- /dev/null
@@ -0,0 +1,61 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  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 "socket-util.h"
+
+typedef struct DnsStream DnsStream;
+
+#include "resolved.h"
+
+struct DnsStream {
+        Manager *manager;
+
+        DnsProtocol protocol;
+
+        int fd;
+        union sockaddr_union peer;
+        socklen_t peer_salen;
+        union sockaddr_union local;
+        socklen_t local_salen;
+        int ifindex;
+        uint32_t ttl;
+
+        sd_event_source *io_event_source;
+        sd_event_source *timeout_event_source;
+
+        be16_t write_size, read_size;
+        DnsPacket *write_packet, *read_packet;
+        size_t n_written, n_read;
+
+        int (*on_packet)(DnsStream *s);
+        int (*complete)(DnsStream *s, int error);
+
+        DnsQueryTransaction *transaction;
+
+        LIST_FIELDS(DnsStream, streams);
+};
+
+int dns_stream_new(Manager *m, DnsStream **s, DnsProtocol protocol, int fd);
+DnsStream *dns_stream_free(DnsStream *s);
+
+int dns_stream_write_packet(DnsStream *s, DnsPacket *p);
diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c
new file mode 100644 (file)
index 0000000..2325ddf
--- /dev/null
@@ -0,0 +1,244 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  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 "list.h"
+
+#include "resolved-dns-zone.h"
+#include "resolved-dns-domain.h"
+#include "resolved-dns-packet.h"
+
+/* Never allow more than 1K entries */
+#define ZONE_MAX 1024
+
+typedef struct DnsZoneItem DnsZoneItem;
+
+struct DnsZoneItem {
+        DnsResourceRecord *rr;
+        bool verified;
+        LIST_FIELDS(DnsZoneItem, by_key);
+        LIST_FIELDS(DnsZoneItem, by_name);
+};
+
+static void dns_zone_item_free(DnsZoneItem *i) {
+        if (!i)
+                return;
+
+        dns_resource_record_unref(i->rr);
+        free(i);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(DnsZoneItem*, dns_zone_item_free);
+
+static void dns_zone_item_remove_and_free(DnsZone *z, DnsZoneItem *i) {
+        DnsZoneItem *first;
+
+        assert(z);
+
+        if (!i)
+                return;
+
+        first = hashmap_get(z->by_key, i->rr->key);
+        LIST_REMOVE(by_key, first, i);
+        if (first)
+                assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0);
+        else
+                hashmap_remove(z->by_key, i->rr->key);
+
+        first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key));
+        LIST_REMOVE(by_name, first, i);
+        if (first)
+                assert_se(hashmap_replace(z->by_name, DNS_RESOURCE_KEY_NAME(first->rr->key), first) >= 0);
+        else
+                hashmap_remove(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key));
+
+        dns_zone_item_free(i);
+}
+
+void dns_zone_flush(DnsZone *z) {
+        DnsZoneItem *i;
+
+        assert(z);
+
+        while ((i = hashmap_first(z->by_key)))
+                dns_zone_item_remove_and_free(z, i);
+
+        assert(hashmap_size(z->by_key) == 0);
+        assert(hashmap_size(z->by_name) == 0);
+
+        hashmap_free(z->by_key);
+        z->by_key = NULL;
+
+        hashmap_free(z->by_name);
+        z->by_name = NULL;
+}
+
+static DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) {
+        DnsZoneItem *i;
+
+        assert(z);
+        assert(rr);
+
+        LIST_FOREACH(by_key, i, hashmap_get(z->by_key, rr->key))
+                if (dns_resource_record_equal(i->rr, rr))
+                        return i;
+
+        return NULL;
+}
+
+void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr) {
+        DnsZoneItem *i;
+
+        assert(z);
+        assert(rr);
+
+        i = dns_zone_get(z, rr);
+        if (i)
+                dns_zone_item_remove_and_free(z, i);
+}
+
+static int dns_zone_init(DnsZone *z) {
+        int r;
+
+        assert(z);
+
+        r = hashmap_ensure_allocated(&z->by_key, dns_resource_key_hash_func, dns_resource_key_compare_func);
+        if (r < 0)
+                return r;
+
+        r = hashmap_ensure_allocated(&z->by_name, dns_name_hash_func, dns_name_compare_func);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) {
+        DnsZoneItem *first;
+        int r;
+
+        first = hashmap_get(z->by_key, i->rr->key);
+        if (first) {
+                LIST_PREPEND(by_key, first, i);
+                assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0);
+        } else {
+                r = hashmap_put(z->by_key, i->rr->key, i);
+                if (r < 0)
+                        return r;
+        }
+
+        first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key));
+        if (first) {
+                LIST_PREPEND(by_name, first, i);
+                assert_se(hashmap_replace(z->by_name, DNS_RESOURCE_KEY_NAME(first->rr->key), first) >= 0);
+        } else {
+                r = hashmap_put(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key), i);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int dns_zone_put(DnsZone *z, DnsResourceRecord *rr) {
+        _cleanup_(dns_zone_item_freep) DnsZoneItem *i = NULL;
+        DnsZoneItem *existing;
+        int r;
+
+        assert(z);
+        assert(rr);
+
+        existing = dns_zone_get(z, rr);
+        if (existing)
+                return 0;
+
+        r = dns_zone_init(z);
+        if (r < 0)
+                return r;
+
+        i = new0(DnsZoneItem, 1);
+        if (!i)
+                return -ENOMEM;
+
+        i->rr = dns_resource_record_ref(rr);
+
+        r = dns_zone_link_item(z, i);
+        if (r < 0)
+                return r;
+
+        i = NULL;
+        return 0;
+}
+
+int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret) {
+        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+        int r;
+        unsigned i, n = 0;
+        bool has_other_rrs = false;
+
+        assert(z);
+        assert(q);
+        assert(ret);
+
+        if (q->n_keys <= 0) {
+                *ret = NULL;
+                return 0;
+        }
+
+        for (i = 0; i < q->n_keys; i++) {
+                DnsZoneItem *j;
+
+                j = hashmap_get(z->by_key, q->keys[i]);
+                if (!j) {
+                        if (hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])))
+                                has_other_rrs = true;
+
+                        continue;
+                }
+
+                LIST_FOREACH(by_name, j, j)
+                        n++;
+        }
+
+        if (n <= 0) {
+                *ret = NULL;
+                return has_other_rrs;
+        }
+
+        answer = dns_answer_new(n);
+        if (!answer)
+                return -ENOMEM;
+
+        for (i = 0; i < q->n_keys; i++) {
+                DnsZoneItem *j;
+
+                j = hashmap_get(z->by_key, q->keys[i]);
+                LIST_FOREACH(by_key, j, j) {
+                        r = dns_answer_add(answer, j->rr);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        *ret = answer;
+        answer = NULL;
+
+        return 1;
+}
diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h
new file mode 100644 (file)
index 0000000..89d0bbe
--- /dev/null
@@ -0,0 +1,40 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  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 "hashmap.h"
+
+typedef struct DnsZone {
+        Hashmap *by_key;
+        Hashmap *by_name;
+} DnsZone;
+
+#include "resolved-dns-rr.h"
+#include "resolved-dns-question.h"
+#include "resolved-dns-answer.h"
+
+void dns_zone_flush(DnsZone *z);
+
+int dns_zone_put(DnsZone *z, DnsResourceRecord *rr);
+void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr);
+
+int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **answer);
index 3c6c757722a0d5203e8485a36918fc93b590d625..6ac7c5be98e4ce92455c527119f50d5f4198d10c 100644 (file)
 #include "strv.h"
 #include "resolved-link.h"
 
 #include "strv.h"
 #include "resolved-link.h"
 
+#define DEFAULT_TTL (10)
+
+static void link_address_add_rrs(LinkAddress *a);
+
 int link_new(Manager *m, Link **ret, int ifindex) {
         _cleanup_(link_freep) Link *l = NULL;
         int r;
 int link_new(Manager *m, Link **ret, int ifindex) {
         _cleanup_(link_freep) Link *l = NULL;
         int r;
@@ -110,6 +114,13 @@ static void link_allocate_scopes(Link *l) {
                 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
 }
 
                 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
 }
 
+static void link_add_rrs(Link *l) {
+        LinkAddress *a;
+
+        LIST_FOREACH(addresses, a, l->addresses)
+                link_address_add_rrs(a);
+}
+
 int link_update_rtnl(Link *l, sd_rtnl_message *m) {
         const char *n = NULL;
         int r;
 int link_update_rtnl(Link *l, sd_rtnl_message *m) {
         const char *n = NULL;
         int r;
@@ -129,6 +140,8 @@ int link_update_rtnl(Link *l, sd_rtnl_message *m) {
         }
 
         link_allocate_scopes(l);
         }
 
         link_allocate_scopes(l);
+        link_add_rrs(l);
+
         return 0;
 }
 
         return 0;
 }
 
@@ -183,6 +196,7 @@ int link_update_monitor(Link *l) {
 
         link_update_dns_servers(l);
         link_allocate_scopes(l);
 
         link_update_dns_servers(l);
         link_allocate_scopes(l);
+        link_add_rrs(l);
 
         return 0;
 }
 
         return 0;
 }
@@ -210,7 +224,7 @@ bool link_relevant(Link *l, int family) {
         return false;
 }
 
         return false;
 }
 
-LinkAddress *link_find_address(Link *l, int family, union in_addr_union *in_addr) {
+LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
         LinkAddress *a;
 
         assert(l);
         LinkAddress *a;
 
         assert(l);
@@ -222,7 +236,7 @@ LinkAddress *link_find_address(Link *l, int family, union in_addr_union *in_addr
         return NULL;
 }
 
         return NULL;
 }
 
-DnsServer* link_find_dns_server(Link *l, int family, union in_addr_union *in_addr) {
+DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
         DnsServer *s;
 
         assert(l);
         DnsServer *s;
 
         assert(l);
@@ -230,7 +244,6 @@ DnsServer* link_find_dns_server(Link *l, int family, union in_addr_union *in_add
         LIST_FOREACH(servers, s, l->dns_servers)
                 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
                         return s;
         LIST_FOREACH(servers, s, l->dns_servers)
                 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
                         return s;
-
         return NULL;
 }
 
         return NULL;
 }
 
@@ -265,7 +278,7 @@ void link_next_dns_server(Link *l) {
         l->current_dns_server = l->dns_servers;
 }
 
         l->current_dns_server = l->dns_servers;
 }
 
-int link_address_new(Link *l, LinkAddress **ret, int family, union in_addr_union *in_addr) {
+int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
         LinkAddress *a;
 
         assert(l);
         LinkAddress *a;
 
         assert(l);
@@ -291,13 +304,130 @@ LinkAddress *link_address_free(LinkAddress *a) {
         if (!a)
                 return NULL;
 
         if (!a)
                 return NULL;
 
-        if (a->link)
+        if (a->link) {
                 LIST_REMOVE(addresses, a->link->addresses, a);
 
                 LIST_REMOVE(addresses, a->link->addresses, a);
 
+                if (a->llmnr_address_rr) {
+
+                        if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
+                                dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
+                        else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
+                                dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
+
+                        dns_resource_record_unref(a->llmnr_address_rr);
+                }
+
+                if (a->llmnr_ptr_rr) {
+                        if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
+                                dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
+                        else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
+                                dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
+
+                        dns_resource_record_unref(a->llmnr_ptr_rr);
+                }
+        }
+
         free(a);
         return NULL;
 }
 
         free(a);
         return NULL;
 }
 
+static void link_address_add_rrs(LinkAddress *a) {
+        int r;
+
+        assert(a);
+
+        if (a->family == AF_INET && a->link->llmnr_ipv4_scope) {
+
+                if (!a->link->manager->host_ipv4_key) {
+                        a->link->manager->host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->hostname);
+                        if (!a->link->manager->host_ipv4_key) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+                }
+
+                if (!a->llmnr_address_rr) {
+                        a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv4_key);
+                        if (!a->llmnr_address_rr) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        a->llmnr_address_rr->a.in_addr = a->in_addr.in;
+                        a->llmnr_address_rr->ttl = DEFAULT_TTL;
+                }
+
+                if (!a->llmnr_ptr_rr) {
+                        r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
+                        if (r < 0)
+                                goto fail;
+
+                        a->llmnr_ptr_rr->ttl = DEFAULT_TTL;
+                }
+
+                if (link_address_relevant(a)) {
+                        r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
+                        if (r < 0)
+                                goto fail;
+
+                        r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
+                        if (r < 0)
+                                goto fail;
+                } else {
+                        dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
+                        dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
+                }
+        }
+
+        if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope) {
+
+                if (!a->link->manager->host_ipv6_key) {
+                        a->link->manager->host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->hostname);
+                        if (!a->link->manager->host_ipv6_key) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+                }
+
+                if (!a->llmnr_address_rr) {
+                        a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv6_key);
+                        if (!a->llmnr_address_rr) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
+                        a->llmnr_address_rr->ttl = DEFAULT_TTL;
+                }
+
+                if (!a->llmnr_ptr_rr) {
+                        r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
+                        if (r < 0)
+                                goto fail;
+
+                        a->llmnr_ptr_rr->ttl = DEFAULT_TTL;
+                }
+
+                if (link_address_relevant(a)) {
+                        r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
+                        if (r < 0)
+                                goto fail;
+
+                        r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
+                        if (r < 0)
+                                goto fail;
+                } else {
+                        dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
+                        dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
+                }
+        }
+
+        return;
+
+fail:
+        log_debug("Failed to update address RRs: %s", strerror(-r));
+}
+
 int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
         int r;
         assert(a);
 int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
         int r;
         assert(a);
@@ -310,6 +440,8 @@ int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
         sd_rtnl_message_addr_get_scope(m, &a->scope);
 
         link_allocate_scopes(a->link);
         sd_rtnl_message_addr_get_scope(m, &a->scope);
 
         link_allocate_scopes(a->link);
+        link_add_rrs(a->link);
+
         return 0;
 }
 
         return 0;
 }
 
index cef0400b217bb192be92100d5552027a9ca2638f..f58bd54203464ade2bc851701162a4e6ee83b86a 100644 (file)
@@ -32,6 +32,7 @@ typedef struct LinkAddress LinkAddress;
 #include "resolved.h"
 #include "resolved-dns-server.h"
 #include "resolved-dns-scope.h"
 #include "resolved.h"
 #include "resolved-dns-server.h"
 #include "resolved-dns-scope.h"
+#include "resolved-dns-rr.h"
 
 struct LinkAddress {
         Link *link;
 
 struct LinkAddress {
         Link *link;
@@ -41,6 +42,9 @@ struct LinkAddress {
 
         unsigned char flags, scope;
 
 
         unsigned char flags, scope;
 
+        DnsResourceRecord *llmnr_address_rr;
+        DnsResourceRecord *llmnr_ptr_rr;
+
         LIST_FIELDS(LinkAddress, addresses);
 };
 
         LIST_FIELDS(LinkAddress, addresses);
 };
 
@@ -71,13 +75,13 @@ Link *link_free(Link *l);
 int link_update_rtnl(Link *l, sd_rtnl_message *m);
 int link_update_monitor(Link *l);
 bool link_relevant(Link *l, int family);
 int link_update_rtnl(Link *l, sd_rtnl_message *m);
 int link_update_monitor(Link *l);
 bool link_relevant(Link *l, int family);
-LinkAddress* link_find_address(Link *l, int family, union in_addr_union *in_addr);
+LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *in_addr);
 
 
-DnsServer* link_find_dns_server(Link *l, int family, union in_addr_union *in_addr);
+DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr);
 DnsServer* link_get_dns_server(Link *l);
 void link_next_dns_server(Link *l);
 
 DnsServer* link_get_dns_server(Link *l);
 void link_next_dns_server(Link *l);
 
-int link_address_new(Link *l, LinkAddress **ret, int family, union in_addr_union *in_addr);
+int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr);
 LinkAddress *link_address_free(LinkAddress *a);
 int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m);
 bool link_address_relevant(LinkAddress *l);
 LinkAddress *link_address_free(LinkAddress *a);
 int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m);
 bool link_address_relevant(LinkAddress *l);
index f4fa197d49ff83e87beb575488a10c2c41c445c2..a8715bd5bf33af8aa7ad26b37588623755645534 100644 (file)
@@ -390,6 +390,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->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->use_llmnr = true;
 
 
         m->use_llmnr = true;
 
@@ -397,6 +398,10 @@ int manager_new(Manager **ret) {
         if (r < 0)
                 return r;
 
         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;
         r = sd_event_default(&m->event);
         if (r < 0)
                 return r;
@@ -422,6 +427,19 @@ int manager_new(Manager **ret) {
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
+        r = manager_llmnr_ipv4_udp_fd(m);
+        if (r < 0)
+                return r;
+        r = manager_llmnr_ipv6_udp_fd(m);
+        if (r < 0)
+                return r;
+        r = manager_llmnr_ipv4_tcp_fd(m);
+        if (r < 0)
+                return r;
+        r = manager_llmnr_ipv6_tcp_fd(m);
+        if (r < 0)
+                return r;
+
         *ret = m;
         m = NULL;
 
         *ret = m;
         m = NULL;
 
@@ -461,10 +479,19 @@ Manager *manager_free(Manager *m) {
         safe_close(m->llmnr_ipv4_udp_fd);
         safe_close(m->llmnr_ipv6_udp_fd);
 
         safe_close(m->llmnr_ipv4_udp_fd);
         safe_close(m->llmnr_ipv6_udp_fd);
 
+        sd_event_source_unref(m->llmnr_ipv4_tcp_event_source);
+        sd_event_source_unref(m->llmnr_ipv6_tcp_event_source);
+        safe_close(m->llmnr_ipv4_tcp_fd);
+        safe_close(m->llmnr_ipv6_tcp_fd);
+
         sd_event_source_unref(m->bus_retry_event_source);
         sd_bus_unref(m->bus);
 
         sd_event_unref(m->event);
         sd_event_source_unref(m->bus_retry_event_source);
         sd_bus_unref(m->bus);
 
         sd_event_unref(m->event);
+
+        dns_resource_key_unref(m->host_ipv4_key);
+        dns_resource_key_unref(m->host_ipv6_key);
+        free(m->hostname);
         free(m);
 
         return NULL;
         free(m);
 
         return NULL;
@@ -545,7 +572,7 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
                 struct cmsghdr header; /* For alignment */
                 uint8_t buffer[CMSG_SPACE(MAX(sizeof(struct in_pktinfo), sizeof(struct in6_pktinfo)))
                                + CMSG_SPACE(int) /* ttl/hoplimit */
                 struct cmsghdr header; /* For alignment */
                 uint8_t buffer[CMSG_SPACE(MAX(sizeof(struct in_pktinfo), sizeof(struct in6_pktinfo)))
                                + CMSG_SPACE(int) /* ttl/hoplimit */
-                               + 1024 /* kernel appears to require extra buffer space */];
+                               + EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */];
         } control;
         union sockaddr_union sa;
         struct msghdr mh = {};
         } control;
         union sockaddr_union sa;
         struct msghdr mh = {};
@@ -595,11 +622,15 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
         p->size = (size_t) l;
 
         p->family = sa.sa.sa_family;
         p->size = (size_t) l;
 
         p->family = sa.sa.sa_family;
-        if (p->family == AF_INET)
+        p->ipproto = IPPROTO_UDP;
+        if (p->family == AF_INET) {
                 p->sender.in = sa.in.sin_addr;
                 p->sender.in = sa.in.sin_addr;
-        else if (p->family == AF_INET6)
+                p->sender_port = be16toh(sa.in.sin_port);
+        } else if (p->family == AF_INET6) {
                 p->sender.in6 = sa.in6.sin6_addr;
                 p->sender.in6 = sa.in6.sin6_addr;
-        else
+                p->sender_port = be16toh(sa.in6.sin6_port);
+                p->ifindex = sa.in6.sin6_scope_id;
+        } else
                 return -EAFNOSUPPORT;
 
         for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
                 return -EAFNOSUPPORT;
 
         for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
@@ -612,7 +643,9 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
                         case IPV6_PKTINFO: {
                                 struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
 
                         case IPV6_PKTINFO: {
                                 struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
 
-                                p->ifindex = i->ipi6_ifindex;
+                                if (p->ifindex <= 0)
+                                        p->ifindex = i->ipi6_ifindex;
+
                                 p->destination.in6 = i->ipi6_addr;
                                 break;
                         }
                                 p->destination.in6 = i->ipi6_addr;
                                 break;
                         }
@@ -630,18 +663,32 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
                         case IP_PKTINFO: {
                                 struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
 
                         case IP_PKTINFO: {
                                 struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
 
-                                p->ifindex = i->ipi_ifindex;
+                                if (p->ifindex <= 0)
+                                        p->ifindex = i->ipi_ifindex;
+
                                 p->destination.in = i->ipi_addr;
                                 break;
                         }
 
                                 p->destination.in = i->ipi_addr;
                                 break;
                         }
 
-                        case IP_RECVTTL:
+                        case IP_TTL:
                                 p->ttl = *(int *) CMSG_DATA(cmsg);
                                 break;
                         }
                 }
         }
 
                                 p->ttl = *(int *) CMSG_DATA(cmsg);
                                 break;
                         }
                 }
         }
 
+        /* The Linux kernel sets the interface index to the loopback
+         * device if the packet came from the local host since it
+         * avoids the routing table in such a case. Let's unset the
+         * interface index in such a case. */
+        if (p->ifindex > 0 && manager_ifindex_is_loopback(m, p->ifindex) != 0)
+                p->ifindex = 0;
+
+        /* If we don't know the interface index still, we look for the
+         * first local interface with a matching address. Yuck! */
+        if (p->ifindex <= 0)
+                p->ifindex = manager_find_ifindex(m, p->family, &p->destination);
+
         *ret = p;
         p = NULL;
 
         *ret = p;
         p = NULL;
 
@@ -658,14 +705,15 @@ static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *use
         if (r <= 0)
                 return r;
 
         if (r <= 0)
                 return r;
 
-        if (dns_packet_validate_reply(p) >= 0) {
+        if (dns_packet_validate_reply(p) > 0) {
                 t = hashmap_get(m->dns_query_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
                 if (!t)
                         return 0;
 
                 dns_query_transaction_process_reply(t, p);
                 t = hashmap_get(m->dns_query_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
                 if (!t)
                         return 0;
 
                 dns_query_transaction_process_reply(t, p);
+
         } else
         } else
-                log_debug("Invalid reply packet.");
+                log_debug("Invalid DNS packet.");
 
         return 0;
 }
 
         return 0;
 }
@@ -754,7 +802,7 @@ static int sendmsg_loop(int fd, struct msghdr *mh, int flags) {
         }
 }
 
         }
 }
 
-static int manager_ipv4_send(Manager *m, int fd, int ifindex, struct in_addr *addr, uint16_t port, DnsPacket *p) {
+static int manager_ipv4_send(Manager *m, int fd, int ifindex, const struct in_addr *addr, uint16_t port, DnsPacket *p) {
         union sockaddr_union sa = {
                 .in.sin_family = AF_INET,
         };
         union sockaddr_union sa = {
                 .in.sin_family = AF_INET,
         };
@@ -803,7 +851,7 @@ static int manager_ipv4_send(Manager *m, int fd, int ifindex, struct in_addr *ad
         return sendmsg_loop(fd, &mh, 0);
 }
 
         return sendmsg_loop(fd, &mh, 0);
 }
 
-static int manager_ipv6_send(Manager *m, int fd, int ifindex, struct in6_addr *addr, uint16_t port, DnsPacket *p) {
+static int manager_ipv6_send(Manager *m, int fd, int ifindex, const struct in6_addr *addr, uint16_t port, DnsPacket *p) {
         union sockaddr_union sa = {
                 .in6.sin6_family = AF_INET6,
         };
         union sockaddr_union sa = {
                 .in6.sin6_family = AF_INET6,
         };
@@ -853,7 +901,7 @@ static int manager_ipv6_send(Manager *m, int fd, int ifindex, struct in6_addr *a
         return sendmsg_loop(fd, &mh, 0);
 }
 
         return sendmsg_loop(fd, &mh, 0);
 }
 
-int manager_send(Manager *m, int fd, int ifindex, int family, union in_addr_union *addr, uint16_t port, DnsPacket *p) {
+int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p) {
         assert(m);
         assert(fd >= 0);
         assert(addr);
         assert(m);
         assert(fd >= 0);
         assert(addr);
@@ -869,7 +917,7 @@ int manager_send(Manager *m, int fd, int ifindex, int family, union in_addr_unio
 }
 
 
 }
 
 
-DnsServer* manager_find_dns_server(Manager *m, int family, union in_addr_union *in_addr) {
+DnsServer* manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr) {
         DnsServer *s;
 
         assert(m);
         DnsServer *s;
 
         assert(m);
@@ -943,13 +991,30 @@ static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *u
         if (r <= 0)
                 return r;
 
         if (r <= 0)
                 return r;
 
-        if (dns_packet_validate_reply(p) >= 0) {
+        if (dns_packet_validate_reply(p) > 0) {
                 t = hashmap_get(m->dns_query_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
                 if (!t)
                         return 0;
 
                 dns_query_transaction_process_reply(t, p);
                 t = hashmap_get(m->dns_query_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
                 if (!t)
                         return 0;
 
                 dns_query_transaction_process_reply(t, p);
-        }
+
+        } else if (dns_packet_validate_query(p) > 0) {
+                Link *l;
+
+                l = hashmap_get(m->links, INT_TO_PTR(p->ifindex));
+                if (l) {
+                        DnsScope *scope = NULL;
+
+                        if (p->family == AF_INET)
+                                scope = l->llmnr_ipv4_scope;
+                        else if (p->family == AF_INET6)
+                                scope = l->llmnr_ipv6_scope;
+
+                        if (scope)
+                                dns_scope_process_query(scope, NULL, p);
+                }
+        } else
+                log_debug("Invalid LLMNR packet.");
 
         return 0;
 }
 
         return 0;
 }
@@ -1108,3 +1173,225 @@ fail:
         m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd);
         return r;
 }
         m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd);
         return r;
 }
+
+static int on_llmnr_stream_packet(DnsStream *s) {
+        assert(s);
+
+        if (dns_packet_validate_query(s->read_packet) > 0) {
+                Link *l;
+
+                l = hashmap_get(s->manager->links, INT_TO_PTR(s->read_packet->ifindex));
+                if (l) {
+                        DnsScope *scope = NULL;
+
+                        if (s->read_packet->family == AF_INET)
+                                scope = l->llmnr_ipv4_scope;
+                        else if (s->read_packet->family == AF_INET6)
+                                scope = l->llmnr_ipv6_scope;
+
+                        if (scope) {
+                                dns_scope_process_query(scope, s, s->read_packet);
+
+                                /* If no reply packet was set, we free the stream */
+                                if (s->write_packet)
+                                        return 0;
+                        }
+                }
+        }
+
+        dns_stream_free(s);
+        return 0;
+}
+
+static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        DnsStream *stream;
+        Manager *m = userdata;
+        int cfd, r;
+
+        cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
+        if (cfd < 0) {
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
+                return -errno;
+        }
+
+        r = dns_stream_new(m, &stream, DNS_PROTOCOL_LLMNR, cfd);
+        if (r < 0) {
+                safe_close(cfd);
+                return r;
+        }
+
+        stream->on_packet = on_llmnr_stream_packet;
+        return 0;
+}
+
+int manager_llmnr_ipv4_tcp_fd(Manager *m) {
+        union sockaddr_union sa = {
+                .in.sin_family = AF_INET,
+                .in.sin_port = htobe16(5355),
+        };
+        static const int one = 1, pmtu = IP_PMTUDISC_DONT;
+        int r;
+
+        assert(m);
+
+        if (m->llmnr_ipv4_tcp_fd >= 0)
+                return m->llmnr_ipv4_tcp_fd;
+
+        m->llmnr_ipv4_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (m->llmnr_ipv4_tcp_fd < 0)
+                return -errno;
+
+        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv4_tcp_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->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = bind(m->llmnr_ipv4_tcp_fd, &sa.sa, sizeof(sa.in));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = listen(m->llmnr_ipv4_tcp_fd, SOMAXCONN);
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = sd_event_add_io(m->event, &m->llmnr_ipv4_tcp_event_source, m->llmnr_ipv4_tcp_fd, EPOLLIN, on_llmnr_stream, m);
+        if (r < 0)
+                goto fail;
+
+        return m->llmnr_ipv4_tcp_fd;
+
+fail:
+        m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd);
+        return r;
+}
+
+int manager_llmnr_ipv6_tcp_fd(Manager *m) {
+        union sockaddr_union sa = {
+                .in6.sin6_family = AF_INET6,
+                .in6.sin6_port = htobe16(5355),
+        };
+        static const int one = 1;
+        int r;
+
+        assert(m);
+
+        if (m->llmnr_ipv6_tcp_fd >= 0)
+                return m->llmnr_ipv6_tcp_fd;
+
+        m->llmnr_ipv6_tcp_fd = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (m->llmnr_ipv6_tcp_fd < 0)
+                return -errno;
+
+        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = bind(m->llmnr_ipv6_tcp_fd, &sa.sa, sizeof(sa.in6));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = listen(m->llmnr_ipv6_tcp_fd, SOMAXCONN);
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, m->llmnr_ipv6_tcp_fd, EPOLLIN, on_llmnr_stream, m);
+        if (r < 0)  {
+                r = -errno;
+                goto fail;
+        }
+
+        return m->llmnr_ipv6_tcp_fd;
+
+fail:
+        m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd);
+        return r;
+}
+
+int manager_ifindex_is_loopback(Manager *m, int ifindex) {
+        Link *l;
+        assert(m);
+
+        if (ifindex <= 0)
+                return -EINVAL;
+
+        l = hashmap_get(m->links, INT_TO_PTR(ifindex));
+        if (l->flags & IFF_LOOPBACK)
+                return 1;
+
+        return 0;
+}
+
+int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr) {
+        Link *l;
+        Iterator i;
+
+        assert(m);
+
+        HASHMAP_FOREACH(l, m->links, i)
+                if (link_find_address(l, family, in_addr))
+                        return l->ifindex;
+
+        return 0;
+}
index 35ef8bdea104b592a24c57c35f7efb1e23e33a12..6aa0fc9e56dd846847ac489cc2a929a9a726842b 100644 (file)
@@ -34,6 +34,7 @@ typedef struct Manager Manager;
 #include "resolved-dns-query.h"
 #include "resolved-dns-server.h"
 #include "resolved-dns-scope.h"
 #include "resolved-dns-query.h"
 #include "resolved-dns-server.h"
 #include "resolved-dns-scope.h"
+#include "resolved-dns-stream.h"
 
 struct Manager {
         sd_event *event;
 
 struct Manager {
         sd_event *event;
@@ -54,6 +55,9 @@ struct Manager {
         LIST_HEAD(DnsQuery, dns_queries);
         unsigned n_dns_queries;
 
         LIST_HEAD(DnsQuery, dns_queries);
         unsigned n_dns_queries;
 
+        LIST_HEAD(DnsStream, dns_streams);
+        unsigned n_dns_streams;
+
         /* Unicast dns */
         int dns_ipv4_fd;
         int dns_ipv6_fd;
         /* Unicast dns */
         int dns_ipv4_fd;
         int dns_ipv6_fd;
@@ -70,15 +74,22 @@ struct Manager {
         /* LLMNR */
         int llmnr_ipv4_udp_fd;
         int llmnr_ipv6_udp_fd;
         /* LLMNR */
         int llmnr_ipv4_udp_fd;
         int llmnr_ipv6_udp_fd;
-        /* int llmnr_ipv4_tcp_fd; */
-        /* int llmnr_ipv6_tcp_fd; */
+        int llmnr_ipv4_tcp_fd;
+        int llmnr_ipv6_tcp_fd;
 
         sd_event_source *llmnr_ipv4_udp_event_source;
         sd_event_source *llmnr_ipv6_udp_event_source;
 
         sd_event_source *llmnr_ipv4_udp_event_source;
         sd_event_source *llmnr_ipv6_udp_event_source;
+        sd_event_source *llmnr_ipv4_tcp_event_source;
+        sd_event_source *llmnr_ipv6_tcp_event_source;
 
         /* dbus */
         sd_bus *bus;
         sd_event_source *bus_retry_event_source;
 
         /* dbus */
         sd_bus *bus;
         sd_event_source *bus_retry_event_source;
+
+        /* The hostname we publish on LLMNR and mDNS */
+        char *hostname;
+        DnsResourceKey *host_ipv4_key;
+        DnsResourceKey *host_ipv6_key;
 };
 
 /* Manager */
 };
 
 /* Manager */
@@ -89,18 +100,23 @@ Manager* manager_free(Manager *m);
 int manager_parse_config_file(Manager *m);
 int manager_write_resolv_conf(Manager *m);
 
 int manager_parse_config_file(Manager *m);
 int manager_write_resolv_conf(Manager *m);
 
-DnsServer* manager_find_dns_server(Manager *m, int family, union in_addr_union *in_addr);
+DnsServer* manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr);
 DnsServer *manager_get_dns_server(Manager *m);
 void manager_next_dns_server(Manager *m);
 uint32_t manager_find_mtu(Manager *m);
 
 DnsServer *manager_get_dns_server(Manager *m);
 void manager_next_dns_server(Manager *m);
 uint32_t manager_find_mtu(Manager *m);
 
-int manager_send(Manager *m, int fd, int ifindex, int family, union in_addr_union *addr, uint16_t port, DnsPacket *p);
+int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p);
 int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret);
 
 int manager_dns_ipv4_fd(Manager *m);
 int manager_dns_ipv6_fd(Manager *m);
 int manager_llmnr_ipv4_udp_fd(Manager *m);
 int manager_llmnr_ipv6_udp_fd(Manager *m);
 int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret);
 
 int manager_dns_ipv4_fd(Manager *m);
 int manager_dns_ipv6_fd(Manager *m);
 int manager_llmnr_ipv4_udp_fd(Manager *m);
 int manager_llmnr_ipv6_udp_fd(Manager *m);
+int manager_llmnr_ipv4_tcp_fd(Manager *m);
+int manager_llmnr_ipv6_tcp_fd(Manager *m);
+
+int manager_ifindex_is_loopback(Manager *m, int ifindex);
+int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr);
 
 int manager_connect_bus(Manager *m);
 
 
 int manager_connect_bus(Manager *m);
 
@@ -108,3 +124,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
 
 const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned length);
 int config_parse_dnsv(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 
 const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned length);
 int config_parse_dnsv(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+
+#define EXTRA_CMSG_SPACE 1024
index ff008bd26fb9ab591810b05db5637f54d070841f..e9a99177fcaf424923827db9074a06ad38820555 100644 (file)
@@ -23,7 +23,7 @@
 
 #include "in-addr-util.h"
 
 
 #include "in-addr-util.h"
 
-int in_addr_null(int family, union in_addr_union *u) {
+int in_addr_null(int family, const union in_addr_union *u) {
         assert(u);
 
         if (family == AF_INET)
         assert(u);
 
         if (family == AF_INET)
@@ -40,7 +40,7 @@ int in_addr_null(int family, union in_addr_union *u) {
 }
 
 
 }
 
 
-int in_addr_equal(int family, union in_addr_union *a, union in_addr_union *b) {
+int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
         assert(a);
         assert(b);
 
         assert(a);
         assert(b);
 
index 98de032d959db300c12e521360bb4fe40b091ee1..cff2c321ed980b2191d84f249fb5e1da24d63874 100644 (file)
@@ -31,8 +31,8 @@ union in_addr_union {
         struct in6_addr in6;
 };
 
         struct in6_addr in6;
 };
 
-int in_addr_null(int family, union in_addr_union *u);
-int in_addr_equal(int family, union in_addr_union *a, union in_addr_union *b);
+int in_addr_null(int family, const union in_addr_union *u);
+int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
 int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
 int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
 int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
 int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
 int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
 int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
index 29852854095f695f846ce2842feb5de034e86ae6..f4378a36b118c1941d92d6ff4669d596f6ebd6a9 100644 (file)
@@ -499,3 +499,7 @@ static inline int setns(int fd, int nstype) {
 
 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
 #endif
 
 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
 #endif
+
+#ifndef IPV6_UNICAST_IF
+#define IPV6_UNICAST_IF 76
+#endif