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