summaryrefslogtreecommitdiffstats
path: root/src/resolve
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2021-04-12 17:53:38 +0200
committerGitHub <noreply@github.com>2021-04-12 17:53:38 +0200
commit59b8635a33a96ef380490be1d9c5640a753df667 (patch)
treed97ff99f648e3392b92ba1fc266c3595fed6b6f5 /src/resolve
parentMerge pull request #13496 from wat-ze-hex/custom-bpf-progs-parameterized-3 (diff)
parentresolved: reply using unicast mDNS when appropriate (diff)
downloadsystemd-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.c2
-rw-r--r--src/resolve/resolved-dns-packet.c29
-rw-r--r--src/resolve/resolved-dns-packet.h2
-rw-r--r--src/resolve/resolved-dns-query.c4
-rw-r--r--src/resolve/resolved-dns-question.c103
-rw-r--r--src/resolve/resolved-dns-question.h46
-rw-r--r--src/resolve/resolved-dns-rr.h8
-rw-r--r--src/resolve/resolved-dns-scope.c14
-rw-r--r--src/resolve/resolved-dns-stub.c4
-rw-r--r--src/resolve/resolved-dns-transaction.h5
-rw-r--r--src/resolve/resolved-mdns.c104
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");