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