From: Dmitry Rozhkov Date: Wed, 4 Oct 2017 07:07:44 +0000 (+0300) Subject: resolved: announce DNS-SD records in mDNS scopes X-Git-Tag: v236~33^2~10 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a2bf8a19cc0a87ba381451fecded15f75a1c0a7c;p=thirdparty%2Fsystemd.git resolved: announce DNS-SD records in mDNS scopes --- diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index eaea30f2272..4a0c1d6a354 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -182,6 +182,19 @@ bool dns_resource_key_is_address(const DnsResourceKey *key) { return key->class == DNS_CLASS_IN && IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_AAAA); } +bool dns_resource_key_is_dnssd_ptr(const DnsResourceKey *key) { + assert(key); + + /* Check if this is a PTR resource key used in + Service Instance Enumeration as described in RFC6763 p4.1. */ + + if (key->type != DNS_TYPE_PTR) + return false; + + return dns_name_endswith(dns_resource_key_name(key), "_tcp.local") || + dns_name_endswith(dns_resource_key_name(key), "_udp.local"); +} + int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) { int r; diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index 7a12fd5c8c3..3f3d46e6b3e 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -298,6 +298,7 @@ DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key); DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key); const char* dns_resource_key_name(const DnsResourceKey *key); bool dns_resource_key_is_address(const DnsResourceKey *key); +bool dns_resource_key_is_dnssd_ptr(const DnsResourceKey *key); int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b); int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain); int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 5b7ac0ea252..dcef094b072 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -29,6 +29,7 @@ #include "random-util.h" #include "resolved-dnssd.h" #include "resolved-dns-scope.h" +#include "resolved-dns-zone.h" #include "resolved-llmnr.h" #include "resolved-mdns.h" #include "socket-util.h" @@ -1065,7 +1066,12 @@ static int on_announcement_timeout(sd_event_source *s, usec_t usec, void *userda int dns_scope_announce(DnsScope *scope, bool goodbye) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - LinkAddress *a; + _cleanup_set_free_ Set *types = NULL; + DnsTransaction *t; + DnsZoneItem *z; + unsigned size = 0; + Iterator iterator; + char *service_type; int r; if (!scope) @@ -1074,18 +1080,79 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) { if (scope->protocol != DNS_PROTOCOL_MDNS) return 0; - answer = dns_answer_new(scope->link->n_addresses * 2); + /* Check if we're done with probing. */ + LIST_FOREACH(transactions_by_scope, t, scope->transactions) + if (DNS_TRANSACTION_IS_LIVE(t->state)) + return 0; + + /* Calculate answer's size. */ + HASHMAP_FOREACH(z, scope->zone.by_key, iterator) { + if (z->state != DNS_ZONE_ITEM_ESTABLISHED) + continue; + + if (z->rr->key->type == DNS_TYPE_PTR && + !dns_zone_contains_name(&scope->zone, z->rr->ptr.name)) { + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + + log_debug("Skip PTR RR <%s> since its counterparts seem to be withdrawn", dns_resource_key_to_string(z->rr->key, key_str, sizeof key_str)); + z->state = DNS_ZONE_ITEM_WITHDRAWN; + continue; + } + + /* Collect service types for _services._dns-sd._udp.local RRs in a set */ + if (!scope->announced && + dns_resource_key_is_dnssd_ptr(z->rr->key)) { + if (!set_contains(types, dns_resource_key_name(z->rr->key))) { + r = set_ensure_allocated(&types, &dns_name_hash_ops); + if (r < 0) + return log_debug_errno(r, "Failed to allocate set: %m"); + + r = set_put(types, dns_resource_key_name(z->rr->key)); + if (r < 0) + return log_debug_errno(r, "Failed to add item to set: %m"); + } + } + + size++; + } + + answer = dns_answer_new(size + set_size(types)); if (!answer) return log_oom(); - 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); + /* Second iteration, actually add RRs to the answer. */ + HASHMAP_FOREACH(z, scope->zone.by_key, iterator) { + DnsAnswerFlags flags; + + if (z->state != DNS_ZONE_ITEM_ESTABLISHED) + continue; + + if (dns_resource_key_is_dnssd_ptr(z->rr->key)) + flags = goodbye ? DNS_ANSWER_GOODBYE : 0; + else + flags = goodbye ? (DNS_ANSWER_GOODBYE|DNS_ANSWER_CACHE_FLUSH) : DNS_ANSWER_CACHE_FLUSH; + + r = dns_answer_add(answer, z->rr, 0 , flags); + if (r < 0) + return log_debug_errno(r, "Failed to add RR to announce: %m"); + } + + /* Since all the active services are in the zone make them discoverable now. */ + SET_FOREACH(service_type, types, iterator) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr; + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, + "_services._dns-sd._udp.local"); + rr->ptr.name = strdup(service_type); + rr->ttl = MDNS_DEFAULT_TTL; + + r = dns_zone_put(&scope->zone, scope, rr, false); if (r < 0) - return log_debug_errno(r, "Failed to add address RR to answer: %m"); + log_warning_errno(r, "Failed to add DNS-SD PTR record to MDNS zone: %m"); - r = dns_answer_add(answer, a->mdns_ptr_rr, 0, goodbye ? DNS_ANSWER_GOODBYE : DNS_ANSWER_CACHE_FLUSH); + r = dns_answer_add(answer, rr, 0 , 0); if (r < 0) - return log_debug_errno(r, "Failed to add PTR RR to answer: %m"); + return log_debug_errno(r, "Failed to add RR to announce: %m"); } if (dns_answer_isempty(answer)) diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index 8080c3e7a1a..53a49285a83 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -680,3 +680,20 @@ bool dns_zone_is_empty(DnsZone *zone) { return hashmap_isempty(zone->by_key); } + +bool dns_zone_contains_name(DnsZone *z, const char *name) { + DnsZoneItem *i, *first; + + first = hashmap_get(z->by_name, name); + if (!first) + return false; + + LIST_FOREACH(by_name, i, first) { + if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) + continue; + + return true; + } + + return false; +} diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h index 55702fb8401..896f2298558 100644 --- a/src/resolve/resolved-dns-zone.h +++ b/src/resolve/resolved-dns-zone.h @@ -84,3 +84,4 @@ void dns_zone_item_probe_stop(DnsZoneItem *i); void dns_zone_dump(DnsZone *zone, FILE *f); bool dns_zone_is_empty(DnsZone *zone); +bool dns_zone_contains_name(DnsZone *z, const char *name);