]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dnssd.c
resolved: support reloading configuration at runtime
[thirdparty/systemd.git] / src / resolve / resolved-dnssd.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
6501dd31
DR
2
3#include "conf-files.h"
4#include "conf-parser.h"
28db6fbf 5#include "constants.h"
6501dd31
DR
6#include "resolved-dnssd.h"
7#include "resolved-dns-rr.h"
8#include "resolved-manager.h"
ae8f0ec3 9#include "resolved-conf.h"
6501dd31
DR
10#include "specifier.h"
11#include "strv.h"
12
eb5f4dde 13#define DNSSD_SERVICE_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/dnssd"))
6501dd31 14
400f54fb
DR
15DnssdTxtData *dnssd_txtdata_free(DnssdTxtData *txt_data) {
16 if (!txt_data)
17 return NULL;
18
19 dns_resource_record_unref(txt_data->rr);
d70f15f5 20 dns_txt_item_free_all(txt_data->txts);
400f54fb
DR
21
22 return mfree(txt_data);
23}
24
25DnssdTxtData *dnssd_txtdata_free_all(DnssdTxtData *txt_data) {
26 DnssdTxtData *next;
27
28 if (!txt_data)
29 return NULL;
30
31 next = txt_data->items_next;
32
33 dnssd_txtdata_free(txt_data);
34
35 return dnssd_txtdata_free_all(next);
36}
37
6501dd31
DR
38DnssdService *dnssd_service_free(DnssdService *service) {
39 if (!service)
40 return NULL;
41
42 if (service->manager)
43 hashmap_remove(service->manager->dnssd_services, service->name);
44
45 dns_resource_record_unref(service->ptr_rr);
88123aa2 46 dns_resource_record_unref(service->sub_ptr_rr);
6501dd31 47 dns_resource_record_unref(service->srv_rr);
400f54fb
DR
48
49 dnssd_txtdata_free_all(service->txt_data_items);
6501dd31
DR
50
51 free(service->filename);
52 free(service->name);
53 free(service->type);
88123aa2 54 free(service->subtype);
6501dd31 55 free(service->name_template);
6501dd31
DR
56
57 return mfree(service);
58}
59
14a52176
LB
60void dnssd_service_clear_on_reload(Hashmap *services) {
61 DnssdService *service;
62
63 HASHMAP_FOREACH(service, services)
64 if (service->config_source == RESOLVE_CONFIG_SOURCE_FILE) {
65 hashmap_remove(services, service->name);
66 dnssd_service_free(service);
67 }
68}
69
6501dd31
DR
70static int dnssd_service_load(Manager *manager, const char *filename) {
71 _cleanup_(dnssd_service_freep) DnssdService *service = NULL;
400f54fb 72 _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
6501dd31
DR
73 char *d;
74 const char *dropin_dirname;
75 int r;
76
77 assert(manager);
78 assert(filename);
79
80 service = new0(DnssdService, 1);
81 if (!service)
82 return log_oom();
83
84 service->filename = strdup(filename);
85 if (!service->filename)
86 return log_oom();
87
88 service->name = strdup(basename(filename));
89 if (!service->name)
90 return log_oom();
91
92 d = endswith(service->name, ".dnssd");
93 if (!d)
94 return -EINVAL;
95
96 assert(streq(d, ".dnssd"));
97
98 *d = '\0';
99
100 dropin_dirname = strjoina(service->name, ".dnssd.d");
101
4f9ff96a 102 r = config_parse_many(
947f59ba 103 STRV_MAKE_CONST(filename), DNSSD_SERVICE_DIRS, dropin_dirname, /* root = */ NULL,
4f9ff96a
LP
104 "Service\0",
105 config_item_perf_lookup, resolved_dnssd_gperf_lookup,
106 CONFIG_PARSE_WARN,
107 service,
ead3a3fc 108 NULL,
4f9ff96a 109 NULL);
6501dd31
DR
110 if (r < 0)
111 return r;
112
d7a0f1f4
FS
113 if (!service->name_template)
114 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
115 "%s doesn't define service instance name",
116 service->name);
117
118 if (!service->type)
119 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
120 "%s doesn't define service type",
121 service->name);
6501dd31 122
64903d18 123 if (!service->txt_data_items) {
400f54fb
DR
124 txt_data = new0(DnssdTxtData, 1);
125 if (!txt_data)
126 return log_oom();
127
d70f15f5 128 r = dns_txt_item_new_empty(&txt_data->txts);
6501dd31
DR
129 if (r < 0)
130 return r;
400f54fb
DR
131
132 LIST_PREPEND(items, service->txt_data_items, txt_data);
15c69d07 133 TAKE_PTR(txt_data);
6501dd31
DR
134 }
135
276abbee 136 r = hashmap_ensure_put(&manager->dnssd_services, &string_hash_ops, service->name, service);
6501dd31
DR
137 if (r < 0)
138 return r;
139
140 service->manager = manager;
141
6db6a464
DR
142 r = dnssd_update_rrs(service);
143 if (r < 0)
144 return r;
145
15c69d07 146 TAKE_PTR(service);
6501dd31
DR
147
148 return 0;
149}
150
9a5893e9 151static int specifier_dnssd_hostname(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
a3f87e32 152 const Manager *m = ASSERT_PTR(userdata);
6501dd31 153
a3f87e32 154 assert(m->llmnr_hostname);
6501dd31 155
454318d3 156 return strdup_to(ret, m->llmnr_hostname);
6501dd31
DR
157}
158
a3f87e32 159int dnssd_render_instance_name(Manager *m, DnssdService *s, char **ret) {
6501dd31 160 static const Specifier specifier_table[] = {
9a5893e9
ZJS
161 { 'a', specifier_architecture, NULL },
162 { 'b', specifier_boot_id, NULL },
163 { 'B', specifier_os_build_id, NULL },
164 { 'H', specifier_dnssd_hostname, NULL },
165 { 'm', specifier_machine_id, NULL },
166 { 'o', specifier_os_id, NULL },
167 { 'v', specifier_kernel_release, NULL },
168 { 'w', specifier_os_version_id, NULL },
169 { 'W', specifier_os_variant_id, NULL },
6501dd31
DR
170 {}
171 };
172 _cleanup_free_ char *name = NULL;
173 int r;
174
a3f87e32 175 assert(m);
07e4a8dc
RB
176 assert(s);
177 assert(s->name_template);
6501dd31 178
a3f87e32 179 r = specifier_printf(s->name_template, DNS_LABEL_MAX, specifier_table, NULL, m, &name);
6501dd31 180 if (r < 0)
07e4a8dc 181 return log_debug_errno(r, "Failed to replace specifiers: %m");
6501dd31 182
baaa35ad 183 if (!dns_service_name_is_valid(name))
07e4a8dc
RB
184 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
185 "Service instance name '%s' is invalid.",
186 name);
6501dd31 187
a3f87e32
YW
188 if (ret)
189 *ret = TAKE_PTR(name);
6501dd31
DR
190
191 return 0;
192}
193
194int dnssd_load(Manager *manager) {
195 _cleanup_strv_free_ char **files = NULL;
6501dd31
DR
196 int r;
197
198 assert(manager);
199
200 if (manager->mdns_support != RESOLVE_SUPPORT_YES)
201 return 0;
202
eb5f4dde 203 r = conf_files_list_strv(&files, ".dnssd", NULL, 0, DNSSD_SERVICE_DIRS);
6501dd31
DR
204 if (r < 0)
205 return log_error_errno(r, "Failed to enumerate .dnssd files: %m");
206
207 STRV_FOREACH_BACKWARDS(f, files) {
208 r = dnssd_service_load(manager, *f);
209 if (r < 0)
aea29a30 210 log_warning_errno(r, "Failed to load '%s': %m", *f);
6501dd31
DR
211 }
212
213 return 0;
214}
215
6db6a464 216int dnssd_update_rrs(DnssdService *s) {
88123aa2 217 _cleanup_free_ char *n = NULL, *service_name = NULL, *full_name = NULL, *sub_name = NULL, *selective_name = NULL;
6db6a464
DR
218 int r;
219
220 assert(s);
400f54fb 221 assert(s->txt_data_items);
6db6a464
DR
222 assert(s->manager);
223
224 s->ptr_rr = dns_resource_record_unref(s->ptr_rr);
88123aa2 225 s->sub_ptr_rr = dns_resource_record_unref(s->sub_ptr_rr);
6db6a464 226 s->srv_rr = dns_resource_record_unref(s->srv_rr);
400f54fb
DR
227 LIST_FOREACH(items, txt_data, s->txt_data_items)
228 txt_data->rr = dns_resource_record_unref(txt_data->rr);
6db6a464 229
a3f87e32 230 r = dnssd_render_instance_name(s->manager, s, &n);
6db6a464
DR
231 if (r < 0)
232 return r;
233
7470cc4c 234 r = dns_name_concat(s->type, "local", 0, &service_name);
6db6a464
DR
235 if (r < 0)
236 return r;
7470cc4c 237 r = dns_name_concat(n, service_name, 0, &full_name);
6db6a464
DR
238 if (r < 0)
239 return r;
88123aa2
RP
240 if (s->subtype) {
241 r = dns_name_concat("_sub", service_name, 0, &sub_name);
242 if (r < 0)
243 return r;
244 r = dns_name_concat(s->subtype, sub_name, 0, &selective_name);
245 if (r < 0)
246 return r;
247 }
6db6a464 248
400f54fb
DR
249 LIST_FOREACH(items, txt_data, s->txt_data_items) {
250 txt_data->rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_TXT,
251 full_name);
252 if (!txt_data->rr)
253 goto oom;
6db6a464 254
400f54fb 255 txt_data->rr->ttl = MDNS_DEFAULT_TTL;
d70f15f5 256 txt_data->rr->txt.items = dns_txt_item_copy(txt_data->txts);
400f54fb
DR
257 if (!txt_data->rr->txt.items)
258 goto oom;
259 }
6db6a464
DR
260
261 s->ptr_rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR,
262 service_name);
263 if (!s->ptr_rr)
264 goto oom;
265
266 s->ptr_rr->ttl = MDNS_DEFAULT_TTL;
267 s->ptr_rr->ptr.name = strdup(full_name);
268 if (!s->ptr_rr->ptr.name)
269 goto oom;
270
88123aa2
RP
271 if (selective_name) {
272 s->sub_ptr_rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, selective_name);
273 if (!s->sub_ptr_rr)
274 goto oom;
275
276 s->sub_ptr_rr->ttl = MDNS_DEFAULT_TTL;
277 s->sub_ptr_rr->ptr.name = strdup(full_name);
278 if (!s->sub_ptr_rr->ptr.name)
279 goto oom;
280 }
281
6db6a464
DR
282 s->srv_rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SRV,
283 full_name);
284 if (!s->srv_rr)
285 goto oom;
286
287 s->srv_rr->ttl = MDNS_DEFAULT_TTL;
288 s->srv_rr->srv.priority = s->priority;
289 s->srv_rr->srv.weight = s->weight;
290 s->srv_rr->srv.port = s->port;
291 s->srv_rr->srv.name = strdup(s->manager->mdns_hostname);
292 if (!s->srv_rr->srv.name)
293 goto oom;
294
295 return 0;
296
297oom:
400f54fb
DR
298 LIST_FOREACH(items, txt_data, s->txt_data_items)
299 txt_data->rr = dns_resource_record_unref(txt_data->rr);
6db6a464 300 s->ptr_rr = dns_resource_record_unref(s->ptr_rr);
88123aa2 301 s->sub_ptr_rr = dns_resource_record_unref(s->sub_ptr_rr);
6db6a464
DR
302 s->srv_rr = dns_resource_record_unref(s->srv_rr);
303 return -ENOMEM;
304}
305
6501dd31
DR
306int dnssd_txt_item_new_from_string(const char *key, const char *value, DnsTxtItem **ret_item) {
307 size_t length;
308 DnsTxtItem *i;
309
310 length = strlen(key);
311
312 if (!isempty(value))
313 length += strlen(value) + 1; /* length of value plus '=' */
314
315 i = malloc0(offsetof(DnsTxtItem, data) + length + 1); /* for safety reasons we add an extra NUL byte */
316 if (!i)
317 return -ENOMEM;
318
319 memcpy(i->data, key, strlen(key));
320 if (!isempty(value)) {
321 memcpy(i->data + strlen(key), "=", 1);
322 memcpy(i->data + strlen(key) + 1, value, strlen(value));
323 }
324 i->length = length;
325
ae2a15bc 326 *ret_item = TAKE_PTR(i);
6501dd31
DR
327
328 return 0;
329}
330
331int dnssd_txt_item_new_from_data(const char *key, const void *data, const size_t size, DnsTxtItem **ret_item) {
332 size_t length;
333 DnsTxtItem *i;
334
335 length = strlen(key);
336
337 if (size > 0)
338 length += size + 1; /* size of date 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 (size > 0) {
346 memcpy(i->data + strlen(key), "=", 1);
347 memcpy(i->data + strlen(key) + 1, data, size);
348 }
349 i->length = length;
350
ae2a15bc 351 *ret_item = TAKE_PTR(i);
6501dd31
DR
352
353 return 0;
354}
c3036641 355
b8d6689a 356int dnssd_signal_conflict(Manager *manager, const char *name) {
c3036641
DR
357 DnssdService *s;
358 int r;
359
b8d6689a
YW
360 if (sd_bus_is_ready(manager->bus) <= 0)
361 return 0;
362
90e74a66 363 HASHMAP_FOREACH(s, manager->dnssd_services) {
c3036641
DR
364 if (s->withdrawn)
365 continue;
366
367 if (dns_name_equal(dns_resource_key_name(s->srv_rr->key), name)) {
368 _cleanup_free_ char *path = NULL;
369
370 s->withdrawn = true;
371
372 r = sd_bus_path_encode("/org/freedesktop/resolve1/dnssd", s->name, &path);
b8d6689a
YW
373 if (r < 0)
374 return log_error_errno(r, "Can't get D-BUS object path: %m");
c3036641
DR
375
376 r = sd_bus_emit_signal(manager->bus,
377 path,
378 "org.freedesktop.resolve1.DnssdService",
379 "Conflicted",
380 NULL);
b8d6689a
YW
381 if (r < 0)
382 return log_error_errno(r, "Cannot emit signal: %m");
c3036641
DR
383
384 break;
385 }
386 }
b8d6689a
YW
387
388 return 0;
c3036641 389}