chiark / gitweb /
resolved: RRSIG records
[elogind.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 }