1 /* SPDX-License-Identifier: LGPL-2.1+ */
11 #include "bus-common-errors.h"
12 #include "in-addr-util.h"
15 #include "resolved-def.h"
16 #include "string-util.h"
18 #include "signal-util.h"
20 NSS_GETHOSTBYNAME_PROTOTYPES(resolve
);
21 NSS_GETHOSTBYADDR_PROTOTYPES(resolve
);
23 static bool bus_error_shall_fallback(sd_bus_error
*e
) {
24 return sd_bus_error_has_name(e
, SD_BUS_ERROR_SERVICE_UNKNOWN
) ||
25 sd_bus_error_has_name(e
, SD_BUS_ERROR_NAME_HAS_NO_OWNER
) ||
26 sd_bus_error_has_name(e
, SD_BUS_ERROR_NO_REPLY
) ||
27 sd_bus_error_has_name(e
, SD_BUS_ERROR_ACCESS_DENIED
);
30 static int count_addresses(sd_bus_message
*m
, int af
, const char **canonical
) {
36 r
= sd_bus_message_enter_container(m
, 'a', "(iiay)");
40 while ((r
= sd_bus_message_enter_container(m
, 'r', "iiay")) > 0) {
43 assert_cc(sizeof(int32_t) == sizeof(int));
45 r
= sd_bus_message_read(m
, "ii", &ifindex
, &family
);
49 r
= sd_bus_message_skip(m
, "ay");
53 r
= sd_bus_message_exit_container(m
);
57 if (af
!= AF_UNSPEC
&& family
!= af
)
65 r
= sd_bus_message_exit_container(m
);
69 r
= sd_bus_message_read(m
, "s", canonical
);
73 r
= sd_bus_message_rewind(m
, true);
80 static uint32_t ifindex_to_scopeid(int family
, const void *a
, int ifindex
) {
83 if (family
!= AF_INET6
)
86 /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */
88 assert(sizeof(in6
) == FAMILY_ADDRESS_SIZE(AF_INET6
));
89 memcpy(&in6
, a
, sizeof(struct in6_addr
));
91 return IN6_IS_ADDR_LINKLOCAL(&in6
) ? ifindex
: 0;
94 enum nss_status
_nss_resolve_gethostbyname4_r(
96 struct gaih_addrtuple
**pat
,
97 char *buffer
, size_t buflen
,
98 int *errnop
, int *h_errnop
,
101 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
102 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
103 struct gaih_addrtuple
*r_tuple
, *r_tuple_first
= NULL
;
104 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
105 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
106 const char *canonical
= NULL
;
112 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
120 r
= sd_bus_open_system(&bus
);
124 r
= sd_bus_message_new_method_call(
127 "org.freedesktop.resolve1",
128 "/org/freedesktop/resolve1",
129 "org.freedesktop.resolve1.Manager",
134 r
= sd_bus_message_set_auto_start(req
, false);
138 r
= sd_bus_message_append(req
, "isit", 0, name
, AF_UNSPEC
, (uint64_t) 0);
142 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
144 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN") ||
145 !bus_error_shall_fallback(&error
))
148 /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails,
149 allowing falling back to other nss modules. Treat all other error conditions as
150 NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this
151 case so that the nsswitch.conf configuration can distuingish such executed but
152 negative replies from complete failure to talk to resolved). */
156 c
= count_addresses(reply
, AF_UNSPEC
, &canonical
);
164 if (isempty(canonical
))
167 l
= strlen(canonical
);
168 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * c
;
171 *h_errnop
= NETDB_INTERNAL
;
172 return NSS_STATUS_TRYAGAIN
;
175 /* First, append name */
177 memcpy(r_name
, canonical
, l
+1);
180 /* Second, append addresses */
181 r_tuple_first
= (struct gaih_addrtuple
*) (buffer
+ idx
);
183 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
187 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
192 assert_cc(sizeof(int32_t) == sizeof(int));
194 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
203 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
207 r
= sd_bus_message_exit_container(reply
);
211 if (!IN_SET(family
, AF_INET
, AF_INET6
))
214 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
219 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
220 r_tuple
->next
= i
== c
-1 ? NULL
: (struct gaih_addrtuple
*) ((char*) r_tuple
+ ALIGN(sizeof(struct gaih_addrtuple
)));
221 r_tuple
->name
= r_name
;
222 r_tuple
->family
= family
;
223 r_tuple
->scopeid
= ifindex_to_scopeid(family
, a
, ifindex
);
224 memcpy(r_tuple
->addr
, a
, sz
);
226 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
236 **pat
= *r_tuple_first
;
238 *pat
= r_tuple_first
;
243 /* Explicitly reset both *h_errnop and h_errno to work around
244 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
245 *h_errnop
= NETDB_SUCCESS
;
248 return NSS_STATUS_SUCCESS
;
252 *h_errnop
= NO_RECOVERY
;
256 *h_errnop
= HOST_NOT_FOUND
;
257 return NSS_STATUS_NOTFOUND
;
260 enum nss_status
_nss_resolve_gethostbyname3_r(
263 struct hostent
*result
,
264 char *buffer
, size_t buflen
,
265 int *errnop
, int *h_errnop
,
269 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
270 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
271 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
272 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
273 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
274 size_t l
, idx
, ms
, alen
;
275 const char *canonical
;
279 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
290 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
295 r
= sd_bus_open_system(&bus
);
299 r
= sd_bus_message_new_method_call(
302 "org.freedesktop.resolve1",
303 "/org/freedesktop/resolve1",
304 "org.freedesktop.resolve1.Manager",
309 r
= sd_bus_message_set_auto_start(req
, false);
313 r
= sd_bus_message_append(req
, "isit", 0, name
, af
, (uint64_t) 0);
317 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
319 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN") ||
320 !bus_error_shall_fallback(&error
))
326 c
= count_addresses(reply
, af
, &canonical
);
334 if (isempty(canonical
))
337 alen
= FAMILY_ADDRESS_SIZE(af
);
338 l
= strlen(canonical
);
340 ms
= ALIGN(l
+1) + c
* ALIGN(alen
) + (c
+2) * sizeof(char*);
344 *h_errnop
= NETDB_INTERNAL
;
345 return NSS_STATUS_TRYAGAIN
;
348 /* First, append name */
350 memcpy(r_name
, canonical
, l
+1);
353 /* Second, create empty aliases array */
354 r_aliases
= buffer
+ idx
;
355 ((char**) r_aliases
)[0] = NULL
;
356 idx
+= sizeof(char*);
358 /* Third, append addresses */
359 r_addr
= buffer
+ idx
;
361 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
365 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
370 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
379 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
383 r
= sd_bus_message_exit_container(reply
);
395 memcpy(r_addr
+ i
*ALIGN(alen
), a
, alen
);
402 idx
+= c
* ALIGN(alen
);
404 /* Fourth, append address pointer array */
405 r_addr_list
= buffer
+ idx
;
406 for (i
= 0; i
< c
; i
++)
407 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
409 ((char**) r_addr_list
)[i
] = NULL
;
410 idx
+= (c
+1) * sizeof(char*);
414 result
->h_name
= r_name
;
415 result
->h_aliases
= (char**) r_aliases
;
416 result
->h_addrtype
= af
;
417 result
->h_length
= alen
;
418 result
->h_addr_list
= (char**) r_addr_list
;
426 /* Explicitly reset both *h_errnop and h_errno to work around
427 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
428 *h_errnop
= NETDB_SUCCESS
;
431 return NSS_STATUS_SUCCESS
;
435 *h_errnop
= NO_RECOVERY
;
439 *h_errnop
= HOST_NOT_FOUND
;
440 return NSS_STATUS_NOTFOUND
;
443 enum nss_status
_nss_resolve_gethostbyaddr2_r(
444 const void* addr
, socklen_t len
,
446 struct hostent
*result
,
447 char *buffer
, size_t buflen
,
448 int *errnop
, int *h_errnop
,
451 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
452 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
453 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
454 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
455 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
456 unsigned c
= 0, i
= 0;
462 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
470 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
471 *errnop
= EAFNOSUPPORT
;
473 return NSS_STATUS_UNAVAIL
;
476 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
478 *h_errnop
= NO_RECOVERY
;
479 return NSS_STATUS_UNAVAIL
;
482 r
= sd_bus_open_system(&bus
);
486 r
= sd_bus_message_new_method_call(
489 "org.freedesktop.resolve1",
490 "/org/freedesktop/resolve1",
491 "org.freedesktop.resolve1.Manager",
496 r
= sd_bus_message_set_auto_start(req
, false);
500 r
= sd_bus_message_append(req
, "ii", 0, af
);
504 r
= sd_bus_message_append_array(req
, 'y', addr
, len
);
508 r
= sd_bus_message_append(req
, "t", (uint64_t) 0);
512 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
514 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN") ||
515 !bus_error_shall_fallback(&error
))
521 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
525 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
533 ms
+= ALIGN(strlen(n
) + 1);
538 r
= sd_bus_message_rewind(reply
, false);
545 ms
+= ALIGN(len
) + /* the address */
546 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
547 c
* sizeof(char*); /* pointers to aliases, plus trailing NULL */
551 *h_errnop
= NETDB_INTERNAL
;
552 return NSS_STATUS_TRYAGAIN
;
555 /* First, place address */
557 memcpy(r_addr
, addr
, len
);
560 /* Second, place address list */
561 r_addr_list
= buffer
+ idx
;
562 ((char**) r_addr_list
)[0] = r_addr
;
563 ((char**) r_addr_list
)[1] = NULL
;
564 idx
+= sizeof(char*) * 2;
566 /* Third, reserve space for the aliases array */
567 r_aliases
= buffer
+ idx
;
568 idx
+= sizeof(char*) * c
;
570 /* Fourth, place aliases */
572 r_name
= buffer
+ idx
;
573 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
582 ((char**) r_aliases
)[i
-1] = p
;
590 ((char**) r_aliases
)[c
-1] = NULL
;
593 result
->h_name
= r_name
;
594 result
->h_aliases
= (char**) r_aliases
;
595 result
->h_addrtype
= af
;
596 result
->h_length
= len
;
597 result
->h_addr_list
= (char**) r_addr_list
;
602 /* Explicitly reset both *h_errnop and h_errno to work around
603 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
604 *h_errnop
= NETDB_SUCCESS
;
607 return NSS_STATUS_SUCCESS
;
611 *h_errnop
= NO_RECOVERY
;
615 *h_errnop
= HOST_NOT_FOUND
;
616 return NSS_STATUS_NOTFOUND
;
619 NSS_GETHOSTBYNAME_FALLBACKS(resolve
);
620 NSS_GETHOSTBYADDR_FALLBACKS(resolve
);