chiark / gitweb /
resolved: add a DNS client stub resolver
[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 void bus_method_resolve_hostname_complete(DnsQuery *q) {
29         int r;
30
31         assert(q);
32
33         switch(q->state) {
34
35         case DNS_QUERY_SKIPPED:
36                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "Not appropriate name servers or networks found");
37                 break;
38
39         case DNS_QUERY_TIMEOUT:
40                 r = sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out");
41                 break;
42
43         case DNS_QUERY_ATTEMPTS_MAX:
44                 r = sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed");
45                 break;
46
47         case DNS_QUERY_FAILURE: {
48                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
49
50                 if (q->rcode == DNS_RCODE_NXDOMAIN)
51                         sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "Hostname %s does not exist", q->request_hostname);
52                 else {
53                         const char *rc, *n;
54                         char p[DECIMAL_STR_MAX(q->rcode)];
55
56                         rc = dns_rcode_to_string(q->rcode);
57                         if (!rc) {
58                                 sprintf(p, "%i", q->rcode);
59                                 rc = p;
60                         }
61
62                         n = strappenda(_BUS_ERROR_DNS, rc);
63
64                         sd_bus_error_setf(&error, n, "Could not resolve hostname %s, server or network returned error %s", q->request_hostname, rc);
65                 }
66
67                 r = sd_bus_reply_method_error(q->request, &error);
68                 break;
69         }
70
71         case DNS_QUERY_SUCCESS: {
72                 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
73                 unsigned i, n, added = 0;
74
75                 assert(q->packet);
76
77                 r = dns_packet_skip_question(q->packet);
78                 if (r < 0)
79                         goto parse_fail;
80
81                 r = sd_bus_message_new_method_return(q->request, &reply);
82                 if (r < 0)
83                         goto finish;
84
85                 r = sd_bus_message_open_container(reply, 'a', "(yayi)");
86                 if (r < 0)
87                         goto finish;
88
89                 n = be16toh(DNS_PACKET_HEADER(q->packet)->ancount) +
90                     be16toh(DNS_PACKET_HEADER(q->packet)->nscount) +
91                     be16toh(DNS_PACKET_HEADER(q->packet)->arcount);
92
93                 for (i = 0; i < n; i++) {
94                         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
95
96                         r = dns_packet_read_rr(q->packet, &rr, NULL);
97                         if (r < 0)
98                                 goto parse_fail;
99
100                         if (rr->key.class != DNS_CLASS_IN)
101                                 continue;
102
103                         if (!(q->request_family != AF_INET6 && rr->key.type == DNS_TYPE_A) &&
104                             !(q->request_family != AF_INET && rr->key.type == DNS_TYPE_AAAA))
105                                 continue;
106
107                         if (!dns_name_equal(rr->key.name, q->request_hostname))
108                                 continue;
109
110                         r = sd_bus_message_open_container(reply, 'r', "yayi");
111                         if (r < 0)
112                                 goto finish;
113
114                         if (rr->key.type == DNS_TYPE_A) {
115                                 r = sd_bus_message_append(reply, "y", AF_INET);
116                                 if (r < 0)
117                                         goto finish;
118
119                                 r = sd_bus_message_append_array(reply, 'y', &rr->a.in_addr, sizeof(struct in_addr));
120                         } else {
121                                 r = sd_bus_message_append(reply, "y", AF_INET6);
122                                 if (r < 0)
123                                         goto finish;
124
125                                 r = sd_bus_message_append_array(reply, 'y', &rr->aaaa.in6_addr, sizeof(struct in6_addr));
126                         }
127                         if (r < 0)
128                                 goto finish;
129
130                         r = sd_bus_message_append(reply, "i", q->packet->ifindex);
131                         if (r < 0)
132                                 goto finish;
133
134                         r = sd_bus_message_close_container(reply);
135                         if (r < 0)
136                                 goto finish;
137
138                         added ++;
139                 }
140
141                 if (added <= 0)
142                         goto parse_fail;
143
144                 r = sd_bus_message_close_container(reply);
145                 if (r < 0)
146                         goto finish;
147
148                 r = sd_bus_send(q->manager->bus, reply, NULL);
149                 break;
150         }
151
152         parse_fail:
153         case DNS_QUERY_INVALID_REPLY:
154                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
155                 break;
156
157         case DNS_QUERY_NULL:
158         case DNS_QUERY_SENT:
159                 assert_not_reached("Unexpected query state");
160         }
161
162 finish:
163         if (r < 0)
164                 log_error("Failed to send bus reply: %s", strerror(-r));
165
166         dns_query_free(q);
167 }
168
169 static int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
170         Manager *m = userdata;
171         const char *hostname;
172         uint8_t family;
173         DnsResourceKey keys[2];
174         DnsQuery *q;
175         unsigned n = 0;
176         int r;
177
178         assert(bus);
179         assert(message);
180         assert(m);
181
182         r = sd_bus_message_read(message, "sy", &hostname, &family);
183         if (r < 0)
184                 return r;
185
186         if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
187                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %u", family);
188
189         if (!hostname_is_valid(hostname))
190                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
191
192         if (family != AF_INET6) {
193                 keys[n].class = DNS_CLASS_IN;
194                 keys[n].type = DNS_TYPE_A;
195                 keys[n].name = (char*) hostname;
196                 n++;
197         }
198
199         if (family != AF_INET) {
200                 keys[n].class = DNS_CLASS_IN;
201                 keys[n].type = DNS_TYPE_AAAA;
202                 keys[n].name = (char*) hostname;
203                 n++;
204         }
205
206         r = dns_query_new(m, &q, keys, n);
207         if (r < 0)
208                 return r;
209
210         q->request = sd_bus_message_ref(message);
211         q->request_family = family;
212         q->request_hostname = hostname;
213         q->complete = bus_method_resolve_hostname_complete;
214
215         r = dns_query_start(q);
216         if (r < 0) {
217                 dns_query_free(q);
218                 return r;
219         }
220
221         return 1;
222 }
223
224 static void bus_method_resolve_address_complete(DnsQuery *q) {
225         _cleanup_free_ char *ip = NULL;
226         int r;
227
228         assert(q);
229
230         in_addr_to_string(q->request_family, &q->request_address, &ip);
231
232         switch(q->state) {
233
234         case DNS_QUERY_SKIPPED:
235                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "Not appropriate name servers or networks found");
236                 break;
237
238         case DNS_QUERY_TIMEOUT:
239                 r = sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out");
240                 break;
241
242         case DNS_QUERY_ATTEMPTS_MAX:
243                 r = sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed");
244                 break;
245
246         case DNS_QUERY_FAILURE: {
247                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
248
249                 if (q->rcode == DNS_RCODE_NXDOMAIN)
250                         sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "No hostname known for address %s ", ip);
251                 else {
252                         const char *rc, *n;
253                         char p[DECIMAL_STR_MAX(q->rcode)];
254
255                         rc = dns_rcode_to_string(q->rcode);
256                         if (!rc) {
257                                 sprintf(p, "%i", q->rcode);
258                                 rc = p;
259                         }
260
261                         n = strappenda(_BUS_ERROR_DNS, rc);
262
263                         sd_bus_error_setf(&error, n, "Could not resolve address %s, server or network returned error %s", ip, rc);
264                 }
265
266                 r = sd_bus_reply_method_error(q->request, &error);
267                 break;
268         }
269
270         case DNS_QUERY_SUCCESS: {
271                 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
272                 unsigned i, n, added = 0;
273                 _cleanup_free_ char *reverse = NULL;
274
275                 assert(q->packet);
276
277                 r = dns_name_reverse(q->request_family, &q->request_address, &reverse);
278                 if (r < 0)
279                         goto finish;
280
281                 r = dns_packet_skip_question(q->packet);
282                 if (r < 0)
283                         goto parse_fail;
284
285                 r = sd_bus_message_new_method_return(q->request, &reply);
286                 if (r < 0)
287                         goto finish;
288
289                 r = sd_bus_message_open_container(reply, 'a', "s");
290                 if (r < 0)
291                         goto finish;
292
293                 n = be16toh(DNS_PACKET_HEADER(q->packet)->ancount) +
294                     be16toh(DNS_PACKET_HEADER(q->packet)->nscount) +
295                     be16toh(DNS_PACKET_HEADER(q->packet)->arcount);
296
297                 for (i = 0; i < n; i++) {
298                         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
299
300                         r = dns_packet_read_rr(q->packet, &rr, NULL);
301                         if (r < 0)
302                                 goto parse_fail;
303
304                         if (rr->key.class != DNS_CLASS_IN)
305                                 continue;
306                         if (rr->key.type != DNS_TYPE_PTR)
307                                 continue;
308                         if (!dns_name_equal(rr->key.name, reverse))
309                                 continue;
310
311                         r = sd_bus_message_append(reply, "s", rr->ptr.name);
312                         if (r < 0)
313                                 goto finish;
314
315                         added ++;
316                 }
317
318                 if (added <= 0)
319                         goto parse_fail;
320
321                 r = sd_bus_message_close_container(reply);
322                 if (r < 0)
323                         goto finish;
324
325                 r = sd_bus_send(q->manager->bus, reply, NULL);
326                 break;
327         }
328
329         parse_fail:
330         case DNS_QUERY_INVALID_REPLY:
331                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
332                 break;
333
334         case DNS_QUERY_NULL:
335         case DNS_QUERY_SENT:
336                 assert_not_reached("Unexpected query state");
337         }
338
339 finish:
340         if (r < 0)
341                 log_error("Failed to send bus reply: %s", strerror(-r));
342
343         dns_query_free(q);
344 }
345
346 static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
347         _cleanup_(dns_resource_key_free) DnsResourceKey key = {};
348         Manager *m = userdata;
349         uint8_t family;
350         const void *d;
351         int ifindex;
352         DnsQuery *q;
353         size_t sz;
354         int r;
355
356         assert(bus);
357         assert(message);
358         assert(m);
359
360         r = sd_bus_message_read(message, "y", &family);
361         if (r < 0)
362                 return r;
363
364         if (!IN_SET(family, AF_INET, AF_INET6))
365                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %u", family);
366
367         r = sd_bus_message_read_array(message, 'y', &d, &sz);
368         if (r < 0)
369                 return r;
370
371         if ((family == AF_INET && sz != sizeof(struct in_addr)) ||
372             (family == AF_INET6 && sz != sizeof(struct in6_addr)))
373                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
374
375         r = sd_bus_message_read(message, "i", &ifindex);
376         if (r < 0)
377                 return r;
378         if (ifindex < 0)
379                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
380
381         key.class = DNS_CLASS_IN;
382         key.type = DNS_TYPE_PTR;
383         r = dns_name_reverse(family, d, &key.name);
384         if (r < 0)
385                 return r;
386
387         r = dns_query_new(m, &q, &key, 1);
388         if (r < 0)
389                 return r;
390
391         q->request = sd_bus_message_ref(message);
392         q->request_family = family;
393         memcpy(&q->request_address, d, sz);
394         q->complete = bus_method_resolve_address_complete;
395
396         r = dns_query_start(q);
397         if (r < 0) {
398                 dns_query_free(q);
399                 return r;
400         }
401
402         return 1;
403 }
404
405 static const sd_bus_vtable resolve_vtable[] = {
406         SD_BUS_VTABLE_START(0),
407         SD_BUS_METHOD("ResolveHostname", "sy", "a(yayi)", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
408         SD_BUS_METHOD("ResolveAddress", "yayi", "as", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
409         SD_BUS_VTABLE_END,
410 };
411
412 static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
413         Manager *m = userdata;
414
415         assert(s);
416         assert(m);
417
418         m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
419
420         manager_connect_bus(m);
421         return 0;
422 }
423
424 int manager_connect_bus(Manager *m) {
425         int r;
426
427         assert(m);
428
429         if (m->bus)
430                 return 0;
431
432         r = sd_bus_default_system(&m->bus);
433         if (r < 0) {
434                 /* We failed to connect? Yuck, we must be in early
435                  * boot. Let's try in 5s again. As soon as we have
436                  * kdbus we can stop doing this... */
437
438                 log_debug("Failed to connect to bus, trying again in 5s: %s", strerror(-r));
439
440                 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);
441                 if (r < 0) {
442                         log_error("Failed to install bus reconnect time event: %s", strerror(-r));
443                         return r;
444                 }
445
446                 return 0;
447         }
448
449         r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1", resolve_vtable, m);
450         if (r < 0) {
451                 log_error("Failed to register object: %s", strerror(-r));
452                 return r;
453         }
454
455         r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
456         if (r < 0) {
457                 log_error("Failed to register name: %s", strerror(-r));
458                 return r;
459         }
460
461         r = sd_bus_attach_event(m->bus, m->event, 0);
462         if (r < 0) {
463                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
464                 return r;
465         }
466
467         return 0;
468 }