chiark / gitweb /
acdfd5240335b6b5e287e6daa67fc9b650c79924
[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                 assert(q->received);
64
65                 if (DNS_PACKET_RCODE(q->received) == DNS_RCODE_NXDOMAIN)
66                         sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", name);
67                 else {
68                         const char *rc, *n;
69                         char p[3]; /* the rcode is 4 bits long */
70
71                         rc = dns_rcode_to_string(DNS_PACKET_RCODE(q->received));
72                         if (!rc) {
73                                 sprintf(p, "%i", DNS_PACKET_RCODE(q->received));
74                                 rc = p;
75                         }
76
77                         n = strappenda(_BUS_ERROR_DNS, rc);
78                         sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", name, rc);
79                 }
80
81                 return sd_bus_reply_method_error(q->request, &error);
82         }
83
84         case DNS_QUERY_NULL:
85         case DNS_QUERY_PENDING:
86         case DNS_QUERY_SUCCESS:
87         default:
88                 assert_not_reached("Impossible state");
89         }
90 }
91
92 static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifindex) {
93         int r;
94
95         assert(reply);
96         assert(rr);
97
98         r = sd_bus_message_open_container(reply, 'r', "yayi");
99         if (r < 0)
100                 return r;
101
102         if (rr->key.type == DNS_TYPE_A) {
103                 r = sd_bus_message_append(reply, "y", AF_INET);
104                 if (r < 0)
105                         return r;
106
107                 r = sd_bus_message_append_array(reply, 'y', &rr->a.in_addr, sizeof(struct in_addr));
108         } else {
109                 r = sd_bus_message_append(reply, "y", AF_INET6);
110                 if (r < 0)
111                         return r;
112
113                 r = sd_bus_message_append_array(reply, 'y', &rr->aaaa.in6_addr, sizeof(struct in6_addr));
114         }
115         if (r < 0)
116                 return r;
117
118         r = sd_bus_message_append(reply, "i", ifindex);
119         if (r < 0)
120                 return r;
121
122         r = sd_bus_message_close_container(reply);
123         if (r < 0)
124                 return r;
125
126         return 0;
127 }
128
129 static void bus_method_resolve_hostname_complete(DnsQuery *q) {
130         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL, *canonical = NULL;
131         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
132         unsigned i, n, added = 0;
133         size_t answer_rindex;
134         int r;
135
136         assert(q);
137
138         if (q->state != DNS_QUERY_SUCCESS) {
139                 r = reply_query_state(q);
140                 goto finish;
141         }
142
143         assert(q->received);
144
145         r = dns_packet_skip_question(q->received);
146         if (r < 0)
147                 goto parse_fail;
148
149         answer_rindex = q->received->rindex;
150
151         r = sd_bus_message_new_method_return(q->request, &reply);
152         if (r < 0)
153                 goto finish;
154
155         r = sd_bus_message_open_container(reply, 'a', "(yayi)");
156         if (r < 0)
157                 goto finish;
158
159         n = DNS_PACKET_ANCOUNT(q->received) +
160             DNS_PACKET_NSCOUNT(q->received) +
161             DNS_PACKET_ARCOUNT(q->received);
162
163         for (i = 0; i < n; i++) {
164                 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
165
166                 r = dns_packet_read_rr(q->received, &rr, NULL);
167                 if (r < 0)
168                         goto parse_fail;
169
170                 r = dns_query_matches_rr(q, rr);
171                 if (r < 0)
172                         goto parse_fail;
173                 if (r == 0) {
174                         /* Hmm, if this is not an address record,
175                            maybe it's a cname? If so, remember this */
176                         r = dns_query_matches_cname(q, rr);
177                         if (r < 0)
178                                 goto parse_fail;
179                         if (r > 0)
180                                 cname = dns_resource_record_ref(rr);
181
182                         continue;
183                 }
184
185                 r = append_address(reply, rr, q->received->ifindex);
186                 if (r < 0)
187                         goto finish;
188
189                 if (!canonical)
190                         canonical = dns_resource_record_ref(rr);
191
192                 added ++;
193         }
194
195         if (added <= 0) {
196                 if (!cname) {
197                         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);
198                         goto finish;
199                 }
200
201                 /* This has a cname? Then update the query with the
202                  * new cname. */
203                 r = dns_query_follow_cname(q, cname->cname.name);
204                 if (r < 0) {
205                         if (r == -ELOOP)
206                                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname);
207                         else
208                                 r = sd_bus_reply_method_errno(q->request, -r, NULL);
209
210                         goto finish;
211                 }
212
213                 /* Before we restart the query, let's see if any of
214                  * the RRs we already got already answers our query */
215                 dns_packet_rewind(q->received, answer_rindex);
216                 for (i = 0; i < n; i++) {
217                         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
218
219                         r = dns_packet_read_rr(q->received, &rr, NULL);
220                         if (r < 0)
221                                 goto parse_fail;
222
223                         r = dns_query_matches_rr(q, rr);
224                         if (r < 0)
225                                 goto parse_fail;
226                         if (r == 0)
227                                 continue;
228
229                         r = append_address(reply, rr, q->received->ifindex);
230                         if (r < 0)
231                                 goto finish;
232
233                         if (!canonical)
234                                 canonical = dns_resource_record_ref(rr);
235
236                         added++;
237                 }
238
239                 /* If we didn't find anything, then let's restart the
240                  * query, this time with the cname */
241                 if (added <= 0) {
242                         r = dns_query_start(q);
243                         if (r == -ESRCH) {
244                                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
245                                 goto finish;
246                         }
247                         if (r < 0) {
248                                 r = sd_bus_reply_method_errno(q->request, -r, NULL);
249                                 goto finish;
250                         }
251                         return;
252                 }
253         }
254
255         r = sd_bus_message_close_container(reply);
256         if (r < 0)
257                 goto finish;
258
259         /* Return the precise spelling and uppercasing reported by the server */
260         assert(canonical);
261         r = sd_bus_message_append(reply, "s", canonical->key.name);
262         if (r < 0)
263                 goto finish;
264
265         r = sd_bus_send(q->manager->bus, reply, NULL);
266         goto finish;
267
268 parse_fail:
269         r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
270
271 finish:
272         if (r < 0)
273                 log_error("Failed to send bus reply: %s", strerror(-r));
274
275         dns_query_free(q);
276 }
277
278 static int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
279         Manager *m = userdata;
280         const char *hostname;
281         uint8_t family;
282         DnsResourceKey keys[2];
283         DnsQuery *q;
284         unsigned n = 0;
285         int r;
286
287         assert(bus);
288         assert(message);
289         assert(m);
290
291         r = sd_bus_message_read(message, "sy", &hostname, &family);
292         if (r < 0)
293                 return r;
294
295         if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
296                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %u", family);
297
298         if (!hostname_is_valid(hostname))
299                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
300
301         if (family != AF_INET6) {
302                 keys[n].class = DNS_CLASS_IN;
303                 keys[n].type = DNS_TYPE_A;
304                 keys[n].name = (char*) hostname;
305                 n++;
306         }
307
308         if (family != AF_INET) {
309                 keys[n].class = DNS_CLASS_IN;
310                 keys[n].type = DNS_TYPE_AAAA;
311                 keys[n].name = (char*) hostname;
312                 n++;
313         }
314
315         r = dns_query_new(m, &q, keys, n);
316         if (r < 0)
317                 return r;
318
319         q->request = sd_bus_message_ref(message);
320         q->request_family = family;
321         q->request_hostname = hostname;
322         q->complete = bus_method_resolve_hostname_complete;
323
324         r = dns_query_start(q);
325         if (r < 0) {
326                 dns_query_free(q);
327
328                 if (r == -ESRCH)
329                         sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
330
331                 return r;
332         }
333
334         return 1;
335 }
336
337 static void bus_method_resolve_address_complete(DnsQuery *q) {
338         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
339         unsigned i, n, added = 0;
340         int r;
341
342         assert(q);
343
344         if (q->state != DNS_QUERY_SUCCESS) {
345                 r = reply_query_state(q);
346                 goto finish;
347         }
348
349         assert(q->received);
350
351         r = dns_packet_skip_question(q->received);
352         if (r < 0)
353                 goto parse_fail;
354
355         r = sd_bus_message_new_method_return(q->request, &reply);
356         if (r < 0)
357                 goto finish;
358
359         r = sd_bus_message_open_container(reply, 'a', "s");
360         if (r < 0)
361                 goto finish;
362
363         n = DNS_PACKET_ANCOUNT(q->received) +
364             DNS_PACKET_NSCOUNT(q->received) +
365             DNS_PACKET_ARCOUNT(q->received);
366
367         for (i = 0; i < n; i++) {
368                 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
369
370                 r = dns_packet_read_rr(q->received, &rr, NULL);
371                 if (r < 0)
372                         goto parse_fail;
373
374                 r = dns_query_matches_rr(q, rr);
375                 if (r < 0)
376                         goto parse_fail;
377                 if (r == 0)
378                         continue;
379
380                 r = sd_bus_message_append(reply, "s", rr->ptr.name);
381                 if (r < 0)
382                         goto finish;
383
384                 added ++;
385         }
386
387         if (added <= 0) {
388                 _cleanup_free_ char *ip = NULL;
389
390                 in_addr_to_string(q->request_family, &q->request_address, &ip);
391
392                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", ip);
393                 goto finish;
394         }
395
396         r = sd_bus_message_close_container(reply);
397         if (r < 0)
398                 goto finish;
399
400         r = sd_bus_send(q->manager->bus, reply, NULL);
401         goto finish;
402
403 parse_fail:
404         r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
405
406 finish:
407         if (r < 0)
408                 log_error("Failed to send bus reply: %s", strerror(-r));
409
410         dns_query_free(q);
411 }
412
413 static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
414         _cleanup_(dns_resource_key_free) DnsResourceKey key = {};
415         Manager *m = userdata;
416         uint8_t family;
417         const void *d;
418         int ifindex;
419         DnsQuery *q;
420         size_t sz;
421         int r;
422
423         assert(bus);
424         assert(message);
425         assert(m);
426
427         r = sd_bus_message_read(message, "y", &family);
428         if (r < 0)
429                 return r;
430
431         if (!IN_SET(family, AF_INET, AF_INET6))
432                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %u", family);
433
434         r = sd_bus_message_read_array(message, 'y', &d, &sz);
435         if (r < 0)
436                 return r;
437
438         if ((family == AF_INET && sz != sizeof(struct in_addr)) ||
439             (family == AF_INET6 && sz != sizeof(struct in6_addr)))
440                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
441
442         r = sd_bus_message_read(message, "i", &ifindex);
443         if (r < 0)
444                 return r;
445         if (ifindex < 0)
446                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
447
448         key.class = DNS_CLASS_IN;
449         key.type = DNS_TYPE_PTR;
450         r = dns_name_reverse(family, d, &key.name);
451         if (r < 0)
452                 return r;
453
454         r = dns_query_new(m, &q, &key, 1);
455         if (r < 0)
456                 return r;
457
458         q->request = sd_bus_message_ref(message);
459         q->request_family = family;
460         memcpy(&q->request_address, d, sz);
461         q->complete = bus_method_resolve_address_complete;
462
463         r = dns_query_start(q);
464         if (r < 0) {
465                 dns_query_free(q);
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", "sy", "a(yayi)s", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
475         SD_BUS_METHOD("ResolveAddress", "yayi", "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 }