1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
6 #include "conf-files.h"
7 #include "conf-parser.h"
9 #include "dns-domain.h"
10 #include "extract-word.h"
12 #include "hexdecoct.h"
13 #include "path-util.h"
14 #include "resolved-conf.h"
15 #include "resolved-dns-rr.h"
16 #include "resolved-dns-zone.h"
17 #include "resolved-dnssd.h"
18 #include "resolved-manager.h"
19 #include "specifier.h"
20 #include "string-util.h"
24 #define DNSSD_SERVICE_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/dnssd"))
26 DnssdTxtData
*dnssd_txtdata_free(DnssdTxtData
*txt_data
) {
30 dns_resource_record_unref(txt_data
->rr
);
31 dns_txt_item_free_all(txt_data
->txts
);
33 return mfree(txt_data
);
36 DnssdTxtData
*dnssd_txtdata_free_all(DnssdTxtData
*txt_data
) {
42 next
= txt_data
->items_next
;
44 dnssd_txtdata_free(txt_data
);
46 return dnssd_txtdata_free_all(next
);
49 DnssdService
*dnssd_service_free(DnssdService
*service
) {
54 hashmap_remove(service
->manager
->dnssd_services
, service
->id
);
56 dns_resource_record_unref(service
->ptr_rr
);
57 dns_resource_record_unref(service
->sub_ptr_rr
);
58 dns_resource_record_unref(service
->srv_rr
);
60 dnssd_txtdata_free_all(service
->txt_data_items
);
65 free(service
->subtype
);
66 free(service
->name_template
);
68 return mfree(service
);
71 void dnssd_service_clear_on_reload(Hashmap
*services
) {
72 DnssdService
*service
;
74 HASHMAP_FOREACH(service
, services
)
75 if (service
->config_source
== RESOLVE_CONFIG_SOURCE_FILE
) {
76 hashmap_remove(services
, service
->id
);
77 dnssd_service_free(service
);
81 static int dnssd_id_from_path(const char *path
, char **ret_id
) {
87 _cleanup_free_
char *fn
= NULL
;
88 r
= path_extract_filename(path
, &fn
);
92 char *d
= endswith(fn
, ".dnssd");
98 *ret_id
= TAKE_PTR(fn
);
102 static int dnssd_service_load(Manager
*manager
, const char *path
) {
103 _cleanup_(dnssd_service_freep
) DnssdService
*service
= NULL
;
104 _cleanup_(dnssd_txtdata_freep
) DnssdTxtData
*txt_data
= NULL
;
105 _cleanup_free_
char *dropin_dirname
= NULL
;
111 service
= new0(DnssdService
, 1);
115 service
->path
= strdup(path
);
119 r
= dnssd_id_from_path(path
, &service
->id
);
121 return log_error_errno(r
, "Failed to extract DNS-SD service id from filename: %m");
123 dropin_dirname
= strjoin(service
->id
, ".dnssd.d");
127 r
= config_parse_many(
128 STRV_MAKE_CONST(path
), DNSSD_SERVICE_DIRS
, dropin_dirname
, /* root = */ NULL
,
130 config_item_perf_lookup
, resolved_dnssd_gperf_lookup
,
138 if (!service
->name_template
)
139 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
140 "%s doesn't define service instance name",
144 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
145 "%s doesn't define service type",
148 if (!service
->txt_data_items
) {
149 txt_data
= new0(DnssdTxtData
, 1);
153 r
= dns_txt_item_new_empty(&txt_data
->txts
);
157 LIST_PREPEND(items
, service
->txt_data_items
, txt_data
);
161 r
= hashmap_ensure_put(&manager
->dnssd_services
, &string_hash_ops
, service
->id
, service
);
165 service
->manager
= manager
;
167 r
= dnssd_update_rrs(service
);
176 static int specifier_dnssd_hostname(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
177 const Manager
*m
= ASSERT_PTR(userdata
);
179 assert(m
->llmnr_hostname
);
181 return strdup_to(ret
, m
->llmnr_hostname
);
184 int dnssd_render_instance_name(Manager
*m
, DnssdService
*s
, char **ret
) {
185 static const Specifier specifier_table
[] = {
186 { 'a', specifier_architecture
, NULL
},
187 { 'b', specifier_boot_id
, NULL
},
188 { 'B', specifier_os_build_id
, NULL
},
189 { 'H', specifier_dnssd_hostname
, NULL
},
190 { 'm', specifier_machine_id
, NULL
},
191 { 'o', specifier_os_id
, NULL
},
192 { 'v', specifier_kernel_release
, NULL
},
193 { 'w', specifier_os_version_id
, NULL
},
194 { 'W', specifier_os_variant_id
, NULL
},
197 _cleanup_free_
char *name
= NULL
;
202 assert(s
->name_template
);
204 r
= specifier_printf(s
->name_template
, DNS_LABEL_MAX
, specifier_table
, NULL
, m
, &name
);
206 return log_debug_errno(r
, "Failed to replace specifiers: %m");
208 if (!dns_service_name_is_valid(name
))
209 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
210 "Service instance name '%s' is invalid.",
214 *ret
= TAKE_PTR(name
);
219 int dnssd_load(Manager
*manager
) {
220 _cleanup_strv_free_
char **files
= NULL
;
225 if (manager
->mdns_support
!= RESOLVE_SUPPORT_YES
)
228 r
= conf_files_list_strv(&files
, ".dnssd", NULL
, 0, DNSSD_SERVICE_DIRS
);
230 return log_error_errno(r
, "Failed to enumerate .dnssd files: %m");
232 STRV_FOREACH_BACKWARDS(f
, files
) {
233 r
= dnssd_service_load(manager
, *f
);
235 log_warning_errno(r
, "Failed to load '%s': %m", *f
);
241 int dnssd_update_rrs(DnssdService
*s
) {
242 _cleanup_free_
char *n
= NULL
, *service_name
= NULL
, *full_name
= NULL
, *sub_name
= NULL
, *selective_name
= NULL
;
246 assert(s
->txt_data_items
);
249 s
->ptr_rr
= dns_resource_record_unref(s
->ptr_rr
);
250 s
->sub_ptr_rr
= dns_resource_record_unref(s
->sub_ptr_rr
);
251 s
->srv_rr
= dns_resource_record_unref(s
->srv_rr
);
252 LIST_FOREACH(items
, txt_data
, s
->txt_data_items
)
253 txt_data
->rr
= dns_resource_record_unref(txt_data
->rr
);
255 r
= dnssd_render_instance_name(s
->manager
, s
, &n
);
259 r
= dns_name_concat(s
->type
, "local", 0, &service_name
);
262 r
= dns_name_concat(n
, service_name
, 0, &full_name
);
266 r
= dns_name_concat("_sub", service_name
, 0, &sub_name
);
269 r
= dns_name_concat(s
->subtype
, sub_name
, 0, &selective_name
);
274 LIST_FOREACH(items
, txt_data
, s
->txt_data_items
) {
275 txt_data
->rr
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_TXT
,
280 txt_data
->rr
->ttl
= MDNS_DEFAULT_TTL
;
281 txt_data
->rr
->txt
.items
= dns_txt_item_copy(txt_data
->txts
);
282 if (!txt_data
->rr
->txt
.items
)
286 s
->ptr_rr
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_PTR
,
291 s
->ptr_rr
->ttl
= MDNS_DEFAULT_TTL
;
292 s
->ptr_rr
->ptr
.name
= strdup(full_name
);
293 if (!s
->ptr_rr
->ptr
.name
)
296 if (selective_name
) {
297 s
->sub_ptr_rr
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_PTR
, selective_name
);
301 s
->sub_ptr_rr
->ttl
= MDNS_DEFAULT_TTL
;
302 s
->sub_ptr_rr
->ptr
.name
= strdup(full_name
);
303 if (!s
->sub_ptr_rr
->ptr
.name
)
307 s
->srv_rr
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_SRV
,
312 s
->srv_rr
->ttl
= MDNS_DEFAULT_TTL
;
313 s
->srv_rr
->srv
.priority
= s
->priority
;
314 s
->srv_rr
->srv
.weight
= s
->weight
;
315 s
->srv_rr
->srv
.port
= s
->port
;
316 s
->srv_rr
->srv
.name
= strdup(s
->manager
->mdns_hostname
);
317 if (!s
->srv_rr
->srv
.name
)
323 LIST_FOREACH(items
, txt_data
, s
->txt_data_items
)
324 txt_data
->rr
= dns_resource_record_unref(txt_data
->rr
);
325 s
->ptr_rr
= dns_resource_record_unref(s
->ptr_rr
);
326 s
->sub_ptr_rr
= dns_resource_record_unref(s
->sub_ptr_rr
);
327 s
->srv_rr
= dns_resource_record_unref(s
->srv_rr
);
331 int dnssd_txt_item_new_from_string(const char *key
, const char *value
, DnsTxtItem
**ret_item
) {
335 length
= strlen(key
);
338 length
+= strlen(value
) + 1; /* length of value plus '=' */
340 i
= malloc0(offsetof(DnsTxtItem
, data
) + length
+ 1); /* for safety reasons we add an extra NUL byte */
344 memcpy(i
->data
, key
, strlen(key
));
345 if (!isempty(value
)) {
346 memcpy(i
->data
+ strlen(key
), "=", 1);
347 memcpy(i
->data
+ strlen(key
) + 1, value
, strlen(value
));
351 *ret_item
= TAKE_PTR(i
);
356 int dnssd_txt_item_new_from_data(const char *key
, const void *data
, const size_t size
, DnsTxtItem
**ret_item
) {
360 length
= strlen(key
);
363 length
+= size
+ 1; /* size of date plus '=' */
365 i
= malloc0(offsetof(DnsTxtItem
, data
) + length
+ 1); /* for safety reasons we add an extra NUL byte */
369 memcpy(i
->data
, key
, strlen(key
));
371 memcpy(i
->data
+ strlen(key
), "=", 1);
372 memcpy(i
->data
+ strlen(key
) + 1, data
, size
);
376 *ret_item
= TAKE_PTR(i
);
381 int dnssd_signal_conflict(Manager
*manager
, const char *name
) {
385 if (sd_bus_is_ready(manager
->bus
) <= 0)
388 HASHMAP_FOREACH(s
, manager
->dnssd_services
) {
392 if (dns_name_equal(dns_resource_key_name(s
->srv_rr
->key
), name
) > 0) {
393 _cleanup_free_
char *path
= NULL
;
397 r
= sd_bus_path_encode("/org/freedesktop/resolve1/dnssd", s
->id
, &path
);
399 return log_error_errno(r
, "Can't get D-BUS object path: %m");
401 r
= sd_bus_emit_signal(manager
->bus
,
403 "org.freedesktop.resolve1.DnssdService",
407 return log_error_errno(r
, "Cannot emit signal: %m");
416 int config_parse_dnssd_service_name(
418 const char *filename
,
421 unsigned section_line
,
428 static const Specifier specifier_table
[] = {
429 { 'a', specifier_architecture
, NULL
},
430 { 'b', specifier_boot_id
, NULL
},
431 { 'B', specifier_os_build_id
, NULL
},
432 { 'H', specifier_hostname
, NULL
}, /* We will use specifier_dnssd_hostname(). */
433 { 'm', specifier_machine_id
, NULL
},
434 { 'o', specifier_os_id
, NULL
},
435 { 'v', specifier_kernel_release
, NULL
},
436 { 'w', specifier_os_version_id
, NULL
},
437 { 'W', specifier_os_variant_id
, NULL
},
440 DnssdService
*s
= ASSERT_PTR(userdata
);
441 _cleanup_free_
char *name
= NULL
;
448 if (isempty(rvalue
)) {
449 s
->name_template
= mfree(s
->name_template
);
453 r
= specifier_printf(rvalue
, DNS_LABEL_MAX
, specifier_table
, NULL
, NULL
, &name
);
455 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
456 "Invalid service instance name template '%s', ignoring assignment: %m", rvalue
);
460 if (!dns_service_name_is_valid(name
)) {
461 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
462 "Service instance name template '%s' renders to invalid name '%s'. Ignoring assignment.",
467 return free_and_strdup_warn(&s
->name_template
, rvalue
);
470 int config_parse_dnssd_service_type(
472 const char *filename
,
475 unsigned section_line
,
482 DnssdService
*s
= ASSERT_PTR(userdata
);
489 if (isempty(rvalue
)) {
490 s
->type
= mfree(s
->type
);
494 if (!dnssd_srv_type_is_valid(rvalue
)) {
495 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Service type is invalid. Ignoring.");
499 r
= free_and_strdup(&s
->type
, rvalue
);
506 int config_parse_dnssd_service_subtype(
508 const char *filename
,
511 unsigned section_line
,
518 DnssdService
*s
= ASSERT_PTR(userdata
);
524 if (isempty(rvalue
)) {
525 s
->subtype
= mfree(s
->subtype
);
529 if (!dns_subtype_name_is_valid(rvalue
)) {
530 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Service subtype is invalid. Ignoring.");
534 return free_and_strdup_warn(&s
->subtype
, rvalue
);
537 int config_parse_dnssd_txt(
539 const char *filename
,
542 unsigned section_line
,
549 _cleanup_(dnssd_txtdata_freep
) DnssdTxtData
*txt_data
= NULL
;
550 DnssdService
*s
= ASSERT_PTR(userdata
);
551 DnsTxtItem
*last
= NULL
;
557 if (isempty(rvalue
)) {
558 /* Flush out collected items */
559 s
->txt_data_items
= dnssd_txtdata_free_all(s
->txt_data_items
);
563 txt_data
= new0(DnssdTxtData
, 1);
568 _cleanup_free_
char *word
= NULL
, *key
= NULL
, *value
= NULL
;
569 _cleanup_free_
void *decoded
= NULL
;
574 r
= extract_first_word(&rvalue
, &word
, NULL
,
575 EXTRACT_UNQUOTE
|EXTRACT_CUNESCAPE
|EXTRACT_UNESCAPE_RELAX
);
581 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
585 r
= split_pair(word
, "=", &key
, &value
);
589 key
= TAKE_PTR(word
);
591 if (!ascii_is_valid(key
)) {
592 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Invalid key, ignoring: %s", key
);
598 case DNS_TXT_ITEM_DATA
:
600 r
= unbase64mem(value
, &decoded
, &length
);
604 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
605 "Invalid base64 encoding, ignoring: %s", value
);
610 r
= dnssd_txt_item_new_from_data(key
, decoded
, length
, &i
);
615 case DNS_TXT_ITEM_TEXT
:
616 r
= dnssd_txt_item_new_from_string(key
, value
, &i
);
622 assert_not_reached();
625 LIST_INSERT_AFTER(items
, txt_data
->txts
, last
, i
);
629 if (txt_data
->txts
) {
630 LIST_PREPEND(items
, s
->txt_data_items
, txt_data
);