chiark / gitweb /
d0c21894e3546b965047311bd4ac02735f256104
[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_QUERY_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_QUERY_TIMEOUT:
49                 return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out");
50
51         case DNS_QUERY_ATTEMPTS_MAX:
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_QUERY_RESOURCES:
55                 return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_RESOURCES, "Not enough resources");
56
57         case DNS_QUERY_INVALID_REPLY:
58                 return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
59
60         case DNS_QUERY_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_QUERY_NULL:
83         case DNS_QUERY_PENDING:
84         case DNS_QUERY_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_QUERY_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 parse_fail;
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 parse_fail;
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 parse_fail;
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                         return;
234                 }
235         }
236
237         r = sd_bus_message_close_container(reply);
238         if (r < 0)
239                 goto finish;
240
241         /* Return the precise spelling and uppercasing reported by the server */
242         assert(canonical);
243         r = sd_bus_message_append(reply, "s", DNS_RESOURCE_KEY_NAME(canonical->key));
244         if (r < 0)
245                 goto finish;
246
247         r = sd_bus_send(q->manager->bus, reply, NULL);
248         goto finish;
249
250 parse_fail:
251         r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
252
253 finish:
254         if (r < 0)
255                 log_error("Failed to send bus reply: %s", strerror(-r));
256
257         dns_query_free(q);
258 }
259
260 static int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
261         _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
262         Manager *m = userdata;
263         const char *hostname;
264         int family;
265         DnsQuery *q;
266         int r;
267
268         assert(bus);
269         assert(message);
270         assert(m);
271
272         r = sd_bus_message_read(message, "si", &hostname, &family);
273         if (r < 0)
274                 return r;
275
276         if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
277                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
278
279         if (!hostname_is_valid(hostname))
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_QUERY_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 parse_fail;
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         goto finish;
386
387 parse_fail:
388         r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
389
390 finish:
391         if (r < 0)
392                 log_error("Failed to send bus reply: %s", strerror(-r));
393
394         dns_query_free(q);
395 }
396
397 static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
398         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
399         _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
400         _cleanup_free_ char *reverse = NULL;
401         Manager *m = userdata;
402         int family, ifindex;
403         const void *d;
404         DnsQuery *q;
405         size_t sz;
406         int r;
407
408         assert(bus);
409         assert(message);
410         assert(m);
411
412         r = sd_bus_message_read(message, "i", &family);
413         if (r < 0)
414                 return r;
415
416         if (!IN_SET(family, AF_INET, AF_INET6))
417                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
418
419         r = sd_bus_message_read_array(message, 'y', &d, &sz);
420         if (r < 0)
421                 return r;
422
423         if (sz != FAMILY_ADDRESS_SIZE(family))
424                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
425
426         r = sd_bus_message_read(message, "i", &ifindex);
427         if (r < 0)
428                 return r;
429         if (ifindex < 0)
430                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
431
432         r = dns_name_reverse(family, d, &reverse);
433         if (r < 0)
434                 return r;
435
436         question = dns_question_new(1);
437         if (!question)
438                 return -ENOMEM;
439
440         key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
441         if (!key)
442                 return -ENOMEM;
443
444         reverse = NULL;
445
446         r = dns_question_add(question, key);
447         if (r < 0)
448                 return r;
449
450         r = dns_query_new(m, &q, question);
451         if (r < 0)
452                 return r;
453
454         q->request = sd_bus_message_ref(message);
455         q->request_family = family;
456         memcpy(&q->request_address, d, sz);
457         q->complete = bus_method_resolve_address_complete;
458
459         r = dns_query_go(q);
460         if (r < 0) {
461                 dns_query_free(q);
462
463                 if (r == -ESRCH)
464                         sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
465
466                 return r;
467         }
468
469         return 1;
470 }
471
472 static const sd_bus_vtable resolve_vtable[] = {
473         SD_BUS_VTABLE_START(0),
474         SD_BUS_METHOD("ResolveHostname", "si", "a(iayi)s", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
475         SD_BUS_METHOD("ResolveAddress", "iayi", "as", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
476         SD_BUS_VTABLE_END,
477 };
478
479 static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
480         Manager *m = userdata;
481
482         assert(s);
483         assert(m);
484
485         m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
486
487         manager_connect_bus(m);
488         return 0;
489 }
490
491 int manager_connect_bus(Manager *m) {
492         int r;
493
494         assert(m);
495
496         if (m->bus)
497                 return 0;
498
499         r = sd_bus_default_system(&m->bus);
500         if (r < 0) {
501                 /* We failed to connect? Yuck, we must be in early
502                  * boot. Let's try in 5s again. As soon as we have
503                  * kdbus we can stop doing this... */
504
505                 log_debug("Failed to connect to bus, trying again in 5s: %s", strerror(-r));
506
507                 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);
508                 if (r < 0) {
509                         log_error("Failed to install bus reconnect time event: %s", strerror(-r));
510                         return r;
511                 }
512
513                 return 0;
514         }
515
516         r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m);
517         if (r < 0) {
518                 log_error("Failed to register object: %s", strerror(-r));
519                 return r;
520         }
521
522         r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
523         if (r < 0) {
524                 log_error("Failed to register name: %s", strerror(-r));
525                 return r;
526         }
527
528         r = sd_bus_attach_event(m->bus, m->event, 0);
529         if (r < 0) {
530                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
531                 return r;
532         }
533
534         return 0;
535 }