]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
6501dd31 | 2 | |
284d7641 DDM |
3 | #include "sd-bus.h" |
4 | ||
b78d73fa | 5 | #include "alloc-util.h" |
6501dd31 DR |
6 | #include "conf-files.h" |
7 | #include "conf-parser.h" | |
28db6fbf | 8 | #include "constants.h" |
68527d30 | 9 | #include "dns-domain.h" |
284d7641 DDM |
10 | #include "extract-word.h" |
11 | #include "hashmap.h" | |
8e1c3459 | 12 | #include "hexdecoct.h" |
0ef0e269 LP |
13 | #include "path-util.h" |
14 | #include "resolved-conf.h" | |
6501dd31 | 15 | #include "resolved-dns-rr.h" |
68527d30 | 16 | #include "resolved-dns-zone.h" |
0ef0e269 | 17 | #include "resolved-dnssd.h" |
6501dd31 DR |
18 | #include "resolved-manager.h" |
19 | #include "specifier.h" | |
284d7641 | 20 | #include "string-util.h" |
6501dd31 | 21 | #include "strv.h" |
e4a08721 | 22 | #include "utf8.h" |
6501dd31 | 23 | |
eb5f4dde | 24 | #define DNSSD_SERVICE_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/dnssd")) |
6501dd31 | 25 | |
400f54fb DR |
26 | DnssdTxtData *dnssd_txtdata_free(DnssdTxtData *txt_data) { |
27 | if (!txt_data) | |
28 | return NULL; | |
29 | ||
30 | dns_resource_record_unref(txt_data->rr); | |
d70f15f5 | 31 | dns_txt_item_free_all(txt_data->txts); |
400f54fb DR |
32 | |
33 | return mfree(txt_data); | |
34 | } | |
35 | ||
36 | DnssdTxtData *dnssd_txtdata_free_all(DnssdTxtData *txt_data) { | |
37 | DnssdTxtData *next; | |
38 | ||
39 | if (!txt_data) | |
40 | return NULL; | |
41 | ||
42 | next = txt_data->items_next; | |
43 | ||
44 | dnssd_txtdata_free(txt_data); | |
45 | ||
46 | return dnssd_txtdata_free_all(next); | |
47 | } | |
48 | ||
6501dd31 DR |
49 | DnssdService *dnssd_service_free(DnssdService *service) { |
50 | if (!service) | |
51 | return NULL; | |
52 | ||
53 | if (service->manager) | |
0ef0e269 | 54 | hashmap_remove(service->manager->dnssd_services, service->id); |
6501dd31 DR |
55 | |
56 | dns_resource_record_unref(service->ptr_rr); | |
88123aa2 | 57 | dns_resource_record_unref(service->sub_ptr_rr); |
6501dd31 | 58 | dns_resource_record_unref(service->srv_rr); |
400f54fb DR |
59 | |
60 | dnssd_txtdata_free_all(service->txt_data_items); | |
6501dd31 | 61 | |
40763016 | 62 | free(service->path); |
0ef0e269 | 63 | free(service->id); |
6501dd31 | 64 | free(service->type); |
88123aa2 | 65 | free(service->subtype); |
6501dd31 | 66 | free(service->name_template); |
6501dd31 DR |
67 | |
68 | return mfree(service); | |
69 | } | |
70 | ||
14a52176 LB |
71 | void dnssd_service_clear_on_reload(Hashmap *services) { |
72 | DnssdService *service; | |
73 | ||
74 | HASHMAP_FOREACH(service, services) | |
75 | if (service->config_source == RESOLVE_CONFIG_SOURCE_FILE) { | |
0ef0e269 | 76 | hashmap_remove(services, service->id); |
14a52176 LB |
77 | dnssd_service_free(service); |
78 | } | |
79 | } | |
80 | ||
0ef0e269 LP |
81 | static int dnssd_id_from_path(const char *path, char **ret_id) { |
82 | int r; | |
83 | ||
84 | assert(path); | |
85 | assert(ret_id); | |
86 | ||
87 | _cleanup_free_ char *fn = NULL; | |
88 | r = path_extract_filename(path, &fn); | |
89 | if (r < 0) | |
90 | return r; | |
91 | ||
92 | char *d = endswith(fn, ".dnssd"); | |
93 | if (!d) | |
94 | return -EINVAL; | |
95 | ||
96 | *d = '\0'; | |
97 | ||
98 | *ret_id = TAKE_PTR(fn); | |
99 | return 0; | |
100 | } | |
101 | ||
40763016 | 102 | static int dnssd_service_load(Manager *manager, const char *path) { |
6501dd31 | 103 | _cleanup_(dnssd_service_freep) DnssdService *service = NULL; |
400f54fb | 104 | _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL; |
0ef0e269 | 105 | _cleanup_free_ char *dropin_dirname = NULL; |
6501dd31 DR |
106 | int r; |
107 | ||
108 | assert(manager); | |
40763016 | 109 | assert(path); |
6501dd31 DR |
110 | |
111 | service = new0(DnssdService, 1); | |
112 | if (!service) | |
113 | return log_oom(); | |
114 | ||
40763016 LP |
115 | service->path = strdup(path); |
116 | if (!service->path) | |
6501dd31 DR |
117 | return log_oom(); |
118 | ||
0ef0e269 LP |
119 | r = dnssd_id_from_path(path, &service->id); |
120 | if (r < 0) | |
121 | return log_error_errno(r, "Failed to extract DNS-SD service id from filename: %m"); | |
6501dd31 | 122 | |
0ef0e269 LP |
123 | dropin_dirname = strjoin(service->id, ".dnssd.d"); |
124 | if (!dropin_dirname) | |
125 | return log_oom(); | |
6501dd31 | 126 | |
4f9ff96a | 127 | r = config_parse_many( |
40763016 | 128 | STRV_MAKE_CONST(path), DNSSD_SERVICE_DIRS, dropin_dirname, /* root = */ NULL, |
4f9ff96a LP |
129 | "Service\0", |
130 | config_item_perf_lookup, resolved_dnssd_gperf_lookup, | |
131 | CONFIG_PARSE_WARN, | |
132 | service, | |
ead3a3fc | 133 | NULL, |
4f9ff96a | 134 | NULL); |
6501dd31 DR |
135 | if (r < 0) |
136 | return r; | |
137 | ||
d7a0f1f4 FS |
138 | if (!service->name_template) |
139 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
140 | "%s doesn't define service instance name", | |
0ef0e269 | 141 | service->id); |
d7a0f1f4 FS |
142 | |
143 | if (!service->type) | |
144 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
145 | "%s doesn't define service type", | |
0ef0e269 | 146 | service->id); |
6501dd31 | 147 | |
64903d18 | 148 | if (!service->txt_data_items) { |
400f54fb DR |
149 | txt_data = new0(DnssdTxtData, 1); |
150 | if (!txt_data) | |
151 | return log_oom(); | |
152 | ||
d70f15f5 | 153 | r = dns_txt_item_new_empty(&txt_data->txts); |
6501dd31 DR |
154 | if (r < 0) |
155 | return r; | |
400f54fb DR |
156 | |
157 | LIST_PREPEND(items, service->txt_data_items, txt_data); | |
15c69d07 | 158 | TAKE_PTR(txt_data); |
6501dd31 DR |
159 | } |
160 | ||
0ef0e269 | 161 | r = hashmap_ensure_put(&manager->dnssd_services, &string_hash_ops, service->id, service); |
6501dd31 DR |
162 | if (r < 0) |
163 | return r; | |
164 | ||
165 | service->manager = manager; | |
166 | ||
6db6a464 DR |
167 | r = dnssd_update_rrs(service); |
168 | if (r < 0) | |
169 | return r; | |
170 | ||
15c69d07 | 171 | TAKE_PTR(service); |
6501dd31 DR |
172 | |
173 | return 0; | |
174 | } | |
175 | ||
9a5893e9 | 176 | static int specifier_dnssd_hostname(char specifier, const void *data, const char *root, const void *userdata, char **ret) { |
a3f87e32 | 177 | const Manager *m = ASSERT_PTR(userdata); |
6501dd31 | 178 | |
a3f87e32 | 179 | assert(m->llmnr_hostname); |
6501dd31 | 180 | |
454318d3 | 181 | return strdup_to(ret, m->llmnr_hostname); |
6501dd31 DR |
182 | } |
183 | ||
a3f87e32 | 184 | int dnssd_render_instance_name(Manager *m, DnssdService *s, char **ret) { |
6501dd31 | 185 | static const Specifier specifier_table[] = { |
9a5893e9 ZJS |
186 | { 'a', specifier_architecture, NULL }, |
187 | { 'b', specifier_boot_id, NULL }, | |
188 | { 'B', specifier_os_build_id, NULL }, | |
189 | { 'H', specifier_dnssd_hostname, NULL }, | |
190 | { 'm', specifier_machine_id, NULL }, | |
191 | { 'o', specifier_os_id, NULL }, | |
192 | { 'v', specifier_kernel_release, NULL }, | |
193 | { 'w', specifier_os_version_id, NULL }, | |
194 | { 'W', specifier_os_variant_id, NULL }, | |
6501dd31 DR |
195 | {} |
196 | }; | |
197 | _cleanup_free_ char *name = NULL; | |
198 | int r; | |
199 | ||
a3f87e32 | 200 | assert(m); |
07e4a8dc RB |
201 | assert(s); |
202 | assert(s->name_template); | |
6501dd31 | 203 | |
a3f87e32 | 204 | r = specifier_printf(s->name_template, DNS_LABEL_MAX, specifier_table, NULL, m, &name); |
6501dd31 | 205 | if (r < 0) |
07e4a8dc | 206 | return log_debug_errno(r, "Failed to replace specifiers: %m"); |
6501dd31 | 207 | |
baaa35ad | 208 | if (!dns_service_name_is_valid(name)) |
07e4a8dc RB |
209 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), |
210 | "Service instance name '%s' is invalid.", | |
211 | name); | |
6501dd31 | 212 | |
a3f87e32 YW |
213 | if (ret) |
214 | *ret = TAKE_PTR(name); | |
6501dd31 DR |
215 | |
216 | return 0; | |
217 | } | |
218 | ||
219 | int dnssd_load(Manager *manager) { | |
220 | _cleanup_strv_free_ char **files = NULL; | |
6501dd31 DR |
221 | int r; |
222 | ||
223 | assert(manager); | |
224 | ||
225 | if (manager->mdns_support != RESOLVE_SUPPORT_YES) | |
226 | return 0; | |
227 | ||
eb5f4dde | 228 | r = conf_files_list_strv(&files, ".dnssd", NULL, 0, DNSSD_SERVICE_DIRS); |
6501dd31 DR |
229 | if (r < 0) |
230 | return log_error_errno(r, "Failed to enumerate .dnssd files: %m"); | |
231 | ||
232 | STRV_FOREACH_BACKWARDS(f, files) { | |
233 | r = dnssd_service_load(manager, *f); | |
234 | if (r < 0) | |
aea29a30 | 235 | log_warning_errno(r, "Failed to load '%s': %m", *f); |
6501dd31 DR |
236 | } |
237 | ||
238 | return 0; | |
239 | } | |
240 | ||
6db6a464 | 241 | int dnssd_update_rrs(DnssdService *s) { |
88123aa2 | 242 | _cleanup_free_ char *n = NULL, *service_name = NULL, *full_name = NULL, *sub_name = NULL, *selective_name = NULL; |
6db6a464 DR |
243 | int r; |
244 | ||
245 | assert(s); | |
400f54fb | 246 | assert(s->txt_data_items); |
6db6a464 DR |
247 | assert(s->manager); |
248 | ||
249 | s->ptr_rr = dns_resource_record_unref(s->ptr_rr); | |
88123aa2 | 250 | s->sub_ptr_rr = dns_resource_record_unref(s->sub_ptr_rr); |
6db6a464 | 251 | s->srv_rr = dns_resource_record_unref(s->srv_rr); |
400f54fb DR |
252 | LIST_FOREACH(items, txt_data, s->txt_data_items) |
253 | txt_data->rr = dns_resource_record_unref(txt_data->rr); | |
6db6a464 | 254 | |
a3f87e32 | 255 | r = dnssd_render_instance_name(s->manager, s, &n); |
6db6a464 DR |
256 | if (r < 0) |
257 | return r; | |
258 | ||
7470cc4c | 259 | r = dns_name_concat(s->type, "local", 0, &service_name); |
6db6a464 DR |
260 | if (r < 0) |
261 | return r; | |
7470cc4c | 262 | r = dns_name_concat(n, service_name, 0, &full_name); |
6db6a464 DR |
263 | if (r < 0) |
264 | return r; | |
88123aa2 RP |
265 | if (s->subtype) { |
266 | r = dns_name_concat("_sub", service_name, 0, &sub_name); | |
267 | if (r < 0) | |
268 | return r; | |
269 | r = dns_name_concat(s->subtype, sub_name, 0, &selective_name); | |
270 | if (r < 0) | |
271 | return r; | |
272 | } | |
6db6a464 | 273 | |
400f54fb DR |
274 | LIST_FOREACH(items, txt_data, s->txt_data_items) { |
275 | txt_data->rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_TXT, | |
276 | full_name); | |
277 | if (!txt_data->rr) | |
278 | goto oom; | |
6db6a464 | 279 | |
400f54fb | 280 | txt_data->rr->ttl = MDNS_DEFAULT_TTL; |
d70f15f5 | 281 | txt_data->rr->txt.items = dns_txt_item_copy(txt_data->txts); |
400f54fb DR |
282 | if (!txt_data->rr->txt.items) |
283 | goto oom; | |
284 | } | |
6db6a464 DR |
285 | |
286 | s->ptr_rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, | |
287 | service_name); | |
288 | if (!s->ptr_rr) | |
289 | goto oom; | |
290 | ||
291 | s->ptr_rr->ttl = MDNS_DEFAULT_TTL; | |
292 | s->ptr_rr->ptr.name = strdup(full_name); | |
293 | if (!s->ptr_rr->ptr.name) | |
294 | goto oom; | |
295 | ||
88123aa2 RP |
296 | if (selective_name) { |
297 | s->sub_ptr_rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, selective_name); | |
298 | if (!s->sub_ptr_rr) | |
299 | goto oom; | |
300 | ||
301 | s->sub_ptr_rr->ttl = MDNS_DEFAULT_TTL; | |
302 | s->sub_ptr_rr->ptr.name = strdup(full_name); | |
303 | if (!s->sub_ptr_rr->ptr.name) | |
304 | goto oom; | |
305 | } | |
306 | ||
6db6a464 DR |
307 | s->srv_rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SRV, |
308 | full_name); | |
309 | if (!s->srv_rr) | |
310 | goto oom; | |
311 | ||
312 | s->srv_rr->ttl = MDNS_DEFAULT_TTL; | |
313 | s->srv_rr->srv.priority = s->priority; | |
314 | s->srv_rr->srv.weight = s->weight; | |
315 | s->srv_rr->srv.port = s->port; | |
316 | s->srv_rr->srv.name = strdup(s->manager->mdns_hostname); | |
317 | if (!s->srv_rr->srv.name) | |
318 | goto oom; | |
319 | ||
320 | return 0; | |
321 | ||
322 | oom: | |
400f54fb DR |
323 | LIST_FOREACH(items, txt_data, s->txt_data_items) |
324 | txt_data->rr = dns_resource_record_unref(txt_data->rr); | |
6db6a464 | 325 | s->ptr_rr = dns_resource_record_unref(s->ptr_rr); |
88123aa2 | 326 | s->sub_ptr_rr = dns_resource_record_unref(s->sub_ptr_rr); |
6db6a464 DR |
327 | s->srv_rr = dns_resource_record_unref(s->srv_rr); |
328 | return -ENOMEM; | |
329 | } | |
330 | ||
6501dd31 DR |
331 | int dnssd_txt_item_new_from_string(const char *key, const char *value, DnsTxtItem **ret_item) { |
332 | size_t length; | |
333 | DnsTxtItem *i; | |
334 | ||
335 | length = strlen(key); | |
336 | ||
337 | if (!isempty(value)) | |
338 | length += strlen(value) + 1; /* length of value plus '=' */ | |
339 | ||
340 | i = malloc0(offsetof(DnsTxtItem, data) + length + 1); /* for safety reasons we add an extra NUL byte */ | |
341 | if (!i) | |
342 | return -ENOMEM; | |
343 | ||
344 | memcpy(i->data, key, strlen(key)); | |
345 | if (!isempty(value)) { | |
346 | memcpy(i->data + strlen(key), "=", 1); | |
347 | memcpy(i->data + strlen(key) + 1, value, strlen(value)); | |
348 | } | |
349 | i->length = length; | |
350 | ||
ae2a15bc | 351 | *ret_item = TAKE_PTR(i); |
6501dd31 DR |
352 | |
353 | return 0; | |
354 | } | |
355 | ||
356 | int dnssd_txt_item_new_from_data(const char *key, const void *data, const size_t size, DnsTxtItem **ret_item) { | |
357 | size_t length; | |
358 | DnsTxtItem *i; | |
359 | ||
360 | length = strlen(key); | |
361 | ||
362 | if (size > 0) | |
363 | length += size + 1; /* size of date plus '=' */ | |
364 | ||
365 | i = malloc0(offsetof(DnsTxtItem, data) + length + 1); /* for safety reasons we add an extra NUL byte */ | |
366 | if (!i) | |
367 | return -ENOMEM; | |
368 | ||
369 | memcpy(i->data, key, strlen(key)); | |
370 | if (size > 0) { | |
371 | memcpy(i->data + strlen(key), "=", 1); | |
372 | memcpy(i->data + strlen(key) + 1, data, size); | |
373 | } | |
374 | i->length = length; | |
375 | ||
ae2a15bc | 376 | *ret_item = TAKE_PTR(i); |
6501dd31 DR |
377 | |
378 | return 0; | |
379 | } | |
c3036641 | 380 | |
b8d6689a | 381 | int dnssd_signal_conflict(Manager *manager, const char *name) { |
c3036641 DR |
382 | DnssdService *s; |
383 | int r; | |
384 | ||
b8d6689a YW |
385 | if (sd_bus_is_ready(manager->bus) <= 0) |
386 | return 0; | |
387 | ||
90e74a66 | 388 | HASHMAP_FOREACH(s, manager->dnssd_services) { |
c3036641 DR |
389 | if (s->withdrawn) |
390 | continue; | |
391 | ||
3ee27b25 | 392 | if (dns_name_equal(dns_resource_key_name(s->srv_rr->key), name) > 0) { |
c3036641 DR |
393 | _cleanup_free_ char *path = NULL; |
394 | ||
395 | s->withdrawn = true; | |
396 | ||
0ef0e269 | 397 | r = sd_bus_path_encode("/org/freedesktop/resolve1/dnssd", s->id, &path); |
b8d6689a YW |
398 | if (r < 0) |
399 | return log_error_errno(r, "Can't get D-BUS object path: %m"); | |
c3036641 DR |
400 | |
401 | r = sd_bus_emit_signal(manager->bus, | |
402 | path, | |
403 | "org.freedesktop.resolve1.DnssdService", | |
404 | "Conflicted", | |
405 | NULL); | |
b8d6689a YW |
406 | if (r < 0) |
407 | return log_error_errno(r, "Cannot emit signal: %m"); | |
c3036641 DR |
408 | |
409 | break; | |
410 | } | |
411 | } | |
b8d6689a YW |
412 | |
413 | return 0; | |
c3036641 | 414 | } |
8e1c3459 LP |
415 | |
416 | int config_parse_dnssd_service_name( | |
417 | const char *unit, | |
418 | const char *filename, | |
419 | unsigned line, | |
420 | const char *section, | |
421 | unsigned section_line, | |
422 | const char *lvalue, | |
423 | int ltype, | |
424 | const char *rvalue, | |
425 | void *data, | |
426 | void *userdata) { | |
427 | ||
428 | static const Specifier specifier_table[] = { | |
429 | { 'a', specifier_architecture, NULL }, | |
430 | { 'b', specifier_boot_id, NULL }, | |
431 | { 'B', specifier_os_build_id, NULL }, | |
432 | { 'H', specifier_hostname, NULL }, /* We will use specifier_dnssd_hostname(). */ | |
433 | { 'm', specifier_machine_id, NULL }, | |
434 | { 'o', specifier_os_id, NULL }, | |
435 | { 'v', specifier_kernel_release, NULL }, | |
436 | { 'w', specifier_os_version_id, NULL }, | |
437 | { 'W', specifier_os_variant_id, NULL }, | |
438 | {} | |
439 | }; | |
440 | DnssdService *s = ASSERT_PTR(userdata); | |
441 | _cleanup_free_ char *name = NULL; | |
442 | int r; | |
443 | ||
444 | assert(filename); | |
445 | assert(lvalue); | |
446 | assert(rvalue); | |
447 | ||
448 | if (isempty(rvalue)) { | |
449 | s->name_template = mfree(s->name_template); | |
450 | return 0; | |
451 | } | |
452 | ||
453 | r = specifier_printf(rvalue, DNS_LABEL_MAX, specifier_table, NULL, NULL, &name); | |
454 | if (r < 0) { | |
455 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
456 | "Invalid service instance name template '%s', ignoring assignment: %m", rvalue); | |
457 | return 0; | |
458 | } | |
459 | ||
460 | if (!dns_service_name_is_valid(name)) { | |
461 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
462 | "Service instance name template '%s' renders to invalid name '%s'. Ignoring assignment.", | |
463 | rvalue, name); | |
464 | return 0; | |
465 | } | |
466 | ||
467 | return free_and_strdup_warn(&s->name_template, rvalue); | |
468 | } | |
469 | ||
470 | int config_parse_dnssd_service_type( | |
471 | const char *unit, | |
472 | const char *filename, | |
473 | unsigned line, | |
474 | const char *section, | |
475 | unsigned section_line, | |
476 | const char *lvalue, | |
477 | int ltype, | |
478 | const char *rvalue, | |
479 | void *data, | |
480 | void *userdata) { | |
481 | ||
482 | DnssdService *s = ASSERT_PTR(userdata); | |
483 | int r; | |
484 | ||
485 | assert(filename); | |
486 | assert(lvalue); | |
487 | assert(rvalue); | |
488 | ||
489 | if (isempty(rvalue)) { | |
490 | s->type = mfree(s->type); | |
491 | return 0; | |
492 | } | |
493 | ||
494 | if (!dnssd_srv_type_is_valid(rvalue)) { | |
495 | log_syntax(unit, LOG_WARNING, filename, line, 0, "Service type is invalid. Ignoring."); | |
496 | return 0; | |
497 | } | |
498 | ||
499 | r = free_and_strdup(&s->type, rvalue); | |
500 | if (r < 0) | |
501 | return log_oom(); | |
502 | ||
503 | return 0; | |
504 | } | |
505 | ||
506 | int config_parse_dnssd_service_subtype( | |
507 | const char *unit, | |
508 | const char *filename, | |
509 | unsigned line, | |
510 | const char *section, | |
511 | unsigned section_line, | |
512 | const char *lvalue, | |
513 | int ltype, | |
514 | const char *rvalue, | |
515 | void *data, | |
516 | void *userdata) { | |
517 | ||
518 | DnssdService *s = ASSERT_PTR(userdata); | |
519 | ||
520 | assert(filename); | |
521 | assert(lvalue); | |
522 | assert(rvalue); | |
523 | ||
524 | if (isempty(rvalue)) { | |
525 | s->subtype = mfree(s->subtype); | |
526 | return 0; | |
527 | } | |
528 | ||
529 | if (!dns_subtype_name_is_valid(rvalue)) { | |
530 | log_syntax(unit, LOG_WARNING, filename, line, 0, "Service subtype is invalid. Ignoring."); | |
531 | return 0; | |
532 | } | |
533 | ||
534 | return free_and_strdup_warn(&s->subtype, rvalue); | |
535 | } | |
536 | ||
537 | int config_parse_dnssd_txt( | |
538 | const char *unit, | |
539 | const char *filename, | |
540 | unsigned line, | |
541 | const char *section, | |
542 | unsigned section_line, | |
543 | const char *lvalue, | |
544 | int ltype, | |
545 | const char *rvalue, | |
546 | void *data, | |
547 | void *userdata) { | |
548 | ||
549 | _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL; | |
550 | DnssdService *s = ASSERT_PTR(userdata); | |
551 | DnsTxtItem *last = NULL; | |
552 | ||
553 | assert(filename); | |
554 | assert(lvalue); | |
555 | assert(rvalue); | |
556 | ||
557 | if (isempty(rvalue)) { | |
558 | /* Flush out collected items */ | |
559 | s->txt_data_items = dnssd_txtdata_free_all(s->txt_data_items); | |
560 | return 0; | |
561 | } | |
562 | ||
563 | txt_data = new0(DnssdTxtData, 1); | |
564 | if (!txt_data) | |
565 | return log_oom(); | |
566 | ||
567 | for (;;) { | |
568 | _cleanup_free_ char *word = NULL, *key = NULL, *value = NULL; | |
569 | _cleanup_free_ void *decoded = NULL; | |
570 | size_t length = 0; | |
571 | DnsTxtItem *i; | |
572 | int r; | |
573 | ||
574 | r = extract_first_word(&rvalue, &word, NULL, | |
575 | EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX); | |
576 | if (r == 0) | |
577 | break; | |
578 | if (r == -ENOMEM) | |
579 | return log_oom(); | |
580 | if (r < 0) { | |
581 | log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); | |
582 | return 0; | |
583 | } | |
584 | ||
585 | r = split_pair(word, "=", &key, &value); | |
586 | if (r == -ENOMEM) | |
587 | return log_oom(); | |
588 | if (r == -EINVAL) | |
589 | key = TAKE_PTR(word); | |
590 | ||
591 | if (!ascii_is_valid(key)) { | |
592 | log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid key, ignoring: %s", key); | |
593 | continue; | |
594 | } | |
595 | ||
596 | switch (ltype) { | |
597 | ||
598 | case DNS_TXT_ITEM_DATA: | |
599 | if (value) { | |
600 | r = unbase64mem(value, &decoded, &length); | |
601 | if (r == -ENOMEM) | |
602 | return log_oom(); | |
603 | if (r < 0) { | |
604 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
605 | "Invalid base64 encoding, ignoring: %s", value); | |
606 | continue; | |
607 | } | |
608 | } | |
609 | ||
610 | r = dnssd_txt_item_new_from_data(key, decoded, length, &i); | |
611 | if (r < 0) | |
612 | return log_oom(); | |
613 | break; | |
614 | ||
615 | case DNS_TXT_ITEM_TEXT: | |
616 | r = dnssd_txt_item_new_from_string(key, value, &i); | |
617 | if (r < 0) | |
618 | return log_oom(); | |
619 | break; | |
620 | ||
621 | default: | |
622 | assert_not_reached(); | |
623 | } | |
624 | ||
625 | LIST_INSERT_AFTER(items, txt_data->txts, last, i); | |
626 | last = i; | |
627 | } | |
628 | ||
629 | if (txt_data->txts) { | |
630 | LIST_PREPEND(items, s->txt_data_items, txt_data); | |
631 | TAKE_PTR(txt_data); | |
632 | } | |
633 | ||
634 | return 0; | |
635 | } |