]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dnssd: support service subtypes
authorRonan Pigott <ronan@rjp.ie>
Sat, 16 Dec 2023 05:55:54 +0000 (22:55 -0700)
committerLuca Boccassi <luca.boccassi@gmail.com>
Mon, 18 Mar 2024 13:16:48 +0000 (13:16 +0000)
A service subtype is used for selective enumeration of services.

man/systemd.dnssd.xml
src/resolve/resolved-conf.c
src/resolve/resolved-conf.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dnssd-bus.c
src/resolve/resolved-dnssd-gperf.gperf
src/resolve/resolved-dnssd.c
src/resolve/resolved-dnssd.h
src/shared/dns-domain.c
src/shared/dns-domain.h

index 2b29a99dc822a1fd1a9c8466a29a3b5e131dacce..a2641e851687690e1fca10791a2376f4566a49ce 100644 (file)
           <xi:include href="version-info.xml" xpointer="v236"/>
           </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>
index b648c3e520342d3556ca27d58896adb8b90c16f7..504da9ebca31ed0eb655ee939be5d1ace8e99156 100644 (file)
@@ -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,
index 07ce2591a9ca2fdb110602f8e9bddcac474cb60e..ca768bb2d9fadfd4ae79e769e6c2a610bff5fa70 100644 (file)
@@ -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);
index a2b49f0b1e76538d77dc3be263bbf7219f7337b2..b1ae36e3909c96cd41e7b554213955e735c2a58c 100644 (file)
@@ -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);
index b2deef5694e30d7402bd0543c4ebcbafe5337a3f..bdb3c2a893ea54b9894dcc190d7d559ed25486f3 100644 (file)
@@ -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);
index f10eae3ceefd6dde4b036ced4dc62fe0469e6178..e78573bec0a40b1e13ec607e66083e3d2137a9b1 100644 (file)
@@ -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
index 994771eca733de3600ba85298f899ef6b942c994..404c290eeceaa332e1f2d0cf8e9a4f10bb7d96e8 100644 (file)
@@ -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;
 }
index e978a0d5fc6ec4e16522bf05f7831a23144ade8f..970f2ba3c86764c0a32d2d14bafe9eb540c71a39 100644 (file)
@@ -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
index 909b4cdcc93aeedce4d5e43deedea64b26ad8c3b..ba24a77dad993feecbe4d4aa8bfc867b765bc417 100644 (file)
@@ -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;
index 331fb89637c52f30fca61172d79a0e7d94e64391..8ad00d6e4bc5a757ecf6bbe76b2d519f1d826f89 100644 (file)
@@ -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);