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
) ||
30 sd_bus_error_has_name(e
, SD_BUS_ERROR_DISCONNECTED
) ||
31 sd_bus_error_has_name(e
, SD_BUS_ERROR_TIMEOUT
);
34 static int count_addresses(sd_bus_message
*m
, int af
, const char **canonical
) {
40 r
= sd_bus_message_enter_container(m
, 'a', "(iiay)");
44 while ((r
= sd_bus_message_enter_container(m
, 'r', "iiay")) > 0) {
47 assert_cc(sizeof(int32_t) == sizeof(int));
49 r
= sd_bus_message_read(m
, "ii", &ifindex
, &family
);
53 r
= sd_bus_message_skip(m
, "ay");
57 r
= sd_bus_message_exit_container(m
);
61 if (af
!= AF_UNSPEC
&& family
!= af
)
69 r
= sd_bus_message_exit_container(m
);
73 r
= sd_bus_message_read(m
, "s", canonical
);
77 r
= sd_bus_message_rewind(m
, true);
84 static uint32_t ifindex_to_scopeid(int family
, const void *a
, int ifindex
) {
87 if (family
!= AF_INET6
)
90 /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */
92 assert(sizeof(in6
) == FAMILY_ADDRESS_SIZE(AF_INET6
));
93 memcpy(&in6
, a
, sizeof(struct in6_addr
));
95 return IN6_IS_ADDR_LINKLOCAL(&in6
) ? ifindex
: 0;
98 static bool avoid_deadlock(void) {
100 /* Check whether this lookup might have a chance of deadlocking because we are called from the service manager
101 * code activating systemd-resolved.service. After all, we shouldn't synchronously do lookups to
102 * systemd-resolved if we are required to finish before it can be started. This of course won't detect all
103 * possible dead locks of this kind, but it should work for the most obvious cases. */
105 if (geteuid() != 0) /* Ignore the env vars unless we are privileged. */
108 return streq_ptr(getenv("SYSTEMD_ACTIVATION_UNIT"), "systemd-resolved.service") &&
109 streq_ptr(getenv("SYSTEMD_ACTIVATION_SCOPE"), "system");
112 enum nss_status
_nss_resolve_gethostbyname4_r(
114 struct gaih_addrtuple
**pat
,
115 char *buffer
, size_t buflen
,
116 int *errnop
, int *h_errnop
,
119 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
120 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
121 struct gaih_addrtuple
*r_tuple
, *r_tuple_first
= NULL
;
122 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
123 const char *canonical
= NULL
;
129 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
137 if (avoid_deadlock()) {
142 r
= sd_bus_open_system(&bus
);
146 r
= sd_bus_message_new_method_call(
149 "org.freedesktop.resolve1",
150 "/org/freedesktop/resolve1",
151 "org.freedesktop.resolve1.Manager",
156 r
= sd_bus_message_set_auto_start(req
, false);
160 r
= sd_bus_message_append(req
, "isit", 0, name
, AF_UNSPEC
, (uint64_t) 0);
164 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
166 if (!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 distinguish 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
;
276 return NSS_STATUS_UNAVAIL
;
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 size_t l
, idx
, ms
, alen
;
297 const char *canonical
;
301 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
312 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
317 if (avoid_deadlock()) {
322 r
= sd_bus_open_system(&bus
);
326 r
= sd_bus_message_new_method_call(
329 "org.freedesktop.resolve1",
330 "/org/freedesktop/resolve1",
331 "org.freedesktop.resolve1.Manager",
336 r
= sd_bus_message_set_auto_start(req
, false);
340 r
= sd_bus_message_append(req
, "isit", 0, name
, af
, (uint64_t) 0);
344 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
346 if (!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
;
464 return NSS_STATUS_UNAVAIL
;
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 unsigned c
= 0, i
= 0;
489 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
497 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
499 *errnop
= EAFNOSUPPORT
;
501 return NSS_STATUS_UNAVAIL
;
504 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
509 if (avoid_deadlock()) {
514 r
= sd_bus_open_system(&bus
);
518 r
= sd_bus_message_new_method_call(
521 "org.freedesktop.resolve1",
522 "/org/freedesktop/resolve1",
523 "org.freedesktop.resolve1.Manager",
528 r
= sd_bus_message_set_auto_start(req
, false);
532 r
= sd_bus_message_append(req
, "ii", 0, af
);
536 r
= sd_bus_message_append_array(req
, 'y', addr
, len
);
540 r
= sd_bus_message_append(req
, "t", (uint64_t) 0);
544 r
= sd_bus_call(bus
, req
, SD_RESOLVED_QUERY_TIMEOUT_USEC
, &error
, &reply
);
546 if (!bus_error_shall_fallback(&error
))
552 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
556 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
564 ms
+= ALIGN(strlen(n
) + 1);
569 r
= sd_bus_message_rewind(reply
, false);
576 ms
+= ALIGN(len
) + /* the address */
577 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
578 c
* sizeof(char*); /* pointers to aliases, plus trailing NULL */
583 *h_errnop
= NETDB_INTERNAL
;
584 return NSS_STATUS_TRYAGAIN
;
587 /* First, place address */
589 memcpy(r_addr
, addr
, len
);
592 /* Second, place address list */
593 r_addr_list
= buffer
+ idx
;
594 ((char**) r_addr_list
)[0] = r_addr
;
595 ((char**) r_addr_list
)[1] = NULL
;
596 idx
+= sizeof(char*) * 2;
598 /* Third, reserve space for the aliases array */
599 r_aliases
= buffer
+ idx
;
600 idx
+= sizeof(char*) * c
;
602 /* Fourth, place aliases */
604 r_name
= buffer
+ idx
;
605 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
614 ((char**) r_aliases
)[i
-1] = p
;
622 ((char**) r_aliases
)[c
-1] = NULL
;
625 result
->h_name
= r_name
;
626 result
->h_aliases
= (char**) r_aliases
;
627 result
->h_addrtype
= af
;
628 result
->h_length
= len
;
629 result
->h_addr_list
= (char**) r_addr_list
;
634 /* Explicitly reset both *h_errnop and h_errno to work around
635 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
636 *h_errnop
= NETDB_SUCCESS
;
639 return NSS_STATUS_SUCCESS
;
644 *h_errnop
= NO_RECOVERY
;
645 return NSS_STATUS_UNAVAIL
;
648 *h_errnop
= HOST_NOT_FOUND
;
649 return NSS_STATUS_NOTFOUND
;
652 NSS_GETHOSTBYNAME_FALLBACKS(resolve
);
653 NSS_GETHOSTBYADDR_FALLBACKS(resolve
);