dnssd: support service subtypes

A service subtype is used for selective enumeration of services.
This commit is contained in:
Ronan Pigott 2023-12-15 22:55:54 -07:00 committed by Luca Boccassi
parent 15d4bedf3d
commit 88123aa21c
10 changed files with 109 additions and 8 deletions

View file

@ -118,6 +118,16 @@
<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>

View 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,

View 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);

View 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);

View 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);

View 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

View 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;
}

View 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

View 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;

View 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);