chiark / gitweb /
bfb060d86bcdca5541a1645aad16681d55132ad0
[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.h"
26 #include "resolved-dns-domain.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         if (!hostname_is_valid(hostname))
279                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
280
281         question = dns_question_new(family == AF_UNSPEC ? 2 : 1);
282         if (!question)
283                 return -ENOMEM;
284
285         if (family != AF_INET6) {
286                 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
287
288                 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, hostname);
289                 if (!key)
290                         return -ENOMEM;
291
292                 r = dns_question_add(question, key);
293                 if (r < 0)
294                         return r;
295         }
296
297         if (family != AF_INET) {
298                 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
299
300                 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, hostname);
301                 if (!key)
302                         return -ENOMEM;
303
304                 r = dns_question_add(question, key);
305                 if (r < 0)
306                         return r;
307         }
308
309         r = dns_query_new(m, &q, question);
310         if (r < 0)
311                 return r;
312
313         q->request = sd_bus_message_ref(message);
314         q->request_family = family;
315         q->request_hostname = hostname;
316         q->complete = bus_method_resolve_hostname_complete;
317
318         r = dns_query_go(q);
319         if (r < 0) {
320                 dns_query_free(q);
321
322                 if (r == -ESRCH)
323                         sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
324
325                 return r;
326         }
327
328         return 1;
329 }
330
331 static void bus_method_resolve_address_complete(DnsQuery *q) {
332         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
333         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
334         unsigned added = 0, i;
335         int r;
336
337         assert(q);
338
339         if (q->state != DNS_TRANSACTION_SUCCESS) {
340                 r = reply_query_state(q);
341                 goto finish;
342         }
343
344         r = sd_bus_message_new_method_return(q->request, &reply);
345         if (r < 0)
346                 goto finish;
347
348         r = sd_bus_message_open_container(reply, 'a', "s");
349         if (r < 0)
350                 goto finish;
351
352         if (q->answer) {
353                 answer = dns_answer_ref(q->answer);
354
355                 for (i = 0; i < answer->n_rrs; i++) {
356                         r = dns_question_matches_rr(q->question, answer->rrs[i]);
357                         if (r < 0)
358                                 goto finish;
359                         if (r == 0)
360                                 continue;
361
362                         r = sd_bus_message_append(reply, "s", answer->rrs[i]->ptr.name);
363                         if (r < 0)
364                                 goto finish;
365
366                         added ++;
367                 }
368         }
369
370         if (added <= 0) {
371                 _cleanup_free_ char *ip = NULL;
372
373                 in_addr_to_string(q->request_family, &q->request_address, &ip);
374
375                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", ip);
376                 goto finish;
377         }
378
379         r = sd_bus_message_close_container(reply);
380         if (r < 0)
381                 goto finish;
382
383         r = sd_bus_send(q->manager->bus, reply, NULL);
384
385 finish:
386         if (r < 0) {
387                 log_error("Failed to send address reply: %s", strerror(-r));
388                 sd_bus_reply_method_errno(q->request, -r, NULL);
389         }
390
391         dns_query_free(q);
392 }
393
394 static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
395         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
396         _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
397         _cleanup_free_ char *reverse = NULL;
398         Manager *m = userdata;
399         int family, ifindex;
400         const void *d;
401         DnsQuery *q;
402         size_t sz;
403         int r;
404
405         assert(bus);
406         assert(message);
407         assert(m);
408
409         r = sd_bus_message_read(message, "i", &family);
410         if (r < 0)
411                 return r;
412
413         if (!IN_SET(family, AF_INET, AF_INET6))
414                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
415
416         r = sd_bus_message_read_array(message, 'y', &d, &sz);
417         if (r < 0)
418                 return r;
419
420         if (sz != FAMILY_ADDRESS_SIZE(family))
421                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
422
423         r = sd_bus_message_read(message, "i", &ifindex);
424         if (r < 0)
425                 return r;
426         if (ifindex < 0)
427                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
428
429         r = dns_name_reverse(family, d, &reverse);
430         if (r < 0)
431                 return r;
432
433         question = dns_question_new(1);
434         if (!question)
435                 return -ENOMEM;
436
437         key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
438         if (!key)
439                 return -ENOMEM;
440
441         reverse = NULL;
442
443         r = dns_question_add(question, key);
444         if (r < 0)
445                 return r;
446
447         r = dns_query_new(m, &q, question);
448         if (r < 0)
449                 return r;
450
451         q->request = sd_bus_message_ref(message);
452         q->request_family = family;
453         memcpy(&q->request_address, d, sz);
454         q->complete = bus_method_resolve_address_complete;
455
456         r = dns_query_go(q);
457         if (r < 0) {
458                 dns_query_free(q);
459
460                 if (r == -ESRCH)
461                         sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
462
463                 return r;
464         }
465
466         return 1;
467 }
468
469 static void bus_method_resolve_record_complete(DnsQuery *q) {
470         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
471         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
472         unsigned added = 0, i;
473         int r;
474
475         assert(q);
476
477         if (q->state != DNS_TRANSACTION_SUCCESS) {
478                 r = reply_query_state(q);
479                 goto finish;
480         }
481
482         r = sd_bus_message_new_method_return(q->request, &reply);
483         if (r < 0)
484                 goto finish;
485
486         r = sd_bus_message_open_container(reply, 'a', "(qqay)");
487         if (r < 0)
488                 goto finish;
489
490         if (q->answer) {
491                 answer = dns_answer_ref(q->answer);
492
493                 for (i = 0; i < answer->n_rrs; i++) {
494                         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
495                         size_t start;
496
497                         r = dns_question_matches_rr(q->question, answer->rrs[i]);
498                         if (r < 0)
499                                 goto finish;
500                         if (r == 0)
501                                 continue;
502
503                         r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
504                         if (r < 0)
505                                 goto finish;
506
507                         r = dns_packet_append_rr(p, answer->rrs[i], &start);
508                         if (r < 0)
509                                 goto finish;
510
511                         r = sd_bus_message_open_container(reply, 'r', "qqay");
512                         if (r < 0)
513                                 goto finish;
514
515                         r = sd_bus_message_append(reply, "qq", answer->rrs[i]->key->class, answer->rrs[i]->key->type);
516                         if (r < 0)
517                                 goto finish;
518
519                         r = sd_bus_message_append_array(reply, 'y', DNS_PACKET_DATA(p) + start, p->size - start);
520                         if (r < 0)
521                                 goto finish;
522
523                         r = sd_bus_message_close_container(reply);
524                         if (r < 0)
525                                 goto finish;
526
527                         added ++;
528                 }
529         }
530
531         if (added <= 0) {
532                 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);
533                 goto finish;
534         }
535
536         r = sd_bus_message_close_container(reply);
537         if (r < 0)
538                 goto finish;
539
540         r = sd_bus_send(q->manager->bus, reply, NULL);
541
542 finish:
543         if (r < 0) {
544                 log_error("Failed to send record reply: %s", strerror(-r));
545                 sd_bus_reply_method_errno(q->request, -r, NULL);
546         }
547
548         dns_query_free(q);
549 }
550
551 static int bus_method_resolve_record(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
552         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
553         _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
554         _cleanup_free_ char *reverse = 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         question = dns_question_new(1);
570         if (!question)
571                 return -ENOMEM;
572
573         key = dns_resource_key_new(class, type, name);
574         if (!key)
575                 return -ENOMEM;
576
577         r = dns_question_add(question, key);
578         if (r < 0)
579                 return r;
580
581         r = dns_query_new(m, &q, question);
582         if (r < 0)
583                 return r;
584
585         q->request = sd_bus_message_ref(message);
586         q->request_hostname = name;
587         q->complete = bus_method_resolve_record_complete;
588
589         r = dns_query_go(q);
590         if (r < 0) {
591                 dns_query_free(q);
592
593                 if (r == -ESRCH)
594                         sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
595
596                 return r;
597         }
598
599         return 1;
600 }
601
602 static const sd_bus_vtable resolve_vtable[] = {
603         SD_BUS_VTABLE_START(0),
604         SD_BUS_METHOD("ResolveHostname", "si", "a(iayi)s", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
605         SD_BUS_METHOD("ResolveAddress", "iayi", "as", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
606         SD_BUS_METHOD("ResolveRecord", "sqq", "a(qqay)", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),
607         SD_BUS_VTABLE_END,
608 };
609
610 static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
611         Manager *m = userdata;
612
613         assert(s);
614         assert(m);
615
616         m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
617
618         manager_connect_bus(m);
619         return 0;
620 }
621
622 int manager_connect_bus(Manager *m) {
623         int r;
624
625         assert(m);
626
627         if (m->bus)
628                 return 0;
629
630         r = sd_bus_default_system(&m->bus);
631         if (r < 0) {
632                 /* We failed to connect? Yuck, we must be in early
633                  * boot. Let's try in 5s again. As soon as we have
634                  * kdbus we can stop doing this... */
635
636                 log_debug("Failed to connect to bus, trying again in 5s: %s", strerror(-r));
637
638                 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);
639                 if (r < 0) {
640                         log_error("Failed to install bus reconnect time event: %s", strerror(-r));
641                         return r;
642                 }
643
644                 return 0;
645         }
646
647         r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m);
648         if (r < 0) {
649                 log_error("Failed to register object: %s", strerror(-r));
650                 return r;
651         }
652
653         r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
654         if (r < 0) {
655                 log_error("Failed to register name: %s", strerror(-r));
656                 return r;
657         }
658
659         r = sd_bus_attach_event(m->bus, m->event, 0);
660         if (r < 0) {
661                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
662                 return r;
663         }
664
665         return 0;
666 }