A service subtype is used for selective enumeration of services.
<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>
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,
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);
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");
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);
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);
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);
%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
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);
free(service->filename);
free(service->name);
free(service->type);
+ free(service->subtype);
free(service->name_template);
return mfree(service);
}
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);
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);
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,
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)
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;
}
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
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;
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);