]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-bus.c
resolved: fix bus signatures to follow family as int change
[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 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', "iayi");
102 if (r < 0)
103 return r;
104
105 if (rr->key.type == DNS_TYPE_A) {
106 r = sd_bus_message_append(reply, "i", 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, "i", 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', "(iayi)");
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 int 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, "si", &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 %i", 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 int 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, "i", &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 %i", family);
409
410 r = sd_bus_message_read_array(message, 'y', &d, &sz);
411 if (r < 0)
412 return r;
413
414 if (sz != FAMILY_ADDRESS_SIZE(family))
415 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
416
417 r = sd_bus_message_read(message, "i", &ifindex);
418 if (r < 0)
419 return r;
420 if (ifindex < 0)
421 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
422
423 key.class = DNS_CLASS_IN;
424 key.type = DNS_TYPE_PTR;
425 r = dns_name_reverse(family, d, &key.name);
426 if (r < 0)
427 return r;
428
429 r = dns_query_new(m, &q, &key, 1);
430 if (r < 0)
431 return r;
432
433 q->request = sd_bus_message_ref(message);
434 q->request_family = family;
435 memcpy(&q->request_address, d, sz);
436 q->complete = bus_method_resolve_address_complete;
437
438 r = dns_query_go(q);
439 if (r < 0) {
440 dns_query_free(q);
441 return r;
442 }
443
444 return 1;
445 }
446
447 static const sd_bus_vtable resolve_vtable[] = {
448 SD_BUS_VTABLE_START(0),
449 SD_BUS_METHOD("ResolveHostname", "si", "a(iayi)s", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
450 SD_BUS_METHOD("ResolveAddress", "iayi", "as", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
451 SD_BUS_VTABLE_END,
452 };
453
454 static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
455 Manager *m = userdata;
456
457 assert(s);
458 assert(m);
459
460 m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
461
462 manager_connect_bus(m);
463 return 0;
464 }
465
466 int manager_connect_bus(Manager *m) {
467 int r;
468
469 assert(m);
470
471 if (m->bus)
472 return 0;
473
474 r = sd_bus_default_system(&m->bus);
475 if (r < 0) {
476 /* We failed to connect? Yuck, we must be in early
477 * boot. Let's try in 5s again. As soon as we have
478 * kdbus we can stop doing this... */
479
480 log_debug("Failed to connect to bus, trying again in 5s: %s", strerror(-r));
481
482 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);
483 if (r < 0) {
484 log_error("Failed to install bus reconnect time event: %s", strerror(-r));
485 return r;
486 }
487
488 return 0;
489 }
490
491 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m);
492 if (r < 0) {
493 log_error("Failed to register object: %s", strerror(-r));
494 return r;
495 }
496
497 r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
498 if (r < 0) {
499 log_error("Failed to register name: %s", strerror(-r));
500 return r;
501 }
502
503 r = sd_bus_attach_event(m->bus, m->event, 0);
504 if (r < 0) {
505 log_error("Failed to attach bus to event loop: %s", strerror(-r));
506 return r;
507 }
508
509 return 0;
510 }