chiark / gitweb /
resolved: implement negative caching
[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
63                 if (q->answer_rcode == DNS_RCODE_NXDOMAIN)
64                         sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", name);
65                 else {
66                         const char *rc, *n;
67                         char p[3]; /* the rcode is 4 bits long */
68
69                         rc = dns_rcode_to_string(q->answer_rcode);
70                         if (!rc) {
71                                 sprintf(p, "%i", q->answer_rcode);
72                                 rc = p;
73                         }
74
75                         n = strappenda(_BUS_ERROR_DNS, rc);
76                         sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", name, rc);
77                 }
78
79                 return sd_bus_reply_method_error(q->request, &error);
80         }
81
82         case DNS_QUERY_NULL:
83         case DNS_QUERY_PENDING:
84         case DNS_QUERY_SUCCESS:
85         default:
86                 assert_not_reached("Impossible state");
87         }
88 }
89
90 static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifindex) {
91         int r;
92
93         assert(reply);
94         assert(rr);
95
96         r = sd_bus_message_open_container(reply, 'r', "iayi");
97         if (r < 0)
98                 return r;
99
100         if (rr->key->type == DNS_TYPE_A) {
101                 r = sd_bus_message_append(reply, "i", AF_INET);
102                 if (r < 0)
103                         return r;
104
105                 r = sd_bus_message_append_array(reply, 'y', &rr->a.in_addr, sizeof(struct in_addr));
106
107         } else if (rr->key->type == DNS_TYPE_AAAA) {
108                 r = sd_bus_message_append(reply, "i", AF_INET6);
109                 if (r < 0)
110                         return r;
111
112                 r = sd_bus_message_append_array(reply, 'y', &rr->aaaa.in6_addr, sizeof(struct in6_addr));
113         } else
114                 return -EAFNOSUPPORT;
115
116         if (r < 0)
117                 return r;
118
119         r = sd_bus_message_append(reply, "i", ifindex);
120         if (r < 0)
121                 return r;
122
123         r = sd_bus_message_close_container(reply);
124         if (r < 0)
125                 return r;
126
127         return 0;
128 }
129
130 static void bus_method_resolve_hostname_complete(DnsQuery *q) {
131         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL, *canonical = NULL;
132         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
133         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
134         unsigned added = 0, i;
135         int r, ifindex;
136
137         assert(q);
138
139         if (q->state != DNS_QUERY_SUCCESS) {
140                 r = reply_query_state(q);
141                 goto finish;
142         }
143
144         r = sd_bus_message_new_method_return(q->request, &reply);
145         if (r < 0)
146                 goto finish;
147
148         r = sd_bus_message_open_container(reply, 'a', "(iayi)");
149         if (r < 0)
150                 goto finish;
151
152         answer = dns_answer_ref(q->answer);
153         ifindex = q->answer_ifindex;
154
155         for (i = 0; i < answer->n_rrs; i++) {
156                 r = dns_question_matches_rr(q->question, answer->rrs[i]);
157                 if (r < 0)
158                         goto parse_fail;
159                 if (r == 0) {
160                         /* Hmm, if this is not an address record,
161                            maybe it's a cname? If so, remember this */
162                         r = dns_question_matches_cname(q->question, answer->rrs[i]);
163                         if (r < 0)
164                                 goto parse_fail;
165                         if (r > 0)
166                                 cname = dns_resource_record_ref(answer->rrs[i]);
167
168                         continue;
169                 }
170
171                 r = append_address(reply, answer->rrs[i], ifindex);
172                 if (r < 0)
173                         goto finish;
174
175                 if (!canonical)
176                         canonical = dns_resource_record_ref(answer->rrs[i]);
177
178                 added ++;
179         }
180
181         if (added <= 0) {
182                 if (!cname) {
183                         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);
184                         goto finish;
185                 }
186
187                 /* This has a cname? Then update the query with the
188                  * new cname. */
189                 r = dns_query_cname_redirect(q, cname->cname.name);
190                 if (r < 0) {
191                         if (r == -ELOOP)
192                                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname);
193                         else
194                                 r = sd_bus_reply_method_errno(q->request, -r, NULL);
195
196                         goto finish;
197                 }
198
199                 /* Before we restart the query, let's see if any of
200                  * the RRs we already got already answers our query */
201                 for (i = 0; i < answer->n_rrs; i++) {
202                         r = dns_question_matches_rr(q->question, answer->rrs[i]);
203                         if (r < 0)
204                                 goto parse_fail;
205                         if (r == 0)
206                                 continue;
207
208                         r = append_address(reply, answer->rrs[i], ifindex);
209                         if (r < 0)
210                                 goto finish;
211
212                         if (!canonical)
213                                 canonical = dns_resource_record_ref(answer->rrs[i]);
214
215                         added++;
216                 }
217
218                 /* If we didn't find anything, then let's restart the
219                  * query, this time with the cname */
220                 if (added <= 0) {
221                         r = dns_query_go(q);
222                         if (r == -ESRCH) {
223                                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
224                                 goto finish;
225                         }
226                         if (r < 0) {
227                                 r = sd_bus_reply_method_errno(q->request, -r, NULL);
228                                 goto finish;
229                         }
230                         return;
231                 }
232         }
233
234         r = sd_bus_message_close_container(reply);
235         if (r < 0)
236                 goto finish;
237
238         /* Return the precise spelling and uppercasing reported by the server */
239         assert(canonical);
240         r = sd_bus_message_append(reply, "s", DNS_RESOURCE_KEY_NAME(canonical->key));
241         if (r < 0)
242                 goto finish;
243
244         r = sd_bus_send(q->manager->bus, reply, NULL);
245         goto finish;
246
247 parse_fail:
248         r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
249
250 finish:
251         if (r < 0)
252                 log_error("Failed to send bus reply: %s", strerror(-r));
253
254         dns_query_free(q);
255 }
256
257 static int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
258         _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
259         Manager *m = userdata;
260         const char *hostname;
261         int family;
262         DnsQuery *q;
263         int r;
264
265         assert(bus);
266         assert(message);
267         assert(m);
268
269         r = sd_bus_message_read(message, "si", &hostname, &family);
270         if (r < 0)
271                 return r;
272
273         if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
274                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
275
276         if (!hostname_is_valid(hostname))
277                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
278
279         question = dns_question_new(family == AF_UNSPEC ? 2 : 1);
280         if (!question)
281                 return -ENOMEM;
282
283         if (family != AF_INET6) {
284                 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
285
286                 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, hostname);
287                 if (!key)
288                         return -ENOMEM;
289
290                 r = dns_question_add(question, key);
291                 if (r < 0)
292                         return r;
293         }
294
295         if (family != AF_INET) {
296                 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
297
298                 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, hostname);
299                 if (!key)
300                         return -ENOMEM;
301
302                 r = dns_question_add(question, key);
303                 if (r < 0)
304                         return r;
305         }
306
307         r = dns_query_new(m, &q, question);
308         if (r < 0)
309                 return r;
310
311         q->request = sd_bus_message_ref(message);
312         q->request_family = family;
313         q->request_hostname = hostname;
314         q->complete = bus_method_resolve_hostname_complete;
315
316         r = dns_query_go(q);
317         if (r < 0) {
318                 dns_query_free(q);
319
320                 if (r == -ESRCH)
321                         sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
322
323                 return r;
324         }
325
326         return 1;
327 }
328
329 static void bus_method_resolve_address_complete(DnsQuery *q) {
330         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
331         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
332         unsigned added = 0, i;
333         int r;
334
335         assert(q);
336
337         if (q->state != DNS_QUERY_SUCCESS) {
338                 r = reply_query_state(q);
339                 goto finish;
340         }
341
342         r = sd_bus_message_new_method_return(q->request, &reply);
343         if (r < 0)
344                 goto finish;
345
346         r = sd_bus_message_open_container(reply, 'a', "s");
347         if (r < 0)
348                 goto finish;
349
350         answer = dns_answer_ref(q->answer);
351
352         for (i = 0; i < answer->n_rrs; i++) {
353                 r = dns_question_matches_rr(q->question, answer->rrs[i]);
354                 if (r < 0)
355                         goto parse_fail;
356                 if (r == 0)
357                         continue;
358
359                 r = sd_bus_message_append(reply, "s", answer->rrs[i]->ptr.name);
360                 if (r < 0)
361                         goto finish;
362
363                 added ++;
364         }
365
366         if (added <= 0) {
367                 _cleanup_free_ char *ip = NULL;
368
369                 in_addr_to_string(q->request_family, &q->request_address, &ip);
370
371                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", ip);
372                 goto finish;
373         }
374
375         r = sd_bus_message_close_container(reply);
376         if (r < 0)
377                 goto finish;
378
379         r = sd_bus_send(q->manager->bus, reply, NULL);
380         goto finish;
381
382 parse_fail:
383         r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
384
385 finish:
386         if (r < 0)
387                 log_error("Failed to send bus reply: %s", strerror(-r));
388
389         dns_query_free(q);
390 }
391
392 static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
393         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
394         _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
395         _cleanup_free_ char *reverse = NULL;
396         Manager *m = userdata;
397         int family, ifindex;
398         const void *d;
399         DnsQuery *q;
400         size_t sz;
401         int r;
402
403         assert(bus);
404         assert(message);
405         assert(m);
406
407         r = sd_bus_message_read(message, "i", &family);
408         if (r < 0)
409                 return r;
410
411         if (!IN_SET(family, AF_INET, AF_INET6))
412                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
413
414         r = sd_bus_message_read_array(message, 'y', &d, &sz);
415         if (r < 0)
416                 return r;
417
418         if (sz != FAMILY_ADDRESS_SIZE(family))
419                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
420
421         r = sd_bus_message_read(message, "i", &ifindex);
422         if (r < 0)
423                 return r;
424         if (ifindex < 0)
425                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
426
427         r = dns_name_reverse(family, d, &reverse);
428         if (r < 0)
429                 return r;
430
431         question = dns_question_new(1);
432         if (!question)
433                 return -ENOMEM;
434
435         key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
436         if (!key)
437                 return -ENOMEM;
438
439         reverse = NULL;
440
441         r = dns_question_add(question, key);
442         if (r < 0)
443                 return r;
444
445         r = dns_query_new(m, &q, question);
446         if (r < 0)
447                 return r;
448
449         q->request = sd_bus_message_ref(message);
450         q->request_family = family;
451         memcpy(&q->request_address, d, sz);
452         q->complete = bus_method_resolve_address_complete;
453
454         r = dns_query_go(q);
455         if (r < 0) {
456                 dns_query_free(q);
457
458                 if (r == -ESRCH)
459                         sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
460
461                 return r;
462         }
463
464         return 1;
465 }
466
467 static const sd_bus_vtable resolve_vtable[] = {
468         SD_BUS_VTABLE_START(0),
469         SD_BUS_METHOD("ResolveHostname", "si", "a(iayi)s", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
470         SD_BUS_METHOD("ResolveAddress", "iayi", "as", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
471         SD_BUS_VTABLE_END,
472 };
473
474 static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
475         Manager *m = userdata;
476
477         assert(s);
478         assert(m);
479
480         m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
481
482         manager_connect_bus(m);
483         return 0;
484 }
485
486 int manager_connect_bus(Manager *m) {
487         int r;
488
489         assert(m);
490
491         if (m->bus)
492                 return 0;
493
494         r = sd_bus_default_system(&m->bus);
495         if (r < 0) {
496                 /* We failed to connect? Yuck, we must be in early
497                  * boot. Let's try in 5s again. As soon as we have
498                  * kdbus we can stop doing this... */
499
500                 log_debug("Failed to connect to bus, trying again in 5s: %s", strerror(-r));
501
502                 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);
503                 if (r < 0) {
504                         log_error("Failed to install bus reconnect time event: %s", strerror(-r));
505                         return r;
506                 }
507
508                 return 0;
509         }
510
511         r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m);
512         if (r < 0) {
513                 log_error("Failed to register object: %s", strerror(-r));
514                 return r;
515         }
516
517         r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
518         if (r < 0) {
519                 log_error("Failed to register name: %s", strerror(-r));
520                 return r;
521         }
522
523         r = sd_bus_attach_event(m->bus, m->event, 0);
524         if (r < 0) {
525                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
526                 return r;
527         }
528
529         return 0;
530 }