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