Merge pull request #4832 from rojkov/mdns

This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2017-02-12 15:38:51 -05:00
commit 01c901e257
21 changed files with 387 additions and 48 deletions

5
NEWS
View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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 {

View File

@ -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)

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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");
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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))

View File

@ -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) {

View File

@ -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,

View File

@ -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;

View File

@ -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);
};

View File

@ -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);

View File

@ -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;

View File

@ -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.");

View File

@ -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);