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