1 /* SPDX-License-Identifier: LGPL-2.1+ */
13 #include "bus-common-errors.h"
14 #include "errno-util.h"
15 #include "in-addr-util.h"
18 #include "resolved-def.h"
19 #include "signal-util.h"
20 #include "string-util.h"
22 NSS_GETHOSTBYNAME_PROTOTYPES(resolve
);
23 NSS_GETHOSTBYADDR_PROTOTYPES(resolve
);
25 static bool bus_error_shall_fallback(sd_bus_error
*e
) {
26 return sd_bus_error_has_name(e
, SD_BUS_ERROR_SERVICE_UNKNOWN
) ||
27 sd_bus_error_has_name(e
, SD_BUS_ERROR_NAME_HAS_NO_OWNER
) ||
28 sd_bus_error_has_name(e
, SD_BUS_ERROR_NO_REPLY
) ||
29 sd_bus_error_has_name(e
, SD_BUS_ERROR_ACCESS_DENIED
);
32 static int count_addresses(sd_bus_message
*m
, int af
, const char **canonical
) {
38 r
= sd_bus_message_enter_container(m
, 'a', "(iiay)");
42 while ((r
= sd_bus_message_enter_container(m
, 'r', "iiay")) > 0) {
45 assert_cc(sizeof(int32_t) == sizeof(int));
47 r
= sd_bus_message_read(m
, "ii", &ifindex
, &family
);
51 r
= sd_bus_message_skip(m
, "ay");
55 r
= sd_bus_message_exit_container(m
);
59 if (af
!= AF_UNSPEC
&& family
!= af
)
67 r
= sd_bus_message_exit_container(m
);
71 r
= sd_bus_message_read(m
, "s", canonical
);
75 r
= sd_bus_message_rewind(m
, true);
82 static uint32_t ifindex_to_scopeid(int family
, const void *a
, int ifindex
) {
85 if (family
!= AF_INET6
)
88 /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */
90 assert(sizeof(in6
) == FAMILY_ADDRESS_SIZE(AF_INET6
));
91 memcpy(&in6
, a
, sizeof(struct in6_addr
));
93 return IN6_IS_ADDR_LINKLOCAL(&in6
) ? ifindex
: 0;
96 static bool avoid_deadlock(void) {
98 /* Check whether this lookup might have a chance of deadlocking because we are called from the service manager
99 * code activating systemd-resolved.service. After all, we shouldn't synchronously do lookups to
100 * systemd-resolved if we are required to finish before it can be started. This of course won't detect all
101 * possible dead locks of this kind, but it should work for the most obvious cases. */
103 if (geteuid() != 0) /* Ignore the env vars unless we are privileged. */
106 return streq_ptr(getenv("SYSTEMD_ACTIVATION_UNIT"), "systemd-resolved.service") &&
107 streq_ptr(getenv("SYSTEMD_ACTIVATION_SCOPE"), "system");
110 enum nss_status
_nss_resolve_gethostbyname4_r(
112 struct gaih_addrtuple
**pat
,
113 char *buffer
, size_t buflen
,
114 int *errnop
, int *h_errnop
,
117 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
118 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
119 struct gaih_addrtuple
*r_tuple
, *r_tuple_first
= NULL
;
120 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
121 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
122 const char *canonical
= NULL
;
128 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
136 if (avoid_deadlock()) {
141 r
= sd_bus_open_system(&bus
);
145 r
= sd_bus_message_new_method_call(
148 "org.freedesktop.resolve1",
149 "/org/freedesktop/resolve1",
150 "org.freedesktop.resolve1.Manager",
155 r
= sd_bus_message_set_auto_start(req
, false);
159 r
= sd_bus_message_append(req
, "isit", 0, name
, AF_UNSPEC
, (uint64_t) 0);
163 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
165 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN") ||
166 !bus_error_shall_fallback(&error
))
169 /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails,
170 allowing falling back to other nss modules. Treat all other error conditions as
171 NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this
172 case so that the nsswitch.conf configuration can distuingish such executed but
173 negative replies from complete failure to talk to resolved). */
177 c
= count_addresses(reply
, AF_UNSPEC
, &canonical
);
185 if (isempty(canonical
))
188 l
= strlen(canonical
);
189 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * c
;
193 *h_errnop
= NETDB_INTERNAL
;
194 return NSS_STATUS_TRYAGAIN
;
197 /* First, append name */
199 memcpy(r_name
, canonical
, l
+1);
202 /* Second, append addresses */
203 r_tuple_first
= (struct gaih_addrtuple
*) (buffer
+ idx
);
205 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
209 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
214 assert_cc(sizeof(int32_t) == sizeof(int));
216 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
225 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
229 r
= sd_bus_message_exit_container(reply
);
233 if (!IN_SET(family
, AF_INET
, AF_INET6
))
236 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
241 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
242 r_tuple
->next
= i
== c
-1 ? NULL
: (struct gaih_addrtuple
*) ((char*) r_tuple
+ ALIGN(sizeof(struct gaih_addrtuple
)));
243 r_tuple
->name
= r_name
;
244 r_tuple
->family
= family
;
245 r_tuple
->scopeid
= ifindex_to_scopeid(family
, a
, ifindex
);
246 memcpy(r_tuple
->addr
, a
, sz
);
248 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
258 **pat
= *r_tuple_first
;
260 *pat
= r_tuple_first
;
265 /* Explicitly reset both *h_errnop and h_errno to work around
266 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
267 *h_errnop
= NETDB_SUCCESS
;
270 return NSS_STATUS_SUCCESS
;
275 *h_errnop
= NO_RECOVERY
;
279 *h_errnop
= HOST_NOT_FOUND
;
280 return NSS_STATUS_NOTFOUND
;
283 enum nss_status
_nss_resolve_gethostbyname3_r(
286 struct hostent
*result
,
287 char *buffer
, size_t buflen
,
288 int *errnop
, int *h_errnop
,
292 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
293 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
294 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
295 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
296 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
297 size_t l
, idx
, ms
, alen
;
298 const char *canonical
;
302 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
313 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
318 if (avoid_deadlock()) {
323 r
= sd_bus_open_system(&bus
);
327 r
= sd_bus_message_new_method_call(
330 "org.freedesktop.resolve1",
331 "/org/freedesktop/resolve1",
332 "org.freedesktop.resolve1.Manager",
337 r
= sd_bus_message_set_auto_start(req
, false);
341 r
= sd_bus_message_append(req
, "isit", 0, name
, af
, (uint64_t) 0);
345 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
347 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN") ||
348 !bus_error_shall_fallback(&error
))
354 c
= count_addresses(reply
, af
, &canonical
);
362 if (isempty(canonical
))
365 alen
= FAMILY_ADDRESS_SIZE(af
);
366 l
= strlen(canonical
);
368 ms
= ALIGN(l
+1) + c
* ALIGN(alen
) + (c
+2) * sizeof(char*);
373 *h_errnop
= NETDB_INTERNAL
;
374 return NSS_STATUS_TRYAGAIN
;
377 /* First, append name */
379 memcpy(r_name
, canonical
, l
+1);
382 /* Second, create empty aliases array */
383 r_aliases
= buffer
+ idx
;
384 ((char**) r_aliases
)[0] = NULL
;
385 idx
+= sizeof(char*);
387 /* Third, append addresses */
388 r_addr
= buffer
+ idx
;
390 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
394 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
399 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
408 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
412 r
= sd_bus_message_exit_container(reply
);
424 memcpy(r_addr
+ i
*ALIGN(alen
), a
, alen
);
431 idx
+= c
* ALIGN(alen
);
433 /* Fourth, append address pointer array */
434 r_addr_list
= buffer
+ idx
;
435 for (i
= 0; i
< c
; i
++)
436 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
438 ((char**) r_addr_list
)[i
] = NULL
;
439 idx
+= (c
+1) * sizeof(char*);
443 result
->h_name
= r_name
;
444 result
->h_aliases
= (char**) r_aliases
;
445 result
->h_addrtype
= af
;
446 result
->h_length
= alen
;
447 result
->h_addr_list
= (char**) r_addr_list
;
455 /* Explicitly reset both *h_errnop and h_errno to work around
456 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
457 *h_errnop
= NETDB_SUCCESS
;
460 return NSS_STATUS_SUCCESS
;
465 *h_errnop
= NO_RECOVERY
;
469 *h_errnop
= HOST_NOT_FOUND
;
470 return NSS_STATUS_NOTFOUND
;
473 enum nss_status
_nss_resolve_gethostbyaddr2_r(
474 const void* addr
, socklen_t len
,
476 struct hostent
*result
,
477 char *buffer
, size_t buflen
,
478 int *errnop
, int *h_errnop
,
481 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
482 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
483 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
484 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
485 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
486 unsigned c
= 0, i
= 0;
492 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
500 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
502 *errnop
= EAFNOSUPPORT
;
504 return NSS_STATUS_UNAVAIL
;
507 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
510 *h_errnop
= NO_RECOVERY
;
511 return NSS_STATUS_UNAVAIL
;
514 if (avoid_deadlock()) {
519 r
= sd_bus_open_system(&bus
);
523 r
= sd_bus_message_new_method_call(
526 "org.freedesktop.resolve1",
527 "/org/freedesktop/resolve1",
528 "org.freedesktop.resolve1.Manager",
533 r
= sd_bus_message_set_auto_start(req
, false);
537 r
= sd_bus_message_append(req
, "ii", 0, af
);
541 r
= sd_bus_message_append_array(req
, 'y', addr
, len
);
545 r
= sd_bus_message_append(req
, "t", (uint64_t) 0);
549 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
551 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN") ||
552 !bus_error_shall_fallback(&error
))
558 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
562 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
570 ms
+= ALIGN(strlen(n
) + 1);
575 r
= sd_bus_message_rewind(reply
, false);
582 ms
+= ALIGN(len
) + /* the address */
583 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
584 c
* sizeof(char*); /* pointers to aliases, plus trailing NULL */
589 *h_errnop
= NETDB_INTERNAL
;
590 return NSS_STATUS_TRYAGAIN
;
593 /* First, place address */
595 memcpy(r_addr
, addr
, len
);
598 /* Second, place address list */
599 r_addr_list
= buffer
+ idx
;
600 ((char**) r_addr_list
)[0] = r_addr
;
601 ((char**) r_addr_list
)[1] = NULL
;
602 idx
+= sizeof(char*) * 2;
604 /* Third, reserve space for the aliases array */
605 r_aliases
= buffer
+ idx
;
606 idx
+= sizeof(char*) * c
;
608 /* Fourth, place aliases */
610 r_name
= buffer
+ idx
;
611 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
620 ((char**) r_aliases
)[i
-1] = p
;
628 ((char**) r_aliases
)[c
-1] = NULL
;
631 result
->h_name
= r_name
;
632 result
->h_aliases
= (char**) r_aliases
;
633 result
->h_addrtype
= af
;
634 result
->h_length
= len
;
635 result
->h_addr_list
= (char**) r_addr_list
;
640 /* Explicitly reset both *h_errnop and h_errno to work around
641 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
642 *h_errnop
= NETDB_SUCCESS
;
645 return NSS_STATUS_SUCCESS
;
650 *h_errnop
= NO_RECOVERY
;
654 *h_errnop
= HOST_NOT_FOUND
;
655 return NSS_STATUS_NOTFOUND
;
658 NSS_GETHOSTBYNAME_FALLBACKS(resolve
);
659 NSS_GETHOSTBYADDR_FALLBACKS(resolve
);