2 This file is part of systemd.
4 Copyright 2014 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include "bus-common-errors.h"
30 #include "in-addr-util.h"
33 #include "string-util.h"
35 #include "signal-util.h"
37 NSS_GETHOSTBYNAME_PROTOTYPES(resolve
);
38 NSS_GETHOSTBYADDR_PROTOTYPES(resolve
);
40 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
42 typedef void (*voidfunc_t
)(void);
44 static voidfunc_t
find_fallback(const char *module
, const char *symbol
) {
47 /* Try to find a fallback NSS module symbol */
49 dl
= dlopen(module
, RTLD_LAZY
|RTLD_NODELETE
);
53 return dlsym(dl
, symbol
);
56 static bool bus_error_shall_fallback(sd_bus_error
*e
) {
57 return sd_bus_error_has_name(e
, SD_BUS_ERROR_SERVICE_UNKNOWN
) ||
58 sd_bus_error_has_name(e
, SD_BUS_ERROR_NAME_HAS_NO_OWNER
) ||
59 sd_bus_error_has_name(e
, SD_BUS_ERROR_NO_REPLY
) ||
60 sd_bus_error_has_name(e
, SD_BUS_ERROR_ACCESS_DENIED
);
63 static int count_addresses(sd_bus_message
*m
, int af
, const char **canonical
) {
69 r
= sd_bus_message_enter_container(m
, 'a', "(iiay)");
73 while ((r
= sd_bus_message_enter_container(m
, 'r', "iiay")) > 0) {
76 assert_cc(sizeof(int32_t) == sizeof(int));
78 r
= sd_bus_message_read(m
, "ii", &ifindex
, &family
);
82 r
= sd_bus_message_skip(m
, "ay");
86 r
= sd_bus_message_exit_container(m
);
90 if (af
!= AF_UNSPEC
&& family
!= af
)
98 r
= sd_bus_message_exit_container(m
);
102 r
= sd_bus_message_read(m
, "s", canonical
);
106 r
= sd_bus_message_rewind(m
, true);
113 static uint32_t ifindex_to_scopeid(int family
, const void *a
, int ifindex
) {
116 if (family
!= AF_INET6
)
119 /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */
121 assert(sizeof(in6
) == FAMILY_ADDRESS_SIZE(AF_INET
));
122 memcpy(&in6
, a
, sizeof(struct in6_addr
));
124 return IN6_IS_ADDR_LINKLOCAL(&in6
) ? ifindex
: 0;
127 enum nss_status
_nss_resolve_gethostbyname4_r(
129 struct gaih_addrtuple
**pat
,
130 char *buffer
, size_t buflen
,
131 int *errnop
, int *h_errnop
,
134 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
135 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
136 struct gaih_addrtuple
*r_tuple
, *r_tuple_first
= NULL
;
137 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
138 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
139 const char *canonical
= NULL
;
144 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
152 r
= sd_bus_open_system(&bus
);
156 r
= sd_bus_message_new_method_call(
159 "org.freedesktop.resolve1",
160 "/org/freedesktop/resolve1",
161 "org.freedesktop.resolve1.Manager",
166 r
= sd_bus_message_set_auto_start(req
, false);
170 r
= sd_bus_message_append(req
, "isit", 0, name
, AF_UNSPEC
, (uint64_t) 0);
174 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
176 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
178 *h_errnop
= HOST_NOT_FOUND
;
179 return NSS_STATUS_NOTFOUND
;
182 if (bus_error_shall_fallback(&error
))
185 /* Treat all other error conditions as NOTFOUND, and fail. This includes DNSSEC errors and
186 suchlike. (We don't use UNAVAIL in this case so that the nsswitch.conf configuration can distuingish
187 such executed but negative replies from complete failure to talk to resolved. */
188 ret
= NSS_STATUS_NOTFOUND
;
192 c
= count_addresses(reply
, AF_UNSPEC
, &canonical
);
199 *h_errnop
= HOST_NOT_FOUND
;
200 return NSS_STATUS_NOTFOUND
;
203 if (isempty(canonical
))
206 l
= strlen(canonical
);
207 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * c
;
210 *h_errnop
= TRY_AGAIN
;
211 return NSS_STATUS_TRYAGAIN
;
214 /* First, append name */
216 memcpy(r_name
, canonical
, l
+1);
219 /* Second, append addresses */
220 r_tuple_first
= (struct gaih_addrtuple
*) (buffer
+ idx
);
222 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
226 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
231 assert_cc(sizeof(int32_t) == sizeof(int));
233 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
242 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
246 r
= sd_bus_message_exit_container(reply
);
250 if (!IN_SET(family
, AF_INET
, AF_INET6
))
253 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
258 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
259 r_tuple
->next
= i
== c
-1 ? NULL
: (struct gaih_addrtuple
*) ((char*) r_tuple
+ ALIGN(sizeof(struct gaih_addrtuple
)));
260 r_tuple
->name
= r_name
;
261 r_tuple
->family
= family
;
262 r_tuple
->scopeid
= ifindex_to_scopeid(family
, a
, ifindex
);
263 memcpy(r_tuple
->addr
, a
, sz
);
265 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
275 **pat
= *r_tuple_first
;
277 *pat
= r_tuple_first
;
282 /* Explicitly reset all error variables */
284 *h_errnop
= NETDB_SUCCESS
;
287 return NSS_STATUS_SUCCESS
;
291 _nss_gethostbyname4_r_t fallback
;
293 fallback
= (_nss_gethostbyname4_r_t
)
294 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r");
297 return fallback(name
, pat
, buffer
, buflen
, errnop
, h_errnop
, ttlp
);
302 *h_errnop
= NO_RECOVERY
;
306 enum nss_status
_nss_resolve_gethostbyname3_r(
309 struct hostent
*result
,
310 char *buffer
, size_t buflen
,
311 int *errnop
, int *h_errnop
,
315 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
316 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
317 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
318 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
319 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
320 size_t l
, idx
, ms
, alen
;
321 const char *canonical
;
324 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
335 if (af
!= AF_INET
&& af
!= AF_INET6
) {
340 r
= sd_bus_open_system(&bus
);
344 r
= sd_bus_message_new_method_call(
347 "org.freedesktop.resolve1",
348 "/org/freedesktop/resolve1",
349 "org.freedesktop.resolve1.Manager",
354 r
= sd_bus_message_set_auto_start(req
, false);
358 r
= sd_bus_message_append(req
, "isit", 0, name
, af
, (uint64_t) 0);
362 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
364 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
366 *h_errnop
= HOST_NOT_FOUND
;
367 return NSS_STATUS_NOTFOUND
;
370 if (bus_error_shall_fallback(&error
))
373 ret
= NSS_STATUS_NOTFOUND
;
377 c
= count_addresses(reply
, af
, &canonical
);
384 *h_errnop
= HOST_NOT_FOUND
;
385 return NSS_STATUS_NOTFOUND
;
388 if (isempty(canonical
))
391 alen
= FAMILY_ADDRESS_SIZE(af
);
392 l
= strlen(canonical
);
394 ms
= ALIGN(l
+1) + c
* ALIGN(alen
) + (c
+2) * sizeof(char*);
398 *h_errnop
= TRY_AGAIN
;
399 return NSS_STATUS_TRYAGAIN
;
402 /* First, append name */
404 memcpy(r_name
, canonical
, l
+1);
407 /* Second, create empty aliases array */
408 r_aliases
= buffer
+ idx
;
409 ((char**) r_aliases
)[0] = NULL
;
410 idx
+= sizeof(char*);
412 /* Third, append addresses */
413 r_addr
= buffer
+ idx
;
415 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
419 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
424 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
433 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
437 r
= sd_bus_message_exit_container(reply
);
449 memcpy(r_addr
+ i
*ALIGN(alen
), a
, alen
);
456 idx
+= c
* ALIGN(alen
);
458 /* Fourth, append address pointer array */
459 r_addr_list
= buffer
+ idx
;
460 for (i
= 0; i
< c
; i
++)
461 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
463 ((char**) r_addr_list
)[i
] = NULL
;
464 idx
+= (c
+1) * sizeof(char*);
468 result
->h_name
= r_name
;
469 result
->h_aliases
= (char**) r_aliases
;
470 result
->h_addrtype
= af
;
471 result
->h_length
= alen
;
472 result
->h_addr_list
= (char**) r_addr_list
;
474 /* Explicitly reset all error variables */
476 *h_errnop
= NETDB_SUCCESS
;
485 return NSS_STATUS_SUCCESS
;
489 _nss_gethostbyname3_r_t fallback
;
491 fallback
= (_nss_gethostbyname3_r_t
)
492 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r");
494 return fallback(name
, af
, result
, buffer
, buflen
, errnop
, h_errnop
, ttlp
, canonp
);
499 *h_errnop
= NO_RECOVERY
;
503 enum nss_status
_nss_resolve_gethostbyaddr2_r(
504 const void* addr
, socklen_t len
,
506 struct hostent
*result
,
507 char *buffer
, size_t buflen
,
508 int *errnop
, int *h_errnop
,
511 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
512 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
513 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
514 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
515 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
516 unsigned c
= 0, i
= 0;
521 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
529 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
530 *errnop
= EAFNOSUPPORT
;
532 return NSS_STATUS_UNAVAIL
;
535 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
537 *h_errnop
= NO_RECOVERY
;
538 return NSS_STATUS_UNAVAIL
;
541 r
= sd_bus_open_system(&bus
);
545 r
= sd_bus_message_new_method_call(
548 "org.freedesktop.resolve1",
549 "/org/freedesktop/resolve1",
550 "org.freedesktop.resolve1.Manager",
555 r
= sd_bus_message_set_auto_start(req
, false);
559 r
= sd_bus_message_append(req
, "ii", 0, af
);
563 r
= sd_bus_message_append_array(req
, 'y', addr
, len
);
567 r
= sd_bus_message_append(req
, "t", (uint64_t) 0);
571 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
573 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
575 *h_errnop
= HOST_NOT_FOUND
;
576 return NSS_STATUS_NOTFOUND
;
579 if (bus_error_shall_fallback(&error
))
582 ret
= NSS_STATUS_NOTFOUND
;
586 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
590 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
598 ms
+= ALIGN(strlen(n
) + 1);
603 r
= sd_bus_message_rewind(reply
, false);
609 *h_errnop
= HOST_NOT_FOUND
;
610 return NSS_STATUS_NOTFOUND
;
613 ms
+= ALIGN(len
) + /* the address */
614 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
615 c
* sizeof(char*); /* pointers to aliases, plus trailing NULL */
619 *h_errnop
= TRY_AGAIN
;
620 return NSS_STATUS_TRYAGAIN
;
623 /* First, place address */
625 memcpy(r_addr
, addr
, len
);
628 /* Second, place address list */
629 r_addr_list
= buffer
+ idx
;
630 ((char**) r_addr_list
)[0] = r_addr
;
631 ((char**) r_addr_list
)[1] = NULL
;
632 idx
+= sizeof(char*) * 2;
634 /* Third, reserve space for the aliases array */
635 r_aliases
= buffer
+ idx
;
636 idx
+= sizeof(char*) * c
;
638 /* Fourth, place aliases */
640 r_name
= buffer
+ idx
;
641 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
650 ((char**) r_aliases
)[i
-1] = p
;
658 ((char**) r_aliases
)[c
-1] = NULL
;
661 result
->h_name
= r_name
;
662 result
->h_aliases
= (char**) r_aliases
;
663 result
->h_addrtype
= af
;
664 result
->h_length
= len
;
665 result
->h_addr_list
= (char**) r_addr_list
;
670 /* Explicitly reset all error variables */
672 *h_errnop
= NETDB_SUCCESS
;
675 return NSS_STATUS_SUCCESS
;
679 _nss_gethostbyaddr2_r_t fallback
;
681 fallback
= (_nss_gethostbyaddr2_r_t
)
682 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
685 return fallback(addr
, len
, af
, result
, buffer
, buflen
, errnop
, h_errnop
, ttlp
);
690 *h_errnop
= NO_RECOVERY
;
694 NSS_GETHOSTBYNAME_FALLBACKS(resolve
);
695 NSS_GETHOSTBYADDR_FALLBACKS(resolve
);