summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRonan Pigott <ronan@rjp.ie>2023-12-16 06:55:54 +0100
committerLuca Boccassi <luca.boccassi@gmail.com>2024-03-18 14:16:48 +0100
commit88123aa21c2611049d01174ccbc662dfac26d9bb (patch)
tree04832fcce7ab077bdeef52d3e7660afa1651bdf0
parentMerge pull request #31778 from yuwata/kbd-util (diff)
downloadsystemd-88123aa21c2611049d01174ccbc662dfac26d9bb.tar.xz
systemd-88123aa21c2611049d01174ccbc662dfac26d9bb.zip
dnssd: support service subtypes
A service subtype is used for selective enumeration of services.
-rw-r--r--man/systemd.dnssd.xml10
-rw-r--r--src/resolve/resolved-conf.c31
-rw-r--r--src/resolve/resolved-conf.h1
-rw-r--r--src/resolve/resolved-dns-scope.c7
-rw-r--r--src/resolve/resolved-dnssd-bus.c2
-rw-r--r--src/resolve/resolved-dnssd-gperf.gperf15
-rw-r--r--src/resolve/resolved-dnssd.c25
-rw-r--r--src/resolve/resolved-dnssd.h2
-rw-r--r--src/shared/dns-domain.c23
-rw-r--r--src/shared/dns-domain.h1
10 files changed, 109 insertions, 8 deletions
diff --git a/man/systemd.dnssd.xml b/man/systemd.dnssd.xml
index 2b29a99dc8..a2641e8516 100644
--- a/man/systemd.dnssd.xml
+++ b/man/systemd.dnssd.xml
@@ -119,6 +119,16 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><varname>SubType=</varname></term>
+ <listitem>
+ <para>A subtype of the network service as defined in the section 7.1 of <ulink
+ url="https://tools.ietf.org/html/rfc6763">RFC 6763</ulink>, e.g. <literal>_printer</literal>.
+ </para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>Port=</varname></term>
<listitem>
<para>An IP port number of the network service.</para>
diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c
index b648c3e520..504da9ebca 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 07ce2591a9..ca768bb2d9 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 a2b49f0b1e..b1ae36e390 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 b2deef5694..bdb3c2a893 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 f10eae3cee..e78573bec0 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 994771eca7..404c290eec 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 e978a0d5fc..970f2ba3c8 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 909b4cdcc9..ba24a77dad 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 331fb89637..8ad00d6e4b 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);