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