]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-bus.c
resolved: DNSKEY records
[thirdparty/systemd.git] / src / resolve / resolved-bus.c
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-dns-domain.h"
26 #include "resolved-bus.h"
27
28 static int reply_query_state(DnsQuery *q) {
29 _cleanup_free_ char *ip = NULL;
30 const char *name;
31 int r;
32
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;
39
40 name = ip;
41 }
42
43 switch (q->state) {
44
45 case DNS_TRANSACTION_NO_SERVERS:
46 return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
47
48 case DNS_TRANSACTION_TIMEOUT:
49 return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out");
50
51 case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED:
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_TRANSACTION_RESOURCES:
55 return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_RESOURCES, "Not enough resources");
56
57 case DNS_TRANSACTION_INVALID_REPLY:
58 return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
59
60 case DNS_TRANSACTION_FAILURE: {
61 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
62
63 if (q->answer_rcode == DNS_RCODE_NXDOMAIN)
64 sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", name);
65 else {
66 const char *rc, *n;
67 char p[3]; /* the rcode is 4 bits long */
68
69 rc = dns_rcode_to_string(q->answer_rcode);
70 if (!rc) {
71 sprintf(p, "%i", q->answer_rcode);
72 rc = p;
73 }
74
75 n = strappenda(_BUS_ERROR_DNS, rc);
76 sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", name, rc);
77 }
78
79 return sd_bus_reply_method_error(q->request, &error);
80 }
81
82 case DNS_TRANSACTION_NULL:
83 case DNS_TRANSACTION_PENDING:
84 case DNS_TRANSACTION_SUCCESS:
85 default:
86 assert_not_reached("Impossible state");
87 }
88 }
89
90 static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifindex) {
91 int r;
92
93 assert(reply);
94 assert(rr);
95
96 r = sd_bus_message_open_container(reply, 'r', "iayi");
97 if (r < 0)
98 return r;
99
100 if (rr->key->type == DNS_TYPE_A) {
101 r = sd_bus_message_append(reply, "i", AF_INET);
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));
106
107 } else if (rr->key->type == DNS_TYPE_AAAA) {
108 r = sd_bus_message_append(reply, "i", AF_INET6);
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));
113 } else
114 return -EAFNOSUPPORT;
115
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
130 static void bus_method_resolve_hostname_complete(DnsQuery *q) {
131 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL, *canonical = NULL;
132 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
133 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
134 unsigned added = 0, i;
135 int r, ifindex;
136
137 assert(q);
138
139 if (q->state != DNS_TRANSACTION_SUCCESS) {
140 r = reply_query_state(q);
141 goto finish;
142 }
143
144 r = sd_bus_message_new_method_return(q->request, &reply);
145 if (r < 0)
146 goto finish;
147
148 r = sd_bus_message_open_container(reply, 'a', "(iayi)");
149 if (r < 0)
150 goto finish;
151
152 ifindex = q->answer_ifindex;
153
154 if (q->answer) {
155 answer = dns_answer_ref(q->answer);
156
157 for (i = 0; i < answer->n_rrs; i++) {
158 r = dns_question_matches_rr(q->question, answer->rrs[i]);
159 if (r < 0)
160 goto finish;
161 if (r == 0) {
162 /* Hmm, if this is not an address record,
163 maybe it's a cname? If so, remember this */
164 r = dns_question_matches_cname(q->question, answer->rrs[i]);
165 if (r < 0)
166 goto finish;
167 if (r > 0)
168 cname = dns_resource_record_ref(answer->rrs[i]);
169
170 continue;
171 }
172
173 r = append_address(reply, answer->rrs[i], ifindex);
174 if (r < 0)
175 goto finish;
176
177 if (!canonical)
178 canonical = dns_resource_record_ref(answer->rrs[i]);
179
180 added ++;
181 }
182 }
183
184 if (added <= 0) {
185 if (!cname) {
186 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);
187 goto finish;
188 }
189
190 /* This has a cname? Then update the query with the
191 * new cname. */
192 r = dns_query_cname_redirect(q, cname->cname.name);
193 if (r < 0) {
194 if (r == -ELOOP)
195 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname);
196 else
197 r = sd_bus_reply_method_errno(q->request, -r, NULL);
198
199 goto finish;
200 }
201
202 /* Before we restart the query, let's see if any of
203 * the RRs we already got already answers our query */
204 for (i = 0; i < answer->n_rrs; i++) {
205 r = dns_question_matches_rr(q->question, answer->rrs[i]);
206 if (r < 0)
207 goto finish;
208 if (r == 0)
209 continue;
210
211 r = append_address(reply, answer->rrs[i], ifindex);
212 if (r < 0)
213 goto finish;
214
215 if (!canonical)
216 canonical = dns_resource_record_ref(answer->rrs[i]);
217
218 added++;
219 }
220
221 /* If we didn't find anything, then let's restart the
222 * query, this time with the cname */
223 if (added <= 0) {
224 r = dns_query_go(q);
225 if (r == -ESRCH) {
226 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
227 goto finish;
228 }
229 if (r < 0) {
230 r = sd_bus_reply_method_errno(q->request, -r, NULL);
231 goto finish;
232 }
233
234 return;
235 }
236 }
237
238 r = sd_bus_message_close_container(reply);
239 if (r < 0)
240 goto finish;
241
242 /* Return the precise spelling and uppercasing reported by the server */
243 assert(canonical);
244 r = sd_bus_message_append(reply, "s", DNS_RESOURCE_KEY_NAME(canonical->key));
245 if (r < 0)
246 goto finish;
247
248 r = sd_bus_send(q->manager->bus, reply, NULL);
249
250 finish:
251 if (r < 0) {
252 log_error("Failed to send hostname reply: %s", strerror(-r));
253 sd_bus_reply_method_errno(q->request, -r, NULL);
254 }
255
256 dns_query_free(q);
257 }
258
259 static int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
260 _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
261 Manager *m = userdata;
262 const char *hostname;
263 int family;
264 DnsQuery *q;
265 int r;
266
267 assert(bus);
268 assert(message);
269 assert(m);
270
271 r = sd_bus_message_read(message, "si", &hostname, &family);
272 if (r < 0)
273 return r;
274
275 if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
276 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
277
278 r = dns_name_normalize(hostname, NULL);
279 if (r < 0)
280 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
281
282 question = dns_question_new(family == AF_UNSPEC ? 2 : 1);
283 if (!question)
284 return -ENOMEM;
285
286 if (family != AF_INET6) {
287 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
288
289 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, hostname);
290 if (!key)
291 return -ENOMEM;
292
293 r = dns_question_add(question, key);
294 if (r < 0)
295 return r;
296 }
297
298 if (family != AF_INET) {
299 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
300
301 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, hostname);
302 if (!key)
303 return -ENOMEM;
304
305 r = dns_question_add(question, key);
306 if (r < 0)
307 return r;
308 }
309
310 r = dns_query_new(m, &q, question);
311 if (r < 0)
312 return r;
313
314 q->request = sd_bus_message_ref(message);
315 q->request_family = family;
316 q->request_hostname = hostname;
317 q->complete = bus_method_resolve_hostname_complete;
318
319 r = dns_query_go(q);
320 if (r < 0) {
321 dns_query_free(q);
322
323 if (r == -ESRCH)
324 sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
325
326 return r;
327 }
328
329 return 1;
330 }
331
332 static void bus_method_resolve_address_complete(DnsQuery *q) {
333 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
334 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
335 unsigned added = 0, i;
336 int r;
337
338 assert(q);
339
340 if (q->state != DNS_TRANSACTION_SUCCESS) {
341 r = reply_query_state(q);
342 goto finish;
343 }
344
345 r = sd_bus_message_new_method_return(q->request, &reply);
346 if (r < 0)
347 goto finish;
348
349 r = sd_bus_message_open_container(reply, 'a', "s");
350 if (r < 0)
351 goto finish;
352
353 if (q->answer) {
354 answer = dns_answer_ref(q->answer);
355
356 for (i = 0; i < answer->n_rrs; i++) {
357 r = dns_question_matches_rr(q->question, answer->rrs[i]);
358 if (r < 0)
359 goto finish;
360 if (r == 0)
361 continue;
362
363 r = sd_bus_message_append(reply, "s", answer->rrs[i]->ptr.name);
364 if (r < 0)
365 goto finish;
366
367 added ++;
368 }
369 }
370
371 if (added <= 0) {
372 _cleanup_free_ char *ip = NULL;
373
374 in_addr_to_string(q->request_family, &q->request_address, &ip);
375
376 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", ip);
377 goto finish;
378 }
379
380 r = sd_bus_message_close_container(reply);
381 if (r < 0)
382 goto finish;
383
384 r = sd_bus_send(q->manager->bus, reply, NULL);
385
386 finish:
387 if (r < 0) {
388 log_error("Failed to send address reply: %s", strerror(-r));
389 sd_bus_reply_method_errno(q->request, -r, NULL);
390 }
391
392 dns_query_free(q);
393 }
394
395 static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
396 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
397 _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
398 _cleanup_free_ char *reverse = NULL;
399 Manager *m = userdata;
400 int family, ifindex;
401 const void *d;
402 DnsQuery *q;
403 size_t sz;
404 int r;
405
406 assert(bus);
407 assert(message);
408 assert(m);
409
410 r = sd_bus_message_read(message, "i", &family);
411 if (r < 0)
412 return r;
413
414 if (!IN_SET(family, AF_INET, AF_INET6))
415 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
416
417 r = sd_bus_message_read_array(message, 'y', &d, &sz);
418 if (r < 0)
419 return r;
420
421 if (sz != FAMILY_ADDRESS_SIZE(family))
422 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
423
424 r = sd_bus_message_read(message, "i", &ifindex);
425 if (r < 0)
426 return r;
427 if (ifindex < 0)
428 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
429
430 r = dns_name_reverse(family, d, &reverse);
431 if (r < 0)
432 return r;
433
434 question = dns_question_new(1);
435 if (!question)
436 return -ENOMEM;
437
438 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
439 if (!key)
440 return -ENOMEM;
441
442 reverse = NULL;
443
444 r = dns_question_add(question, key);
445 if (r < 0)
446 return r;
447
448 r = dns_query_new(m, &q, question);
449 if (r < 0)
450 return r;
451
452 q->request = sd_bus_message_ref(message);
453 q->request_family = family;
454 memcpy(&q->request_address, d, sz);
455 q->complete = bus_method_resolve_address_complete;
456
457 r = dns_query_go(q);
458 if (r < 0) {
459 dns_query_free(q);
460
461 if (r == -ESRCH)
462 sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
463
464 return r;
465 }
466
467 return 1;
468 }
469
470 static void bus_method_resolve_record_complete(DnsQuery *q) {
471 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
472 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
473 unsigned added = 0, i;
474 int r;
475
476 assert(q);
477
478 if (q->state != DNS_TRANSACTION_SUCCESS) {
479 r = reply_query_state(q);
480 goto finish;
481 }
482
483 r = sd_bus_message_new_method_return(q->request, &reply);
484 if (r < 0)
485 goto finish;
486
487 r = sd_bus_message_open_container(reply, 'a', "(qqay)");
488 if (r < 0)
489 goto finish;
490
491 if (q->answer) {
492 answer = dns_answer_ref(q->answer);
493
494 for (i = 0; i < answer->n_rrs; i++) {
495 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
496 size_t start;
497
498 r = dns_question_matches_rr(q->question, answer->rrs[i]);
499 if (r < 0)
500 goto finish;
501 if (r == 0)
502 continue;
503
504 r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
505 if (r < 0)
506 goto finish;
507
508 r = dns_packet_append_rr(p, answer->rrs[i], &start);
509 if (r < 0)
510 goto finish;
511
512 r = sd_bus_message_open_container(reply, 'r', "qqay");
513 if (r < 0)
514 goto finish;
515
516 r = sd_bus_message_append(reply, "qq", answer->rrs[i]->key->class, answer->rrs[i]->key->type);
517 if (r < 0)
518 goto finish;
519
520 r = sd_bus_message_append_array(reply, 'y', DNS_PACKET_DATA(p) + start, p->size - start);
521 if (r < 0)
522 goto finish;
523
524 r = sd_bus_message_close_container(reply);
525 if (r < 0)
526 goto finish;
527
528 added ++;
529 }
530 }
531
532 if (added <= 0) {
533 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", q->request_hostname);
534 goto finish;
535 }
536
537 r = sd_bus_message_close_container(reply);
538 if (r < 0)
539 goto finish;
540
541 r = sd_bus_send(q->manager->bus, reply, NULL);
542
543 finish:
544 if (r < 0) {
545 log_error("Failed to send record reply: %s", strerror(-r));
546 sd_bus_reply_method_errno(q->request, -r, NULL);
547 }
548
549 dns_query_free(q);
550 }
551
552 static int bus_method_resolve_record(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
553 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
554 _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
555 Manager *m = userdata;
556 DnsQuery *q;
557 int r;
558 uint16_t class, type;
559 const char *name;
560
561 assert(bus);
562 assert(message);
563 assert(m);
564
565 r = sd_bus_message_read(message, "sqq", &name, &class, &type);
566 if (r < 0)
567 return r;
568
569 r = dns_name_normalize(name, NULL);
570 if (r < 0)
571 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid name '%s'", name);
572
573 question = dns_question_new(1);
574 if (!question)
575 return -ENOMEM;
576
577 key = dns_resource_key_new(class, type, name);
578 if (!key)
579 return -ENOMEM;
580
581 r = dns_question_add(question, key);
582 if (r < 0)
583 return r;
584
585 r = dns_query_new(m, &q, question);
586 if (r < 0)
587 return r;
588
589 q->request = sd_bus_message_ref(message);
590 q->request_hostname = name;
591 q->complete = bus_method_resolve_record_complete;
592
593 r = dns_query_go(q);
594 if (r < 0) {
595 dns_query_free(q);
596
597 if (r == -ESRCH)
598 sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
599
600 return r;
601 }
602
603 return 1;
604 }
605
606 static const sd_bus_vtable resolve_vtable[] = {
607 SD_BUS_VTABLE_START(0),
608 SD_BUS_METHOD("ResolveHostname", "si", "a(iayi)s", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
609 SD_BUS_METHOD("ResolveAddress", "iayi", "as", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
610 SD_BUS_METHOD("ResolveRecord", "sqq", "a(qqay)", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),
611 SD_BUS_VTABLE_END,
612 };
613
614 static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
615 Manager *m = userdata;
616
617 assert(s);
618 assert(m);
619
620 m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
621
622 manager_connect_bus(m);
623 return 0;
624 }
625
626 int manager_connect_bus(Manager *m) {
627 int r;
628
629 assert(m);
630
631 if (m->bus)
632 return 0;
633
634 r = sd_bus_default_system(&m->bus);
635 if (r < 0) {
636 /* We failed to connect? Yuck, we must be in early
637 * boot. Let's try in 5s again. As soon as we have
638 * kdbus we can stop doing this... */
639
640 log_debug("Failed to connect to bus, trying again in 5s: %s", strerror(-r));
641
642 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);
643 if (r < 0) {
644 log_error("Failed to install bus reconnect time event: %s", strerror(-r));
645 return r;
646 }
647
648 return 0;
649 }
650
651 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m);
652 if (r < 0) {
653 log_error("Failed to register object: %s", strerror(-r));
654 return r;
655 }
656
657 r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
658 if (r < 0) {
659 log_error("Failed to register name: %s", strerror(-r));
660 return r;
661 }
662
663 r = sd_bus_attach_event(m->bus, m->event, 0);
664 if (r < 0) {
665 log_error("Failed to attach bus to event loop: %s", strerror(-r));
666 return r;
667 }
668
669 return 0;
670 }