]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dnssd.c
core/scope: drop effectively unused unit_watch_pidref() calls (#38186)
[thirdparty/systemd.git] / src / resolve / resolved-dnssd.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
6501dd31 2
284d7641
DDM
3#include "sd-bus.h"
4
b78d73fa 5#include "alloc-util.h"
6501dd31
DR
6#include "conf-files.h"
7#include "conf-parser.h"
28db6fbf 8#include "constants.h"
68527d30 9#include "dns-domain.h"
284d7641
DDM
10#include "extract-word.h"
11#include "hashmap.h"
8e1c3459 12#include "hexdecoct.h"
0ef0e269
LP
13#include "path-util.h"
14#include "resolved-conf.h"
6501dd31 15#include "resolved-dns-rr.h"
68527d30 16#include "resolved-dns-zone.h"
0ef0e269 17#include "resolved-dnssd.h"
6501dd31
DR
18#include "resolved-manager.h"
19#include "specifier.h"
284d7641 20#include "string-util.h"
6501dd31 21#include "strv.h"
e4a08721 22#include "utf8.h"
6501dd31 23
eb5f4dde 24#define DNSSD_SERVICE_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/dnssd"))
6501dd31 25
400f54fb
DR
26DnssdTxtData *dnssd_txtdata_free(DnssdTxtData *txt_data) {
27 if (!txt_data)
28 return NULL;
29
30 dns_resource_record_unref(txt_data->rr);
d70f15f5 31 dns_txt_item_free_all(txt_data->txts);
400f54fb
DR
32
33 return mfree(txt_data);
34}
35
36DnssdTxtData *dnssd_txtdata_free_all(DnssdTxtData *txt_data) {
37 DnssdTxtData *next;
38
39 if (!txt_data)
40 return NULL;
41
42 next = txt_data->items_next;
43
44 dnssd_txtdata_free(txt_data);
45
46 return dnssd_txtdata_free_all(next);
47}
48
6501dd31
DR
49DnssdService *dnssd_service_free(DnssdService *service) {
50 if (!service)
51 return NULL;
52
53 if (service->manager)
0ef0e269 54 hashmap_remove(service->manager->dnssd_services, service->id);
6501dd31
DR
55
56 dns_resource_record_unref(service->ptr_rr);
88123aa2 57 dns_resource_record_unref(service->sub_ptr_rr);
6501dd31 58 dns_resource_record_unref(service->srv_rr);
400f54fb
DR
59
60 dnssd_txtdata_free_all(service->txt_data_items);
6501dd31 61
40763016 62 free(service->path);
0ef0e269 63 free(service->id);
6501dd31 64 free(service->type);
88123aa2 65 free(service->subtype);
6501dd31 66 free(service->name_template);
6501dd31
DR
67
68 return mfree(service);
69}
70
14a52176
LB
71void dnssd_service_clear_on_reload(Hashmap *services) {
72 DnssdService *service;
73
74 HASHMAP_FOREACH(service, services)
75 if (service->config_source == RESOLVE_CONFIG_SOURCE_FILE) {
0ef0e269 76 hashmap_remove(services, service->id);
14a52176
LB
77 dnssd_service_free(service);
78 }
79}
80
0ef0e269
LP
81static int dnssd_id_from_path(const char *path, char **ret_id) {
82 int r;
83
84 assert(path);
85 assert(ret_id);
86
87 _cleanup_free_ char *fn = NULL;
88 r = path_extract_filename(path, &fn);
89 if (r < 0)
90 return r;
91
92 char *d = endswith(fn, ".dnssd");
93 if (!d)
94 return -EINVAL;
95
96 *d = '\0';
97
98 *ret_id = TAKE_PTR(fn);
99 return 0;
100}
101
40763016 102static int dnssd_service_load(Manager *manager, const char *path) {
6501dd31 103 _cleanup_(dnssd_service_freep) DnssdService *service = NULL;
400f54fb 104 _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
0ef0e269 105 _cleanup_free_ char *dropin_dirname = NULL;
6501dd31
DR
106 int r;
107
108 assert(manager);
40763016 109 assert(path);
6501dd31
DR
110
111 service = new0(DnssdService, 1);
112 if (!service)
113 return log_oom();
114
40763016
LP
115 service->path = strdup(path);
116 if (!service->path)
6501dd31
DR
117 return log_oom();
118
0ef0e269
LP
119 r = dnssd_id_from_path(path, &service->id);
120 if (r < 0)
121 return log_error_errno(r, "Failed to extract DNS-SD service id from filename: %m");
6501dd31 122
0ef0e269
LP
123 dropin_dirname = strjoin(service->id, ".dnssd.d");
124 if (!dropin_dirname)
125 return log_oom();
6501dd31 126
4f9ff96a 127 r = config_parse_many(
40763016 128 STRV_MAKE_CONST(path), DNSSD_SERVICE_DIRS, dropin_dirname, /* root = */ NULL,
4f9ff96a
LP
129 "Service\0",
130 config_item_perf_lookup, resolved_dnssd_gperf_lookup,
131 CONFIG_PARSE_WARN,
132 service,
ead3a3fc 133 NULL,
4f9ff96a 134 NULL);
6501dd31
DR
135 if (r < 0)
136 return r;
137
d7a0f1f4
FS
138 if (!service->name_template)
139 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
140 "%s doesn't define service instance name",
0ef0e269 141 service->id);
d7a0f1f4
FS
142
143 if (!service->type)
144 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
145 "%s doesn't define service type",
0ef0e269 146 service->id);
6501dd31 147
64903d18 148 if (!service->txt_data_items) {
400f54fb
DR
149 txt_data = new0(DnssdTxtData, 1);
150 if (!txt_data)
151 return log_oom();
152
d70f15f5 153 r = dns_txt_item_new_empty(&txt_data->txts);
6501dd31
DR
154 if (r < 0)
155 return r;
400f54fb
DR
156
157 LIST_PREPEND(items, service->txt_data_items, txt_data);
15c69d07 158 TAKE_PTR(txt_data);
6501dd31
DR
159 }
160
0ef0e269 161 r = hashmap_ensure_put(&manager->dnssd_services, &string_hash_ops, service->id, service);
6501dd31
DR
162 if (r < 0)
163 return r;
164
165 service->manager = manager;
166
6db6a464
DR
167 r = dnssd_update_rrs(service);
168 if (r < 0)
169 return r;
170
15c69d07 171 TAKE_PTR(service);
6501dd31
DR
172
173 return 0;
174}
175
9a5893e9 176static int specifier_dnssd_hostname(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
a3f87e32 177 const Manager *m = ASSERT_PTR(userdata);
6501dd31 178
a3f87e32 179 assert(m->llmnr_hostname);
6501dd31 180
454318d3 181 return strdup_to(ret, m->llmnr_hostname);
6501dd31
DR
182}
183
a3f87e32 184int dnssd_render_instance_name(Manager *m, DnssdService *s, char **ret) {
6501dd31 185 static const Specifier specifier_table[] = {
9a5893e9
ZJS
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 },
6501dd31
DR
195 {}
196 };
197 _cleanup_free_ char *name = NULL;
198 int r;
199
a3f87e32 200 assert(m);
07e4a8dc
RB
201 assert(s);
202 assert(s->name_template);
6501dd31 203
a3f87e32 204 r = specifier_printf(s->name_template, DNS_LABEL_MAX, specifier_table, NULL, m, &name);
6501dd31 205 if (r < 0)
07e4a8dc 206 return log_debug_errno(r, "Failed to replace specifiers: %m");
6501dd31 207
baaa35ad 208 if (!dns_service_name_is_valid(name))
07e4a8dc
RB
209 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
210 "Service instance name '%s' is invalid.",
211 name);
6501dd31 212
a3f87e32
YW
213 if (ret)
214 *ret = TAKE_PTR(name);
6501dd31
DR
215
216 return 0;
217}
218
219int dnssd_load(Manager *manager) {
220 _cleanup_strv_free_ char **files = NULL;
6501dd31
DR
221 int r;
222
223 assert(manager);
224
225 if (manager->mdns_support != RESOLVE_SUPPORT_YES)
226 return 0;
227
eb5f4dde 228 r = conf_files_list_strv(&files, ".dnssd", NULL, 0, DNSSD_SERVICE_DIRS);
6501dd31
DR
229 if (r < 0)
230 return log_error_errno(r, "Failed to enumerate .dnssd files: %m");
231
232 STRV_FOREACH_BACKWARDS(f, files) {
233 r = dnssd_service_load(manager, *f);
234 if (r < 0)
aea29a30 235 log_warning_errno(r, "Failed to load '%s': %m", *f);
6501dd31
DR
236 }
237
238 return 0;
239}
240
6db6a464 241int dnssd_update_rrs(DnssdService *s) {
88123aa2 242 _cleanup_free_ char *n = NULL, *service_name = NULL, *full_name = NULL, *sub_name = NULL, *selective_name = NULL;
6db6a464
DR
243 int r;
244
245 assert(s);
400f54fb 246 assert(s->txt_data_items);
6db6a464
DR
247 assert(s->manager);
248
249 s->ptr_rr = dns_resource_record_unref(s->ptr_rr);
88123aa2 250 s->sub_ptr_rr = dns_resource_record_unref(s->sub_ptr_rr);
6db6a464 251 s->srv_rr = dns_resource_record_unref(s->srv_rr);
400f54fb
DR
252 LIST_FOREACH(items, txt_data, s->txt_data_items)
253 txt_data->rr = dns_resource_record_unref(txt_data->rr);
6db6a464 254
a3f87e32 255 r = dnssd_render_instance_name(s->manager, s, &n);
6db6a464
DR
256 if (r < 0)
257 return r;
258
7470cc4c 259 r = dns_name_concat(s->type, "local", 0, &service_name);
6db6a464
DR
260 if (r < 0)
261 return r;
7470cc4c 262 r = dns_name_concat(n, service_name, 0, &full_name);
6db6a464
DR
263 if (r < 0)
264 return r;
88123aa2
RP
265 if (s->subtype) {
266 r = dns_name_concat("_sub", service_name, 0, &sub_name);
267 if (r < 0)
268 return r;
269 r = dns_name_concat(s->subtype, sub_name, 0, &selective_name);
270 if (r < 0)
271 return r;
272 }
6db6a464 273
400f54fb
DR
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,
276 full_name);
277 if (!txt_data->rr)
278 goto oom;
6db6a464 279
400f54fb 280 txt_data->rr->ttl = MDNS_DEFAULT_TTL;
d70f15f5 281 txt_data->rr->txt.items = dns_txt_item_copy(txt_data->txts);
400f54fb
DR
282 if (!txt_data->rr->txt.items)
283 goto oom;
284 }
6db6a464
DR
285
286 s->ptr_rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR,
287 service_name);
288 if (!s->ptr_rr)
289 goto oom;
290
291 s->ptr_rr->ttl = MDNS_DEFAULT_TTL;
292 s->ptr_rr->ptr.name = strdup(full_name);
293 if (!s->ptr_rr->ptr.name)
294 goto oom;
295
88123aa2
RP
296 if (selective_name) {
297 s->sub_ptr_rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, selective_name);
298 if (!s->sub_ptr_rr)
299 goto oom;
300
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)
304 goto oom;
305 }
306
6db6a464
DR
307 s->srv_rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SRV,
308 full_name);
309 if (!s->srv_rr)
310 goto oom;
311
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)
318 goto oom;
319
320 return 0;
321
322oom:
400f54fb
DR
323 LIST_FOREACH(items, txt_data, s->txt_data_items)
324 txt_data->rr = dns_resource_record_unref(txt_data->rr);
6db6a464 325 s->ptr_rr = dns_resource_record_unref(s->ptr_rr);
88123aa2 326 s->sub_ptr_rr = dns_resource_record_unref(s->sub_ptr_rr);
6db6a464
DR
327 s->srv_rr = dns_resource_record_unref(s->srv_rr);
328 return -ENOMEM;
329}
330
6501dd31
DR
331int dnssd_txt_item_new_from_string(const char *key, const char *value, DnsTxtItem **ret_item) {
332 size_t length;
333 DnsTxtItem *i;
334
335 length = strlen(key);
336
337 if (!isempty(value))
338 length += strlen(value) + 1; /* length of value plus '=' */
339
340 i = malloc0(offsetof(DnsTxtItem, data) + length + 1); /* for safety reasons we add an extra NUL byte */
341 if (!i)
342 return -ENOMEM;
343
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));
348 }
349 i->length = length;
350
ae2a15bc 351 *ret_item = TAKE_PTR(i);
6501dd31
DR
352
353 return 0;
354}
355
356int dnssd_txt_item_new_from_data(const char *key, const void *data, const size_t size, DnsTxtItem **ret_item) {
357 size_t length;
358 DnsTxtItem *i;
359
360 length = strlen(key);
361
362 if (size > 0)
363 length += size + 1; /* size of date plus '=' */
364
365 i = malloc0(offsetof(DnsTxtItem, data) + length + 1); /* for safety reasons we add an extra NUL byte */
366 if (!i)
367 return -ENOMEM;
368
369 memcpy(i->data, key, strlen(key));
370 if (size > 0) {
371 memcpy(i->data + strlen(key), "=", 1);
372 memcpy(i->data + strlen(key) + 1, data, size);
373 }
374 i->length = length;
375
ae2a15bc 376 *ret_item = TAKE_PTR(i);
6501dd31
DR
377
378 return 0;
379}
c3036641 380
b8d6689a 381int dnssd_signal_conflict(Manager *manager, const char *name) {
c3036641
DR
382 DnssdService *s;
383 int r;
384
b8d6689a
YW
385 if (sd_bus_is_ready(manager->bus) <= 0)
386 return 0;
387
90e74a66 388 HASHMAP_FOREACH(s, manager->dnssd_services) {
c3036641
DR
389 if (s->withdrawn)
390 continue;
391
3ee27b25 392 if (dns_name_equal(dns_resource_key_name(s->srv_rr->key), name) > 0) {
c3036641
DR
393 _cleanup_free_ char *path = NULL;
394
395 s->withdrawn = true;
396
0ef0e269 397 r = sd_bus_path_encode("/org/freedesktop/resolve1/dnssd", s->id, &path);
b8d6689a
YW
398 if (r < 0)
399 return log_error_errno(r, "Can't get D-BUS object path: %m");
c3036641
DR
400
401 r = sd_bus_emit_signal(manager->bus,
402 path,
403 "org.freedesktop.resolve1.DnssdService",
404 "Conflicted",
405 NULL);
b8d6689a
YW
406 if (r < 0)
407 return log_error_errno(r, "Cannot emit signal: %m");
c3036641
DR
408
409 break;
410 }
411 }
b8d6689a
YW
412
413 return 0;
c3036641 414}
8e1c3459
LP
415
416int config_parse_dnssd_service_name(
417 const char *unit,
418 const char *filename,
419 unsigned line,
420 const char *section,
421 unsigned section_line,
422 const char *lvalue,
423 int ltype,
424 const char *rvalue,
425 void *data,
426 void *userdata) {
427
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 },
438 {}
439 };
440 DnssdService *s = ASSERT_PTR(userdata);
441 _cleanup_free_ char *name = NULL;
442 int r;
443
444 assert(filename);
445 assert(lvalue);
446 assert(rvalue);
447
448 if (isempty(rvalue)) {
449 s->name_template = mfree(s->name_template);
450 return 0;
451 }
452
453 r = specifier_printf(rvalue, DNS_LABEL_MAX, specifier_table, NULL, NULL, &name);
454 if (r < 0) {
455 log_syntax(unit, LOG_WARNING, filename, line, r,
456 "Invalid service instance name template '%s', ignoring assignment: %m", rvalue);
457 return 0;
458 }
459
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.",
463 rvalue, name);
464 return 0;
465 }
466
467 return free_and_strdup_warn(&s->name_template, rvalue);
468}
469
470int config_parse_dnssd_service_type(
471 const char *unit,
472 const char *filename,
473 unsigned line,
474 const char *section,
475 unsigned section_line,
476 const char *lvalue,
477 int ltype,
478 const char *rvalue,
479 void *data,
480 void *userdata) {
481
482 DnssdService *s = ASSERT_PTR(userdata);
483 int r;
484
485 assert(filename);
486 assert(lvalue);
487 assert(rvalue);
488
489 if (isempty(rvalue)) {
490 s->type = mfree(s->type);
491 return 0;
492 }
493
494 if (!dnssd_srv_type_is_valid(rvalue)) {
495 log_syntax(unit, LOG_WARNING, filename, line, 0, "Service type is invalid. Ignoring.");
496 return 0;
497 }
498
499 r = free_and_strdup(&s->type, rvalue);
500 if (r < 0)
501 return log_oom();
502
503 return 0;
504}
505
506int config_parse_dnssd_service_subtype(
507 const char *unit,
508 const char *filename,
509 unsigned line,
510 const char *section,
511 unsigned section_line,
512 const char *lvalue,
513 int ltype,
514 const char *rvalue,
515 void *data,
516 void *userdata) {
517
518 DnssdService *s = ASSERT_PTR(userdata);
519
520 assert(filename);
521 assert(lvalue);
522 assert(rvalue);
523
524 if (isempty(rvalue)) {
525 s->subtype = mfree(s->subtype);
526 return 0;
527 }
528
529 if (!dns_subtype_name_is_valid(rvalue)) {
530 log_syntax(unit, LOG_WARNING, filename, line, 0, "Service subtype is invalid. Ignoring.");
531 return 0;
532 }
533
534 return free_and_strdup_warn(&s->subtype, rvalue);
535}
536
537int config_parse_dnssd_txt(
538 const char *unit,
539 const char *filename,
540 unsigned line,
541 const char *section,
542 unsigned section_line,
543 const char *lvalue,
544 int ltype,
545 const char *rvalue,
546 void *data,
547 void *userdata) {
548
549 _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
550 DnssdService *s = ASSERT_PTR(userdata);
551 DnsTxtItem *last = NULL;
552
553 assert(filename);
554 assert(lvalue);
555 assert(rvalue);
556
557 if (isempty(rvalue)) {
558 /* Flush out collected items */
559 s->txt_data_items = dnssd_txtdata_free_all(s->txt_data_items);
560 return 0;
561 }
562
563 txt_data = new0(DnssdTxtData, 1);
564 if (!txt_data)
565 return log_oom();
566
567 for (;;) {
568 _cleanup_free_ char *word = NULL, *key = NULL, *value = NULL;
569 _cleanup_free_ void *decoded = NULL;
570 size_t length = 0;
571 DnsTxtItem *i;
572 int r;
573
574 r = extract_first_word(&rvalue, &word, NULL,
575 EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX);
576 if (r == 0)
577 break;
578 if (r == -ENOMEM)
579 return log_oom();
580 if (r < 0) {
581 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
582 return 0;
583 }
584
585 r = split_pair(word, "=", &key, &value);
586 if (r == -ENOMEM)
587 return log_oom();
588 if (r == -EINVAL)
589 key = TAKE_PTR(word);
590
591 if (!ascii_is_valid(key)) {
592 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid key, ignoring: %s", key);
593 continue;
594 }
595
596 switch (ltype) {
597
598 case DNS_TXT_ITEM_DATA:
599 if (value) {
600 r = unbase64mem(value, &decoded, &length);
601 if (r == -ENOMEM)
602 return log_oom();
603 if (r < 0) {
604 log_syntax(unit, LOG_WARNING, filename, line, r,
605 "Invalid base64 encoding, ignoring: %s", value);
606 continue;
607 }
608 }
609
610 r = dnssd_txt_item_new_from_data(key, decoded, length, &i);
611 if (r < 0)
612 return log_oom();
613 break;
614
615 case DNS_TXT_ITEM_TEXT:
616 r = dnssd_txt_item_new_from_string(key, value, &i);
617 if (r < 0)
618 return log_oom();
619 break;
620
621 default:
622 assert_not_reached();
623 }
624
625 LIST_INSERT_AFTER(items, txt_data->txts, last, i);
626 last = i;
627 }
628
629 if (txt_data->txts) {
630 LIST_PREPEND(items, s->txt_data_items, txt_data);
631 TAKE_PTR(txt_data);
632 }
633
634 return 0;
635}