resolve_test_template + {
'sources' : files('test-dns-answer.c'),
},
+ resolve_test_template + {
+ 'sources' : files('test-dns-browse-services.c'),
+ },
resolve_test_template + {
'sources' : files('test-dns-cache.c'),
},
service->service_browser->question_utf8,
service->service_browser->question_idna,
/* question_bypass= */ NULL,
- service->service_browser->ifindex,
+ service->ifindex,
service->service_browser->flags);
if (r < 0)
return log_error_errno(r, "Failed to create mDNS query for maintenance: %m");
return 0;
}
-bool dns_service_match_and_update(DnssdDiscoveredService *services, DnsResourceRecord *rr, int owner_family, usec_t until) {
+static int mdns_answer_item_ifindex(DnsServiceBrowser *sb, DnsAnswerItem *item) {
+ assert(sb);
+ assert(item);
+
+ return item->ifindex > 0 ? item->ifindex : sb->ifindex;
+}
+
+static int dns_service_matches(
+ DnssdDiscoveredService *service,
+ DnsResourceRecord *rr,
+ int owner_family,
+ int ifindex) {
+
+ int r;
+
+ assert(service);
+ assert(rr);
+
+ r = dns_resource_record_equal(service->rr, rr);
+ if (r <= 0)
+ return r;
+
+ return service->family == owner_family && service->ifindex == ifindex;
+}
+
+int dns_service_match_and_update(
+ DnssdDiscoveredService *services,
+ DnsResourceRecord *rr,
+ int owner_family,
+ int ifindex,
+ usec_t until) {
+
usec_t t = now(CLOCK_BOOTTIME);
+ int r;
+
+ /* Check if a discovered service matching the given resource record, owner family, and ifindex exists
+ * in the list. If found, update the service's expiration time if the new 'until' is later, unless the
+ * TTL is <= 1 (goodbye packet). Return positive if a matching service is found, zero otherwise. */
- /* Check if a discovered service matching the given resource record and owner family exists in the list.
- * If found, update the service's expiration time if the new 'until' is later, unless the TTL is <= 1 (goodbye packet).
- * Return true if a matching service is found, false otherwise. */
+ LIST_FOREACH(dns_services, service, services) {
+ r = dns_service_matches(service, rr, owner_family, ifindex);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
- LIST_FOREACH(dns_services, service, services)
- if (dns_resource_record_equal(service->rr, rr) > 0 && service->family == owner_family) {
- if (rr->ttl <= 1)
- return true;
+ if (rr->ttl <= 1)
+ return 1;
- if (service->until < until)
- mdns_service_update(service, rr, t, until);
+ if (service->until < until)
+ mdns_service_update(service, rr, t, until);
- return true;
- }
+ return 1;
+ }
- return false;
+ return 0;
+}
+
+int mdns_answer_contains_service(
+ DnsServiceBrowser *sb,
+ DnsAnswer *answer,
+ DnssdDiscoveredService *service) {
+
+ DnsAnswerItem *item;
+ int r;
+
+ assert(sb);
+ assert(service);
+
+ DNS_ANSWER_FOREACH_ITEM(item, answer) {
+ r = dns_service_matches(
+ service,
+ item->rr,
+ service->family,
+ mdns_answer_item_ifindex(sb, item));
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 1;
+ }
+
+ return 0;
}
void dns_browse_services_purge(Manager *m, int family) {
DNS_ANSWER_FOREACH_ITEM(item, answer) {
_cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *entry = NULL;
- int ifindex;
+ int ifindex = mdns_answer_item_ifindex(sb, item);
- if (dns_service_match_and_update(sb->dns_services, item->rr, owner_family, item->until))
+ r = dns_service_match_and_update(sb->dns_services, item->rr, owner_family, ifindex, item->until);
+ if (r < 0) {
+ log_error_errno(r, "Failed to match DNS service: %m");
+ goto finish;
+ }
+ if (r > 0)
continue;
r = dns_service_split(item->rr->ptr.name, &name, &type, &domain);
if (!type)
continue;
- /* Prefer the per-item ifindex, fall back to the service browser's ifindex */
- ifindex = item->ifindex > 0 ? item->ifindex : sb->ifindex;
-
r = dns_add_new_service(sb, item->rr, owner_family, ifindex, item->until);
if (r < 0) {
log_error_errno(r, "Failed to add new DNS service: %m");
if (service->family != owner_family)
continue;
- if (dns_answer_contains(answer, service->rr))
+ r = mdns_answer_contains_service(sb, answer, service);
+ if (r < 0) {
+ log_error_errno(r, "Failed to match DNS answer against service list: %m");
+ goto finish;
+ }
+ if (r > 0)
continue;
r = dns_service_split(service->rr->ptr.name, &name, &type, &domain);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServiceBrowser *, dns_service_browser_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnssdDiscoveredService *, dnssd_discovered_service_unref);
-bool dns_service_match_and_update(DnssdDiscoveredService *services, DnsResourceRecord *rr, int owner_family, usec_t until);
+int dns_service_match_and_update(
+ DnssdDiscoveredService *services,
+ DnsResourceRecord *rr,
+ int owner_family,
+ int ifindex,
+ usec_t until);
+int mdns_answer_contains_service(
+ DnsServiceBrowser *sb,
+ DnsAnswer *answer,
+ DnssdDiscoveredService *service);
int mdns_manage_services_answer(DnsServiceBrowser *sb, DnsAnswer *answer, int owner_family);
int dns_add_new_service(DnsServiceBrowser *sb, DnsResourceRecord *rr, int owner_family, int ifindex, usec_t until);
int mdns_service_update(DnssdDiscoveredService *service, DnsResourceRecord *rr, usec_t t, usec_t until);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <string.h>
+#include <sys/socket.h>
+
+#include "dns-answer.h"
+#include "dns-rr.h"
+#include "resolved-dns-browse-services.h"
+#include "tests.h"
+
+static DnsResourceRecord *new_test_service_rr(uint32_t ttl) {
+ DnsResourceRecord *rr;
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, "_http._tcp.local");
+ ASSERT_NOT_NULL(rr);
+
+ rr->ttl = ttl;
+ rr->ptr.name = strdup("Same Service._http._tcp.local");
+ ASSERT_NOT_NULL(rr->ptr.name);
+
+ return rr;
+}
+
+TEST(dns_service_match_and_update_goodbye_and_expiry) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ ASSERT_NOT_NULL(rr = new_test_service_rr(120));
+
+ DnssdDiscoveredService service = {
+ .rr = rr,
+ .family = AF_INET,
+ .ifindex = 2,
+ .until = 10,
+ };
+
+ ASSERT_OK_POSITIVE(dns_service_match_and_update(&service, rr, AF_INET, 2, 100));
+ ASSERT_EQ(service.until, (usec_t) 100);
+
+ ASSERT_OK_POSITIVE(dns_service_match_and_update(&service, rr, AF_INET, 2, 75));
+ ASSERT_EQ(service.until, (usec_t) 100);
+
+ rr = dns_resource_record_unref(rr);
+ ASSERT_NOT_NULL(rr = new_test_service_rr(1));
+ service.rr = rr;
+ service.until = 10;
+
+ ASSERT_OK_POSITIVE(dns_service_match_and_update(&service, rr, AF_INET, 2, 200));
+ ASSERT_EQ(service.until, (usec_t) 10);
+}
+
+TEST(dns_service_match_and_update_ifindex) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ ASSERT_NOT_NULL(rr = new_test_service_rr(120));
+
+ DnssdDiscoveredService service = {
+ .rr = rr,
+ .family = AF_INET,
+ .ifindex = 2,
+ };
+
+ ASSERT_OK_ZERO(dns_service_match_and_update(&service, rr, AF_INET, 3, 100));
+ ASSERT_EQ(service.until, (usec_t) 0);
+
+ ASSERT_OK_POSITIVE(dns_service_match_and_update(&service, rr, AF_INET, 2, 100));
+ ASSERT_EQ(service.until, (usec_t) 100);
+}
+
+TEST(dns_service_match_and_update_ifindex_list) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ DnssdDiscoveredService *services = NULL;
+
+ ASSERT_NOT_NULL(rr = new_test_service_rr(120));
+
+ DnssdDiscoveredService service2 = {
+ .rr = rr,
+ .family = AF_INET,
+ .ifindex = 2,
+ .until = 10,
+ };
+ DnssdDiscoveredService service3 = {
+ .rr = rr,
+ .family = AF_INET,
+ .ifindex = 3,
+ .until = 20,
+ };
+
+ LIST_PREPEND(dns_services, services, &service3);
+ LIST_PREPEND(dns_services, services, &service2);
+
+ ASSERT_OK_POSITIVE(dns_service_match_and_update(services, rr, AF_INET, 3, 100));
+ ASSERT_EQ(service2.until, (usec_t) 10);
+ ASSERT_EQ(service3.until, (usec_t) 100);
+}
+
+TEST(dns_service_match_and_update_error) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL, *other = NULL;
+
+ ASSERT_NOT_NULL(rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, ".."));
+ ASSERT_NOT_NULL(other = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, ".."));
+
+ DnssdDiscoveredService service = {
+ .rr = rr,
+ .family = AF_INET,
+ .ifindex = 2,
+ };
+
+ ASSERT_ERROR(dns_service_match_and_update(&service, other, AF_INET, 2, 100), EINVAL);
+}
+
+TEST(mdns_answer_contains_service_ifindex) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer2 = NULL;
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer3 = NULL;
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ ASSERT_NOT_NULL(answer = dns_answer_new(0));
+ ASSERT_NOT_NULL(answer2 = dns_answer_new(0));
+ ASSERT_NOT_NULL(answer3 = dns_answer_new(0));
+ ASSERT_NOT_NULL(rr = new_test_service_rr(120));
+
+ DnsServiceBrowser sb_all = {
+ .ifindex = 0,
+ };
+ DnsServiceBrowser sb_scoped = {
+ .ifindex = 2,
+ };
+ DnssdDiscoveredService service = {
+ .rr = rr,
+ .family = AF_INET,
+ .ifindex = 2,
+ };
+
+ ASSERT_OK_POSITIVE(dns_answer_add(answer, rr, 3, DNS_ANSWER_CACHEABLE, /* rrsig= */ NULL));
+ ASSERT_OK_ZERO(mdns_answer_contains_service(&sb_all, answer, &service));
+
+ ASSERT_OK_POSITIVE(dns_answer_add(answer, rr, 2, DNS_ANSWER_CACHEABLE, /* rrsig= */ NULL));
+ ASSERT_OK_POSITIVE(mdns_answer_contains_service(&sb_all, answer, &service));
+
+ ASSERT_OK_POSITIVE(dns_answer_add(answer2, rr, 0, DNS_ANSWER_CACHEABLE, /* rrsig= */ NULL));
+ ASSERT_OK_POSITIVE(mdns_answer_contains_service(&sb_scoped, answer2, &service));
+
+ ASSERT_OK_POSITIVE(dns_answer_add(answer3, rr, 3, DNS_ANSWER_CACHEABLE, /* rrsig= */ NULL));
+ ASSERT_OK_ZERO(mdns_answer_contains_service(&sb_scoped, answer3, &service));
+
+ sb_scoped.ifindex = 3;
+ ASSERT_OK_ZERO(mdns_answer_contains_service(&sb_scoped, answer2, &service));
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);