From 88123aa21c2611049d01174ccbc662dfac26d9bb Mon Sep 17 00:00:00 2001 From: Ronan Pigott Date: Fri, 15 Dec 2023 22:55:54 -0700 Subject: [PATCH] dnssd: support service subtypes A service subtype is used for selective enumeration of services. --- man/systemd.dnssd.xml | 10 +++++++++ src/resolve/resolved-conf.c | 31 ++++++++++++++++++++++++++ src/resolve/resolved-conf.h | 1 + src/resolve/resolved-dns-scope.c | 7 ++++++ src/resolve/resolved-dnssd-bus.c | 2 ++ src/resolve/resolved-dnssd-gperf.gperf | 15 +++++++------ src/resolve/resolved-dnssd.c | 25 ++++++++++++++++++++- src/resolve/resolved-dnssd.h | 2 ++ src/shared/dns-domain.c | 23 +++++++++++++++++++ src/shared/dns-domain.h | 1 + 10 files changed, 109 insertions(+), 8 deletions(-) diff --git a/man/systemd.dnssd.xml b/man/systemd.dnssd.xml index 2b29a99dc82..a2641e85168 100644 --- a/man/systemd.dnssd.xml +++ b/man/systemd.dnssd.xml @@ -118,6 +118,16 @@ + + SubType= + + A subtype of the network service as defined in the section 7.1 of RFC 6763, e.g. _printer. + + + + + Port= diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index b648c3e5203..504da9ebca3 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -299,6 +299,37 @@ int config_parse_dnssd_service_type( return 0; } +int config_parse_dnssd_service_subtype( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + DnssdService *s = ASSERT_PTR(userdata); + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + s->subtype = mfree(s->subtype); + return 0; + } + + if (!dns_subtype_name_is_valid(rvalue)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Service subtype is invalid. Ignoring."); + return 0; + } + + return free_and_strdup_warn(&s->subtype, rvalue); +} + int config_parse_dnssd_txt( const char *unit, const char *filename, diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h index 07ce2591a9c..ca768bb2d9f 100644 --- a/src/resolve/resolved-conf.h +++ b/src/resolve/resolved-conf.h @@ -17,6 +17,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_dns_servers); CONFIG_PARSER_PROTOTYPE(config_parse_search_domains); CONFIG_PARSER_PROTOTYPE(config_parse_dns_stub_listener_mode); CONFIG_PARSER_PROTOTYPE(config_parse_dnssd_service_name); +CONFIG_PARSER_PROTOTYPE(config_parse_dnssd_service_subtype); CONFIG_PARSER_PROTOTYPE(config_parse_dnssd_service_type); CONFIG_PARSER_PROTOTYPE(config_parse_dnssd_txt); CONFIG_PARSER_PROTOTYPE(config_parse_dns_stub_listener_extra); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index a2b49f0b1e7..b1ae36e3909 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -1614,6 +1614,12 @@ int dns_scope_add_dnssd_services(DnsScope *scope) { if (r < 0) log_warning_errno(r, "Failed to add PTR record to MDNS zone: %m"); + if (service->sub_ptr_rr) { + r = dns_zone_put(&scope->zone, scope, service->sub_ptr_rr, false); + if (r < 0) + log_warning_errno(r, "Failed to add selective PTR record to MDNS zone: %m"); + } + r = dns_zone_put(&scope->zone, scope, service->srv_rr, true); if (r < 0) log_warning_errno(r, "Failed to add SRV record to MDNS zone: %m"); @@ -1646,6 +1652,7 @@ int dns_scope_remove_dnssd_services(DnsScope *scope) { HASHMAP_FOREACH(service, scope->manager->dnssd_services) { dns_zone_remove_rr(&scope->zone, service->ptr_rr); + dns_zone_remove_rr(&scope->zone, service->sub_ptr_rr); dns_zone_remove_rr(&scope->zone, service->srv_rr); LIST_FOREACH(items, txt_data, service->txt_data_items) dns_zone_remove_rr(&scope->zone, txt_data->rr); diff --git a/src/resolve/resolved-dnssd-bus.c b/src/resolve/resolved-dnssd-bus.c index b2deef5694e..bdb3c2a893e 100644 --- a/src/resolve/resolved-dnssd-bus.c +++ b/src/resolve/resolved-dnssd-bus.c @@ -40,6 +40,7 @@ int bus_dnssd_method_unregister(sd_bus_message *message, void *userdata, sd_bus_ log_warning_errno(r, "Failed to send goodbye messages in IPv4 scope: %m"); dns_zone_remove_rr(&l->mdns_ipv4_scope->zone, s->ptr_rr); + dns_zone_remove_rr(&l->mdns_ipv4_scope->zone, s->sub_ptr_rr); dns_zone_remove_rr(&l->mdns_ipv4_scope->zone, s->srv_rr); LIST_FOREACH(items, txt_data, s->txt_data_items) dns_zone_remove_rr(&l->mdns_ipv4_scope->zone, txt_data->rr); @@ -51,6 +52,7 @@ int bus_dnssd_method_unregister(sd_bus_message *message, void *userdata, sd_bus_ log_warning_errno(r, "Failed to send goodbye messages in IPv6 scope: %m"); dns_zone_remove_rr(&l->mdns_ipv6_scope->zone, s->ptr_rr); + dns_zone_remove_rr(&l->mdns_ipv6_scope->zone, s->sub_ptr_rr); dns_zone_remove_rr(&l->mdns_ipv6_scope->zone, s->srv_rr); LIST_FOREACH(items, txt_data, s->txt_data_items) dns_zone_remove_rr(&l->mdns_ipv6_scope->zone, txt_data->rr); diff --git a/src/resolve/resolved-dnssd-gperf.gperf b/src/resolve/resolved-dnssd-gperf.gperf index f10eae3ceef..e78573bec0a 100644 --- a/src/resolve/resolved-dnssd-gperf.gperf +++ b/src/resolve/resolved-dnssd-gperf.gperf @@ -16,10 +16,11 @@ struct ConfigPerfItem; %struct-type %includes %% -Service.Name, config_parse_dnssd_service_name, 0, 0 -Service.Type, config_parse_dnssd_service_type, 0, 0 -Service.Port, config_parse_ip_port, 0, offsetof(DnssdService, port) -Service.Priority, config_parse_uint16, 0, offsetof(DnssdService, priority) -Service.Weight, config_parse_uint16, 0, offsetof(DnssdService, weight) -Service.TxtText, config_parse_dnssd_txt, DNS_TXT_ITEM_TEXT, 0 -Service.TxtData, config_parse_dnssd_txt, DNS_TXT_ITEM_DATA, 0 +Service.Name, config_parse_dnssd_service_name, 0, 0 +Service.Type, config_parse_dnssd_service_type, 0, 0 +Service.SubType, config_parse_dnssd_service_subtype, 0, 0 +Service.Port, config_parse_ip_port, 0, offsetof(DnssdService, port) +Service.Priority, config_parse_uint16, 0, offsetof(DnssdService, priority) +Service.Weight, config_parse_uint16, 0, offsetof(DnssdService, weight) +Service.TxtText, config_parse_dnssd_txt, DNS_TXT_ITEM_TEXT, 0 +Service.TxtData, config_parse_dnssd_txt, DNS_TXT_ITEM_DATA, 0 diff --git a/src/resolve/resolved-dnssd.c b/src/resolve/resolved-dnssd.c index 994771eca73..404c290eece 100644 --- a/src/resolve/resolved-dnssd.c +++ b/src/resolve/resolved-dnssd.c @@ -43,6 +43,7 @@ DnssdService *dnssd_service_free(DnssdService *service) { hashmap_remove(service->manager->dnssd_services, service->name); dns_resource_record_unref(service->ptr_rr); + dns_resource_record_unref(service->sub_ptr_rr); dns_resource_record_unref(service->srv_rr); dnssd_txtdata_free_all(service->txt_data_items); @@ -50,6 +51,7 @@ DnssdService *dnssd_service_free(DnssdService *service) { free(service->filename); free(service->name); free(service->type); + free(service->subtype); free(service->name_template); return mfree(service); @@ -208,7 +210,7 @@ int dnssd_load(Manager *manager) { } int dnssd_update_rrs(DnssdService *s) { - _cleanup_free_ char *n = NULL, *service_name = NULL, *full_name = NULL; + _cleanup_free_ char *n = NULL, *service_name = NULL, *full_name = NULL, *sub_name = NULL, *selective_name = NULL; int r; assert(s); @@ -216,6 +218,7 @@ int dnssd_update_rrs(DnssdService *s) { assert(s->manager); s->ptr_rr = dns_resource_record_unref(s->ptr_rr); + s->sub_ptr_rr = dns_resource_record_unref(s->sub_ptr_rr); s->srv_rr = dns_resource_record_unref(s->srv_rr); LIST_FOREACH(items, txt_data, s->txt_data_items) txt_data->rr = dns_resource_record_unref(txt_data->rr); @@ -230,6 +233,14 @@ int dnssd_update_rrs(DnssdService *s) { r = dns_name_concat(n, service_name, 0, &full_name); if (r < 0) return r; + if (s->subtype) { + r = dns_name_concat("_sub", service_name, 0, &sub_name); + if (r < 0) + return r; + r = dns_name_concat(s->subtype, sub_name, 0, &selective_name); + if (r < 0) + return r; + } LIST_FOREACH(items, txt_data, s->txt_data_items) { txt_data->rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_TXT, @@ -253,6 +264,17 @@ int dnssd_update_rrs(DnssdService *s) { if (!s->ptr_rr->ptr.name) goto oom; + if (selective_name) { + s->sub_ptr_rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, selective_name); + if (!s->sub_ptr_rr) + goto oom; + + s->sub_ptr_rr->ttl = MDNS_DEFAULT_TTL; + s->sub_ptr_rr->ptr.name = strdup(full_name); + if (!s->sub_ptr_rr->ptr.name) + goto oom; + } + s->srv_rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SRV, full_name); if (!s->srv_rr) @@ -272,6 +294,7 @@ oom: LIST_FOREACH(items, txt_data, s->txt_data_items) txt_data->rr = dns_resource_record_unref(txt_data->rr); s->ptr_rr = dns_resource_record_unref(s->ptr_rr); + s->sub_ptr_rr = dns_resource_record_unref(s->sub_ptr_rr); s->srv_rr = dns_resource_record_unref(s->srv_rr); return -ENOMEM; } diff --git a/src/resolve/resolved-dnssd.h b/src/resolve/resolved-dnssd.h index e978a0d5fc6..970f2ba3c86 100644 --- a/src/resolve/resolved-dnssd.h +++ b/src/resolve/resolved-dnssd.h @@ -29,11 +29,13 @@ struct DnssdService { char *name; char *name_template; char *type; + char *subtype; uint16_t port; uint16_t priority; uint16_t weight; DnsResourceRecord *ptr_rr; + DnsResourceRecord *sub_ptr_rr; DnsResourceRecord *srv_rr; /* Section 6.8 of RFC 6763 allows having service diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 909b4cdcc93..ba24a77dad9 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -980,6 +980,29 @@ bool dns_service_name_is_valid(const char *name) { return true; } +bool dns_subtype_name_is_valid(const char *name) { + size_t l; + + /* This more or less implements RFC 6763, Section 7.2 */ + + if (!name) + return false; + + if (!utf8_is_valid(name)) + return false; + + if (string_has_cc(name, NULL)) + return false; + + l = strlen(name); + if (l <= 0) + return false; + if (l > DNS_LABEL_MAX) + return false; + + return true; +} + int dns_service_join(const char *name, const char *type, const char *domain, char **ret) { char escaped[DNS_LABEL_ESCAPED_MAX]; _cleanup_free_ char *n = NULL; diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 331fb89637c..8ad00d6e4bc 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -83,6 +83,7 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, boo bool dns_srv_type_is_valid(const char *name); bool dnssd_srv_type_is_valid(const char *name); bool dns_service_name_is_valid(const char *name); +bool dns_subtype_name_is_valid(const char *name); int dns_service_join(const char *name, const char *type, const char *domain, char **ret); int dns_service_split(const char *joined, char **ret_name, char **ret_type, char **ret_domain);