chiark / gitweb /
resolved: accept UTF-8 hostnames from bus clients
[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_TRANSACTION_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_TRANSACTION_TIMEOUT:
49                 return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out");
50
51         case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED:
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_TRANSACTION_RESOURCES:
55                 return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_RESOURCES, "Not enough resources");
56
57         case DNS_TRANSACTION_INVALID_REPLY:
58                 return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
59
60         case DNS_TRANSACTION_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_TRANSACTION_NULL:
83         case DNS_TRANSACTION_PENDING:
84         case DNS_TRANSACTION_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_TRANSACTION_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         ifindex = q->answer_ifindex;
153
154         if (q->answer) {
155                 answer = dns_answer_ref(q->answer);
156
157                 for (i = 0; i < answer->n_rrs; i++) {
158                         r = dns_question_matches_rr(q->question, answer->rrs[i]);
159                         if (r < 0)
160                                 goto finish;
161                         if (r == 0) {
162                                 /* Hmm, if this is not an address record,
163                                    maybe it's a cname? If so, remember this */
164                                 r = dns_question_matches_cname(q->question, answer->rrs[i]);
165                                 if (r < 0)
166                                         goto finish;
167                                 if (r > 0)
168                                         cname = dns_resource_record_ref(answer->rrs[i]);
169
170                                 continue;
171                         }
172
173                         r = append_address(reply, answer->rrs[i], ifindex);
174                         if (r < 0)
175                                 goto finish;
176
177                         if (!canonical)
178                                 canonical = dns_resource_record_ref(answer->rrs[i]);
179
180                         added ++;
181                 }
182         }
183
184         if (added <= 0) {
185                 if (!cname) {
186                         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);
187                         goto finish;
188                 }
189
190                 /* This has a cname? Then update the query with the
191                  * new cname. */
192                 r = dns_query_cname_redirect(q, cname->cname.name);
193                 if (r < 0) {
194                         if (r == -ELOOP)
195                                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname);
196                         else
197                                 r = sd_bus_reply_method_errno(q->request, -r, NULL);
198
199                         goto finish;
200                 }
201
202                 /* Before we restart the query, let's see if any of
203                  * the RRs we already got already answers our query */
204                 for (i = 0; i < answer->n_rrs; i++) {
205                         r = dns_question_matches_rr(q->question, answer->rrs[i]);
206                         if (r < 0)
207                                 goto finish;
208                         if (r == 0)
209                                 continue;
210
211                         r = append_address(reply, answer->rrs[i], ifindex);
212                         if (r < 0)
213                                 goto finish;
214
215                         if (!canonical)
216                                 canonical = dns_resource_record_ref(answer->rrs[i]);
217
218                         added++;
219                 }
220
221                 /* If we didn't find anything, then let's restart the
222                  * query, this time with the cname */
223                 if (added <= 0) {
224                         r = dns_query_go(q);
225                         if (r == -ESRCH) {
226                                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
227                                 goto finish;
228                         }
229                         if (r < 0) {
230                                 r = sd_bus_reply_method_errno(q->request, -r, NULL);
231                                 goto finish;
232                         }
233
234                         return;
235                 }
236         }
237
238         r = sd_bus_message_close_container(reply);
239         if (r < 0)
240                 goto finish;
241
242         /* Return the precise spelling and uppercasing reported by the server */
243         assert(canonical);
244         r = sd_bus_message_append(reply, "s", DNS_RESOURCE_KEY_NAME(canonical->key));
245         if (r < 0)
246                 goto finish;
247
248         r = sd_bus_send(q->manager->bus, reply, NULL);
249
250 finish:
251         if (r < 0) {
252                 log_error("Failed to send hostname reply: %s", strerror(-r));
253                 sd_bus_reply_method_errno(q->request, -r, NULL);
254         }
255
256         dns_query_free(q);
257 }
258
259 static int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
260         _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
261         Manager *m = userdata;
262         const char *hostname;
263         int family;
264         DnsQuery *q;
265         int r;
266
267         assert(bus);
268         assert(message);
269         assert(m);
270
271         r = sd_bus_message_read(message, "si", &hostname, &family);
272         if (r < 0)
273                 return r;
274
275         if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
276                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
277
278         r = dns_name_normalize(hostname, NULL);
279         if (r < 0)
280                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
281
282         question = dns_question_new(family == AF_UNSPEC ? 2 : 1);
283         if (!question)
284                 return -ENOMEM;
285
286         if (family != AF_INET6) {
287                 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
288
289                 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, hostname);
290                 if (!key)
291                         return -ENOMEM;
292
293                 r = dns_question_add(question, key);
294                 if (r < 0)
295                         return r;
296         }
297
298         if (family != AF_INET) {
299                 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
300
301                 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, hostname);
302                 if (!key)
303                         return -ENOMEM;
304
305                 r = dns_question_add(question, key);
306                 if (r < 0)
307                         return r;
308         }
309
310         r = dns_query_new(m, &q, question);
311         if (r < 0)
312                 return r;
313
314         q->request = sd_bus_message_ref(message);
315         q->request_family = family;
316         q->request_hostname = hostname;
317         q->complete = bus_method_resolve_hostname_complete;
318
319         r = dns_query_go(q);
320         if (r < 0) {
321                 dns_query_free(q);
322
323                 if (r == -ESRCH)
324                         sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
325
326                 return r;
327         }
328
329         return 1;
330 }
331
332 static void bus_method_resolve_address_complete(DnsQuery *q) {
333         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
334         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
335         unsigned added = 0, i;
336         int r;
337
338         assert(q);
339
340         if (q->state != DNS_TRANSACTION_SUCCESS) {
341                 r = reply_query_state(q);
342                 goto finish;
343         }
344
345         r = sd_bus_message_new_method_return(q->request, &reply);
346         if (r < 0)
347                 goto finish;
348
349         r = sd_bus_message_open_container(reply, 'a', "s");
350         if (r < 0)
351                 goto finish;
352
353         if (q->answer) {
354                 answer = dns_answer_ref(q->answer);
355
356                 for (i = 0; i < answer->n_rrs; i++) {
357                         r = dns_question_matches_rr(q->question, answer->rrs[i]);
358                         if (r < 0)
359                                 goto finish;
360                         if (r == 0)
361                                 continue;
362
363                         r = sd_bus_message_append(reply, "s", answer->rrs[i]->ptr.name);
364                         if (r < 0)
365                                 goto finish;
366
367                         added ++;
368                 }
369         }
370
371         if (added <= 0) {
372                 _cleanup_free_ char *ip = NULL;
373
374                 in_addr_to_string(q->request_family, &q->request_address, &ip);
375
376                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", ip);
377                 goto finish;
378         }
379
380         r = sd_bus_message_close_container(reply);
381         if (r < 0)
382                 goto finish;
383
384         r = sd_bus_send(q->manager->bus, reply, NULL);
385
386 finish:
387         if (r < 0) {
388                 log_error("Failed to send address reply: %s", strerror(-r));
389                 sd_bus_reply_method_errno(q->request, -r, NULL);
390         }
391
392         dns_query_free(q);
393 }
394
395 static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
396         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
397         _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
398         _cleanup_free_ char *reverse = NULL;
399         Manager *m = userdata;
400         int family, ifindex;
401         const void *d;
402         DnsQuery *q;
403         size_t sz;
404         int r;
405
406         assert(bus);
407         assert(message);
408         assert(m);
409
410         r = sd_bus_message_read(message, "i", &family);
411         if (r < 0)
412                 return r;
413
414         if (!IN_SET(family, AF_INET, AF_INET6))
415                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
416
417         r = sd_bus_message_read_array(message, 'y', &d, &sz);
418         if (r < 0)
419                 return r;
420
421         if (sz != FAMILY_ADDRESS_SIZE(family))
422                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
423
424         r = sd_bus_message_read(message, "i", &ifindex);
425         if (r < 0)
426                 return r;
427         if (ifindex < 0)
428                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
429
430         r = dns_name_reverse(family, d, &reverse);
431         if (r < 0)
432                 return r;
433
434         question = dns_question_new(1);
435         if (!question)
436                 return -ENOMEM;
437
438         key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
439         if (!key)
440                 return -ENOMEM;
441
442         reverse = NULL;
443
444         r = dns_question_add(question, key);
445         if (r < 0)
446                 return r;
447
448         r = dns_query_new(m, &q, question);
449         if (r < 0)
450                 return r;
451
452         q->request = sd_bus_message_ref(message);
453         q->request_family = family;
454         memcpy(&q->request_address, d, sz);
455         q->complete = bus_method_resolve_address_complete;
456
457         r = dns_query_go(q);
458         if (r < 0) {
459                 dns_query_free(q);
460
461                 if (r == -ESRCH)
462                         sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
463
464                 return r;
465         }
466
467         return 1;
468 }
469
470 static void bus_method_resolve_record_complete(DnsQuery *q) {
471         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
472         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
473         unsigned added = 0, i;
474         int r;
475
476         assert(q);
477
478         if (q->state != DNS_TRANSACTION_SUCCESS) {
479                 r = reply_query_state(q);
480                 goto finish;
481         }
482
483         r = sd_bus_message_new_method_return(q->request, &reply);
484         if (r < 0)
485                 goto finish;
486
487         r = sd_bus_message_open_container(reply, 'a', "(qqay)");
488         if (r < 0)
489                 goto finish;
490
491         if (q->answer) {
492                 answer = dns_answer_ref(q->answer);
493
494                 for (i = 0; i < answer->n_rrs; i++) {
495                         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
496                         size_t start;
497
498                         r = dns_question_matches_rr(q->question, answer->rrs[i]);
499                         if (r < 0)
500                                 goto finish;
501                         if (r == 0)
502                                 continue;
503
504                         r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
505                         if (r < 0)
506                                 goto finish;
507
508                         r = dns_packet_append_rr(p, answer->rrs[i], &start);
509                         if (r < 0)
510                                 goto finish;
511
512                         r = sd_bus_message_open_container(reply, 'r', "qqay");
513                         if (r < 0)
514                                 goto finish;
515
516                         r = sd_bus_message_append(reply, "qq", answer->rrs[i]->key->class, answer->rrs[i]->key->type);
517                         if (r < 0)
518                                 goto finish;
519
520                         r = sd_bus_message_append_array(reply, 'y', DNS_PACKET_DATA(p) + start, p->size - start);
521                         if (r < 0)
522                                 goto finish;
523
524                         r = sd_bus_message_close_container(reply);
525                         if (r < 0)
526                                 goto finish;
527
528                         added ++;
529                 }
530         }
531
532         if (added <= 0) {
533                 r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", q->request_hostname);
534                 goto finish;
535         }
536
537         r = sd_bus_message_close_container(reply);
538         if (r < 0)
539                 goto finish;
540
541         r = sd_bus_send(q->manager->bus, reply, NULL);
542
543 finish:
544         if (r < 0) {
545                 log_error("Failed to send record reply: %s", strerror(-r));
546                 sd_bus_reply_method_errno(q->request, -r, NULL);
547         }
548
549         dns_query_free(q);
550 }
551
552 static int bus_method_resolve_record(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
553         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
554         _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
555         _cleanup_free_ char *reverse = NULL;
556         Manager *m = userdata;
557         DnsQuery *q;
558         int r;
559         uint16_t class, type;
560         const char *name;
561
562         assert(bus);
563         assert(message);
564         assert(m);
565
566         r = sd_bus_message_read(message, "sqq", &name, &class, &type);
567         if (r < 0)
568                 return r;
569
570         r = dns_name_normalize(name, NULL);
571         if (r < 0)
572                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid name '%s'", name);
573
574         question = dns_question_new(1);
575         if (!question)
576                 return -ENOMEM;
577
578         key = dns_resource_key_new(class, type, name);
579         if (!key)
580                 return -ENOMEM;
581
582         r = dns_question_add(question, key);
583         if (r < 0)
584                 return r;
585
586         r = dns_query_new(m, &q, question);
587         if (r < 0)
588                 return r;
589
590         q->request = sd_bus_message_ref(message);
591         q->request_hostname = name;
592         q->complete = bus_method_resolve_record_complete;
593
594         r = dns_query_go(q);
595         if (r < 0) {
596                 dns_query_free(q);
597
598                 if (r == -ESRCH)
599                         sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
600
601                 return r;
602         }
603
604         return 1;
605 }
606
607 static const sd_bus_vtable resolve_vtable[] = {
608         SD_BUS_VTABLE_START(0),
609         SD_BUS_METHOD("ResolveHostname", "si", "a(iayi)s", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
610         SD_BUS_METHOD("ResolveAddress", "iayi", "as", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
611         SD_BUS_METHOD("ResolveRecord", "sqq", "a(qqay)", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),
612         SD_BUS_VTABLE_END,
613 };
614
615 static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
616         Manager *m = userdata;
617
618         assert(s);
619         assert(m);
620
621         m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
622
623         manager_connect_bus(m);
624         return 0;
625 }
626
627 int manager_connect_bus(Manager *m) {
628         int r;
629
630         assert(m);
631
632         if (m->bus)
633                 return 0;
634
635         r = sd_bus_default_system(&m->bus);
636         if (r < 0) {
637                 /* We failed to connect? Yuck, we must be in early
638                  * boot. Let's try in 5s again. As soon as we have
639                  * kdbus we can stop doing this... */
640
641                 log_debug("Failed to connect to bus, trying again in 5s: %s", strerror(-r));
642
643                 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);
644                 if (r < 0) {
645                         log_error("Failed to install bus reconnect time event: %s", strerror(-r));
646                         return r;
647                 }
648
649                 return 0;
650         }
651
652         r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m);
653         if (r < 0) {
654                 log_error("Failed to register object: %s", strerror(-r));
655                 return r;
656         }
657
658         r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
659         if (r < 0) {
660                 log_error("Failed to register name: %s", strerror(-r));
661                 return r;
662         }
663
664         r = sd_bus_attach_event(m->bus, m->event, 0);
665         if (r < 0) {
666                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
667                 return r;
668         }
669
670         return 0;
671 }