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