chiark / gitweb /
642332790cf8ba58cf4d47f5f2cc9177592d9836
[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                 int rcode;
63
64                 rcode = dns_query_get_rcode(q);
65                 if (rcode < 0)
66                         return rcode;
67
68                 if (rcode == DNS_RCODE_NXDOMAIN)
69                         sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", name);
70                 else {
71                         const char *rc, *n;
72                         char p[3]; /* the rcode is 4 bits long */
73
74                         rc = dns_rcode_to_string(rcode);
75                         if (!rc) {
76                                 sprintf(p, "%i", rcode);
77                                 rc = p;
78                         }
79
80                         n = strappenda(_BUS_ERROR_DNS, rc);
81                         sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", name, rc);
82                 }
83
84                 return sd_bus_reply_method_error(q->request, &error);
85         }
86
87         case DNS_QUERY_NULL:
88         case DNS_QUERY_PENDING:
89         case DNS_QUERY_SUCCESS:
90         default:
91                 assert_not_reached("Impossible state");
92         }
93 }
94
95 static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifindex) {
96         int r;
97
98         assert(reply);
99         assert(rr);
100
101         r = sd_bus_message_open_container(reply, 'r', "yayi");
102         if (r < 0)
103                 return r;
104
105         if (rr->key.type == DNS_TYPE_A) {
106                 r = sd_bus_message_append(reply, "y", AF_INET);
107                 if (r < 0)
108                         return r;
109
110                 r = sd_bus_message_append_array(reply, 'y', &rr->a.in_addr, sizeof(struct in_addr));
111         } else {
112                 r = sd_bus_message_append(reply, "y", AF_INET6);
113                 if (r < 0)
114                         return r;
115
116                 r = sd_bus_message_append_array(reply, 'y', &rr->aaaa.in6_addr, sizeof(struct in6_addr));
117         }
118         if (r < 0)
119                 return r;
120
121         r = sd_bus_message_append(reply, "i", ifindex);
122         if (r < 0)
123                 return r;
124
125         r = sd_bus_message_close_container(reply);
126         if (r < 0)
127                 return r;
128
129         return 0;
130 }
131
132 static void bus_method_resolve_hostname_complete(DnsQuery *q) {
133         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL, *canonical = NULL;
134         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
135         DnsResourceRecord **rrs;
136         unsigned added = 0;
137         int ifindex;
138         int r, n, i;
139
140         assert(q);
141
142         if (q->state != DNS_QUERY_SUCCESS) {
143                 r = reply_query_state(q);
144                 goto finish;
145         }
146
147         n = dns_query_get_rrs(q, &rrs);
148         if (n < 0) {
149                 r = n;
150                 goto parse_fail;
151         }
152
153         r = sd_bus_message_new_method_return(q->request, &reply);
154         if (r < 0)
155                 goto finish;
156
157         r = sd_bus_message_open_container(reply, 'a', "(yayi)");
158         if (r < 0)
159                 goto finish;
160
161         ifindex = dns_query_get_ifindex(q);
162         if (ifindex < 0)
163                 ifindex = 0;
164
165         for (i = 0; i < n; i++) {
166                 r = dns_query_matches_rr(q, rrs[i]);
167                 if (r < 0)
168                         goto parse_fail;
169                 if (r == 0) {
170                         /* Hmm, if this is not an address record,
171                            maybe it's a cname? If so, remember this */
172                         r = dns_query_matches_cname(q, rrs[i]);
173                         if (r < 0)
174                                 goto parse_fail;
175                         if (r > 0)
176                                 cname = dns_resource_record_ref(rrs[i]);
177
178                         continue;
179                 }
180
181                 r = append_address(reply, rrs[i], ifindex);
182                 if (r < 0)
183                         goto finish;
184
185                 if (!canonical)
186                         canonical = dns_resource_record_ref(rrs[i]);
187
188                 added ++;
189         }
190
191         if (added <= 0) {
192                 if (!cname) {
193                         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);
194                         goto finish;
195                 }
196
197                 /* This has a cname? Then update the query with the
198                  * new cname. */
199                 r = dns_query_cname_redirect(q, cname->cname.name);
200                 if (r < 0) {
201                         if (r == -ELOOP)
202                                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname);
203                         else
204                                 r = sd_bus_reply_method_errno(q->request, -r, NULL);
205
206                         goto finish;
207                 }
208
209                 /* Before we restart the query, let's see if any of
210                  * the RRs we already got already answers our query */
211                 for (i = 0; i < n; i++) {
212                         r = dns_query_matches_rr(q, rrs[i]);
213                         if (r < 0)
214                                 goto parse_fail;
215                         if (r == 0)
216                                 continue;
217
218                         r = append_address(reply, rrs[i], ifindex);
219                         if (r < 0)
220                                 goto finish;
221
222                         if (!canonical)
223                                 canonical = dns_resource_record_ref(rrs[i]);
224
225                         added++;
226                 }
227
228                 /* If we didn't find anything, then let's restart the
229                  * query, this time with the cname */
230                 if (added <= 0) {
231                         r = dns_query_go(q);
232                         if (r == -ESRCH) {
233                                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
234                                 goto finish;
235                         }
236                         if (r < 0) {
237                                 r = sd_bus_reply_method_errno(q->request, -r, NULL);
238                                 goto finish;
239                         }
240                         return;
241                 }
242         }
243
244         r = sd_bus_message_close_container(reply);
245         if (r < 0)
246                 goto finish;
247
248         /* Return the precise spelling and uppercasing reported by the server */
249         assert(canonical);
250         r = sd_bus_message_append(reply, "s", canonical->key.name);
251         if (r < 0)
252                 goto finish;
253
254         r = sd_bus_send(q->manager->bus, reply, NULL);
255         goto finish;
256
257 parse_fail:
258         r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
259
260 finish:
261         if (r < 0)
262                 log_error("Failed to send bus reply: %s", strerror(-r));
263
264         dns_query_free(q);
265 }
266
267 static int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
268         Manager *m = userdata;
269         const char *hostname;
270         uint8_t family;
271         DnsResourceKey keys[2];
272         DnsQuery *q;
273         unsigned n = 0;
274         int r;
275
276         assert(bus);
277         assert(message);
278         assert(m);
279
280         r = sd_bus_message_read(message, "sy", &hostname, &family);
281         if (r < 0)
282                 return r;
283
284         if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
285                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %u", family);
286
287         if (!hostname_is_valid(hostname))
288                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
289
290         if (family != AF_INET6) {
291                 keys[n].class = DNS_CLASS_IN;
292                 keys[n].type = DNS_TYPE_A;
293                 keys[n].name = (char*) hostname;
294                 n++;
295         }
296
297         if (family != AF_INET) {
298                 keys[n].class = DNS_CLASS_IN;
299                 keys[n].type = DNS_TYPE_AAAA;
300                 keys[n].name = (char*) hostname;
301                 n++;
302         }
303
304         r = dns_query_new(m, &q, keys, n);
305         if (r < 0)
306                 return r;
307
308         q->request = sd_bus_message_ref(message);
309         q->request_family = family;
310         q->request_hostname = hostname;
311         q->complete = bus_method_resolve_hostname_complete;
312
313         r = dns_query_go(q);
314         if (r < 0) {
315                 dns_query_free(q);
316
317                 if (r == -ESRCH)
318                         sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
319
320                 return r;
321         }
322
323         return 1;
324 }
325
326 static void bus_method_resolve_address_complete(DnsQuery *q) {
327         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
328         DnsResourceRecord **rrs;
329         unsigned added = 0;
330         int r, n, i;
331
332         assert(q);
333
334         if (q->state != DNS_QUERY_SUCCESS) {
335                 r = reply_query_state(q);
336                 goto finish;
337         }
338
339         n = dns_query_get_rrs(q, &rrs);
340         if (n < 0) {
341                 r = n;
342                 goto parse_fail;
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         for (i = 0; i < n; i++) {
354                 r = dns_query_matches_rr(q, rrs[i]);
355                 if (r < 0)
356                         goto parse_fail;
357                 if (r == 0)
358                         continue;
359
360                 r = sd_bus_message_append(reply, "s", rrs[i]->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 any RR of requested 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         _cleanup_free_ char *ip = NULL;
396         Manager *m = userdata;
397         uint8_t family;
398         const void *d;
399         int ifindex;
400         DnsQuery *q;
401         size_t sz;
402         int r;
403
404         assert(bus);
405         assert(message);
406         assert(m);
407
408         r = sd_bus_message_read(message, "y", &family);
409         if (r < 0)
410                 return r;
411
412         if (!IN_SET(family, AF_INET, AF_INET6))
413                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %u", family);
414
415         r = sd_bus_message_read_array(message, 'y', &d, &sz);
416         if (r < 0)
417                 return r;
418
419         if ((family == AF_INET && sz != sizeof(struct in_addr)) ||
420             (family == AF_INET6 && sz != sizeof(struct in6_addr)))
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         key.class = DNS_CLASS_IN;
430         key.type = DNS_TYPE_PTR;
431         r = dns_name_reverse(family, d, &key.name);
432         if (r < 0)
433                 return r;
434
435         r = dns_query_new(m, &q, &key, 1);
436         if (r < 0)
437                 return r;
438
439         q->request = sd_bus_message_ref(message);
440         q->request_family = family;
441         memcpy(&q->request_address, d, sz);
442         q->complete = bus_method_resolve_address_complete;
443
444         r = dns_query_go(q);
445         if (r < 0) {
446                 dns_query_free(q);
447                 return r;
448         }
449
450         return 1;
451 }
452
453 static const sd_bus_vtable resolve_vtable[] = {
454         SD_BUS_VTABLE_START(0),
455         SD_BUS_METHOD("ResolveHostname", "sy", "a(yayi)s", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
456         SD_BUS_METHOD("ResolveAddress", "yayi", "as", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
457         SD_BUS_VTABLE_END,
458 };
459
460 static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
461         Manager *m = userdata;
462
463         assert(s);
464         assert(m);
465
466         m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
467
468         manager_connect_bus(m);
469         return 0;
470 }
471
472 int manager_connect_bus(Manager *m) {
473         int r;
474
475         assert(m);
476
477         if (m->bus)
478                 return 0;
479
480         r = sd_bus_default_system(&m->bus);
481         if (r < 0) {
482                 /* We failed to connect? Yuck, we must be in early
483                  * boot. Let's try in 5s again. As soon as we have
484                  * kdbus we can stop doing this... */
485
486                 log_debug("Failed to connect to bus, trying again in 5s: %s", strerror(-r));
487
488                 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);
489                 if (r < 0) {
490                         log_error("Failed to install bus reconnect time event: %s", strerror(-r));
491                         return r;
492                 }
493
494                 return 0;
495         }
496
497         r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m);
498         if (r < 0) {
499                 log_error("Failed to register object: %s", strerror(-r));
500                 return r;
501         }
502
503         r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
504         if (r < 0) {
505                 log_error("Failed to register name: %s", strerror(-r));
506                 return r;
507         }
508
509         r = sd_bus_attach_event(m->bus, m->event, 0);
510         if (r < 0) {
511                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
512                 return r;
513         }
514
515         return 0;
516 }