]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-resolve/nss-resolve.c
nss-resolve: also fall back to nss-dns if dbus doesn't work
[thirdparty/systemd.git] / src / nss-resolve / nss-resolve.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 <dlfcn.h>
23 #include <errno.h>
24 #include <netdb.h>
25 #include <nss.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "sd-bus.h"
30
31 #include "bus-common-errors.h"
32 #include "in-addr-util.h"
33 #include "macro.h"
34 #include "nss-util.h"
35 #include "string-util.h"
36 #include "util.h"
37 #include "signal-util.h"
38
39 NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
40 NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
41
42 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
43
44 typedef void (*voidfunc_t)(void);
45
46 static voidfunc_t find_fallback(const char *module, const char *symbol) {
47 void *dl;
48
49 /* Try to find a fallback NSS module symbol */
50
51 dl = dlopen(module, RTLD_LAZY|RTLD_NODELETE);
52 if (!dl)
53 return NULL;
54
55 return dlsym(dl, symbol);
56 }
57
58 static bool bus_error_shall_fallback(sd_bus_error *e) {
59 return sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
60 sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER) ||
61 sd_bus_error_has_name(e, SD_BUS_ERROR_NO_REPLY) ||
62 sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED);
63 }
64
65 static int count_addresses(sd_bus_message *m, int af, const char **canonical) {
66 int c = 0, r;
67
68 assert(m);
69 assert(canonical);
70
71 r = sd_bus_message_enter_container(m, 'a', "(iiay)");
72 if (r < 0)
73 return r;
74
75 while ((r = sd_bus_message_enter_container(m, 'r', "iiay")) > 0) {
76 int family, ifindex;
77
78 assert_cc(sizeof(int32_t) == sizeof(int));
79
80 r = sd_bus_message_read(m, "ii", &ifindex, &family);
81 if (r < 0)
82 return r;
83
84 r = sd_bus_message_skip(m, "ay");
85 if (r < 0)
86 return r;
87
88 r = sd_bus_message_exit_container(m);
89 if (r < 0)
90 return r;
91
92 if (af != AF_UNSPEC && family != af)
93 continue;
94
95 c ++;
96 }
97 if (r < 0)
98 return r;
99
100 r = sd_bus_message_exit_container(m);
101 if (r < 0)
102 return r;
103
104 r = sd_bus_message_read(m, "s", canonical);
105 if (r < 0)
106 return r;
107
108 r = sd_bus_message_rewind(m, true);
109 if (r < 0)
110 return r;
111
112 return c;
113 }
114
115 enum nss_status _nss_resolve_gethostbyname4_r(
116 const char *name,
117 struct gaih_addrtuple **pat,
118 char *buffer, size_t buflen,
119 int *errnop, int *h_errnop,
120 int32_t *ttlp) {
121
122 enum nss_status (*fallback)(
123 const char *name,
124 struct gaih_addrtuple **pat,
125 char *buffer, size_t buflen,
126 int *errnop, int *h_errnop,
127 int32_t *ttlp);
128
129 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
130 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
131 struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
132 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
133 const char *canonical = NULL;
134 size_t l, ms, idx;
135 char *r_name;
136 int c, r, i = 0;
137
138 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
139
140 assert(name);
141 assert(pat);
142 assert(buffer);
143 assert(errnop);
144 assert(h_errnop);
145
146 r = sd_bus_open_system(&bus);
147 if (r < 0)
148 goto fallback;
149
150 r = sd_bus_message_new_method_call(
151 bus,
152 &req,
153 "org.freedesktop.resolve1",
154 "/org/freedesktop/resolve1",
155 "org.freedesktop.resolve1.Manager",
156 "ResolveHostname");
157 if (r < 0)
158 goto fail;
159
160 r = sd_bus_message_set_auto_start(req, false);
161 if (r < 0)
162 goto fail;
163
164 r = sd_bus_message_append(req, "isit", 0, name, AF_UNSPEC, (uint64_t) 0);
165 if (r < 0)
166 goto fail;
167
168 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
169 if (r < 0) {
170 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
171 *errnop = ESRCH;
172 *h_errnop = HOST_NOT_FOUND;
173 return NSS_STATUS_NOTFOUND;
174 }
175
176 if (bus_error_shall_fallback(&error))
177 goto fallback;
178
179 goto fail;
180 }
181
182 c = count_addresses(reply, AF_UNSPEC, &canonical);
183 if (c < 0) {
184 r = c;
185 goto fail;
186 }
187 if (c == 0) {
188 *errnop = ESRCH;
189 *h_errnop = HOST_NOT_FOUND;
190 return NSS_STATUS_NOTFOUND;
191 }
192
193 if (isempty(canonical))
194 canonical = name;
195
196 l = strlen(canonical);
197 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
198 if (buflen < ms) {
199 *errnop = ENOMEM;
200 *h_errnop = TRY_AGAIN;
201 return NSS_STATUS_TRYAGAIN;
202 }
203
204 /* First, append name */
205 r_name = buffer;
206 memcpy(r_name, canonical, l+1);
207 idx = ALIGN(l+1);
208
209 /* Second, append addresses */
210 r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
211
212 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
213 if (r < 0)
214 goto fail;
215
216 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
217 int family, ifindex;
218 const void *a;
219 size_t sz;
220
221 assert_cc(sizeof(int32_t) == sizeof(int));
222
223 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
224 if (r < 0)
225 goto fail;
226
227 if (ifindex < 0) {
228 r = -EINVAL;
229 goto fail;
230 }
231
232 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
233 if (r < 0)
234 goto fail;
235
236 r = sd_bus_message_exit_container(reply);
237 if (r < 0)
238 goto fail;
239
240 if (!IN_SET(family, AF_INET, AF_INET6))
241 continue;
242
243 if (sz != FAMILY_ADDRESS_SIZE(family)) {
244 r = -EINVAL;
245 goto fail;
246 }
247
248 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
249 r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
250 r_tuple->name = r_name;
251 r_tuple->family = family;
252 r_tuple->scopeid = ifindex;
253 memcpy(r_tuple->addr, a, sz);
254
255 idx += ALIGN(sizeof(struct gaih_addrtuple));
256 i++;
257 }
258 if (r < 0)
259 goto fail;
260
261 assert(i == c);
262 assert(idx == ms);
263
264 if (*pat)
265 **pat = *r_tuple_first;
266 else
267 *pat = r_tuple_first;
268
269 if (ttlp)
270 *ttlp = 0;
271
272 /* Explicitly reset all error variables */
273 *errnop = 0;
274 *h_errnop = NETDB_SUCCESS;
275 h_errno = 0;
276
277 return NSS_STATUS_SUCCESS;
278
279 fallback:
280 fallback = (enum nss_status (*)(const char *name,
281 struct gaih_addrtuple **pat,
282 char *buffer, size_t buflen,
283 int *errnop, int *h_errnop,
284 int32_t *ttlp))
285 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r");
286
287 if (fallback)
288 return fallback(name, pat, buffer, buflen, errnop, h_errnop, ttlp);
289
290 fail:
291 *errnop = -r;
292 *h_errnop = NO_RECOVERY;
293 return NSS_STATUS_UNAVAIL;
294 }
295
296 enum nss_status _nss_resolve_gethostbyname3_r(
297 const char *name,
298 int af,
299 struct hostent *result,
300 char *buffer, size_t buflen,
301 int *errnop, int *h_errnop,
302 int32_t *ttlp,
303 char **canonp) {
304
305 enum nss_status (*fallback)(
306 const char *name,
307 int af,
308 struct hostent *result,
309 char *buffer, size_t buflen,
310 int *errnop, int *h_errnop,
311 int32_t *ttlp,
312 char **canonp);
313
314 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
315 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
316 char *r_name, *r_aliases, *r_addr, *r_addr_list;
317 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
318 size_t l, idx, ms, alen;
319 const char *canonical;
320 int c, r, i = 0;
321
322 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
323
324 assert(name);
325 assert(result);
326 assert(buffer);
327 assert(errnop);
328 assert(h_errnop);
329
330 if (af == AF_UNSPEC)
331 af = AF_INET;
332
333 if (af != AF_INET && af != AF_INET6) {
334 r = -EAFNOSUPPORT;
335 goto fail;
336 }
337
338 r = sd_bus_open_system(&bus);
339 if (r < 0)
340 goto fallback;
341
342 r = sd_bus_message_new_method_call(
343 bus,
344 &req,
345 "org.freedesktop.resolve1",
346 "/org/freedesktop/resolve1",
347 "org.freedesktop.resolve1.Manager",
348 "ResolveHostname");
349 if (r < 0)
350 goto fail;
351
352 r = sd_bus_message_set_auto_start(req, false);
353 if (r < 0)
354 goto fail;
355
356 r = sd_bus_message_append(req, "isit", 0, name, af, (uint64_t) 0);
357 if (r < 0)
358 goto fail;
359
360 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
361 if (r < 0) {
362 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
363 *errnop = ESRCH;
364 *h_errnop = HOST_NOT_FOUND;
365 return NSS_STATUS_NOTFOUND;
366 }
367
368 if (bus_error_shall_fallback(&error))
369 goto fallback;
370
371 goto fail;
372 }
373
374 c = count_addresses(reply, af, &canonical);
375 if (c < 0) {
376 r = c;
377 goto fail;
378 }
379 if (c == 0) {
380 *errnop = ESRCH;
381 *h_errnop = HOST_NOT_FOUND;
382 return NSS_STATUS_NOTFOUND;
383 }
384
385 if (isempty(canonical))
386 canonical = name;
387
388 alen = FAMILY_ADDRESS_SIZE(af);
389 l = strlen(canonical);
390
391 ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*);
392
393 if (buflen < ms) {
394 *errnop = ENOMEM;
395 *h_errnop = TRY_AGAIN;
396 return NSS_STATUS_TRYAGAIN;
397 }
398
399 /* First, append name */
400 r_name = buffer;
401 memcpy(r_name, canonical, l+1);
402 idx = ALIGN(l+1);
403
404 /* Second, create empty aliases array */
405 r_aliases = buffer + idx;
406 ((char**) r_aliases)[0] = NULL;
407 idx += sizeof(char*);
408
409 /* Third, append addresses */
410 r_addr = buffer + idx;
411
412 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
413 if (r < 0)
414 goto fail;
415
416 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
417 int ifindex, family;
418 const void *a;
419 size_t sz;
420
421 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
422 if (r < 0)
423 goto fail;
424
425 if (ifindex < 0) {
426 r = -EINVAL;
427 goto fail;
428 }
429
430 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
431 if (r < 0)
432 goto fail;
433
434 r = sd_bus_message_exit_container(reply);
435 if (r < 0)
436 goto fail;
437
438 if (family != af)
439 continue;
440
441 if (sz != alen) {
442 r = -EINVAL;
443 goto fail;
444 }
445
446 memcpy(r_addr + i*ALIGN(alen), a, alen);
447 i++;
448 }
449 if (r < 0)
450 goto fail;
451
452 assert(i == c);
453 idx += c * ALIGN(alen);
454
455 /* Fourth, append address pointer array */
456 r_addr_list = buffer + idx;
457 for (i = 0; i < c; i++)
458 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
459
460 ((char**) r_addr_list)[i] = NULL;
461 idx += (c+1) * sizeof(char*);
462
463 assert(idx == ms);
464
465 result->h_name = r_name;
466 result->h_aliases = (char**) r_aliases;
467 result->h_addrtype = af;
468 result->h_length = alen;
469 result->h_addr_list = (char**) r_addr_list;
470
471 /* Explicitly reset all error variables */
472 *errnop = 0;
473 *h_errnop = NETDB_SUCCESS;
474 h_errno = 0;
475
476 if (ttlp)
477 *ttlp = 0;
478
479 if (canonp)
480 *canonp = r_name;
481
482 return NSS_STATUS_SUCCESS;
483
484 fallback:
485 fallback = (enum nss_status (*)(const char *name,
486 int af,
487 struct hostent *result,
488 char *buffer, size_t buflen,
489 int *errnop, int *h_errnop,
490 int32_t *ttlp,
491 char **canonp))
492 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r");
493 if (fallback)
494 return fallback(name, af, result, buffer, buflen, errnop, h_errnop, ttlp, canonp);
495
496 fail:
497 *errnop = -r;
498 *h_errnop = NO_RECOVERY;
499 return NSS_STATUS_UNAVAIL;
500 }
501
502 enum nss_status _nss_resolve_gethostbyaddr2_r(
503 const void* addr, socklen_t len,
504 int af,
505 struct hostent *result,
506 char *buffer, size_t buflen,
507 int *errnop, int *h_errnop,
508 int32_t *ttlp) {
509
510 enum nss_status (*fallback)(
511 const void* addr, socklen_t len,
512 int af,
513 struct hostent *result,
514 char *buffer, size_t buflen,
515 int *errnop, int *h_errnop,
516 int32_t *ttlp);
517
518
519 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
520 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
521 char *r_name, *r_aliases, *r_addr, *r_addr_list;
522 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
523 unsigned c = 0, i = 0;
524 size_t ms = 0, idx;
525 const char *n;
526 int r, ifindex;
527
528 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
529
530 assert(addr);
531 assert(result);
532 assert(buffer);
533 assert(errnop);
534 assert(h_errnop);
535
536 if (!IN_SET(af, AF_INET, AF_INET6)) {
537 *errnop = EAFNOSUPPORT;
538 *h_errnop = NO_DATA;
539 return NSS_STATUS_UNAVAIL;
540 }
541
542 if (len != FAMILY_ADDRESS_SIZE(af)) {
543 *errnop = EINVAL;
544 *h_errnop = NO_RECOVERY;
545 return NSS_STATUS_UNAVAIL;
546 }
547
548 r = sd_bus_open_system(&bus);
549 if (r < 0)
550 goto fallback;
551
552 r = sd_bus_message_new_method_call(
553 bus,
554 &req,
555 "org.freedesktop.resolve1",
556 "/org/freedesktop/resolve1",
557 "org.freedesktop.resolve1.Manager",
558 "ResolveAddress");
559 if (r < 0)
560 goto fail;
561
562 r = sd_bus_message_set_auto_start(req, false);
563 if (r < 0)
564 goto fail;
565
566 r = sd_bus_message_append(req, "ii", 0, af);
567 if (r < 0)
568 goto fail;
569
570 r = sd_bus_message_append_array(req, 'y', addr, len);
571 if (r < 0)
572 goto fail;
573
574 r = sd_bus_message_append(req, "t", (uint64_t) 0);
575 if (r < 0)
576 goto fail;
577
578 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
579 if (r < 0) {
580 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
581 *errnop = ESRCH;
582 *h_errnop = HOST_NOT_FOUND;
583 return NSS_STATUS_NOTFOUND;
584 }
585
586 if (bus_error_shall_fallback(&error))
587 goto fallback;
588
589
590 *errnop = -r;
591 *h_errnop = NO_RECOVERY;
592 return NSS_STATUS_UNAVAIL;
593 }
594
595 r = sd_bus_message_enter_container(reply, 'a', "(is)");
596 if (r < 0)
597 goto fail;
598
599 while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) {
600
601 if (ifindex < 0) {
602 r = -EINVAL;
603 goto fail;
604 }
605
606 c++;
607 ms += ALIGN(strlen(n) + 1);
608 }
609 if (r < 0)
610 goto fail;
611
612 r = sd_bus_message_rewind(reply, false);
613 if (r < 0)
614 return r;
615
616 if (c <= 0) {
617 *errnop = ESRCH;
618 *h_errnop = HOST_NOT_FOUND;
619 return NSS_STATUS_NOTFOUND;
620 }
621
622 ms += ALIGN(len) + /* the address */
623 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
624 c * sizeof(char*); /* pointers to aliases, plus trailing NULL */
625
626 if (buflen < ms) {
627 *errnop = ENOMEM;
628 *h_errnop = TRY_AGAIN;
629 return NSS_STATUS_TRYAGAIN;
630 }
631
632 /* First, place address */
633 r_addr = buffer;
634 memcpy(r_addr, addr, len);
635 idx = ALIGN(len);
636
637 /* Second, place address list */
638 r_addr_list = buffer + idx;
639 ((char**) r_addr_list)[0] = r_addr;
640 ((char**) r_addr_list)[1] = NULL;
641 idx += sizeof(char*) * 2;
642
643 /* Third, reserve space for the aliases array */
644 r_aliases = buffer + idx;
645 idx += sizeof(char*) * c;
646
647 /* Fourth, place aliases */
648 i = 0;
649 r_name = buffer + idx;
650 while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) {
651 char *p;
652 size_t l;
653
654 l = strlen(n);
655 p = buffer + idx;
656 memcpy(p, n, l+1);
657
658 if (i > 0)
659 ((char**) r_aliases)[i-1] = p;
660 i++;
661
662 idx += ALIGN(l+1);
663 }
664 if (r < 0)
665 goto fail;
666
667 ((char**) r_aliases)[c-1] = NULL;
668 assert(idx == ms);
669
670 result->h_name = r_name;
671 result->h_aliases = (char**) r_aliases;
672 result->h_addrtype = af;
673 result->h_length = len;
674 result->h_addr_list = (char**) r_addr_list;
675
676 if (ttlp)
677 *ttlp = 0;
678
679 /* Explicitly reset all error variables */
680 *errnop = 0;
681 *h_errnop = NETDB_SUCCESS;
682 h_errno = 0;
683
684 return NSS_STATUS_SUCCESS;
685
686 fallback:
687 fallback = (enum nss_status (*)(
688 const void* addr, socklen_t len,
689 int af,
690 struct hostent *result,
691 char *buffer, size_t buflen,
692 int *errnop, int *h_errnop,
693 int32_t *ttlp))
694 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
695
696 if (fallback)
697 return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp);
698
699 fail:
700 *errnop = -r;
701 *h_errnop = NO_RECOVERY;
702 return NSS_STATUS_UNAVAIL;
703 }
704
705 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
706 NSS_GETHOSTBYADDR_FALLBACKS(resolve);