]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-bus.c
resolved: add a DNS client stub resolver
[thirdparty/systemd.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 }