1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "conf-files.h"
4 #include "conf-parser.h"
6 #include "resolved-dnssd.h"
7 #include "resolved-dns-rr.h"
8 #include "resolved-manager.h"
9 #include "resolved-conf.h"
10 #include "specifier.h"
13 #define DNSSD_SERVICE_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/dnssd"))
15 DnssdTxtData
*dnssd_txtdata_free(DnssdTxtData
*txt_data
) {
19 dns_resource_record_unref(txt_data
->rr
);
20 dns_txt_item_free_all(txt_data
->txt
);
22 return mfree(txt_data
);
25 DnssdTxtData
*dnssd_txtdata_free_all(DnssdTxtData
*txt_data
) {
31 next
= txt_data
->items_next
;
33 dnssd_txtdata_free(txt_data
);
35 return dnssd_txtdata_free_all(next
);
38 DnssdService
*dnssd_service_free(DnssdService
*service
) {
43 hashmap_remove(service
->manager
->dnssd_services
, service
->name
);
45 dns_resource_record_unref(service
->ptr_rr
);
46 dns_resource_record_unref(service
->srv_rr
);
48 dnssd_txtdata_free_all(service
->txt_data_items
);
50 free(service
->filename
);
53 free(service
->name_template
);
55 return mfree(service
);
58 static int dnssd_service_load(Manager
*manager
, const char *filename
) {
59 _cleanup_(dnssd_service_freep
) DnssdService
*service
= NULL
;
60 _cleanup_(dnssd_txtdata_freep
) DnssdTxtData
*txt_data
= NULL
;
62 const char *dropin_dirname
;
68 service
= new0(DnssdService
, 1);
72 service
->filename
= strdup(filename
);
73 if (!service
->filename
)
76 service
->name
= strdup(basename(filename
));
80 d
= endswith(service
->name
, ".dnssd");
84 assert(streq(d
, ".dnssd"));
88 dropin_dirname
= strjoina(service
->name
, ".dnssd.d");
90 r
= config_parse_many(
91 STRV_MAKE_CONST(filename
), DNSSD_SERVICE_DIRS
, dropin_dirname
,
93 config_item_perf_lookup
, resolved_dnssd_gperf_lookup
,
100 if (!service
->name_template
)
101 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
102 "%s doesn't define service instance name",
106 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
107 "%s doesn't define service type",
110 if (LIST_IS_EMPTY(service
->txt_data_items
)) {
111 txt_data
= new0(DnssdTxtData
, 1);
115 r
= dns_txt_item_new_empty(&txt_data
->txt
);
119 LIST_PREPEND(items
, service
->txt_data_items
, txt_data
);
123 r
= hashmap_ensure_put(&manager
->dnssd_services
, &string_hash_ops
, service
->name
, service
);
127 service
->manager
= manager
;
129 r
= dnssd_update_rrs(service
);
138 static int specifier_dnssd_host_name(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
139 DnssdService
*s
= (DnssdService
*) userdata
;
144 assert(s
->manager
->llmnr_hostname
);
146 n
= strdup(s
->manager
->llmnr_hostname
);
154 int dnssd_render_instance_name(DnssdService
*s
, char **ret_name
) {
155 static const Specifier specifier_table
[] = {
156 { 'a', specifier_architecture
, NULL
},
157 { 'b', specifier_boot_id
, NULL
},
158 { 'B', specifier_os_build_id
, NULL
},
159 { 'H', specifier_dnssd_host_name
, NULL
},
160 { 'm', specifier_machine_id
, NULL
},
161 { 'o', specifier_os_id
, NULL
},
162 { 'v', specifier_kernel_release
, NULL
},
163 { 'w', specifier_os_version_id
, NULL
},
164 { 'W', specifier_os_variant_id
, NULL
},
167 _cleanup_free_
char *name
= NULL
;
171 assert(s
->name_template
);
173 r
= specifier_printf(s
->name_template
, DNS_LABEL_MAX
, specifier_table
, NULL
, s
, &name
);
175 return log_debug_errno(r
, "Failed to replace specifiers: %m");
177 if (!dns_service_name_is_valid(name
))
178 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
179 "Service instance name '%s' is invalid.",
182 *ret_name
= TAKE_PTR(name
);
187 int dnssd_load(Manager
*manager
) {
188 _cleanup_strv_free_
char **files
= NULL
;
194 if (manager
->mdns_support
!= RESOLVE_SUPPORT_YES
)
197 r
= conf_files_list_strv(&files
, ".dnssd", NULL
, 0, DNSSD_SERVICE_DIRS
);
199 return log_error_errno(r
, "Failed to enumerate .dnssd files: %m");
201 STRV_FOREACH_BACKWARDS(f
, files
) {
202 r
= dnssd_service_load(manager
, *f
);
204 log_warning_errno(r
, "Failed to load '%s': %m", *f
);;
210 int dnssd_update_rrs(DnssdService
*s
) {
211 _cleanup_free_
char *n
= NULL
;
212 _cleanup_free_
char *service_name
= NULL
;
213 _cleanup_free_
char *full_name
= NULL
;
217 assert(s
->txt_data_items
);
220 s
->ptr_rr
= dns_resource_record_unref(s
->ptr_rr
);
221 s
->srv_rr
= dns_resource_record_unref(s
->srv_rr
);
222 LIST_FOREACH(items
, txt_data
, s
->txt_data_items
)
223 txt_data
->rr
= dns_resource_record_unref(txt_data
->rr
);
225 r
= dnssd_render_instance_name(s
, &n
);
229 r
= dns_name_concat(s
->type
, "local", 0, &service_name
);
232 r
= dns_name_concat(n
, service_name
, 0, &full_name
);
236 LIST_FOREACH(items
, txt_data
, s
->txt_data_items
) {
237 txt_data
->rr
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_TXT
,
242 txt_data
->rr
->ttl
= MDNS_DEFAULT_TTL
;
243 txt_data
->rr
->txt
.items
= dns_txt_item_copy(txt_data
->txt
);
244 if (!txt_data
->rr
->txt
.items
)
248 s
->ptr_rr
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_PTR
,
253 s
->ptr_rr
->ttl
= MDNS_DEFAULT_TTL
;
254 s
->ptr_rr
->ptr
.name
= strdup(full_name
);
255 if (!s
->ptr_rr
->ptr
.name
)
258 s
->srv_rr
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_SRV
,
263 s
->srv_rr
->ttl
= MDNS_DEFAULT_TTL
;
264 s
->srv_rr
->srv
.priority
= s
->priority
;
265 s
->srv_rr
->srv
.weight
= s
->weight
;
266 s
->srv_rr
->srv
.port
= s
->port
;
267 s
->srv_rr
->srv
.name
= strdup(s
->manager
->mdns_hostname
);
268 if (!s
->srv_rr
->srv
.name
)
274 LIST_FOREACH(items
, txt_data
, s
->txt_data_items
)
275 txt_data
->rr
= dns_resource_record_unref(txt_data
->rr
);
276 s
->ptr_rr
= dns_resource_record_unref(s
->ptr_rr
);
277 s
->srv_rr
= dns_resource_record_unref(s
->srv_rr
);
281 int dnssd_txt_item_new_from_string(const char *key
, const char *value
, DnsTxtItem
**ret_item
) {
285 length
= strlen(key
);
288 length
+= strlen(value
) + 1; /* length of value plus '=' */
290 i
= malloc0(offsetof(DnsTxtItem
, data
) + length
+ 1); /* for safety reasons we add an extra NUL byte */
294 memcpy(i
->data
, key
, strlen(key
));
295 if (!isempty(value
)) {
296 memcpy(i
->data
+ strlen(key
), "=", 1);
297 memcpy(i
->data
+ strlen(key
) + 1, value
, strlen(value
));
301 *ret_item
= TAKE_PTR(i
);
306 int dnssd_txt_item_new_from_data(const char *key
, const void *data
, const size_t size
, DnsTxtItem
**ret_item
) {
310 length
= strlen(key
);
313 length
+= size
+ 1; /* size of date plus '=' */
315 i
= malloc0(offsetof(DnsTxtItem
, data
) + length
+ 1); /* for safety reasons we add an extra NUL byte */
319 memcpy(i
->data
, key
, strlen(key
));
321 memcpy(i
->data
+ strlen(key
), "=", 1);
322 memcpy(i
->data
+ strlen(key
) + 1, data
, size
);
326 *ret_item
= TAKE_PTR(i
);
331 int dnssd_signal_conflict(Manager
*manager
, const char *name
) {
335 if (sd_bus_is_ready(manager
->bus
) <= 0)
338 HASHMAP_FOREACH(s
, manager
->dnssd_services
) {
342 if (dns_name_equal(dns_resource_key_name(s
->srv_rr
->key
), name
)) {
343 _cleanup_free_
char *path
= NULL
;
347 r
= sd_bus_path_encode("/org/freedesktop/resolve1/dnssd", s
->name
, &path
);
349 return log_error_errno(r
, "Can't get D-BUS object path: %m");
351 r
= sd_bus_emit_signal(manager
->bus
,
353 "org.freedesktop.resolve1.DnssdService",
357 return log_error_errno(r
, "Cannot emit signal: %m");