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