mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-22 13:33:56 +03:00
Merge pull request #4832 from rojkov/mdns
This commit is contained in:
commit
01c901e257
5
NEWS
5
NEWS
@ -30,6 +30,11 @@ CHANGES WITH 233 in spe
|
||||
during startup. If no such message is sent, the service now fails,
|
||||
even if the main process exited with a successful exit code.
|
||||
|
||||
* The option MulticastDNS= of network configuration files has got
|
||||
actual implementation. With MulticastDNS=yes a host can resolve
|
||||
names of remote hosts and to reply to mDNS's A and AAAA requests
|
||||
from the hosts.
|
||||
|
||||
CHANGES WITH 232:
|
||||
|
||||
* The new RemoveIPC= option can be used to remove IPC objects owned by
|
||||
|
@ -182,7 +182,9 @@
|
||||
(i.e. classic unicast DNS), <literal>llmnr</literal> (<ulink
|
||||
url="https://tools.ietf.org/html/rfc4795">Link-Local Multicast Name Resolution</ulink>),
|
||||
<literal>llmnr-ipv4</literal>, <literal>llmnr-ipv6</literal> (LLMNR via the indicated underlying IP
|
||||
protocols). By default the lookup is done via all protocols suitable for the lookup. If used, limits the set of
|
||||
protocols), <literal>mdns</literal> (<ulink url="https://www.ietf.org/rfc/rfc6762.txt">Multicast DNS</ulink>),
|
||||
<literal>mdns-ipv4</literal>, <literal>mdns-ipv6</literal> (MDNS via the indicated underlying IP protocols).
|
||||
By default the lookup is done via all protocols suitable for the lookup. If used, limits the set of
|
||||
protocols that may be used. Use this option multiple times to enable resolving via multiple protocols at the
|
||||
same time. The setting <literal>llmnr</literal> is identical to specifying this switch once with
|
||||
<literal>llmnr-ipv4</literal> and once via <literal>llmnr-ipv6</literal>. Note that this option does not force
|
||||
|
@ -1528,7 +1528,7 @@ static int status_all(sd_bus *bus) {
|
||||
static void help_protocol_types(void) {
|
||||
if (arg_legend)
|
||||
puts("Known protocol types:");
|
||||
puts("dns\nllmnr\nllmnr-ipv4\nllmnr-ipv6");
|
||||
puts("dns\nllmnr\nllmnr-ipv4\nllmnr-ipv6\nmdns\nmnds-ipv4\nmdns-ipv6");
|
||||
}
|
||||
|
||||
static void help_dns_types(void) {
|
||||
@ -1726,6 +1726,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_flags |= SD_RESOLVED_LLMNR_IPV4;
|
||||
else if (streq(optarg, "llmnr-ipv6"))
|
||||
arg_flags |= SD_RESOLVED_LLMNR_IPV6;
|
||||
else if (streq(optarg, "mdns"))
|
||||
arg_flags |= SD_RESOLVED_MDNS;
|
||||
else if (streq(optarg, "mdns-ipv4"))
|
||||
arg_flags |= SD_RESOLVED_MDNS_IPV4;
|
||||
else if (streq(optarg, "mdns-ipv6"))
|
||||
arg_flags |= SD_RESOLVED_MDNS_IPV6;
|
||||
else {
|
||||
log_error("Unknown protocol specifier: %s", optarg);
|
||||
return -EINVAL;
|
||||
|
@ -33,9 +33,11 @@ typedef struct DnsAnswerItem DnsAnswerItem;
|
||||
* Note that we usually encode the empty DnsAnswer object as a simple NULL. */
|
||||
|
||||
typedef enum DnsAnswerFlags {
|
||||
DNS_ANSWER_AUTHENTICATED = 1, /* Item has been authenticated */
|
||||
DNS_ANSWER_CACHEABLE = 2, /* Item is subject to caching */
|
||||
DNS_ANSWER_SHARED_OWNER = 4, /* For mDNS: RRset may be owner by multiple peers */
|
||||
DNS_ANSWER_AUTHENTICATED = 1, /* Item has been authenticated */
|
||||
DNS_ANSWER_CACHEABLE = 2, /* Item is subject to caching */
|
||||
DNS_ANSWER_SHARED_OWNER = 4, /* For mDNS: RRset may be owner by multiple peers */
|
||||
DNS_ANSWER_CACHE_FLUSH = 8, /* For mDNS: sets cache-flush bit in the rrclass of response records */
|
||||
DNS_ANSWER_GOODBYE = 16, /* For mDNS: item is subject to disappear */
|
||||
} DnsAnswerFlags;
|
||||
|
||||
struct DnsAnswerItem {
|
||||
|
@ -980,7 +980,7 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
|
||||
if (!j->shared_owner)
|
||||
continue;
|
||||
|
||||
r = dns_packet_append_rr(p, j->rr, NULL, NULL);
|
||||
r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
|
||||
if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
|
||||
/* For mDNS, if we're unable to stuff all known answers into the given packet,
|
||||
* allocate a new one, push the RR into that one and link it to the current one.
|
||||
@ -995,7 +995,7 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
|
||||
|
||||
/* continue with new packet */
|
||||
p = p->more;
|
||||
r = dns_packet_append_rr(p, j->rr, NULL, NULL);
|
||||
r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
|
@ -569,8 +569,9 @@ fail:
|
||||
return r;
|
||||
}
|
||||
|
||||
int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) {
|
||||
int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, const DnsAnswerFlags flags, size_t *start) {
|
||||
size_t saved_size;
|
||||
uint16_t class;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
@ -586,7 +587,8 @@ int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start)
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = dns_packet_append_uint16(p, k->class, NULL);
|
||||
class = flags & DNS_ANSWER_CACHE_FLUSH ? k->class | MDNS_RR_CACHE_FLUSH : k->class;
|
||||
r = dns_packet_append_uint16(p, class, NULL);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
@ -791,9 +793,10 @@ int dns_packet_truncate_opt(DnsPacket *p) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start) {
|
||||
int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAnswerFlags flags, size_t *start, size_t *rdata_start) {
|
||||
|
||||
size_t saved_size, rdlength_offset, end, rdlength, rds;
|
||||
uint32_t ttl;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
@ -801,11 +804,12 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
|
||||
|
||||
saved_size = p->size;
|
||||
|
||||
r = dns_packet_append_key(p, rr->key, NULL);
|
||||
r = dns_packet_append_key(p, rr->key, flags, NULL);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = dns_packet_append_uint32(p, rr->ttl, NULL);
|
||||
ttl = flags & DNS_ANSWER_GOODBYE ? 0 : rr->ttl;
|
||||
r = dns_packet_append_uint32(p, ttl, NULL);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
@ -1143,7 +1147,7 @@ int dns_packet_append_question(DnsPacket *p, DnsQuestion *q) {
|
||||
assert(p);
|
||||
|
||||
DNS_QUESTION_FOREACH(key, q) {
|
||||
r = dns_packet_append_key(p, key, NULL);
|
||||
r = dns_packet_append_key(p, key, 0, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -1153,12 +1157,13 @@ int dns_packet_append_question(DnsPacket *p, DnsQuestion *q) {
|
||||
|
||||
int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a) {
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags flags;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, a) {
|
||||
r = dns_packet_append_rr(p, rr, NULL, NULL);
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, flags, a) {
|
||||
r = dns_packet_append_rr(p, rr, flags, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -209,8 +209,8 @@ int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start);
|
||||
int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_t *start);
|
||||
int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, bool canonical_candidate, size_t *start);
|
||||
int dns_packet_append_name(DnsPacket *p, const char *name, bool allow_compression, bool canonical_candidate, 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, size_t *rdata_start);
|
||||
int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, const DnsAnswerFlags flags, size_t *start);
|
||||
int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAnswerFlags flags, size_t *start, size_t *rdata_start);
|
||||
int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, int rcode, size_t *start);
|
||||
int dns_packet_append_question(DnsPacket *p, DnsQuestion *q);
|
||||
int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a);
|
||||
|
@ -1262,7 +1262,7 @@ int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
|
||||
if (rr->wire_format && rr->wire_format_canonical == canonical)
|
||||
return 0;
|
||||
|
||||
r = dns_packet_append_rr(&packet, rr, &start, &rds);
|
||||
r = dns_packet_append_rr(&packet, rr, 0, &start, &rds);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -124,6 +124,8 @@ DnsScope* dns_scope_free(DnsScope *s) {
|
||||
ordered_hashmap_free(s->conflict_queue);
|
||||
sd_event_source_unref(s->conflict_event_source);
|
||||
|
||||
sd_event_source_unref(s->announce_event_source);
|
||||
|
||||
dns_cache_flush(&s->cache);
|
||||
dns_zone_flush(&s->zone);
|
||||
|
||||
@ -549,7 +551,11 @@ static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in
|
||||
.imr_ifindex = s->link->ifindex,
|
||||
};
|
||||
|
||||
fd = manager_llmnr_ipv4_udp_fd(s->manager);
|
||||
if (s->protocol == DNS_PROTOCOL_LLMNR)
|
||||
fd = manager_llmnr_ipv4_udp_fd(s->manager);
|
||||
else
|
||||
fd = manager_mdns_ipv4_fd(s->manager);
|
||||
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
@ -568,7 +574,11 @@ static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in
|
||||
.ipv6mr_interface = s->link->ifindex,
|
||||
};
|
||||
|
||||
fd = manager_llmnr_ipv6_udp_fd(s->manager);
|
||||
if (s->protocol == DNS_PROTOCOL_LLMNR)
|
||||
fd = manager_llmnr_ipv6_udp_fd(s->manager);
|
||||
else
|
||||
fd = manager_mdns_ipv6_fd(s->manager);
|
||||
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
@ -601,7 +611,7 @@ int dns_scope_mdns_membership(DnsScope *s, bool b) {
|
||||
return dns_scope_multicast_membership(s, b, MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_IPV6_ADDRESS);
|
||||
}
|
||||
|
||||
static int dns_scope_make_reply_packet(
|
||||
int dns_scope_make_reply_packet(
|
||||
DnsScope *s,
|
||||
uint16_t id,
|
||||
int rcode,
|
||||
@ -783,8 +793,15 @@ DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key,
|
||||
/* Try to find an ongoing transaction that is a equal to the
|
||||
* specified question */
|
||||
t = hashmap_get(scope->transactions_by_key, key);
|
||||
if (!t)
|
||||
return NULL;
|
||||
if (!t) {
|
||||
DnsResourceKey *key_any;
|
||||
|
||||
key_any = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_ANY, dns_resource_key_name(key));
|
||||
t = hashmap_get(scope->transactions_by_key, key_any);
|
||||
key_any = dns_resource_key_unref(key_any);
|
||||
if (!t)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Refuse reusing transactions that completed based on cached
|
||||
* data instead of a real packet, if that's requested. */
|
||||
@ -830,11 +847,11 @@ static int dns_scope_make_conflict_packet(
|
||||
DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
|
||||
DNS_PACKET_HEADER(p)->arcount = htobe16(1);
|
||||
|
||||
r = dns_packet_append_key(p, rr->key, NULL);
|
||||
r = dns_packet_append_key(p, rr->key, 0, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_packet_append_rr(p, rr, NULL, NULL);
|
||||
r = dns_packet_append_rr(p, rr, 0, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -928,17 +945,19 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) {
|
||||
assert(scope);
|
||||
assert(p);
|
||||
|
||||
if (p->protocol != DNS_PROTOCOL_LLMNR)
|
||||
if (!IN_SET(p->protocol, DNS_PROTOCOL_LLMNR, DNS_PROTOCOL_MDNS))
|
||||
return;
|
||||
|
||||
if (DNS_PACKET_RRCOUNT(p) <= 0)
|
||||
return;
|
||||
|
||||
if (DNS_PACKET_LLMNR_C(p) != 0)
|
||||
return;
|
||||
if (p->protocol == DNS_PROTOCOL_LLMNR) {
|
||||
if (DNS_PACKET_LLMNR_C(p) != 0)
|
||||
return;
|
||||
|
||||
if (DNS_PACKET_LLMNR_T(p) != 0)
|
||||
return;
|
||||
if (DNS_PACKET_LLMNR_T(p) != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (manager_our_packet(scope->manager, p))
|
||||
return;
|
||||
@ -1041,3 +1060,76 @@ int dns_scope_ifindex(DnsScope *s) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_announcement_timeout(sd_event_source *s, usec_t usec, void *userdata) {
|
||||
DnsScope *scope = userdata;
|
||||
|
||||
assert(s);
|
||||
|
||||
scope->announce_event_source = sd_event_source_unref(scope->announce_event_source);
|
||||
|
||||
dns_scope_announce(scope, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dns_scope_announce(DnsScope *scope, bool goodbye) {
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||
LinkAddress *a;
|
||||
int r;
|
||||
|
||||
if (!scope)
|
||||
return;
|
||||
|
||||
if (scope->protocol != DNS_PROTOCOL_MDNS)
|
||||
return;
|
||||
|
||||
answer = dns_answer_new(4);
|
||||
LIST_FOREACH(addresses, a, scope->link->addresses) {
|
||||
r = dns_answer_add(answer, a->mdns_address_rr, 0, goodbye ? DNS_ANSWER_GOODBYE : DNS_ANSWER_CACHE_FLUSH);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to add address RR to answer: %m");
|
||||
return;
|
||||
}
|
||||
r = dns_answer_add(answer, a->mdns_ptr_rr, 0, goodbye ? DNS_ANSWER_GOODBYE : DNS_ANSWER_CACHE_FLUSH);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to add PTR RR to answer: %m");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (dns_answer_isempty(answer))
|
||||
return;
|
||||
|
||||
r = dns_scope_make_reply_packet(scope, 0, DNS_RCODE_SUCCESS, NULL, answer, NULL, false, &p);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to build reply packet: %m");
|
||||
return;
|
||||
}
|
||||
r = dns_scope_emit_udp(scope, -1, p);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to send reply packet: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
/* In section 8.3 of RFC6762: "The Multicast DNS responder MUST send at least two unsolicited
|
||||
* responses, one second apart." */
|
||||
if (!scope->announced) {
|
||||
usec_t ts;
|
||||
|
||||
scope->announced = true;
|
||||
|
||||
assert_se(sd_event_now(scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
|
||||
ts += MDNS_ANNOUNCE_DELAY;
|
||||
|
||||
r = sd_event_add_time(
|
||||
scope->manager->event,
|
||||
&scope->announce_event_source,
|
||||
clock_boottime_or_monotonic(),
|
||||
ts,
|
||||
MDNS_JITTER_RANGE_USEC,
|
||||
on_announcement_timeout, scope);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to schedule second announcement: %m");
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,9 @@ struct DnsScope {
|
||||
OrderedHashmap *conflict_queue;
|
||||
sd_event_source *conflict_event_source;
|
||||
|
||||
bool announced:1;
|
||||
sd_event_source *announce_event_source;
|
||||
|
||||
RateLimit ratelimit;
|
||||
|
||||
usec_t resend_timeout;
|
||||
@ -96,6 +99,7 @@ void dns_scope_next_dns_server(DnsScope *s);
|
||||
int dns_scope_llmnr_membership(DnsScope *s, bool b);
|
||||
int dns_scope_mdns_membership(DnsScope *s, bool b);
|
||||
|
||||
int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQuestion *q, DnsAnswer *answer, DnsAnswer *soa, bool tentative, DnsPacket **ret);
|
||||
void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p);
|
||||
|
||||
DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, bool cache_ok);
|
||||
@ -112,3 +116,5 @@ bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name);
|
||||
bool dns_scope_network_good(DnsScope *s);
|
||||
|
||||
int dns_scope_ifindex(DnsScope *s);
|
||||
|
||||
void dns_scope_announce(DnsScope *scope, bool goodbye);
|
||||
|
@ -70,7 +70,7 @@ static int dns_stub_make_reply_packet(
|
||||
|
||||
continue;
|
||||
add:
|
||||
r = dns_packet_append_rr(*p, rr, NULL, NULL);
|
||||
r = dns_packet_append_rr(*p, rr, 0, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -363,6 +363,8 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
|
||||
SET_FOREACH_MOVE(z, t->notify_zone_items_done, t->notify_zone_items)
|
||||
dns_zone_item_notify(z);
|
||||
SWAP_TWO(t->notify_zone_items, t->notify_zone_items_done);
|
||||
if (t->probing)
|
||||
dns_scope_announce(t->scope, false);
|
||||
|
||||
SET_FOREACH_MOVE(d, t->notify_transactions_done, t->notify_transactions)
|
||||
dns_transaction_notify(d, t);
|
||||
@ -1012,15 +1014,20 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
|
||||
if (r > 0) /* Transaction got restarted... */
|
||||
return;
|
||||
|
||||
if (IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR)) {
|
||||
if (IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR, DNS_PROTOCOL_MDNS)) {
|
||||
|
||||
/* Only consider responses with equivalent query section to the request */
|
||||
r = dns_packet_is_reply_for(p, t->key);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
if (r == 0) {
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
|
||||
return;
|
||||
/* When dealing with protocols other than mDNS only consider responses with
|
||||
* equivalent query section to the request. For mDNS this check doesn't make
|
||||
* sense, because the section 6 of RFC6762 states that "Multicast DNS responses MUST NOT
|
||||
* contain any questions in the Question Section". */
|
||||
if (t->scope->protocol != DNS_PROTOCOL_MDNS) {
|
||||
r = dns_packet_is_reply_for(p, t->key);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
if (r == 0) {
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Install the answer as answer to the transaction */
|
||||
@ -1213,7 +1220,10 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
|
||||
|
||||
case DNS_PROTOCOL_MDNS:
|
||||
assert(t->n_attempts > 0);
|
||||
return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
|
||||
if (t->probing)
|
||||
return MDNS_PROBING_INTERVAL_USEC;
|
||||
else
|
||||
return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
|
||||
|
||||
case DNS_PROTOCOL_LLMNR:
|
||||
return t->scope->resend_timeout;
|
||||
@ -1367,7 +1377,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_packet_append_key(p, t->key, NULL);
|
||||
r = dns_packet_append_key(p, t->key, 0, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1399,7 +1409,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
|
||||
if (qdcount >= UINT16_MAX)
|
||||
break;
|
||||
|
||||
r = dns_packet_append_key(p, other->key, NULL);
|
||||
r = dns_packet_append_key(p, other->key, 0, NULL);
|
||||
|
||||
/*
|
||||
* If we can't stuff more questions into the packet, just give up.
|
||||
@ -1426,7 +1436,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
|
||||
(void) sd_event_source_set_description(other->timeout_event_source, "dns-transaction-timeout");
|
||||
|
||||
other->state = DNS_TRANSACTION_PENDING;
|
||||
other->next_attempt_after = ts;
|
||||
@ -1468,7 +1478,7 @@ static int dns_transaction_make_packet(DnsTransaction *t) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_packet_append_key(p, t->key, NULL);
|
||||
r = dns_packet_append_key(p, t->key, 0, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -78,6 +78,8 @@ struct DnsTransaction {
|
||||
|
||||
bool clamp_ttl:1;
|
||||
|
||||
bool probing:1;
|
||||
|
||||
DnsPacket *sent, *received;
|
||||
|
||||
DnsAnswer *answer;
|
||||
@ -172,10 +174,20 @@ DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_;
|
||||
#define MDNS_JITTER_MIN_USEC (20 * USEC_PER_MSEC)
|
||||
#define MDNS_JITTER_RANGE_USEC (100 * USEC_PER_MSEC)
|
||||
|
||||
/* mDNS probing interval, see RFC 6762 Section 8.1 */
|
||||
#define MDNS_PROBING_INTERVAL_USEC (250 * USEC_PER_MSEC)
|
||||
|
||||
/* Maximum attempts to send DNS requests, across all DNS servers */
|
||||
#define DNS_TRANSACTION_ATTEMPTS_MAX 16
|
||||
|
||||
/* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */
|
||||
#define LLMNR_TRANSACTION_ATTEMPTS_MAX 3
|
||||
|
||||
#define TRANSACTION_ATTEMPTS_MAX(p) ((p) == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX)
|
||||
/* Maximum attempts to send MDNS requests, see RFC 6762 Section 8.1 */
|
||||
#define MDNS_TRANSACTION_ATTEMPTS_MAX 3
|
||||
|
||||
#define TRANSACTION_ATTEMPTS_MAX(p) (((p) == DNS_PROTOCOL_LLMNR) ? \
|
||||
LLMNR_TRANSACTION_ATTEMPTS_MAX : \
|
||||
(((p) == DNS_PROTOCOL_MDNS) ? \
|
||||
MDNS_TRANSACTION_ATTEMPTS_MAX : \
|
||||
DNS_TRANSACTION_ATTEMPTS_MAX))
|
||||
|
@ -196,6 +196,7 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) {
|
||||
goto gc;
|
||||
|
||||
i->probe_transaction = t;
|
||||
t->probing = true;
|
||||
|
||||
if (t->state == DNS_TRANSACTION_NULL) {
|
||||
|
||||
|
@ -37,6 +37,9 @@ typedef enum DnsZoneItemState DnsZoneItemState;
|
||||
/* RFC 4795 Section 2.8. suggests a TTL of 30s by default */
|
||||
#define LLMNR_DEFAULT_TTL (30)
|
||||
|
||||
/* RFC 6762 Section 10. suggests a TTL of 120s by default */
|
||||
#define MDNS_DEFAULT_TTL (120)
|
||||
|
||||
enum DnsZoneItemState {
|
||||
DNS_ZONE_ITEM_PROBING,
|
||||
DNS_ZONE_ITEM_ESTABLISHED,
|
||||
|
@ -85,6 +85,10 @@ Link *link_free(Link *l) {
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
/* Send goodbye messages. */
|
||||
dns_scope_announce(l->mdns_ipv4_scope, true);
|
||||
dns_scope_announce(l->mdns_ipv6_scope, true);
|
||||
|
||||
link_flush_settings(l);
|
||||
|
||||
while (l->addresses)
|
||||
@ -692,10 +696,26 @@ LinkAddress *link_address_free(LinkAddress *a) {
|
||||
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);
|
||||
}
|
||||
|
||||
if (a->mdns_address_rr) {
|
||||
if (a->family == AF_INET && a->link->mdns_ipv4_scope)
|
||||
dns_zone_remove_rr(&a->link->mdns_ipv4_scope->zone, a->mdns_address_rr);
|
||||
else if (a->family == AF_INET6 && a->link->mdns_ipv6_scope)
|
||||
dns_zone_remove_rr(&a->link->mdns_ipv6_scope->zone, a->mdns_address_rr);
|
||||
}
|
||||
|
||||
if (a->mdns_ptr_rr) {
|
||||
if (a->family == AF_INET && a->link->mdns_ipv4_scope)
|
||||
dns_zone_remove_rr(&a->link->mdns_ipv4_scope->zone, a->mdns_ptr_rr);
|
||||
else if (a->family == AF_INET6 && a->link->mdns_ipv6_scope)
|
||||
dns_zone_remove_rr(&a->link->mdns_ipv6_scope->zone, a->mdns_ptr_rr);
|
||||
}
|
||||
}
|
||||
|
||||
dns_resource_record_unref(a->llmnr_address_rr);
|
||||
dns_resource_record_unref(a->llmnr_ptr_rr);
|
||||
dns_resource_record_unref(a->mdns_address_rr);
|
||||
dns_resource_record_unref(a->mdns_ptr_rr);
|
||||
|
||||
return mfree(a);
|
||||
}
|
||||
@ -746,7 +766,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
|
||||
|
||||
r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
|
||||
log_warning_errno(r, "Failed to add IPv4 PTR record to LLMNR zone: %m");
|
||||
} else {
|
||||
if (a->llmnr_address_rr) {
|
||||
if (a->link->llmnr_ipv4_scope)
|
||||
@ -760,6 +780,59 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
|
||||
a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!force_remove &&
|
||||
link_address_relevant(a, true) &&
|
||||
a->link->mdns_ipv4_scope &&
|
||||
a->link->mdns_support == RESOLVE_SUPPORT_YES &&
|
||||
a->link->manager->mdns_support == RESOLVE_SUPPORT_YES) {
|
||||
if (!a->link->manager->mdns_host_ipv4_key) {
|
||||
a->link->manager->mdns_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->mdns_hostname);
|
||||
if (!a->link->manager->mdns_host_ipv4_key) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (!a->mdns_address_rr) {
|
||||
a->mdns_address_rr = dns_resource_record_new(a->link->manager->mdns_host_ipv4_key);
|
||||
if (!a->mdns_address_rr) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
a->mdns_address_rr->a.in_addr = a->in_addr.in;
|
||||
a->mdns_address_rr->ttl = MDNS_DEFAULT_TTL;
|
||||
}
|
||||
|
||||
if (!a->mdns_ptr_rr) {
|
||||
r = dns_resource_record_new_reverse(&a->mdns_ptr_rr, a->family, &a->in_addr, a->link->manager->mdns_hostname);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
a->mdns_ptr_rr->ttl = MDNS_DEFAULT_TTL;
|
||||
}
|
||||
|
||||
r = dns_zone_put(&a->link->mdns_ipv4_scope->zone, a->link->mdns_ipv4_scope, a->mdns_address_rr, true);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to add A record to MDNS zone: %m");
|
||||
|
||||
r = dns_zone_put(&a->link->mdns_ipv4_scope->zone, a->link->mdns_ipv4_scope, a->mdns_ptr_rr, false);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to add IPv4 PTR record to MDNS zone: %m");
|
||||
} else {
|
||||
if (a->mdns_address_rr) {
|
||||
if (a->link->mdns_ipv4_scope)
|
||||
dns_zone_remove_rr(&a->link->mdns_ipv4_scope->zone, a->mdns_address_rr);
|
||||
a->mdns_address_rr = dns_resource_record_unref(a->mdns_address_rr);
|
||||
}
|
||||
|
||||
if (a->mdns_ptr_rr) {
|
||||
if (a->link->mdns_ipv4_scope)
|
||||
dns_zone_remove_rr(&a->link->mdns_ipv4_scope->zone, a->mdns_ptr_rr);
|
||||
a->mdns_ptr_rr = dns_resource_record_unref(a->mdns_ptr_rr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (a->family == AF_INET6) {
|
||||
@ -817,6 +890,60 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
|
||||
a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!force_remove &&
|
||||
link_address_relevant(a, true) &&
|
||||
a->link->mdns_ipv6_scope &&
|
||||
a->link->mdns_support == RESOLVE_SUPPORT_YES &&
|
||||
a->link->manager->mdns_support == RESOLVE_SUPPORT_YES) {
|
||||
|
||||
if (!a->link->manager->mdns_host_ipv6_key) {
|
||||
a->link->manager->mdns_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->mdns_hostname);
|
||||
if (!a->link->manager->mdns_host_ipv6_key) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (!a->mdns_address_rr) {
|
||||
a->mdns_address_rr = dns_resource_record_new(a->link->manager->mdns_host_ipv6_key);
|
||||
if (!a->mdns_address_rr) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
a->mdns_address_rr->aaaa.in6_addr = a->in_addr.in6;
|
||||
a->mdns_address_rr->ttl = MDNS_DEFAULT_TTL;
|
||||
}
|
||||
|
||||
if (!a->mdns_ptr_rr) {
|
||||
r = dns_resource_record_new_reverse(&a->mdns_ptr_rr, a->family, &a->in_addr, a->link->manager->mdns_hostname);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
a->mdns_ptr_rr->ttl = MDNS_DEFAULT_TTL;
|
||||
}
|
||||
|
||||
r = dns_zone_put(&a->link->mdns_ipv6_scope->zone, a->link->mdns_ipv6_scope, a->mdns_address_rr, true);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to add AAAA record to MDNS zone: %m");
|
||||
|
||||
r = dns_zone_put(&a->link->mdns_ipv6_scope->zone, a->link->mdns_ipv6_scope, a->mdns_ptr_rr, false);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to add IPv6 PTR record to MDNS zone: %m");
|
||||
} else {
|
||||
if (a->mdns_address_rr) {
|
||||
if (a->link->mdns_ipv6_scope)
|
||||
dns_zone_remove_rr(&a->link->mdns_ipv6_scope->zone, a->mdns_address_rr);
|
||||
a->mdns_address_rr = dns_resource_record_unref(a->mdns_address_rr);
|
||||
}
|
||||
|
||||
if (a->mdns_ptr_rr) {
|
||||
if (a->link->mdns_ipv6_scope)
|
||||
dns_zone_remove_rr(&a->link->mdns_ipv6_scope->zone, a->mdns_ptr_rr);
|
||||
a->mdns_ptr_rr = dns_resource_record_unref(a->mdns_ptr_rr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -47,6 +47,8 @@ struct LinkAddress {
|
||||
|
||||
DnsResourceRecord *llmnr_address_rr;
|
||||
DnsResourceRecord *llmnr_ptr_rr;
|
||||
DnsResourceRecord *mdns_address_rr;
|
||||
DnsResourceRecord *mdns_ptr_rr;
|
||||
|
||||
LIST_FIELDS(LinkAddress, addresses);
|
||||
};
|
||||
|
@ -505,7 +505,7 @@ int manager_new(Manager **ret) {
|
||||
m->hostname_fd = -1;
|
||||
|
||||
m->llmnr_support = RESOLVE_SUPPORT_YES;
|
||||
m->mdns_support = RESOLVE_SUPPORT_NO;
|
||||
m->mdns_support = RESOLVE_SUPPORT_YES;
|
||||
m->dnssec_mode = DEFAULT_DNSSEC_MODE;
|
||||
m->enable_cache = true;
|
||||
m->dns_stub_listener_mode = DNS_STUB_LISTENER_UDP;
|
||||
@ -628,6 +628,8 @@ Manager *manager_free(Manager *m) {
|
||||
|
||||
dns_resource_key_unref(m->llmnr_host_ipv4_key);
|
||||
dns_resource_key_unref(m->llmnr_host_ipv6_key);
|
||||
dns_resource_key_unref(m->mdns_host_ipv4_key);
|
||||
dns_resource_key_unref(m->mdns_host_ipv6_key);
|
||||
|
||||
sd_event_source_unref(m->hostname_event_source);
|
||||
safe_close(m->hostname_fd);
|
||||
@ -1016,6 +1018,8 @@ void manager_refresh_rrs(Manager *m) {
|
||||
|
||||
m->llmnr_host_ipv4_key = dns_resource_key_unref(m->llmnr_host_ipv4_key);
|
||||
m->llmnr_host_ipv6_key = dns_resource_key_unref(m->llmnr_host_ipv6_key);
|
||||
m->mdns_host_ipv4_key = dns_resource_key_unref(m->mdns_host_ipv4_key);
|
||||
m->mdns_host_ipv6_key = dns_resource_key_unref(m->mdns_host_ipv6_key);
|
||||
|
||||
HASHMAP_FOREACH(l, m->links, i) {
|
||||
link_add_rrs(l, true);
|
||||
|
@ -114,6 +114,8 @@ struct Manager {
|
||||
char *mdns_hostname;
|
||||
DnsResourceKey *llmnr_host_ipv4_key;
|
||||
DnsResourceKey *llmnr_host_ipv6_key;
|
||||
DnsResourceKey *mdns_host_ipv4_key;
|
||||
DnsResourceKey *mdns_host_ipv6_key;
|
||||
|
||||
/* Watch the system hostname */
|
||||
int hostname_fd;
|
||||
|
@ -67,6 +67,52 @@ eaddrinuse:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) {
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
|
||||
DnsResourceKey *key = NULL;
|
||||
bool tentative = false;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(p);
|
||||
|
||||
r = dns_packet_extract(p);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to extract resource records from incoming packet: %m");
|
||||
return r;
|
||||
}
|
||||
|
||||
/* TODO: there might be more than one question in mDNS queries. */
|
||||
assert_return((dns_question_size(p->question) > 0), -EINVAL);
|
||||
key = p->question->keys[0];
|
||||
|
||||
r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to lookup key: %m");
|
||||
return r;
|
||||
}
|
||||
if (r == 0)
|
||||
return 0;
|
||||
|
||||
r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, NULL, answer, NULL, false, &reply);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to build reply packet: %m");
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!ratelimit_test(&s->ratelimit))
|
||||
return 0;
|
||||
|
||||
r = dns_scope_emit_udp(s, -1, reply);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to send reply packet: %m");
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||
Manager *m = userdata;
|
||||
@ -77,6 +123,9 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (manager_our_packet(m, p))
|
||||
return 0;
|
||||
|
||||
scope = manager_find_scope(m, p);
|
||||
if (!scope) {
|
||||
log_warning("Got mDNS UDP packet on unknown scope. Ignoring.");
|
||||
@ -115,6 +164,12 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
|
||||
dns_name_endswith(name, "local") > 0))
|
||||
return 0;
|
||||
|
||||
if (rr->ttl == 0) {
|
||||
log_debug("Got a goodbye packet");
|
||||
/* See the section 10.1 of RFC6762 */
|
||||
rr->ttl = 1;
|
||||
}
|
||||
|
||||
t = dns_scope_find_transaction(scope, rr->key, false);
|
||||
if (t)
|
||||
dns_transaction_process_reply(t, p);
|
||||
@ -125,7 +180,11 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
|
||||
} else if (dns_packet_validate_query(p) > 0) {
|
||||
log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));
|
||||
|
||||
dns_scope_process_query(scope, NULL, p);
|
||||
r = mdns_scope_process_query(scope, p);
|
||||
if (r < 0) {
|
||||
log_debug("mDNS query processing failed.");
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
log_debug("Invalid mDNS UDP packet.");
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "resolved-manager.h"
|
||||
|
||||
#define MDNS_PORT 5353
|
||||
#define MDNS_ANNOUNCE_DELAY (1 * USEC_PER_SEC)
|
||||
|
||||
int manager_mdns_ipv4_fd(Manager *m);
|
||||
int manager_mdns_ipv6_fd(Manager *m);
|
||||
|
Loading…
Reference in New Issue
Block a user