chiark / gitweb /
ebb97d7fd34e487a2b327fe9fa7b576df5a9be08
[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, "Not appropriate name servers or networks 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;
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                 added ++;
190         }
191
192         if (added <= 0) {
193                 if (!cname) {
194                         r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have RR of this type", q->request_hostname);
195                         goto finish;
196                 }
197
198                 /* This has a cname? Then update the query with the
199                  * new cname. */
200                 r = dns_query_follow_cname(q, cname->cname.name);
201                 if (r < 0) {
202                         if (r == -ELOOP)
203                                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname);
204                         else
205                                 r = sd_bus_reply_method_errno(q->request, -r, NULL);
206
207                         goto finish;
208                 }
209
210                 /* Before we restart the query, let's see if any of
211                  * the RRs we already got already answers our query */
212                 dns_packet_rewind(q->received, answer_rindex);
213                 for (i = 0; i < n; i++) {
214                         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
215
216                         r = dns_packet_read_rr(q->received, &rr, NULL);
217                         if (r < 0)
218                                 goto parse_fail;
219
220                         r = dns_query_matches_rr(q, rr);
221                         if (r < 0)
222                                 goto parse_fail;
223                         if (r == 0)
224                                 continue;
225
226                         r = append_address(reply, rr, q->received->ifindex);
227                         if (r < 0)
228                                 goto finish;
229
230                         added++;
231                 }
232
233                 /* If we didn't find anything, then let's restart the
234                  * query, this time with the cname */
235                 if (added <= 0) {
236                         r = dns_query_start(q);
237                         if (r < 0) {
238                                 r = sd_bus_reply_method_errno(q->request, -r, NULL);
239                                 goto finish;
240                         }
241                         return;
242                 }
243         }
244
245         r = sd_bus_message_close_container(reply);
246         if (r < 0)
247                 goto finish;
248
249         r = sd_bus_send(q->manager->bus, reply, NULL);
250         goto finish;
251
252 parse_fail:
253         r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
254
255 finish:
256         if (r < 0)
257                 log_error("Failed to send bus reply: %s", strerror(-r));
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         Manager *m = userdata;
264         const char *hostname;
265         uint8_t family;
266         DnsResourceKey keys[2];
267         DnsQuery *q;
268         unsigned n = 0;
269         int r;
270
271         assert(bus);
272         assert(message);
273         assert(m);
274
275         r = sd_bus_message_read(message, "sy", &hostname, &family);
276         if (r < 0)
277                 return r;
278
279         if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
280                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %u", family);
281
282         if (!hostname_is_valid(hostname))
283                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
284
285         if (family != AF_INET6) {
286                 keys[n].class = DNS_CLASS_IN;
287                 keys[n].type = DNS_TYPE_A;
288                 keys[n].name = (char*) hostname;
289                 n++;
290         }
291
292         if (family != AF_INET) {
293                 keys[n].class = DNS_CLASS_IN;
294                 keys[n].type = DNS_TYPE_AAAA;
295                 keys[n].name = (char*) hostname;
296                 n++;
297         }
298
299         r = dns_query_new(m, &q, keys, n);
300         if (r < 0)
301                 return r;
302
303         q->request = sd_bus_message_ref(message);
304         q->request_family = family;
305         q->request_hostname = hostname;
306         q->complete = bus_method_resolve_hostname_complete;
307
308         r = dns_query_start(q);
309         if (r < 0) {
310                 dns_query_free(q);
311                 return r;
312         }
313
314         return 1;
315 }
316
317 static void bus_method_resolve_address_complete(DnsQuery *q) {
318         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
319         unsigned i, n, added = 0;
320         int r;
321
322         assert(q);
323
324         if (q->state != DNS_QUERY_SUCCESS) {
325                 r = reply_query_state(q);
326                 goto finish;
327         }
328
329         assert(q->received);
330
331         r = dns_packet_skip_question(q->received);
332         if (r < 0)
333                 goto parse_fail;
334
335         r = sd_bus_message_new_method_return(q->request, &reply);
336         if (r < 0)
337                 goto finish;
338
339         r = sd_bus_message_open_container(reply, 'a', "s");
340         if (r < 0)
341                 goto finish;
342
343         n = DNS_PACKET_ANCOUNT(q->received) +
344             DNS_PACKET_NSCOUNT(q->received) +
345             DNS_PACKET_ARCOUNT(q->received);
346
347         for (i = 0; i < n; i++) {
348                 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
349
350                 r = dns_packet_read_rr(q->received, &rr, NULL);
351                 if (r < 0)
352                         goto parse_fail;
353
354                 r = dns_query_matches_rr(q, rr);
355                 if (r < 0)
356                         goto parse_fail;
357                 if (r == 0)
358                         continue;
359
360                 r = sd_bus_message_append(reply, "s", rr->ptr.name);
361                 if (r < 0)
362                         goto finish;
363
364                 added ++;
365         }
366
367         if (added <= 0) {
368                 _cleanup_free_ char *ip = NULL;
369
370                 in_addr_to_string(q->request_family, &q->request_address, &ip);
371
372                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have RR of this type", ip);
373                 goto finish;
374         }
375
376         r = sd_bus_message_close_container(reply);
377         if (r < 0)
378                 goto finish;
379
380         r = sd_bus_send(q->manager->bus, reply, NULL);
381         goto finish;
382
383 parse_fail:
384         r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
385
386 finish:
387         if (r < 0)
388                 log_error("Failed to send bus reply: %s", strerror(-r));
389
390         dns_query_free(q);
391 }
392
393 static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
394         _cleanup_(dns_resource_key_free) DnsResourceKey key = {};
395         Manager *m = userdata;
396         uint8_t family;
397         const void *d;
398         int ifindex;
399         DnsQuery *q;
400         size_t sz;
401         int r;
402
403         assert(bus);
404         assert(message);
405         assert(m);
406
407         r = sd_bus_message_read(message, "y", &family);
408         if (r < 0)
409                 return r;
410
411         if (!IN_SET(family, AF_INET, AF_INET6))
412                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %u", family);
413
414         r = sd_bus_message_read_array(message, 'y', &d, &sz);
415         if (r < 0)
416                 return r;
417
418         if ((family == AF_INET && sz != sizeof(struct in_addr)) ||
419             (family == AF_INET6 && sz != sizeof(struct in6_addr)))
420                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
421
422         r = sd_bus_message_read(message, "i", &ifindex);
423         if (r < 0)
424                 return r;
425         if (ifindex < 0)
426                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
427
428         key.class = DNS_CLASS_IN;
429         key.type = DNS_TYPE_PTR;
430         r = dns_name_reverse(family, d, &key.name);
431         if (r < 0)
432                 return r;
433
434         r = dns_query_new(m, &q, &key, 1);
435         if (r < 0)
436                 return r;
437
438         q->request = sd_bus_message_ref(message);
439         q->request_family = family;
440         memcpy(&q->request_address, d, sz);
441         q->complete = bus_method_resolve_address_complete;
442
443         r = dns_query_start(q);
444         if (r < 0) {
445                 dns_query_free(q);
446                 return r;
447         }
448
449         return 1;
450 }
451
452 static const sd_bus_vtable resolve_vtable[] = {
453         SD_BUS_VTABLE_START(0),
454         SD_BUS_METHOD("ResolveHostname", "sy", "a(yayi)", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
455         SD_BUS_METHOD("ResolveAddress", "yayi", "as", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
456         SD_BUS_VTABLE_END,
457 };
458
459 static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
460         Manager *m = userdata;
461
462         assert(s);
463         assert(m);
464
465         m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
466
467         manager_connect_bus(m);
468         return 0;
469 }
470
471 int manager_connect_bus(Manager *m) {
472         int r;
473
474         assert(m);
475
476         if (m->bus)
477                 return 0;
478
479         r = sd_bus_default_system(&m->bus);
480         if (r < 0) {
481                 /* We failed to connect? Yuck, we must be in early
482                  * boot. Let's try in 5s again. As soon as we have
483                  * kdbus we can stop doing this... */
484
485                 log_debug("Failed to connect to bus, trying again in 5s: %s", strerror(-r));
486
487                 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);
488                 if (r < 0) {
489                         log_error("Failed to install bus reconnect time event: %s", strerror(-r));
490                         return r;
491                 }
492
493                 return 0;
494         }
495
496         r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m);
497         if (r < 0) {
498                 log_error("Failed to register object: %s", strerror(-r));
499                 return r;
500         }
501
502         r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
503         if (r < 0) {
504                 log_error("Failed to register name: %s", strerror(-r));
505                 return r;
506         }
507
508         r = sd_bus_attach_event(m->bus, m->event, 0);
509         if (r < 0) {
510                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
511                 return r;
512         }
513
514         return 0;
515 }