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