]>
| 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" |
| 5751b236 | 10 | #include "dns-rr.h" |
| 284d7641 DDM |
11 | #include "extract-word.h" |
| 12 | #include "hashmap.h" | |
| 8e1c3459 | 13 | #include "hexdecoct.h" |
| 0ef0e269 LP |
14 | #include "path-util.h" |
| 15 | #include "resolved-conf.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 | ||
| 108bd5c2 | 49 | DnssdRegisteredService *dnssd_registered_service_free(DnssdRegisteredService *service) { |
| 6501dd31 DR |
50 | if (!service) |
| 51 | return NULL; | |
| 52 | ||
| 53 | if (service->manager) | |
| 108bd5c2 | 54 | hashmap_remove(service->manager->dnssd_registered_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 | ||
| 108bd5c2 VCS |
71 | void dnssd_registered_service_clear_on_reload(Hashmap *services) { |
| 72 | DnssdRegisteredService *service; | |
| 14a52176 LB |
73 | |
| 74 | HASHMAP_FOREACH(service, services) | |
| 75 | if (service->config_source == RESOLVE_CONFIG_SOURCE_FILE) { | |
| 0ef0e269 | 76 | hashmap_remove(services, service->id); |
| 108bd5c2 | 77 | dnssd_registered_service_free(service); |
| 14a52176 LB |
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 | ||
| 108bd5c2 VCS |
102 | static int dnssd_registered_service_load(Manager *manager, const char *path) { |
| 103 | _cleanup_(dnssd_registered_service_freep) DnssdRegisteredService *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 | 110 | |
| 108bd5c2 | 111 | service = new0(DnssdRegisteredService, 1); |
| 6501dd31 DR |
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( |
| 93378148 | 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 | ||
| 108bd5c2 | 161 | r = hashmap_ensure_put(&manager->dnssd_registered_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 | ||
| 108bd5c2 | 184 | int dnssd_render_instance_name(Manager *m, DnssdRegisteredService *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) { | |
| 108bd5c2 | 233 | r = dnssd_registered_service_load(manager, *f); |
| 6501dd31 | 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 | ||
| 108bd5c2 | 241 | int dnssd_update_rrs(DnssdRegisteredService *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) { |
| 108bd5c2 | 382 | DnssdRegisteredService *s; |
| c3036641 DR |
383 | int r; |
| 384 | ||
| b8d6689a YW |
385 | if (sd_bus_is_ready(manager->bus) <= 0) |
| 386 | return 0; | |
| 387 | ||
| 108bd5c2 | 388 | HASHMAP_FOREACH(s, manager->dnssd_registered_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 | 415 | |
| a5527e22 | 416 | int config_parse_dnssd_name( |
| 8e1c3459 LP |
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 | }; | |
| 108bd5c2 | 440 | DnssdRegisteredService *s = ASSERT_PTR(userdata); |
| 8e1c3459 LP |
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 | ||
| a5527e22 | 470 | int config_parse_dnssd_type( |
| 8e1c3459 LP |
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 | ||
| 108bd5c2 | 482 | DnssdRegisteredService *s = ASSERT_PTR(userdata); |
| 8e1c3459 LP |
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 | ||
| a5527e22 | 506 | int config_parse_dnssd_subtype( |
| 8e1c3459 LP |
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 | ||
| 108bd5c2 | 518 | DnssdRegisteredService *s = ASSERT_PTR(userdata); |
| 8e1c3459 LP |
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; | |
| 108bd5c2 | 550 | DnssdRegisteredService *s = ASSERT_PTR(userdata); |
| 8e1c3459 LP |
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 | } |