]>
Commit | Line | Data |
---|---|---|
a032b68d MB |
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | ||
3 | #include "in-addr-util.h" | |
4 | #include "resolved-dns-synthesize.h" | |
5 | #include "resolved-varlink.h" | |
6 | #include "socket-netlink.h" | |
7 | ||
8 | typedef struct LookupParameters { | |
9 | int ifindex; | |
10 | uint64_t flags; | |
11 | int family; | |
12 | union in_addr_union address; | |
13 | size_t address_size; | |
14 | char *name; | |
15 | } LookupParameters; | |
16 | ||
17 | static void lookup_parameters_destroy(LookupParameters *p) { | |
18 | assert(p); | |
19 | free(p->name); | |
20 | } | |
21 | ||
22 | static int reply_query_state(DnsQuery *q) { | |
23 | ||
24 | assert(q); | |
25 | assert(q->varlink_request); | |
26 | ||
27 | switch (q->state) { | |
28 | ||
29 | case DNS_TRANSACTION_NO_SERVERS: | |
30 | return varlink_error(q->varlink_request, "io.systemd.Resolve.NoNameServers", NULL); | |
31 | ||
32 | case DNS_TRANSACTION_TIMEOUT: | |
33 | return varlink_error(q->varlink_request, "io.systemd.Resolve.QueryTimedOut", NULL); | |
34 | ||
35 | case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED: | |
36 | return varlink_error(q->varlink_request, "io.systemd.Resolve.MaxAttemptsReached", NULL); | |
37 | ||
38 | case DNS_TRANSACTION_INVALID_REPLY: | |
39 | return varlink_error(q->varlink_request, "io.systemd.Resolve.InvalidReply", NULL); | |
40 | ||
41 | case DNS_TRANSACTION_ERRNO: | |
42 | return varlink_error_errno(q->varlink_request, q->answer_errno); | |
43 | ||
44 | case DNS_TRANSACTION_ABORTED: | |
45 | return varlink_error(q->varlink_request, "io.systemd.Resolve.QueryAborted", NULL); | |
46 | ||
47 | case DNS_TRANSACTION_DNSSEC_FAILED: | |
48 | return varlink_errorb(q->varlink_request, "io.systemd.Resolve.DNSSECValidationFailed", | |
49 | JSON_BUILD_OBJECT(JSON_BUILD_PAIR("result", JSON_BUILD_STRING(dnssec_result_to_string(q->answer_dnssec_result))))); | |
50 | ||
51 | case DNS_TRANSACTION_NO_TRUST_ANCHOR: | |
52 | return varlink_error(q->varlink_request, "io.systemd.Resolve.NoTrustAnchor", NULL); | |
53 | ||
54 | case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED: | |
55 | return varlink_error(q->varlink_request, "io.systemd.Resolve.ResourceRecordTypeUnsupported", NULL); | |
56 | ||
57 | case DNS_TRANSACTION_NETWORK_DOWN: | |
58 | return varlink_error(q->varlink_request, "io.systemd.Resolve.NetworkDown", NULL); | |
59 | ||
3a6ce677 BR |
60 | case DNS_TRANSACTION_NO_SOURCE: |
61 | return varlink_error(q->varlink_request, "io.systemd.Resolve.NoSource", NULL); | |
62 | ||
63 | case DNS_TRANSACTION_STUB_LOOP: | |
64 | return varlink_error(q->varlink_request, "io.systemd.Resolve.StubLoop", NULL); | |
65 | ||
a032b68d MB |
66 | case DNS_TRANSACTION_NOT_FOUND: |
67 | /* We return this as NXDOMAIN. This is only generated when a host doesn't implement LLMNR/TCP, and we | |
68 | * thus quickly know that we cannot resolve an in-addr.arpa or ip6.arpa address. */ | |
69 | return varlink_errorb(q->varlink_request, "io.systemd.Resolve.DNSError", | |
70 | JSON_BUILD_OBJECT(JSON_BUILD_PAIR("rcode", JSON_BUILD_INTEGER(DNS_RCODE_NXDOMAIN)))); | |
71 | ||
72 | case DNS_TRANSACTION_RCODE_FAILURE: | |
73 | return varlink_errorb(q->varlink_request, "io.systemd.Resolve.DNSError", | |
74 | JSON_BUILD_OBJECT(JSON_BUILD_PAIR("rcode", JSON_BUILD_INTEGER(q->answer_rcode)))); | |
75 | ||
76 | case DNS_TRANSACTION_NULL: | |
77 | case DNS_TRANSACTION_PENDING: | |
78 | case DNS_TRANSACTION_VALIDATING: | |
79 | case DNS_TRANSACTION_SUCCESS: | |
80 | default: | |
ea0999c9 | 81 | assert_not_reached(); |
a032b68d MB |
82 | } |
83 | } | |
84 | ||
85 | static void vl_on_disconnect(VarlinkServer *s, Varlink *link, void *userdata) { | |
86 | DnsQuery *q; | |
87 | ||
88 | assert(s); | |
89 | assert(link); | |
90 | ||
91 | q = varlink_get_userdata(link); | |
92 | if (!q) | |
93 | return; | |
94 | ||
95 | if (!DNS_TRANSACTION_IS_LIVE(q->state)) | |
96 | return; | |
97 | ||
98 | log_debug("Client of active query vanished, aborting query."); | |
99 | dns_query_complete(q, DNS_TRANSACTION_ABORTED); | |
100 | } | |
101 | ||
102 | static bool validate_and_mangle_flags( | |
103 | const char *name, | |
104 | uint64_t *flags, | |
105 | uint64_t ok) { | |
106 | ||
107 | assert(flags); | |
108 | ||
109 | /* This checks that the specified client-provided flags parameter actually makes sense, and mangles | |
110 | * it slightly. Specifically: | |
111 | * | |
3a6ce677 | 112 | * 1. We check that only the protocol flags and a bunch of NO_XYZ flags are on at most, plus the |
a032b68d MB |
113 | * method-specific flags specified in 'ok'. |
114 | * | |
115 | * 2. If no protocols are enabled we automatically convert that to "all protocols are enabled". | |
116 | * | |
117 | * The second rule means that clients can just pass 0 as flags for the common case, and all supported | |
118 | * protocols are enabled. Moreover it's useful so that client's do not have to be aware of all | |
119 | * protocols implemented in resolved, but can use 0 as protocols flags set as indicator for | |
120 | * "everything". | |
121 | */ | |
122 | ||
3a6ce677 BR |
123 | if (*flags & ~(SD_RESOLVED_PROTOCOLS_ALL| |
124 | SD_RESOLVED_NO_CNAME| | |
125 | SD_RESOLVED_NO_VALIDATE| | |
126 | SD_RESOLVED_NO_SYNTHESIZE| | |
127 | SD_RESOLVED_NO_CACHE| | |
128 | SD_RESOLVED_NO_ZONE| | |
129 | SD_RESOLVED_NO_TRUST_ANCHOR| | |
130 | SD_RESOLVED_NO_NETWORK| | |
131 | ok)) | |
a032b68d MB |
132 | return false; |
133 | ||
134 | if ((*flags & SD_RESOLVED_PROTOCOLS_ALL) == 0) /* If no protocol is enabled, enable all */ | |
135 | *flags |= SD_RESOLVED_PROTOCOLS_ALL; | |
136 | ||
137 | /* If the SD_RESOLVED_NO_SEARCH flag is acceptable, and the query name is dot-suffixed, turn off | |
138 | * search domains. Note that DNS name normalization drops the dot suffix, hence we propagate this | |
139 | * into the flags field as early as we can. */ | |
140 | if (name && FLAGS_SET(ok, SD_RESOLVED_NO_SEARCH) && dns_name_dot_suffixed(name) > 0) | |
141 | *flags |= SD_RESOLVED_NO_SEARCH; | |
142 | ||
143 | return true; | |
144 | } | |
145 | ||
9cde670f | 146 | static void vl_method_resolve_hostname_complete(DnsQuery *query) { |
a032b68d MB |
147 | _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; |
148 | _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; | |
9cde670f | 149 | _cleanup_(dns_query_freep) DnsQuery *q = query; |
a032b68d MB |
150 | _cleanup_free_ char *normalized = NULL; |
151 | DnsResourceRecord *rr; | |
152 | DnsQuestion *question; | |
153 | int ifindex, r; | |
154 | ||
155 | assert(q); | |
156 | ||
157 | if (q->state != DNS_TRANSACTION_SUCCESS) { | |
158 | r = reply_query_state(q); | |
159 | goto finish; | |
160 | } | |
161 | ||
3a6ce677 | 162 | r = dns_query_process_cname_many(q); |
a032b68d MB |
163 | if (r == -ELOOP) { |
164 | r = varlink_error(q->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL); | |
165 | goto finish; | |
166 | } | |
167 | if (r < 0) | |
168 | goto finish; | |
9cde670f LB |
169 | if (r == DNS_QUERY_CNAME) { |
170 | /* This was a cname, and the query was restarted. */ | |
171 | TAKE_PTR(q); | |
a032b68d | 172 | return; |
9cde670f | 173 | } |
a032b68d MB |
174 | |
175 | question = dns_query_question_for_protocol(q, q->answer_protocol); | |
176 | ||
177 | DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { | |
178 | _cleanup_(json_variant_unrefp) JsonVariant *entry = NULL; | |
179 | int family; | |
180 | const void *p; | |
181 | ||
182 | r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); | |
183 | if (r < 0) | |
184 | goto finish; | |
185 | if (r == 0) | |
186 | continue; | |
187 | ||
188 | if (rr->key->type == DNS_TYPE_A) { | |
189 | family = AF_INET; | |
190 | p = &rr->a.in_addr; | |
191 | } else if (rr->key->type == DNS_TYPE_AAAA) { | |
192 | family = AF_INET6; | |
193 | p = &rr->aaaa.in6_addr; | |
194 | } else { | |
195 | r = -EAFNOSUPPORT; | |
196 | goto finish; | |
197 | } | |
198 | ||
199 | r = json_build(&entry, | |
200 | JSON_BUILD_OBJECT( | |
3a6ce677 | 201 | JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", JSON_BUILD_INTEGER(ifindex)), |
a032b68d MB |
202 | JSON_BUILD_PAIR("family", JSON_BUILD_INTEGER(family)), |
203 | JSON_BUILD_PAIR("address", JSON_BUILD_BYTE_ARRAY(p, FAMILY_ADDRESS_SIZE(family))))); | |
204 | if (r < 0) | |
205 | goto finish; | |
206 | ||
207 | if (!canonical) | |
208 | canonical = dns_resource_record_ref(rr); | |
209 | ||
210 | r = json_variant_append_array(&array, entry); | |
211 | if (r < 0) | |
212 | goto finish; | |
213 | } | |
214 | ||
215 | if (json_variant_is_blank_object(array)) { | |
216 | r = varlink_error(q->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL); | |
217 | goto finish; | |
218 | } | |
219 | ||
220 | assert(canonical); | |
221 | r = dns_name_normalize(dns_resource_key_name(canonical->key), 0, &normalized); | |
222 | if (r < 0) | |
223 | goto finish; | |
224 | ||
225 | r = varlink_replyb(q->varlink_request, | |
226 | JSON_BUILD_OBJECT( | |
227 | JSON_BUILD_PAIR("addresses", JSON_BUILD_VARIANT(array)), | |
228 | JSON_BUILD_PAIR("name", JSON_BUILD_STRING(normalized)), | |
3a6ce677 | 229 | JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(dns_query_reply_flags_make(q))))); |
a032b68d MB |
230 | finish: |
231 | if (r < 0) { | |
232 | log_error_errno(r, "Failed to send hostname reply: %m"); | |
233 | r = varlink_error_errno(q->varlink_request, r); | |
234 | } | |
a032b68d MB |
235 | } |
236 | ||
237 | static int parse_as_address(Varlink *link, LookupParameters *p) { | |
238 | _cleanup_free_ char *canonical = NULL; | |
239 | int r, ff, parsed_ifindex, ifindex; | |
240 | union in_addr_union parsed; | |
241 | ||
242 | assert(link); | |
243 | assert(p); | |
244 | ||
245 | /* Check if this parses as literal address. If so, just parse it and return that, do not involve networking */ | |
246 | r = in_addr_ifindex_from_string_auto(p->name, &ff, &parsed, &parsed_ifindex); | |
247 | if (r < 0) | |
248 | return 0; /* not a literal address */ | |
249 | ||
250 | /* Make sure the data we parsed matches what is requested */ | |
251 | if ((p->family != AF_UNSPEC && ff != p->family) || | |
252 | (p->ifindex > 0 && parsed_ifindex > 0 && parsed_ifindex != p->ifindex)) | |
253 | return varlink_error(link, "io.systemd.Resolve.NoSuchResourceRecord", NULL); | |
254 | ||
255 | ifindex = parsed_ifindex > 0 ? parsed_ifindex : p->ifindex; | |
256 | ||
257 | /* Reformat the address as string, to return as canonicalized name */ | |
258 | r = in_addr_ifindex_to_string(ff, &parsed, ifindex, &canonical); | |
259 | if (r < 0) | |
260 | return r; | |
261 | ||
262 | return varlink_replyb( | |
263 | link, | |
264 | JSON_BUILD_OBJECT( | |
265 | JSON_BUILD_PAIR("addresses", | |
266 | JSON_BUILD_ARRAY( | |
267 | JSON_BUILD_OBJECT( | |
268 | JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", JSON_BUILD_INTEGER(ifindex)), | |
269 | JSON_BUILD_PAIR("family", JSON_BUILD_INTEGER(ff)), | |
270 | JSON_BUILD_PAIR("address", JSON_BUILD_BYTE_ARRAY(&parsed, FAMILY_ADDRESS_SIZE(ff)))))), | |
271 | JSON_BUILD_PAIR("name", JSON_BUILD_STRING(canonical)), | |
3a6ce677 BR |
272 | JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(SD_RESOLVED_FLAGS_MAKE(dns_synthesize_protocol(p->flags), ff, true, true)| |
273 | SD_RESOLVED_SYNTHETIC)))); | |
a032b68d MB |
274 | } |
275 | ||
276 | static int vl_method_resolve_hostname(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { | |
277 | static const JsonDispatch dispatch_table[] = { | |
278 | { "ifindex", JSON_VARIANT_UNSIGNED, json_dispatch_int, offsetof(LookupParameters, ifindex), 0 }, | |
279 | { "name", JSON_VARIANT_STRING, json_dispatch_string, offsetof(LookupParameters, name), JSON_MANDATORY }, | |
280 | { "family", JSON_VARIANT_UNSIGNED, json_dispatch_int, offsetof(LookupParameters, family), 0 }, | |
281 | { "flags", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(LookupParameters, flags), 0 }, | |
282 | {} | |
283 | }; | |
284 | ||
285 | _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL; | |
286 | _cleanup_(lookup_parameters_destroy) LookupParameters p = { | |
287 | .family = AF_UNSPEC, | |
288 | }; | |
9cde670f | 289 | _cleanup_(dns_query_freep) DnsQuery *q = NULL; |
3a6ce677 | 290 | Manager *m; |
a032b68d MB |
291 | int r; |
292 | ||
293 | assert(link); | |
3a6ce677 BR |
294 | |
295 | m = varlink_server_get_userdata(varlink_get_server(link)); | |
a032b68d MB |
296 | assert(m); |
297 | ||
298 | if (FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) | |
299 | return -EINVAL; | |
300 | ||
301 | r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); | |
302 | if (r < 0) | |
303 | return r; | |
304 | ||
305 | if (p.ifindex < 0) | |
306 | return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("ifindex")); | |
307 | ||
308 | r = dns_name_is_valid(p.name); | |
309 | if (r < 0) | |
310 | return r; | |
311 | if (r == 0) | |
312 | return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("name")); | |
313 | ||
314 | if (!IN_SET(p.family, AF_UNSPEC, AF_INET, AF_INET6)) | |
315 | return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("family")); | |
316 | ||
317 | if (!validate_and_mangle_flags(p.name, &p.flags, SD_RESOLVED_NO_SEARCH)) | |
318 | return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("flags")); | |
319 | ||
320 | r = parse_as_address(link, &p); | |
321 | if (r != 0) | |
322 | return r; | |
323 | ||
324 | r = dns_question_new_address(&question_utf8, p.family, p.name, false); | |
325 | if (r < 0) | |
326 | return r; | |
327 | ||
328 | r = dns_question_new_address(&question_idna, p.family, p.name, true); | |
329 | if (r < 0 && r != -EALREADY) | |
330 | return r; | |
331 | ||
3a6ce677 | 332 | r = dns_query_new(m, &q, question_utf8, question_idna ?: question_utf8, NULL, p.ifindex, p.flags); |
a032b68d MB |
333 | if (r < 0) |
334 | return r; | |
335 | ||
336 | q->varlink_request = varlink_ref(link); | |
337 | varlink_set_userdata(link, q); | |
338 | q->request_family = p.family; | |
339 | q->complete = vl_method_resolve_hostname_complete; | |
340 | ||
341 | r = dns_query_go(q); | |
342 | if (r < 0) | |
9cde670f | 343 | return r; |
a032b68d | 344 | |
9cde670f | 345 | TAKE_PTR(q); |
a032b68d | 346 | return 1; |
a032b68d MB |
347 | } |
348 | ||
349 | static int json_dispatch_address(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { | |
350 | LookupParameters *p = userdata; | |
351 | union in_addr_union buf = {}; | |
352 | JsonVariant *i; | |
353 | size_t n, k = 0; | |
354 | ||
355 | assert(variant); | |
356 | assert(p); | |
357 | ||
358 | if (!json_variant_is_array(variant)) | |
359 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name)); | |
360 | ||
361 | n = json_variant_elements(variant); | |
362 | if (!IN_SET(n, 4, 16)) | |
363 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is array of unexpected size.", strna(name)); | |
364 | ||
365 | JSON_VARIANT_ARRAY_FOREACH(i, variant) { | |
ea0999c9 | 366 | int64_t b; |
a032b68d MB |
367 | |
368 | if (!json_variant_is_integer(i)) | |
369 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is not an integer.", k, strna(name)); | |
370 | ||
371 | b = json_variant_integer(i); | |
372 | if (b < 0 || b > 0xff) | |
373 | return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is out of range 0…255.", k, strna(name)); | |
374 | ||
375 | buf.bytes[k++] = (uint8_t) b; | |
376 | } | |
377 | ||
378 | p->address = buf; | |
379 | p->address_size = k; | |
380 | ||
381 | return 0; | |
382 | } | |
383 | ||
9cde670f | 384 | static void vl_method_resolve_address_complete(DnsQuery *query) { |
a032b68d | 385 | _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; |
9cde670f | 386 | _cleanup_(dns_query_freep) DnsQuery *q = query; |
a032b68d MB |
387 | DnsQuestion *question; |
388 | DnsResourceRecord *rr; | |
389 | int ifindex, r; | |
390 | ||
391 | assert(q); | |
392 | ||
393 | if (q->state != DNS_TRANSACTION_SUCCESS) { | |
394 | r = reply_query_state(q); | |
395 | goto finish; | |
396 | } | |
397 | ||
3a6ce677 | 398 | r = dns_query_process_cname_many(q); |
a032b68d MB |
399 | if (r == -ELOOP) { |
400 | r = varlink_error(q->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL); | |
401 | goto finish; | |
402 | } | |
403 | if (r < 0) | |
404 | goto finish; | |
9cde670f LB |
405 | if (r == DNS_QUERY_CNAME) { |
406 | /* This was a cname, and the query was restarted. */ | |
407 | TAKE_PTR(q); | |
a032b68d | 408 | return; |
9cde670f | 409 | } |
a032b68d MB |
410 | |
411 | question = dns_query_question_for_protocol(q, q->answer_protocol); | |
412 | ||
413 | DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { | |
414 | _cleanup_(json_variant_unrefp) JsonVariant *entry = NULL; | |
415 | _cleanup_free_ char *normalized = NULL; | |
416 | ||
417 | r = dns_question_matches_rr(question, rr, NULL); | |
418 | if (r < 0) | |
419 | goto finish; | |
420 | if (r == 0) | |
421 | continue; | |
422 | ||
423 | r = dns_name_normalize(rr->ptr.name, 0, &normalized); | |
424 | if (r < 0) | |
425 | goto finish; | |
426 | ||
427 | r = json_build(&entry, | |
428 | JSON_BUILD_OBJECT( | |
3a6ce677 | 429 | JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", JSON_BUILD_INTEGER(ifindex)), |
a032b68d MB |
430 | JSON_BUILD_PAIR("name", JSON_BUILD_STRING(normalized)))); |
431 | if (r < 0) | |
432 | goto finish; | |
433 | ||
434 | r = json_variant_append_array(&array, entry); | |
435 | if (r < 0) | |
436 | goto finish; | |
437 | } | |
438 | ||
439 | if (json_variant_is_blank_object(array)) { | |
440 | r = varlink_error(q->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL); | |
441 | goto finish; | |
442 | } | |
443 | ||
444 | r = varlink_replyb(q->varlink_request, | |
445 | JSON_BUILD_OBJECT( | |
446 | JSON_BUILD_PAIR("names", JSON_BUILD_VARIANT(array)), | |
3a6ce677 | 447 | JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(dns_query_reply_flags_make(q))))); |
a032b68d MB |
448 | finish: |
449 | if (r < 0) { | |
450 | log_error_errno(r, "Failed to send address reply: %m"); | |
451 | r = varlink_error_errno(q->varlink_request, r); | |
452 | } | |
a032b68d MB |
453 | } |
454 | ||
455 | static int vl_method_resolve_address(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { | |
456 | static const JsonDispatch dispatch_table[] = { | |
457 | { "ifindex", JSON_VARIANT_UNSIGNED, json_dispatch_int, offsetof(LookupParameters, ifindex), 0 }, | |
458 | { "family", JSON_VARIANT_UNSIGNED, json_dispatch_int, offsetof(LookupParameters, family), JSON_MANDATORY }, | |
459 | { "address", JSON_VARIANT_ARRAY, json_dispatch_address, 0, JSON_MANDATORY }, | |
460 | { "flags", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(LookupParameters, flags), 0 }, | |
461 | {} | |
462 | }; | |
463 | ||
464 | _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; | |
465 | _cleanup_(lookup_parameters_destroy) LookupParameters p = { | |
466 | .family = AF_UNSPEC, | |
467 | }; | |
9cde670f | 468 | _cleanup_(dns_query_freep) DnsQuery *q = NULL; |
3a6ce677 | 469 | Manager *m; |
a032b68d MB |
470 | int r; |
471 | ||
472 | assert(link); | |
3a6ce677 BR |
473 | |
474 | m = varlink_server_get_userdata(varlink_get_server(link)); | |
a032b68d MB |
475 | assert(m); |
476 | ||
477 | if (FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) | |
478 | return -EINVAL; | |
479 | ||
480 | r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); | |
481 | if (r < 0) | |
482 | return r; | |
483 | ||
484 | if (p.ifindex < 0) | |
485 | return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("ifindex")); | |
486 | ||
9cde670f | 487 | if (!IN_SET(p.family, AF_INET, AF_INET6)) |
a032b68d MB |
488 | return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("family")); |
489 | ||
490 | if (FAMILY_ADDRESS_SIZE(p.family) != p.address_size) | |
491 | return varlink_error(link, "io.systemd.UserDatabase.BadAddressSize", NULL); | |
492 | ||
493 | if (!validate_and_mangle_flags(NULL, &p.flags, 0)) | |
494 | return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("flags")); | |
495 | ||
496 | r = dns_question_new_reverse(&question, p.family, &p.address); | |
497 | if (r < 0) | |
498 | return r; | |
499 | ||
3a6ce677 | 500 | r = dns_query_new(m, &q, question, question, NULL, p.ifindex, p.flags|SD_RESOLVED_NO_SEARCH); |
a032b68d MB |
501 | if (r < 0) |
502 | return r; | |
503 | ||
504 | q->varlink_request = varlink_ref(link); | |
505 | varlink_set_userdata(link, q); | |
506 | ||
507 | q->request_family = p.family; | |
508 | q->request_address = p.address; | |
509 | q->complete = vl_method_resolve_address_complete; | |
510 | ||
511 | r = dns_query_go(q); | |
512 | if (r < 0) | |
9cde670f | 513 | return r; |
a032b68d | 514 | |
9cde670f | 515 | TAKE_PTR(q); |
a032b68d | 516 | return 1; |
a032b68d MB |
517 | } |
518 | ||
519 | int manager_varlink_init(Manager *m) { | |
520 | _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL; | |
521 | int r; | |
522 | ||
523 | assert(m); | |
524 | ||
525 | if (m->varlink_server) | |
526 | return 0; | |
527 | ||
528 | r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID); | |
529 | if (r < 0) | |
530 | return log_error_errno(r, "Failed to allocate varlink server object: %m"); | |
531 | ||
532 | varlink_server_set_userdata(s, m); | |
533 | ||
534 | r = varlink_server_bind_method_many( | |
535 | s, | |
536 | "io.systemd.Resolve.ResolveHostname", vl_method_resolve_hostname, | |
537 | "io.systemd.Resolve.ResolveAddress", vl_method_resolve_address); | |
538 | if (r < 0) | |
539 | return log_error_errno(r, "Failed to register varlink methods: %m"); | |
540 | ||
541 | r = varlink_server_bind_disconnect(s, vl_on_disconnect); | |
542 | if (r < 0) | |
543 | return log_error_errno(r, "Failed to register varlink disconnect handler: %m"); | |
544 | ||
545 | r = varlink_server_listen_address(s, "/run/systemd/resolve/io.systemd.Resolve", 0666); | |
546 | if (r < 0) | |
547 | return log_error_errno(r, "Failed to bind to varlink socket: %m"); | |
548 | ||
549 | r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL); | |
550 | if (r < 0) | |
551 | return log_error_errno(r, "Failed to attach varlink connection to event loop: %m"); | |
552 | ||
553 | m->varlink_server = TAKE_PTR(s); | |
554 | return 0; | |
555 | } | |
556 | ||
557 | void manager_varlink_done(Manager *m) { | |
558 | assert(m); | |
559 | ||
560 | m->varlink_server = varlink_server_unref(m->varlink_server); | |
561 | } |