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
;
191 *h_errnop
= NETDB_INTERNAL
;
192 return NSS_STATUS_TRYAGAIN
;
195 /* First, append name */
197 memcpy(r_name
, canonical
, l
+1);
200 /* Second, append addresses */
201 r_tuple_first
= (struct gaih_addrtuple
*) (buffer
+ idx
);
203 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
207 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
212 assert_cc(sizeof(int32_t) == sizeof(int));
214 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
223 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
227 r
= sd_bus_message_exit_container(reply
);
231 if (!IN_SET(family
, AF_INET
, AF_INET6
))
234 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
239 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
240 r_tuple
->next
= i
== c
-1 ? NULL
: (struct gaih_addrtuple
*) ((char*) r_tuple
+ ALIGN(sizeof(struct gaih_addrtuple
)));
241 r_tuple
->name
= r_name
;
242 r_tuple
->family
= family
;
243 r_tuple
->scopeid
= ifindex_to_scopeid(family
, a
, ifindex
);
244 memcpy(r_tuple
->addr
, a
, sz
);
246 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
256 **pat
= *r_tuple_first
;
258 *pat
= r_tuple_first
;
263 /* Explicitly reset both *h_errnop and h_errno to work around
264 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
265 *h_errnop
= NETDB_SUCCESS
;
268 return NSS_STATUS_SUCCESS
;
273 *h_errnop
= NO_RECOVERY
;
277 *h_errnop
= HOST_NOT_FOUND
;
278 return NSS_STATUS_NOTFOUND
;
281 enum nss_status
_nss_resolve_gethostbyname3_r(
284 struct hostent
*result
,
285 char *buffer
, size_t buflen
,
286 int *errnop
, int *h_errnop
,
290 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
291 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
292 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
293 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
294 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
295 size_t l
, idx
, ms
, alen
;
296 const char *canonical
;
300 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
311 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
316 if (avoid_deadlock()) {
321 r
= sd_bus_open_system(&bus
);
325 r
= sd_bus_message_new_method_call(
328 "org.freedesktop.resolve1",
329 "/org/freedesktop/resolve1",
330 "org.freedesktop.resolve1.Manager",
335 r
= sd_bus_message_set_auto_start(req
, false);
339 r
= sd_bus_message_append(req
, "isit", 0, name
, af
, (uint64_t) 0);
343 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
345 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN") ||
346 !bus_error_shall_fallback(&error
))
352 c
= count_addresses(reply
, af
, &canonical
);
360 if (isempty(canonical
))
363 alen
= FAMILY_ADDRESS_SIZE(af
);
364 l
= strlen(canonical
);
366 ms
= ALIGN(l
+1) + c
* ALIGN(alen
) + (c
+2) * sizeof(char*);
371 *h_errnop
= NETDB_INTERNAL
;
372 return NSS_STATUS_TRYAGAIN
;
375 /* First, append name */
377 memcpy(r_name
, canonical
, l
+1);
380 /* Second, create empty aliases array */
381 r_aliases
= buffer
+ idx
;
382 ((char**) r_aliases
)[0] = NULL
;
383 idx
+= sizeof(char*);
385 /* Third, append addresses */
386 r_addr
= buffer
+ idx
;
388 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
392 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
397 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
406 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
410 r
= sd_bus_message_exit_container(reply
);
422 memcpy(r_addr
+ i
*ALIGN(alen
), a
, alen
);
429 idx
+= c
* ALIGN(alen
);
431 /* Fourth, append address pointer array */
432 r_addr_list
= buffer
+ idx
;
433 for (i
= 0; i
< c
; i
++)
434 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
436 ((char**) r_addr_list
)[i
] = NULL
;
437 idx
+= (c
+1) * sizeof(char*);
441 result
->h_name
= r_name
;
442 result
->h_aliases
= (char**) r_aliases
;
443 result
->h_addrtype
= af
;
444 result
->h_length
= alen
;
445 result
->h_addr_list
= (char**) r_addr_list
;
453 /* Explicitly reset both *h_errnop and h_errno to work around
454 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
455 *h_errnop
= NETDB_SUCCESS
;
458 return NSS_STATUS_SUCCESS
;
463 *h_errnop
= NO_RECOVERY
;
467 *h_errnop
= HOST_NOT_FOUND
;
468 return NSS_STATUS_NOTFOUND
;
471 enum nss_status
_nss_resolve_gethostbyaddr2_r(
472 const void* addr
, socklen_t len
,
474 struct hostent
*result
,
475 char *buffer
, size_t buflen
,
476 int *errnop
, int *h_errnop
,
479 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
480 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
481 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
482 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
483 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
484 unsigned c
= 0, i
= 0;
490 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
498 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
500 *errnop
= EAFNOSUPPORT
;
502 return NSS_STATUS_UNAVAIL
;
505 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
508 *h_errnop
= NO_RECOVERY
;
509 return NSS_STATUS_UNAVAIL
;
512 if (avoid_deadlock()) {
517 r
= sd_bus_open_system(&bus
);
521 r
= sd_bus_message_new_method_call(
524 "org.freedesktop.resolve1",
525 "/org/freedesktop/resolve1",
526 "org.freedesktop.resolve1.Manager",
531 r
= sd_bus_message_set_auto_start(req
, false);
535 r
= sd_bus_message_append(req
, "ii", 0, af
);
539 r
= sd_bus_message_append_array(req
, 'y', addr
, len
);
543 r
= sd_bus_message_append(req
, "t", (uint64_t) 0);
547 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
549 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN") ||
550 !bus_error_shall_fallback(&error
))
556 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
560 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
568 ms
+= ALIGN(strlen(n
) + 1);
573 r
= sd_bus_message_rewind(reply
, false);
580 ms
+= ALIGN(len
) + /* the address */
581 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
582 c
* sizeof(char*); /* pointers to aliases, plus trailing NULL */
587 *h_errnop
= NETDB_INTERNAL
;
588 return NSS_STATUS_TRYAGAIN
;
591 /* First, place address */
593 memcpy(r_addr
, addr
, len
);
596 /* Second, place address list */
597 r_addr_list
= buffer
+ idx
;
598 ((char**) r_addr_list
)[0] = r_addr
;
599 ((char**) r_addr_list
)[1] = NULL
;
600 idx
+= sizeof(char*) * 2;
602 /* Third, reserve space for the aliases array */
603 r_aliases
= buffer
+ idx
;
604 idx
+= sizeof(char*) * c
;
606 /* Fourth, place aliases */
608 r_name
= buffer
+ idx
;
609 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
618 ((char**) r_aliases
)[i
-1] = p
;
626 ((char**) r_aliases
)[c
-1] = NULL
;
629 result
->h_name
= r_name
;
630 result
->h_aliases
= (char**) r_aliases
;
631 result
->h_addrtype
= af
;
632 result
->h_length
= len
;
633 result
->h_addr_list
= (char**) r_addr_list
;
638 /* Explicitly reset both *h_errnop and h_errno to work around
639 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
640 *h_errnop
= NETDB_SUCCESS
;
643 return NSS_STATUS_SUCCESS
;
648 *h_errnop
= NO_RECOVERY
;
652 *h_errnop
= HOST_NOT_FOUND
;
653 return NSS_STATUS_NOTFOUND
;
656 NSS_GETHOSTBYNAME_FALLBACKS(resolve
);
657 NSS_GETHOSTBYADDR_FALLBACKS(resolve
);