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 static bool avoid_deadlock(void) {
96 /* Check whether this lookup might have a chance of deadlocking because we are called from the service manager
97 * code activating systemd-resolved.service. After all, we shouldn't synchronously do lookups to
98 * systemd-resolved if we are required to finish before it can be started. This of course won't detect all
99 * possible dead locks of this kind, but it should work for the most obvious cases. */
101 if (geteuid() != 0) /* Ignore the env vars unless we are privileged. */
104 return streq_ptr(getenv("SYSTEMD_ACTIVATION_UNIT"), "systemd-resolved.service") &&
105 streq_ptr(getenv("SYSTEMD_ACTIVATION_SCOPE"), "system");
108 enum nss_status
_nss_resolve_gethostbyname4_r(
110 struct gaih_addrtuple
**pat
,
111 char *buffer
, size_t buflen
,
112 int *errnop
, int *h_errnop
,
115 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
116 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
117 struct gaih_addrtuple
*r_tuple
, *r_tuple_first
= NULL
;
118 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
119 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
120 const char *canonical
= NULL
;
126 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
134 if (avoid_deadlock()) {
139 r
= sd_bus_open_system(&bus
);
143 r
= sd_bus_message_new_method_call(
146 "org.freedesktop.resolve1",
147 "/org/freedesktop/resolve1",
148 "org.freedesktop.resolve1.Manager",
153 r
= sd_bus_message_set_auto_start(req
, false);
157 r
= sd_bus_message_append(req
, "isit", 0, name
, AF_UNSPEC
, (uint64_t) 0);
161 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
163 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN") ||
164 !bus_error_shall_fallback(&error
))
167 /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails,
168 allowing falling back to other nss modules. Treat all other error conditions as
169 NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this
170 case so that the nsswitch.conf configuration can distuingish such executed but
171 negative replies from complete failure to talk to resolved). */
175 c
= count_addresses(reply
, AF_UNSPEC
, &canonical
);
183 if (isempty(canonical
))
186 l
= strlen(canonical
);
187 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * c
;
190 *h_errnop
= NETDB_INTERNAL
;
191 return NSS_STATUS_TRYAGAIN
;
194 /* First, append name */
196 memcpy(r_name
, canonical
, l
+1);
199 /* Second, append addresses */
200 r_tuple_first
= (struct gaih_addrtuple
*) (buffer
+ idx
);
202 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
206 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
211 assert_cc(sizeof(int32_t) == sizeof(int));
213 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
222 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
226 r
= sd_bus_message_exit_container(reply
);
230 if (!IN_SET(family
, AF_INET
, AF_INET6
))
233 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
238 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
239 r_tuple
->next
= i
== c
-1 ? NULL
: (struct gaih_addrtuple
*) ((char*) r_tuple
+ ALIGN(sizeof(struct gaih_addrtuple
)));
240 r_tuple
->name
= r_name
;
241 r_tuple
->family
= family
;
242 r_tuple
->scopeid
= ifindex_to_scopeid(family
, a
, ifindex
);
243 memcpy(r_tuple
->addr
, a
, sz
);
245 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
255 **pat
= *r_tuple_first
;
257 *pat
= r_tuple_first
;
262 /* Explicitly reset both *h_errnop and h_errno to work around
263 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
264 *h_errnop
= NETDB_SUCCESS
;
267 return NSS_STATUS_SUCCESS
;
271 *h_errnop
= NO_RECOVERY
;
275 *h_errnop
= HOST_NOT_FOUND
;
276 return NSS_STATUS_NOTFOUND
;
279 enum nss_status
_nss_resolve_gethostbyname3_r(
282 struct hostent
*result
,
283 char *buffer
, size_t buflen
,
284 int *errnop
, int *h_errnop
,
288 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
289 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
290 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
291 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
292 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
293 size_t l
, idx
, ms
, alen
;
294 const char *canonical
;
298 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
309 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
314 if (avoid_deadlock()) {
319 r
= sd_bus_open_system(&bus
);
323 r
= sd_bus_message_new_method_call(
326 "org.freedesktop.resolve1",
327 "/org/freedesktop/resolve1",
328 "org.freedesktop.resolve1.Manager",
333 r
= sd_bus_message_set_auto_start(req
, false);
337 r
= sd_bus_message_append(req
, "isit", 0, name
, af
, (uint64_t) 0);
341 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
343 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN") ||
344 !bus_error_shall_fallback(&error
))
350 c
= count_addresses(reply
, af
, &canonical
);
358 if (isempty(canonical
))
361 alen
= FAMILY_ADDRESS_SIZE(af
);
362 l
= strlen(canonical
);
364 ms
= ALIGN(l
+1) + c
* ALIGN(alen
) + (c
+2) * sizeof(char*);
368 *h_errnop
= NETDB_INTERNAL
;
369 return NSS_STATUS_TRYAGAIN
;
372 /* First, append name */
374 memcpy(r_name
, canonical
, l
+1);
377 /* Second, create empty aliases array */
378 r_aliases
= buffer
+ idx
;
379 ((char**) r_aliases
)[0] = NULL
;
380 idx
+= sizeof(char*);
382 /* Third, append addresses */
383 r_addr
= buffer
+ idx
;
385 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
389 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
394 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
403 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
407 r
= sd_bus_message_exit_container(reply
);
419 memcpy(r_addr
+ i
*ALIGN(alen
), a
, alen
);
426 idx
+= c
* ALIGN(alen
);
428 /* Fourth, append address pointer array */
429 r_addr_list
= buffer
+ idx
;
430 for (i
= 0; i
< c
; i
++)
431 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
433 ((char**) r_addr_list
)[i
] = NULL
;
434 idx
+= (c
+1) * sizeof(char*);
438 result
->h_name
= r_name
;
439 result
->h_aliases
= (char**) r_aliases
;
440 result
->h_addrtype
= af
;
441 result
->h_length
= alen
;
442 result
->h_addr_list
= (char**) r_addr_list
;
450 /* Explicitly reset both *h_errnop and h_errno to work around
451 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
452 *h_errnop
= NETDB_SUCCESS
;
455 return NSS_STATUS_SUCCESS
;
459 *h_errnop
= NO_RECOVERY
;
463 *h_errnop
= HOST_NOT_FOUND
;
464 return NSS_STATUS_NOTFOUND
;
467 enum nss_status
_nss_resolve_gethostbyaddr2_r(
468 const void* addr
, socklen_t len
,
470 struct hostent
*result
,
471 char *buffer
, size_t buflen
,
472 int *errnop
, int *h_errnop
,
475 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
476 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
477 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
478 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
479 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
480 unsigned c
= 0, i
= 0;
486 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
494 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
495 *errnop
= EAFNOSUPPORT
;
497 return NSS_STATUS_UNAVAIL
;
500 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
502 *h_errnop
= NO_RECOVERY
;
503 return NSS_STATUS_UNAVAIL
;
506 if (avoid_deadlock()) {
511 r
= sd_bus_open_system(&bus
);
515 r
= sd_bus_message_new_method_call(
518 "org.freedesktop.resolve1",
519 "/org/freedesktop/resolve1",
520 "org.freedesktop.resolve1.Manager",
525 r
= sd_bus_message_set_auto_start(req
, false);
529 r
= sd_bus_message_append(req
, "ii", 0, af
);
533 r
= sd_bus_message_append_array(req
, 'y', addr
, len
);
537 r
= sd_bus_message_append(req
, "t", (uint64_t) 0);
541 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
543 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN") ||
544 !bus_error_shall_fallback(&error
))
550 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
554 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
562 ms
+= ALIGN(strlen(n
) + 1);
567 r
= sd_bus_message_rewind(reply
, false);
574 ms
+= ALIGN(len
) + /* the address */
575 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
576 c
* sizeof(char*); /* pointers to aliases, plus trailing NULL */
580 *h_errnop
= NETDB_INTERNAL
;
581 return NSS_STATUS_TRYAGAIN
;
584 /* First, place address */
586 memcpy(r_addr
, addr
, len
);
589 /* Second, place address list */
590 r_addr_list
= buffer
+ idx
;
591 ((char**) r_addr_list
)[0] = r_addr
;
592 ((char**) r_addr_list
)[1] = NULL
;
593 idx
+= sizeof(char*) * 2;
595 /* Third, reserve space for the aliases array */
596 r_aliases
= buffer
+ idx
;
597 idx
+= sizeof(char*) * c
;
599 /* Fourth, place aliases */
601 r_name
= buffer
+ idx
;
602 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
611 ((char**) r_aliases
)[i
-1] = p
;
619 ((char**) r_aliases
)[c
-1] = NULL
;
622 result
->h_name
= r_name
;
623 result
->h_aliases
= (char**) r_aliases
;
624 result
->h_addrtype
= af
;
625 result
->h_length
= len
;
626 result
->h_addr_list
= (char**) r_addr_list
;
631 /* Explicitly reset both *h_errnop and h_errno to work around
632 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
633 *h_errnop
= NETDB_SUCCESS
;
636 return NSS_STATUS_SUCCESS
;
640 *h_errnop
= NO_RECOVERY
;
644 *h_errnop
= HOST_NOT_FOUND
;
645 return NSS_STATUS_NOTFOUND
;
648 NSS_GETHOSTBYNAME_FALLBACKS(resolve
);
649 NSS_GETHOSTBYADDR_FALLBACKS(resolve
);