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