]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-bus.c
resolved: rework logic so that we can share transactions between queries of different...
[thirdparty/systemd.git] / src / resolve / resolved-bus.c
CommitLineData
74b2466e
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include "bus-errors.h"
23#include "bus-util.h"
24
25#include "resolved.h"
26#include "resolved-dns-domain.h"
27
ad867662
LP
28static int reply_query_state(DnsQuery *q) {
29 _cleanup_free_ char *ip = NULL;
30 const char *name;
74b2466e
LP
31 int r;
32
ad867662
LP
33 if (q->request_hostname)
34 name = q->request_hostname;
35 else {
36 r = in_addr_to_string(q->request_family, &q->request_address, &ip);
37 if (r < 0)
38 return r;
74b2466e 39
ad867662
LP
40 name = ip;
41 }
42
43 switch (q->state) {
74b2466e 44
ad867662 45 case DNS_QUERY_NO_SERVERS:
309e9d86 46 return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
74b2466e
LP
47
48 case DNS_QUERY_TIMEOUT:
ad867662 49 return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out");
74b2466e
LP
50
51 case DNS_QUERY_ATTEMPTS_MAX:
ad867662
LP
52 return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed");
53
54 case DNS_QUERY_RESOURCES:
55 return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_RESOURCES, "Not enough resources");
56
57 case DNS_QUERY_INVALID_REPLY:
58 return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
74b2466e
LP
59
60 case DNS_QUERY_FAILURE: {
61 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
62
faa133f3 63 if (q->answer_rcode == DNS_RCODE_NXDOMAIN)
ad867662 64 sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", name);
74b2466e
LP
65 else {
66 const char *rc, *n;
ad867662 67 char p[3]; /* the rcode is 4 bits long */
74b2466e 68
faa133f3 69 rc = dns_rcode_to_string(q->answer_rcode);
74b2466e 70 if (!rc) {
faa133f3 71 sprintf(p, "%i", q->answer_rcode);
74b2466e
LP
72 rc = p;
73 }
74
75 n = strappenda(_BUS_ERROR_DNS, rc);
ad867662 76 sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", name, rc);
74b2466e
LP
77 }
78
ad867662 79 return sd_bus_reply_method_error(q->request, &error);
74b2466e
LP
80 }
81
ad867662 82 case DNS_QUERY_NULL:
8ba9fd9c 83 case DNS_QUERY_PENDING:
ad867662 84 case DNS_QUERY_SUCCESS:
8ba9fd9c 85 default:
ad867662
LP
86 assert_not_reached("Impossible state");
87 }
88}
74b2466e 89
8ba9fd9c
LP
90static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifindex) {
91 int r;
92
93 assert(reply);
94 assert(rr);
95
0dd25fb9 96 r = sd_bus_message_open_container(reply, 'r', "iayi");
8ba9fd9c
LP
97 if (r < 0)
98 return r;
99
faa133f3 100 if (rr->key->type == DNS_TYPE_A) {
0dd25fb9 101 r = sd_bus_message_append(reply, "i", AF_INET);
8ba9fd9c
LP
102 if (r < 0)
103 return r;
104
105 r = sd_bus_message_append_array(reply, 'y', &rr->a.in_addr, sizeof(struct in_addr));
faa133f3
LP
106
107 } else if (rr->key->type == DNS_TYPE_AAAA) {
0dd25fb9 108 r = sd_bus_message_append(reply, "i", AF_INET6);
8ba9fd9c
LP
109 if (r < 0)
110 return r;
111
112 r = sd_bus_message_append_array(reply, 'y', &rr->aaaa.in6_addr, sizeof(struct in6_addr));
faa133f3
LP
113 } else
114 return -EAFNOSUPPORT;
115
8ba9fd9c
LP
116 if (r < 0)
117 return r;
118
119 r = sd_bus_message_append(reply, "i", ifindex);
120 if (r < 0)
121 return r;
122
123 r = sd_bus_message_close_container(reply);
124 if (r < 0)
125 return r;
126
127 return 0;
128}
129
ad867662 130static void bus_method_resolve_hostname_complete(DnsQuery *q) {
309e9d86 131 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL, *canonical = NULL;
ad867662 132 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
faa133f3
LP
133 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
134 unsigned added = 0, i;
135 int r, ifindex;
74b2466e 136
ad867662 137 assert(q);
74b2466e 138
ad867662
LP
139 if (q->state != DNS_QUERY_SUCCESS) {
140 r = reply_query_state(q);
141 goto finish;
142 }
74b2466e 143
ad867662
LP
144 r = sd_bus_message_new_method_return(q->request, &reply);
145 if (r < 0)
146 goto finish;
74b2466e 147
0dd25fb9 148 r = sd_bus_message_open_container(reply, 'a', "(iayi)");
ad867662
LP
149 if (r < 0)
150 goto finish;
74b2466e 151
faa133f3
LP
152 answer = dns_answer_ref(q->answer);
153 ifindex = q->answer_ifindex;
8ba9fd9c 154
faa133f3
LP
155 for (i = 0; i < answer->n_rrs; i++) {
156 r = dns_question_matches_rr(q->question, answer->rrs[i]);
8ba9fd9c
LP
157 if (r < 0)
158 goto parse_fail;
159 if (r == 0) {
160 /* Hmm, if this is not an address record,
161 maybe it's a cname? If so, remember this */
faa133f3 162 r = dns_question_matches_cname(q->question, answer->rrs[i]);
8ba9fd9c
LP
163 if (r < 0)
164 goto parse_fail;
165 if (r > 0)
faa133f3 166 cname = dns_resource_record_ref(answer->rrs[i]);
74b2466e 167
ad867662 168 continue;
8ba9fd9c 169 }
74b2466e 170
faa133f3 171 r = append_address(reply, answer->rrs[i], ifindex);
ad867662
LP
172 if (r < 0)
173 goto finish;
74b2466e 174
309e9d86 175 if (!canonical)
faa133f3 176 canonical = dns_resource_record_ref(answer->rrs[i]);
309e9d86 177
8ba9fd9c
LP
178 added ++;
179 }
74b2466e 180
8ba9fd9c
LP
181 if (added <= 0) {
182 if (!cname) {
309e9d86 183 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of requested type", q->request_hostname);
ad867662 184 goto finish;
8ba9fd9c 185 }
74b2466e 186
8ba9fd9c
LP
187 /* This has a cname? Then update the query with the
188 * new cname. */
322345fd 189 r = dns_query_cname_redirect(q, cname->cname.name);
8ba9fd9c
LP
190 if (r < 0) {
191 if (r == -ELOOP)
192 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname);
193 else
194 r = sd_bus_reply_method_errno(q->request, -r, NULL);
74b2466e 195
74b2466e 196 goto finish;
8ba9fd9c 197 }
74b2466e 198
8ba9fd9c
LP
199 /* Before we restart the query, let's see if any of
200 * the RRs we already got already answers our query */
faa133f3
LP
201 for (i = 0; i < answer->n_rrs; i++) {
202 r = dns_question_matches_rr(q->question, answer->rrs[i]);
8ba9fd9c
LP
203 if (r < 0)
204 goto parse_fail;
205 if (r == 0)
206 continue;
207
faa133f3 208 r = append_address(reply, answer->rrs[i], ifindex);
8ba9fd9c
LP
209 if (r < 0)
210 goto finish;
211
309e9d86 212 if (!canonical)
faa133f3 213 canonical = dns_resource_record_ref(answer->rrs[i]);
309e9d86 214
8ba9fd9c
LP
215 added++;
216 }
217
218 /* If we didn't find anything, then let's restart the
219 * query, this time with the cname */
220 if (added <= 0) {
322345fd 221 r = dns_query_go(q);
309e9d86
LP
222 if (r == -ESRCH) {
223 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
224 goto finish;
225 }
8ba9fd9c
LP
226 if (r < 0) {
227 r = sd_bus_reply_method_errno(q->request, -r, NULL);
228 goto finish;
229 }
230 return;
231 }
74b2466e
LP
232 }
233
ad867662
LP
234 r = sd_bus_message_close_container(reply);
235 if (r < 0)
236 goto finish;
237
309e9d86
LP
238 /* Return the precise spelling and uppercasing reported by the server */
239 assert(canonical);
faa133f3 240 r = sd_bus_message_append(reply, "s", DNS_RESOURCE_KEY_NAME(canonical->key));
309e9d86
LP
241 if (r < 0)
242 goto finish;
243
ad867662
LP
244 r = sd_bus_send(q->manager->bus, reply, NULL);
245 goto finish;
246
247parse_fail:
248 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
249
74b2466e
LP
250finish:
251 if (r < 0)
252 log_error("Failed to send bus reply: %s", strerror(-r));
253
254 dns_query_free(q);
255}
256
257static int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
faa133f3 258 _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
74b2466e
LP
259 Manager *m = userdata;
260 const char *hostname;
0dd25fb9 261 int family;
74b2466e 262 DnsQuery *q;
74b2466e
LP
263 int r;
264
265 assert(bus);
266 assert(message);
267 assert(m);
268
0dd25fb9 269 r = sd_bus_message_read(message, "si", &hostname, &family);
74b2466e
LP
270 if (r < 0)
271 return r;
272
273 if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
0dd25fb9 274 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
74b2466e
LP
275
276 if (!hostname_is_valid(hostname))
277 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
278
faa133f3
LP
279 question = dns_question_new(family == AF_UNSPEC ? 2 : 1);
280 if (!question)
281 return -ENOMEM;
282
74b2466e 283 if (family != AF_INET6) {
faa133f3
LP
284 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
285
286 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, hostname);
287 if (!key)
288 return -ENOMEM;
289
290 r = dns_question_add(question, key);
291 if (r < 0)
292 return r;
74b2466e
LP
293 }
294
295 if (family != AF_INET) {
faa133f3
LP
296 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
297
298 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, hostname);
299 if (!key)
300 return -ENOMEM;
301
302 r = dns_question_add(question, key);
303 if (r < 0)
304 return r;
74b2466e
LP
305 }
306
faa133f3 307 r = dns_query_new(m, &q, question);
74b2466e
LP
308 if (r < 0)
309 return r;
310
311 q->request = sd_bus_message_ref(message);
312 q->request_family = family;
313 q->request_hostname = hostname;
314 q->complete = bus_method_resolve_hostname_complete;
315
322345fd 316 r = dns_query_go(q);
74b2466e
LP
317 if (r < 0) {
318 dns_query_free(q);
309e9d86
LP
319
320 if (r == -ESRCH)
321 sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
322
74b2466e
LP
323 return r;
324 }
325
326 return 1;
327}
328
329static void bus_method_resolve_address_complete(DnsQuery *q) {
ad867662 330 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
faa133f3
LP
331 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
332 unsigned added = 0, i;
333 int r;
74b2466e
LP
334
335 assert(q);
336
ad867662
LP
337 if (q->state != DNS_QUERY_SUCCESS) {
338 r = reply_query_state(q);
339 goto finish;
340 }
74b2466e 341
ad867662
LP
342 r = sd_bus_message_new_method_return(q->request, &reply);
343 if (r < 0)
344 goto finish;
74b2466e 345
ad867662
LP
346 r = sd_bus_message_open_container(reply, 'a', "s");
347 if (r < 0)
348 goto finish;
74b2466e 349
faa133f3
LP
350 answer = dns_answer_ref(q->answer);
351
352 for (i = 0; i < answer->n_rrs; i++) {
353 r = dns_question_matches_rr(q->question, answer->rrs[i]);
8ba9fd9c
LP
354 if (r < 0)
355 goto parse_fail;
356 if (r == 0)
ad867662 357 continue;
74b2466e 358
faa133f3 359 r = sd_bus_message_append(reply, "s", answer->rrs[i]->ptr.name);
74b2466e
LP
360 if (r < 0)
361 goto finish;
362
ad867662
LP
363 added ++;
364 }
74b2466e 365
ad867662
LP
366 if (added <= 0) {
367 _cleanup_free_ char *ip = NULL;
74b2466e 368
ad867662 369 in_addr_to_string(q->request_family, &q->request_address, &ip);
74b2466e 370
309e9d86 371 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", ip);
ad867662 372 goto finish;
74b2466e
LP
373 }
374
ad867662
LP
375 r = sd_bus_message_close_container(reply);
376 if (r < 0)
377 goto finish;
74b2466e 378
ad867662
LP
379 r = sd_bus_send(q->manager->bus, reply, NULL);
380 goto finish;
381
382parse_fail:
383 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
74b2466e
LP
384
385finish:
386 if (r < 0)
387 log_error("Failed to send bus reply: %s", strerror(-r));
388
389 dns_query_free(q);
390}
391
392static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
faa133f3
LP
393 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
394 _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
395 _cleanup_free_ char *reverse = NULL;
74b2466e 396 Manager *m = userdata;
faa133f3 397 int family, ifindex;
74b2466e 398 const void *d;
74b2466e
LP
399 DnsQuery *q;
400 size_t sz;
401 int r;
402
403 assert(bus);
404 assert(message);
405 assert(m);
406
0dd25fb9 407 r = sd_bus_message_read(message, "i", &family);
74b2466e
LP
408 if (r < 0)
409 return r;
410
411 if (!IN_SET(family, AF_INET, AF_INET6))
0dd25fb9 412 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
74b2466e
LP
413
414 r = sd_bus_message_read_array(message, 'y', &d, &sz);
415 if (r < 0)
416 return r;
417
0dd25fb9 418 if (sz != FAMILY_ADDRESS_SIZE(family))
74b2466e
LP
419 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
420
421 r = sd_bus_message_read(message, "i", &ifindex);
422 if (r < 0)
423 return r;
424 if (ifindex < 0)
425 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
426
faa133f3
LP
427 r = dns_name_reverse(family, d, &reverse);
428 if (r < 0)
429 return r;
430
431 question = dns_question_new(1);
432 if (!question)
433 return -ENOMEM;
434
435 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
436 if (!key)
437 return -ENOMEM;
438
439 reverse = NULL;
440
441 r = dns_question_add(question, key);
74b2466e
LP
442 if (r < 0)
443 return r;
444
faa133f3 445 r = dns_query_new(m, &q, question);
74b2466e
LP
446 if (r < 0)
447 return r;
448
449 q->request = sd_bus_message_ref(message);
450 q->request_family = family;
451 memcpy(&q->request_address, d, sz);
452 q->complete = bus_method_resolve_address_complete;
453
322345fd 454 r = dns_query_go(q);
74b2466e
LP
455 if (r < 0) {
456 dns_query_free(q);
faa133f3
LP
457
458 if (r == -ESRCH)
459 sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
460
74b2466e
LP
461 return r;
462 }
463
464 return 1;
465}
466
467static const sd_bus_vtable resolve_vtable[] = {
468 SD_BUS_VTABLE_START(0),
878cd63d
LP
469 SD_BUS_METHOD("ResolveHostname", "si", "a(iayi)s", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
470 SD_BUS_METHOD("ResolveAddress", "iayi", "as", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
74b2466e
LP
471 SD_BUS_VTABLE_END,
472};
473
474static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
475 Manager *m = userdata;
476
477 assert(s);
478 assert(m);
479
480 m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
481
482 manager_connect_bus(m);
483 return 0;
484}
485
486int manager_connect_bus(Manager *m) {
487 int r;
488
489 assert(m);
490
491 if (m->bus)
492 return 0;
493
494 r = sd_bus_default_system(&m->bus);
495 if (r < 0) {
496 /* We failed to connect? Yuck, we must be in early
497 * boot. Let's try in 5s again. As soon as we have
498 * kdbus we can stop doing this... */
499
500 log_debug("Failed to connect to bus, trying again in 5s: %s", strerror(-r));
501
502 r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m);
503 if (r < 0) {
504 log_error("Failed to install bus reconnect time event: %s", strerror(-r));
505 return r;
506 }
507
508 return 0;
509 }
510
4d1cf1e2 511 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m);
74b2466e
LP
512 if (r < 0) {
513 log_error("Failed to register object: %s", strerror(-r));
514 return r;
515 }
516
517 r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
518 if (r < 0) {
519 log_error("Failed to register name: %s", strerror(-r));
520 return r;
521 }
522
523 r = sd_bus_attach_event(m->bus, m->event, 0);
524 if (r < 0) {
525 log_error("Failed to attach bus to event loop: %s", strerror(-r));
526 return r;
527 }
528
529 return 0;
530}