1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright 2014 Lennart Poettering
14 #include "bus-common-errors.h"
15 #include "in-addr-util.h"
18 #include "resolved-def.h"
19 #include "string-util.h"
21 #include "signal-util.h"
23 NSS_GETHOSTBYNAME_PROTOTYPES(resolve
);
24 NSS_GETHOSTBYADDR_PROTOTYPES(resolve
);
26 static bool bus_error_shall_fallback(sd_bus_error
*e
) {
27 return sd_bus_error_has_name(e
, SD_BUS_ERROR_SERVICE_UNKNOWN
) ||
28 sd_bus_error_has_name(e
, SD_BUS_ERROR_NAME_HAS_NO_OWNER
) ||
29 sd_bus_error_has_name(e
, SD_BUS_ERROR_NO_REPLY
) ||
30 sd_bus_error_has_name(e
, SD_BUS_ERROR_ACCESS_DENIED
);
33 static int count_addresses(sd_bus_message
*m
, int af
, const char **canonical
) {
39 r
= sd_bus_message_enter_container(m
, 'a', "(iiay)");
43 while ((r
= sd_bus_message_enter_container(m
, 'r', "iiay")) > 0) {
46 assert_cc(sizeof(int32_t) == sizeof(int));
48 r
= sd_bus_message_read(m
, "ii", &ifindex
, &family
);
52 r
= sd_bus_message_skip(m
, "ay");
56 r
= sd_bus_message_exit_container(m
);
60 if (af
!= AF_UNSPEC
&& family
!= af
)
68 r
= sd_bus_message_exit_container(m
);
72 r
= sd_bus_message_read(m
, "s", canonical
);
76 r
= sd_bus_message_rewind(m
, true);
83 static uint32_t ifindex_to_scopeid(int family
, const void *a
, int ifindex
) {
86 if (family
!= AF_INET6
)
89 /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */
91 assert(sizeof(in6
) == FAMILY_ADDRESS_SIZE(AF_INET6
));
92 memcpy(&in6
, a
, sizeof(struct in6_addr
));
94 return IN6_IS_ADDR_LINKLOCAL(&in6
) ? ifindex
: 0;
97 enum nss_status
_nss_resolve_gethostbyname4_r(
99 struct gaih_addrtuple
**pat
,
100 char *buffer
, size_t buflen
,
101 int *errnop
, int *h_errnop
,
104 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
105 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
106 struct gaih_addrtuple
*r_tuple
, *r_tuple_first
= NULL
;
107 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
108 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
109 const char *canonical
= NULL
;
114 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
122 r
= sd_bus_open_system(&bus
);
126 r
= sd_bus_message_new_method_call(
129 "org.freedesktop.resolve1",
130 "/org/freedesktop/resolve1",
131 "org.freedesktop.resolve1.Manager",
136 r
= sd_bus_message_set_auto_start(req
, false);
140 r
= sd_bus_message_append(req
, "isit", 0, name
, AF_UNSPEC
, (uint64_t) 0);
144 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
146 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
148 *h_errnop
= HOST_NOT_FOUND
;
149 return NSS_STATUS_NOTFOUND
;
152 /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails,
153 allowing falling back to other nss modules. Treat all other error conditions as
154 NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this
155 case so that the nsswitch.conf configuration can distuingish such executed but
156 negative replies from complete failure to talk to resolved). */
157 if (!bus_error_shall_fallback(&error
))
158 ret
= NSS_STATUS_NOTFOUND
;
163 c
= count_addresses(reply
, AF_UNSPEC
, &canonical
);
170 *h_errnop
= HOST_NOT_FOUND
;
171 return NSS_STATUS_NOTFOUND
;
174 if (isempty(canonical
))
177 l
= strlen(canonical
);
178 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * c
;
181 *h_errnop
= NETDB_INTERNAL
;
182 return NSS_STATUS_TRYAGAIN
;
185 /* First, append name */
187 memcpy(r_name
, canonical
, l
+1);
190 /* Second, append addresses */
191 r_tuple_first
= (struct gaih_addrtuple
*) (buffer
+ idx
);
193 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
197 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
202 assert_cc(sizeof(int32_t) == sizeof(int));
204 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
213 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
217 r
= sd_bus_message_exit_container(reply
);
221 if (!IN_SET(family
, AF_INET
, AF_INET6
))
224 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
229 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
230 r_tuple
->next
= i
== c
-1 ? NULL
: (struct gaih_addrtuple
*) ((char*) r_tuple
+ ALIGN(sizeof(struct gaih_addrtuple
)));
231 r_tuple
->name
= r_name
;
232 r_tuple
->family
= family
;
233 r_tuple
->scopeid
= ifindex_to_scopeid(family
, a
, ifindex
);
234 memcpy(r_tuple
->addr
, a
, sz
);
236 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
246 **pat
= *r_tuple_first
;
248 *pat
= r_tuple_first
;
253 /* Explicitly reset all error variables */
255 *h_errnop
= NETDB_SUCCESS
;
258 return NSS_STATUS_SUCCESS
;
262 *h_errnop
= NO_RECOVERY
;
266 enum nss_status
_nss_resolve_gethostbyname3_r(
269 struct hostent
*result
,
270 char *buffer
, size_t buflen
,
271 int *errnop
, int *h_errnop
,
275 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
276 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
277 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
278 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
279 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
280 size_t l
, idx
, ms
, alen
;
281 const char *canonical
;
284 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
295 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
300 r
= sd_bus_open_system(&bus
);
304 r
= sd_bus_message_new_method_call(
307 "org.freedesktop.resolve1",
308 "/org/freedesktop/resolve1",
309 "org.freedesktop.resolve1.Manager",
314 r
= sd_bus_message_set_auto_start(req
, false);
318 r
= sd_bus_message_append(req
, "isit", 0, name
, af
, (uint64_t) 0);
322 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
324 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
326 *h_errnop
= HOST_NOT_FOUND
;
327 return NSS_STATUS_NOTFOUND
;
330 if (!bus_error_shall_fallback(&error
))
331 ret
= NSS_STATUS_NOTFOUND
;
336 c
= count_addresses(reply
, af
, &canonical
);
343 *h_errnop
= HOST_NOT_FOUND
;
344 return NSS_STATUS_NOTFOUND
;
347 if (isempty(canonical
))
350 alen
= FAMILY_ADDRESS_SIZE(af
);
351 l
= strlen(canonical
);
353 ms
= ALIGN(l
+1) + c
* ALIGN(alen
) + (c
+2) * sizeof(char*);
357 *h_errnop
= NETDB_INTERNAL
;
358 return NSS_STATUS_TRYAGAIN
;
361 /* First, append name */
363 memcpy(r_name
, canonical
, l
+1);
366 /* Second, create empty aliases array */
367 r_aliases
= buffer
+ idx
;
368 ((char**) r_aliases
)[0] = NULL
;
369 idx
+= sizeof(char*);
371 /* Third, append addresses */
372 r_addr
= buffer
+ idx
;
374 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
378 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
383 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
392 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
396 r
= sd_bus_message_exit_container(reply
);
408 memcpy(r_addr
+ i
*ALIGN(alen
), a
, alen
);
415 idx
+= c
* ALIGN(alen
);
417 /* Fourth, append address pointer array */
418 r_addr_list
= buffer
+ idx
;
419 for (i
= 0; i
< c
; i
++)
420 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
422 ((char**) r_addr_list
)[i
] = NULL
;
423 idx
+= (c
+1) * sizeof(char*);
427 result
->h_name
= r_name
;
428 result
->h_aliases
= (char**) r_aliases
;
429 result
->h_addrtype
= af
;
430 result
->h_length
= alen
;
431 result
->h_addr_list
= (char**) r_addr_list
;
433 /* Explicitly reset all error variables */
435 *h_errnop
= NETDB_SUCCESS
;
444 return NSS_STATUS_SUCCESS
;
448 *h_errnop
= NO_RECOVERY
;
452 enum nss_status
_nss_resolve_gethostbyaddr2_r(
453 const void* addr
, socklen_t len
,
455 struct hostent
*result
,
456 char *buffer
, size_t buflen
,
457 int *errnop
, int *h_errnop
,
460 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
461 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
462 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
463 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
464 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
465 unsigned c
= 0, i
= 0;
470 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
478 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
479 *errnop
= EAFNOSUPPORT
;
481 return NSS_STATUS_UNAVAIL
;
484 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
486 *h_errnop
= NO_RECOVERY
;
487 return NSS_STATUS_UNAVAIL
;
490 r
= sd_bus_open_system(&bus
);
494 r
= sd_bus_message_new_method_call(
497 "org.freedesktop.resolve1",
498 "/org/freedesktop/resolve1",
499 "org.freedesktop.resolve1.Manager",
504 r
= sd_bus_message_set_auto_start(req
, false);
508 r
= sd_bus_message_append(req
, "ii", 0, af
);
512 r
= sd_bus_message_append_array(req
, 'y', addr
, len
);
516 r
= sd_bus_message_append(req
, "t", (uint64_t) 0);
520 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
522 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
524 *h_errnop
= HOST_NOT_FOUND
;
525 return NSS_STATUS_NOTFOUND
;
528 if (!bus_error_shall_fallback(&error
))
529 ret
= NSS_STATUS_NOTFOUND
;
534 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
538 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
546 ms
+= ALIGN(strlen(n
) + 1);
551 r
= sd_bus_message_rewind(reply
, false);
557 *h_errnop
= HOST_NOT_FOUND
;
558 return NSS_STATUS_NOTFOUND
;
561 ms
+= ALIGN(len
) + /* the address */
562 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
563 c
* sizeof(char*); /* pointers to aliases, plus trailing NULL */
567 *h_errnop
= NETDB_INTERNAL
;
568 return NSS_STATUS_TRYAGAIN
;
571 /* First, place address */
573 memcpy(r_addr
, addr
, len
);
576 /* Second, place address list */
577 r_addr_list
= buffer
+ idx
;
578 ((char**) r_addr_list
)[0] = r_addr
;
579 ((char**) r_addr_list
)[1] = NULL
;
580 idx
+= sizeof(char*) * 2;
582 /* Third, reserve space for the aliases array */
583 r_aliases
= buffer
+ idx
;
584 idx
+= sizeof(char*) * c
;
586 /* Fourth, place aliases */
588 r_name
= buffer
+ idx
;
589 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
598 ((char**) r_aliases
)[i
-1] = p
;
606 ((char**) r_aliases
)[c
-1] = NULL
;
609 result
->h_name
= r_name
;
610 result
->h_aliases
= (char**) r_aliases
;
611 result
->h_addrtype
= af
;
612 result
->h_length
= len
;
613 result
->h_addr_list
= (char**) r_addr_list
;
618 /* Explicitly reset all error variables */
620 *h_errnop
= NETDB_SUCCESS
;
623 return NSS_STATUS_SUCCESS
;
627 *h_errnop
= NO_RECOVERY
;
631 NSS_GETHOSTBYNAME_FALLBACKS(resolve
);
632 NSS_GETHOSTBYADDR_FALLBACKS(resolve
);