diff options
author | Lennart Poettering <lennart@poettering.net> | 2021-04-12 17:53:38 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-12 17:53:38 +0200 |
commit | 59b8635a33a96ef380490be1d9c5640a753df667 (patch) | |
tree | d97ff99f648e3392b92ba1fc266c3595fed6b6f5 /src/resolve | |
parent | Merge pull request #13496 from wat-ze-hex/custom-bpf-progs-parameterized-3 (diff) | |
parent | resolved: reply using unicast mDNS when appropriate (diff) | |
download | systemd-59b8635a33a96ef380490be1d9c5640a753df667.tar.xz systemd-59b8635a33a96ef380490be1d9c5640a753df667.zip |
Merge pull request #18701 from bugaevc/mdns-unicast
resolved: reply using unicast mDNS when appropriate
Diffstat (limited to 'src/resolve')
-rw-r--r-- | src/resolve/resolved-bus.c | 2 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.c | 29 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.h | 2 | ||||
-rw-r--r-- | src/resolve/resolved-dns-query.c | 4 | ||||
-rw-r--r-- | src/resolve/resolved-dns-question.c | 103 | ||||
-rw-r--r-- | src/resolve/resolved-dns-question.h | 46 | ||||
-rw-r--r-- | src/resolve/resolved-dns-rr.h | 8 | ||||
-rw-r--r-- | src/resolve/resolved-dns-scope.c | 14 | ||||
-rw-r--r-- | src/resolve/resolved-dns-stub.c | 4 | ||||
-rw-r--r-- | src/resolve/resolved-dns-transaction.h | 5 | ||||
-rw-r--r-- | src/resolve/resolved-mdns.c | 104 |
11 files changed, 231 insertions, 90 deletions
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 03e8fa49a8..31c96706a2 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -765,7 +765,7 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd if (!key) return -ENOMEM; - r = dns_question_add(question, key); + r = dns_question_add(question, key, 0); if (r < 0) return r; diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 8de407d21a..a197f94bcf 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -627,7 +627,7 @@ int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, const DnsAnswer if (r < 0) goto fail; - class = flags & DNS_ANSWER_CACHE_FLUSH ? k->class | MDNS_RR_CACHE_FLUSH : k->class; + class = flags & DNS_ANSWER_CACHE_FLUSH ? k->class | MDNS_RR_CACHE_FLUSH_OR_QU : k->class; r = dns_packet_append_uint16(p, class, NULL); if (r < 0) goto fail; @@ -1628,12 +1628,12 @@ static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t siz int dns_packet_read_key( DnsPacket *p, DnsResourceKey **ret, - bool *ret_cache_flush, + bool *ret_cache_flush_or_qu, size_t *ret_start) { _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; _cleanup_free_ char *name = NULL; - bool cache_flush = false; + bool cache_flush_or_qu = false; uint16_t class, type; int r; @@ -1653,11 +1653,11 @@ int dns_packet_read_key( return r; if (p->protocol == DNS_PROTOCOL_MDNS) { - /* See RFC6762, Section 10.2 */ + /* See RFC6762, sections 5.4 and 10.2 */ - if (type != DNS_TYPE_OPT && (class & MDNS_RR_CACHE_FLUSH)) { - class &= ~MDNS_RR_CACHE_FLUSH; - cache_flush = true; + if (type != DNS_TYPE_OPT && (class & MDNS_RR_CACHE_FLUSH_OR_QU)) { + class &= ~MDNS_RR_CACHE_FLUSH_OR_QU; + cache_flush_or_qu = true; } } @@ -1672,8 +1672,8 @@ int dns_packet_read_key( *ret = key; } - if (ret_cache_flush) - *ret_cache_flush = cache_flush; + if (ret_cache_flush_or_qu) + *ret_cache_flush_or_qu = cache_flush_or_qu; if (ret_start) *ret_start = rewinder.saved_rindex; @@ -2221,15 +2221,12 @@ static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question) for (i = 0; i < n; i++) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - bool cache_flush; + bool qu; - r = dns_packet_read_key(p, &key, &cache_flush, NULL); + r = dns_packet_read_key(p, &key, &qu, NULL); if (r < 0) return r; - if (cache_flush) - return -EBADMSG; - if (!dns_type_is_valid_query(key->type)) return -EBADMSG; @@ -2240,7 +2237,7 @@ static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question) /* Already in the Question, let's skip */ continue; - r = dns_question_add_raw(question, key); + r = dns_question_add_raw(question, key, qu ? DNS_QUESTION_WANTS_UNICAST_REPLY : 0); if (r < 0) return r; } @@ -2451,7 +2448,7 @@ int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key) { if (p->question->n_keys != 1) return 0; - return dns_resource_key_equal(p->question->keys[0], key); + return dns_resource_key_equal(dns_question_first_key(p->question), key); } int dns_packet_patch_max_udp_size(DnsPacket *p, uint16_t max_udp_size) { diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 7b2abe3e76..e9820795c0 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -233,7 +233,7 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start); int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start); int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start); int dns_packet_read_name(DnsPacket *p, char **ret, bool allow_compression, size_t *start); -int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start); +int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush_or_qu, size_t *start); int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start); void dns_packet_rewind(DnsPacket *p, size_t idx); diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index ce58680cf6..5517db149d 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -231,7 +231,7 @@ static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) { assert(dns_question_size(c->query->question_bypass->question) == 1); - if (!dns_scope_good_key(c->scope, c->query->question_bypass->question->keys[0])) + if (!dns_scope_good_key(c->scope, dns_question_first_key(c->query->question_bypass->question))) return 0; r = dns_query_candidate_add_transaction(c, NULL, c->query->question_bypass); @@ -502,7 +502,7 @@ int dns_query_new( /* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */ DNS_QUESTION_FOREACH(key, question_idna) { - r = dns_question_contains(question_utf8, key); + r = dns_question_contains_key(question_utf8, key); if (r < 0) return r; if (r > 0) diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index ef40932630..aefdaa0eeb 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -11,7 +11,7 @@ DnsQuestion *dns_question_new(size_t n) { if (n > UINT16_MAX) /* We can only place 64K key in an question section at max */ n = UINT16_MAX; - q = malloc0(offsetof(DnsQuestion, keys) + sizeof(DnsResourceKey*) * n); + q = malloc0(offsetof(DnsQuestion, items) + sizeof(DnsQuestionItem) * n); if (!q) return NULL; @@ -22,18 +22,19 @@ DnsQuestion *dns_question_new(size_t n) { } static DnsQuestion *dns_question_free(DnsQuestion *q) { - size_t i; + DnsResourceKey *key; assert(q); - for (i = 0; i < q->n_keys; i++) - dns_resource_key_unref(q->keys[i]); + DNS_QUESTION_FOREACH(key, q) + dns_resource_key_unref(key); + return mfree(q); } DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsQuestion, dns_question, dns_question_free); -int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key) { +int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags) { /* Insert without checking for duplicates. */ assert(key); @@ -42,11 +43,15 @@ int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key) { if (q->n_keys >= q->n_allocated) return -ENOSPC; - q->keys[q->n_keys++] = dns_resource_key_ref(key); + q->items[q->n_keys++] = (DnsQuestionItem) { + .key = dns_resource_key_ref(key), + .flags = flags, + }; return 0; } -int dns_question_add(DnsQuestion *q, DnsResourceKey *key) { +int dns_question_add(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags) { + DnsQuestionItem *item; int r; assert(key); @@ -54,19 +59,20 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key) { if (!q) return -ENOSPC; - for (size_t i = 0; i < q->n_keys; i++) { - r = dns_resource_key_equal(q->keys[i], key); + + DNS_QUESTION_FOREACH_ITEM(item, q) { + r = dns_resource_key_equal(item->key, key); if (r < 0) return r; - if (r > 0) + if (r > 0 && item->flags == flags) return 0; } - return dns_question_add_raw(q, key); + return dns_question_add_raw(q, key, flags); } int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) { - size_t i; + DnsResourceKey *key; int r; assert(rr); @@ -74,8 +80,8 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *s if (!q) return 0; - for (i = 0; i < q->n_keys; i++) { - r = dns_resource_key_match_rr(q->keys[i], rr, search_domain); + DNS_QUESTION_FOREACH(key, q) { + r = dns_resource_key_match_rr(key, rr, search_domain); if (r != 0) return r; } @@ -84,7 +90,7 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *s } int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) { - size_t i; + DnsResourceKey *key; int r; assert(rr); @@ -95,12 +101,12 @@ int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, c if (!IN_SET(rr->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME)) return 0; - for (i = 0; i < q->n_keys; i++) { + DNS_QUESTION_FOREACH(key, q) { /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */ - if (!dns_type_may_redirect(q->keys[i]->type)) + if (!dns_type_may_redirect(key->type)) return 0; - r = dns_resource_key_match_cname_or_dname(q->keys[i], rr->key, search_domain); + r = dns_resource_key_match_cname_or_dname(key, rr->key, search_domain); if (r != 0) return r; } @@ -122,38 +128,39 @@ int dns_question_is_valid_for_query(DnsQuestion *q) { if (q->n_keys > 65535) return 0; - name = dns_resource_key_name(q->keys[0]); + name = dns_resource_key_name(q->items[0].key); if (!name) return 0; /* Check that all keys in this question bear the same name */ for (i = 0; i < q->n_keys; i++) { - assert(q->keys[i]); + assert(q->items[i].key); if (i > 0) { - r = dns_name_equal(dns_resource_key_name(q->keys[i]), name); + r = dns_name_equal(dns_resource_key_name(q->items[i].key), name); if (r <= 0) return r; } - if (!dns_type_is_valid_query(q->keys[i]->type)) + if (!dns_type_is_valid_query(q->items[i].key->type)) return 0; } return 1; } -int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k) { +int dns_question_contains_key(DnsQuestion *q, const DnsResourceKey *k) { size_t j; int r; assert(k); - if (!a) + if (!q) return 0; - for (j = 0; j < a->n_keys; j++) { - r = dns_resource_key_equal(a->keys[j], k); + + for (j = 0; j < q->n_keys; j++) { + r = dns_resource_key_equal(q->items[j].key, k); if (r != 0) return r; } @@ -161,8 +168,25 @@ int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k) { return 0; } +static int dns_question_contains_item(DnsQuestion *q, const DnsQuestionItem *i) { + DnsQuestionItem *item; + int r; + + assert(i); + + DNS_QUESTION_FOREACH_ITEM(item, q) { + if (item->flags != i->flags) + continue; + r = dns_resource_key_equal(item->key, i->key); + if (r != 0) + return r; + } + + return false; +} + int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) { - size_t j; + DnsQuestionItem *item; int r; if (a == b) @@ -173,16 +197,15 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) { if (!b) return a->n_keys == 0; - /* Checks if all keys in a are also contained b, and vice versa */ + /* Checks if all items in a are also contained b, and vice versa */ - for (j = 0; j < a->n_keys; j++) { - r = dns_question_contains(b, a->keys[j]); + DNS_QUESTION_FOREACH_ITEM(item, a) { + r = dns_question_contains_item(b, item); if (r <= 0) return r; } - - for (j = 0; j < b->n_keys; j++) { - r = dns_question_contains(a, b->keys[j]); + DNS_QUESTION_FOREACH_ITEM(item, b) { + r = dns_question_contains_item(a, item); if (r <= 0) return r; } @@ -249,7 +272,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, if (!k) return -ENOMEM; - r = dns_question_add(n, k); + r = dns_question_add(n, k, 0); if (r < 0) return r; } @@ -267,7 +290,7 @@ const char *dns_question_first_name(DnsQuestion *q) { if (q->n_keys < 1) return NULL; - return dns_resource_key_name(q->keys[0]); + return dns_resource_key_name(q->items[0].key); } int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna) { @@ -306,7 +329,7 @@ int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bo if (!key) return -ENOMEM; - r = dns_question_add(q, key); + r = dns_question_add(q, key, 0); if (r < 0) return r; } @@ -318,7 +341,7 @@ int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bo if (!key) return -ENOMEM; - r = dns_question_add(q, key); + r = dns_question_add(q, key, 0); if (r < 0) return r; } @@ -354,7 +377,7 @@ int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_ reverse = NULL; - r = dns_question_add(q, key); + r = dns_question_add(q, key, 0); if (r < 0) return r; @@ -426,7 +449,7 @@ int dns_question_new_service( if (!key) return -ENOMEM; - r = dns_question_add(q, key); + r = dns_question_add(q, key, 0); if (r < 0) return r; @@ -436,7 +459,7 @@ int dns_question_new_service( if (!key) return -ENOMEM; - r = dns_question_add(q, key); + r = dns_question_add(q, key, 0); if (r < 0) return r; } diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h index 8f9a84c82d..31b8a2ec3e 100644 --- a/src/resolve/resolved-dns-question.h +++ b/src/resolve/resolved-dns-question.h @@ -2,16 +2,26 @@ #pragma once typedef struct DnsQuestion DnsQuestion; +typedef struct DnsQuestionItem DnsQuestionItem; #include "macro.h" #include "resolved-dns-rr.h" /* A simple array of resource keys */ +typedef enum DnsQuestionFlags { + DNS_QUESTION_WANTS_UNICAST_REPLY = 1 << 0, /* For mDNS: sender is willing to accept unicast replies */ +} DnsQuestionFlags; + +struct DnsQuestionItem { + DnsResourceKey *key; + DnsQuestionFlags flags; +}; + struct DnsQuestion { unsigned n_ref; size_t n_keys, n_allocated; - DnsResourceKey* keys[0]; + DnsQuestionItem items[0]; }; DnsQuestion *dns_question_new(size_t n); @@ -22,13 +32,13 @@ int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bo int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a); int dns_question_new_service(DnsQuestion **ret, const char *service, const char *type, const char *domain, bool with_txt, bool convert_idna); -int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key); -int dns_question_add(DnsQuestion *q, DnsResourceKey *key); +int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags); +int dns_question_add(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags); int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain); int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char* search_domain); int dns_question_is_valid_for_query(DnsQuestion *q); -int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k); +int dns_question_contains_key(DnsQuestion *q, const DnsResourceKey *k); int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b); int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret); @@ -37,6 +47,10 @@ void dns_question_dump(DnsQuestion *q, FILE *f); const char *dns_question_first_name(DnsQuestion *q); +static inline DnsResourceKey *dns_question_first_key(DnsQuestion *q) { + return (q && q->n_keys > 0) ? q->items[0].key : NULL; +} + static inline size_t dns_question_size(DnsQuestion *q) { return q ? q->n_keys : 0; } @@ -47,12 +61,22 @@ static inline bool dns_question_isempty(DnsQuestion *q) { DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref); -#define _DNS_QUESTION_FOREACH(u, key, q) \ - for (size_t UNIQ_T(i, u) = ({ \ - (key) = ((q) && (q)->n_keys > 0) ? (q)->keys[0] : NULL; \ - 0; \ - }); \ - (q) && (UNIQ_T(i, u) < (q)->n_keys); \ - UNIQ_T(i, u)++, (key) = (UNIQ_T(i, u) < (q)->n_keys ? (q)->keys[UNIQ_T(i, u)] : NULL)) +#define _DNS_QUESTION_FOREACH(u, k, q) \ + for (size_t UNIQ_T(i, u) = ({ \ + (k) = ((q) && (q)->n_keys > 0) ? (q)->items[0].key : NULL; \ + 0; \ + }); \ + (q) && (UNIQ_T(i, u) < (q)->n_keys); \ + UNIQ_T(i, u)++, (k) = (UNIQ_T(i, u) < (q)->n_keys ? (q)->items[UNIQ_T(i, u)].key : NULL)) #define DNS_QUESTION_FOREACH(key, q) _DNS_QUESTION_FOREACH(UNIQ, key, q) + +#define _DNS_QUESTION_FOREACH_ITEM(u, item, q) \ + for (size_t UNIQ_T(i, u) = ({ \ + (item) = dns_question_isempty(q) ? NULL : (q)->items; \ + 0; \ + }); \ + UNIQ_T(i, u) < dns_question_size(q); \ + UNIQ_T(i, u)++, (item) = (UNIQ_T(i, u) < dns_question_size(q) ? (q)->items + UNIQ_T(i, u) : NULL)) + +#define DNS_QUESTION_FOREACH_ITEM(item, q) _DNS_QUESTION_FOREACH_ITEM(UNIQ, item, q) diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index ab48ea5f2a..43bbcb3073 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -16,12 +16,12 @@ typedef struct DnsResourceRecord DnsResourceRecord; typedef struct DnsTxtItem DnsTxtItem; /* DNSKEY RR flags */ -#define DNSKEY_FLAG_SEP (UINT16_C(1) << 0) -#define DNSKEY_FLAG_REVOKE (UINT16_C(1) << 7) -#define DNSKEY_FLAG_ZONE_KEY (UINT16_C(1) << 8) +#define DNSKEY_FLAG_SEP (UINT16_C(1) << 0) +#define DNSKEY_FLAG_REVOKE (UINT16_C(1) << 7) +#define DNSKEY_FLAG_ZONE_KEY (UINT16_C(1) << 8) /* mDNS RR flags */ -#define MDNS_RR_CACHE_FLUSH (UINT16_C(1) << 15) +#define MDNS_RR_CACHE_FLUSH_OR_QU (UINT16_C(1) << 15) /* DNSSEC algorithm identifiers, see * http://tools.ietf.org/html/rfc4034#appendix-A.1 and diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 67c6f54dc5..81c62bdca6 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -287,17 +287,23 @@ static int dns_scope_emit_one(DnsScope *s, int fd, int family, DnsPacket *p) { return -EBUSY; if (family == AF_INET) { - addr.in = MDNS_MULTICAST_IPV4_ADDRESS; + if (in4_addr_is_null(&p->destination.in)) + addr.in = MDNS_MULTICAST_IPV4_ADDRESS; + else + addr = p->destination; fd = manager_mdns_ipv4_fd(s->manager); } else if (family == AF_INET6) { - addr.in6 = MDNS_MULTICAST_IPV6_ADDRESS; + if (in6_addr_is_null(&p->destination.in6)) + addr.in6 = MDNS_MULTICAST_IPV6_ADDRESS; + else + addr = p->destination; fd = manager_mdns_ipv6_fd(s->manager); } else return -EAFNOSUPPORT; if (fd < 0) return fd; - r = manager_send(s->manager, fd, s->link->ifindex, family, &addr, MDNS_PORT, NULL, p); + r = manager_send(s->manager, fd, s->link->ifindex, family, &addr, p->destination_port ?: MDNS_PORT, NULL, p); if (r < 0) return r; @@ -984,7 +990,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { } assert(dns_question_size(p->question) == 1); - key = p->question->keys[0]; + key = dns_question_first_key(p->question); r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative); if (r < 0) { diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index f82a997c18..5b9d32f001 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -882,13 +882,13 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea return; } - if (dns_type_is_obsolete(p->question->keys[0]->type)) { + if (dns_type_is_obsolete(dns_question_first_key(p->question)->type)) { log_debug("Got message with obsolete key type, refusing."); dns_stub_send_failure(m, l, s, p, DNS_RCODE_REFUSED, false); return; } - if (dns_type_is_zone_transer(p->question->keys[0]->type)) { + if (dns_type_is_zone_transer(dns_question_first_key(p->question)->type)) { log_debug("Got request for zone transfer, refusing."); dns_stub_send_failure(m, l, s, p, DNS_RCODE_REFUSED, false); return; diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index a8ec6e18d5..c2d73cbedc 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -166,10 +166,7 @@ static inline DnsResourceKey *dns_transaction_key(DnsTransaction *t) { assert(t->bypass); - if (dns_question_isempty(t->bypass->question)) - return NULL; - - return t->bypass->question->keys[0]; + return dns_question_first_key(t->bypass->question); } static inline uint64_t dns_transaction_source_to_query_flags(DnsTransactionSource s) { diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c index 2857b58e89..0d19d08455 100644 --- a/src/resolve/resolved-mdns.c +++ b/src/resolve/resolved-mdns.c @@ -10,7 +10,7 @@ #include "resolved-mdns.h" #include "sort-util.h" -#define CLEAR_CACHE_FLUSH(x) (~MDNS_RR_CACHE_FLUSH & (x)) +#define CLEAR_CACHE_FLUSH(x) (~MDNS_RR_CACHE_FLUSH_OR_QU & (x)) void manager_mdns_stop(Manager *m) { assert(m); @@ -173,12 +173,69 @@ static int mdns_do_tiebreak(DnsResourceKey *key, DnsAnswer *answer, DnsPacket *p return 0; } +static bool mdns_should_reply_using_unicast(DnsPacket *p) { + DnsQuestionItem *item; + + /* Work out if we should respond using multicast or unicast. */ + + /* The query was a legacy "one-shot mDNS query", RFC 6762, sections 5.1 and 6.7 */ + if (p->sender_port != MDNS_PORT) + return true; + + /* The query was a "direct unicast query", RFC 6762, section 5.5 */ + switch (p->family) { + case AF_INET: + if (!in4_addr_equal(&p->destination.in, &MDNS_MULTICAST_IPV4_ADDRESS)) + return true; + break; + case AF_INET6: + if (!in6_addr_equal(&p->destination.in6, &MDNS_MULTICAST_IPV6_ADDRESS)) + return true; + break; + } + + /* All the questions in the query had a QU bit set, RFC 6762, section 5.4 */ + DNS_QUESTION_FOREACH_ITEM(item, p->question) { + if (!FLAGS_SET(item->flags, DNS_QUESTION_WANTS_UNICAST_REPLY)) + return false; + } + return true; +} + +static bool sender_on_local_subnet(DnsScope *s, DnsPacket *p) { + LinkAddress *a; + int r; + + /* Check whether the sender is on a local subnet. */ + + if (!s->link) + return false; + + LIST_FOREACH(addresses, a, s->link->addresses) { + if (a->family != p->family) + continue; + if (a->prefixlen == UCHAR_MAX) /* don't know subnet mask */ + continue; + + r = in_addr_prefix_covers(a->family, &a->in_addr, a->prefixlen, &p->sender); + if (r < 0) + log_debug_errno(r, "Failed to determine whether link address covers sender address: %m"); + if (r > 0) + return true; + } + + return false; +} + + static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) { _cleanup_(dns_answer_unrefp) DnsAnswer *full_answer = NULL; _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; DnsResourceKey *key = NULL; DnsResourceRecord *rr; bool tentative = false; + bool legacy_query = p->sender_port != MDNS_PORT; + bool unicast_reply; int r; assert(s); @@ -190,8 +247,18 @@ static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) { assert_return((dns_question_size(p->question) > 0), -EINVAL); + unicast_reply = mdns_should_reply_using_unicast(p); + if (unicast_reply && !sender_on_local_subnet(s, p)) { + /* RFC 6762, section 5.5 recommends silently ignoring unicast queries + * from senders outside the local network, so that we don't reveal our + * internal network structure to outsiders. */ + log_debug("Sender wants a unicast reply, but is not on a local subnet. Ignoring."); + return 0; + } + DNS_QUESTION_FOREACH(key, p->question) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; + DnsAnswerItem *item; r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative); if (r < 0) @@ -222,21 +289,48 @@ static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) { } } - r = dns_answer_extend(&full_answer, answer); - if (r < 0) - return log_debug_errno(r, "Failed to extend answer: %m"); + if (dns_answer_isempty(answer)) + continue; + + /* Copy answer items from full_answer to answer, tweaking them if needed. */ + if (full_answer) { + r = dns_answer_reserve(&full_answer, dns_answer_size(answer)); + if (r < 0) + return log_debug_errno(r, "Failed to reserve space in answer"); + } else { + full_answer = dns_answer_new(dns_answer_size(answer)); + if (!full_answer) + return log_oom(); + } + + DNS_ANSWER_FOREACH_ITEM(item, answer) { + DnsAnswerFlags flags = item->flags; + /* The cache-flush bit must not be set in legacy unicast responses. + * See section 6.7 of RFC 6762. */ + if (legacy_query) + flags &= ~DNS_ANSWER_CACHE_FLUSH; + r = dns_answer_add(full_answer, item->rr, item->ifindex, flags, item->rrsig); + if (r < 0) + return log_debug_errno(r, "Failed to extend answer: %m"); + } } if (dns_answer_isempty(full_answer)) return 0; - r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, NULL, full_answer, NULL, false, &reply); + r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, + legacy_query ? p->question : NULL, full_answer, + NULL, false, &reply); if (r < 0) return log_debug_errno(r, "Failed to build reply packet: %m"); if (!ratelimit_below(&s->ratelimit)) return 0; + if (unicast_reply) { + reply->destination = p->sender; + reply->destination_port = p->sender_port; + } r = dns_scope_emit_udp(s, -1, AF_UNSPEC, reply); if (r < 0) return log_debug_errno(r, "Failed to send reply packet: %m"); |