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