1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "sd-varlink.h"
11 #include "errno-util.h"
12 #include "glyph-util.h"
13 #include "in-addr-util.h"
14 #include "json-util.h"
16 #include "resolved-def.h"
17 #include "signal-util.h"
18 #include "string-util.h"
20 #include "time-util.h"
22 static sd_json_dispatch_flags_t json_dispatch_flags
= SD_JSON_ALLOW_EXTENSIONS
;
24 static void setup_logging(void) {
25 log_parse_environment_variables();
28 json_dispatch_flags
= SD_JSON_LOG
;
31 static void setup_logging_once(void) {
32 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
33 assert_se(pthread_once(&once
, setup_logging
) == 0);
36 #define NSS_ENTRYPOINT_BEGIN \
37 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); \
40 NSS_GETHOSTBYNAME_PROTOTYPES(resolve
);
41 NSS_GETHOSTBYADDR_PROTOTYPES(resolve
);
43 static bool error_shall_fallback(const char *error_id
) {
44 /* The Varlink errors where we shall signal "please fallback" back to the NSS stack, so that some
45 * fallback module can be loaded. (These are mostly all Varlink-internal errors, as apparently we
46 * then were unable to even do IPC with systemd-resolved.) */
47 return STR_IN_SET(error_id
,
48 SD_VARLINK_ERROR_DISCONNECTED
,
49 SD_VARLINK_ERROR_TIMEOUT
,
50 SD_VARLINK_ERROR_PROTOCOL
,
51 SD_VARLINK_ERROR_INTERFACE_NOT_FOUND
,
52 SD_VARLINK_ERROR_METHOD_NOT_FOUND
,
53 SD_VARLINK_ERROR_METHOD_NOT_IMPLEMENTED
);
56 static bool error_shall_try_again(const char *error_id
) {
57 /* The Varlink errors where we shall signal "can't answer now but might be able to later" back to the
58 * NSS stack. These are all errors that indicate lack of configuration or network problems. */
59 return STR_IN_SET(error_id
,
60 "io.systemd.Resolve.NoNameServers",
61 "io.systemd.Resolve.QueryTimedOut",
62 "io.systemd.Resolve.MaxAttemptsReached",
63 "io.systemd.Resolve.NetworkDown");
66 static int connect_to_resolved(sd_varlink
**ret
) {
67 _cleanup_(sd_varlink_unrefp
) sd_varlink
*link
= NULL
;
70 r
= sd_varlink_connect_address(&link
, "/run/systemd/resolve/io.systemd.Resolve");
74 r
= sd_varlink_set_relative_timeout(link
, SD_RESOLVED_QUERY_TIMEOUT_USEC
);
78 *ret
= TAKE_PTR(link
);
82 static uint32_t ifindex_to_scopeid(int family
, const void *a
, int ifindex
) {
85 if (family
!= AF_INET6
|| ifindex
== 0)
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_addr_is_link_local(&in6
) ? ifindex
: 0;
96 static int json_dispatch_family(const char *name
, sd_json_variant
*variant
, sd_json_dispatch_flags_t flags
, void *userdata
) {
97 int *family
= ASSERT_PTR(userdata
);
102 if (!sd_json_variant_is_integer(variant
))
103 return json_log(variant
, flags
, SYNTHETIC_ERRNO(EINVAL
), "JSON field '%s' is not an integer.", strna(name
));
105 t
= sd_json_variant_integer(variant
);
106 if (t
< 0 || t
> INT_MAX
)
107 return json_log(variant
, flags
, SYNTHETIC_ERRNO(EINVAL
), "JSON field '%s' is not a valid family.", strna(name
));
113 typedef struct ResolveHostnameReply
{
114 sd_json_variant
*addresses
;
117 } ResolveHostnameReply
;
119 static void resolve_hostname_reply_destroy(ResolveHostnameReply
*p
) {
122 sd_json_variant_unref(p
->addresses
);
126 static const sd_json_dispatch_field resolve_hostname_reply_dispatch_table
[] = {
127 { "addresses", SD_JSON_VARIANT_ARRAY
, sd_json_dispatch_variant
, offsetof(ResolveHostnameReply
, addresses
), SD_JSON_MANDATORY
},
128 { "name", SD_JSON_VARIANT_STRING
, sd_json_dispatch_string
, offsetof(ResolveHostnameReply
, name
), 0 },
129 { "flags", _SD_JSON_VARIANT_TYPE_INVALID
, sd_json_dispatch_uint64
, offsetof(ResolveHostnameReply
, flags
), 0 },
133 typedef struct AddressParameters
{
136 union in_addr_union address
;
140 static int json_dispatch_address(const char *name
, sd_json_variant
*variant
, sd_json_dispatch_flags_t flags
, void *userdata
) {
141 AddressParameters
*p
= ASSERT_PTR(userdata
);
142 union in_addr_union buf
= {};
148 if (!sd_json_variant_is_array(variant
))
149 return json_log(variant
, flags
, SYNTHETIC_ERRNO(EINVAL
), "JSON field '%s' is not an array.", strna(name
));
151 n
= sd_json_variant_elements(variant
);
152 if (!IN_SET(n
, 4, 16))
153 return json_log(variant
, flags
, SYNTHETIC_ERRNO(EINVAL
), "JSON field '%s' is array of unexpected size.", strna(name
));
155 JSON_VARIANT_ARRAY_FOREACH(i
, variant
) {
158 if (!sd_json_variant_is_integer(i
))
159 return json_log(variant
, flags
, SYNTHETIC_ERRNO(EINVAL
), "Element %zu of JSON field '%s' is not an integer.", k
, strna(name
));
161 b
= sd_json_variant_integer(i
);
162 if (b
< 0 || b
> 0xff)
163 return json_log(variant
, flags
, SYNTHETIC_ERRNO(EINVAL
),
164 "Element %zu of JSON field '%s' is out of range 0%s255.",
165 k
, strna(name
), glyph(GLYPH_ELLIPSIS
));
167 buf
.bytes
[k
++] = (uint8_t) b
;
176 static const sd_json_dispatch_field address_parameters_dispatch_table
[] = {
177 { "ifindex", SD_JSON_VARIANT_INTEGER
, json_dispatch_ifindex
, offsetof(AddressParameters
, ifindex
), 0 },
178 { "family", SD_JSON_VARIANT_INTEGER
, json_dispatch_family
, offsetof(AddressParameters
, family
), SD_JSON_MANDATORY
},
179 { "address", SD_JSON_VARIANT_ARRAY
, json_dispatch_address
, 0, SD_JSON_MANDATORY
},
183 static uint64_t query_flag(
190 r
= secure_getenv_bool(name
);
192 return r
== value
? flag
: 0;
194 log_debug_errno(r
, "Failed to parse $%s, ignoring.", name
);
198 static uint64_t query_flags(void) {
199 /* Allow callers to turn off validation, synthetization, caching, etc., when we resolve via
201 return query_flag("SYSTEMD_NSS_RESOLVE_VALIDATE", 0, SD_RESOLVED_NO_VALIDATE
) |
202 query_flag("SYSTEMD_NSS_RESOLVE_SYNTHESIZE", 0, SD_RESOLVED_NO_SYNTHESIZE
) |
203 query_flag("SYSTEMD_NSS_RESOLVE_CACHE", 0, SD_RESOLVED_NO_CACHE
) |
204 query_flag("SYSTEMD_NSS_RESOLVE_ZONE", 0, SD_RESOLVED_NO_ZONE
) |
205 query_flag("SYSTEMD_NSS_RESOLVE_TRUST_ANCHOR", 0, SD_RESOLVED_NO_TRUST_ANCHOR
) |
206 query_flag("SYSTEMD_NSS_RESOLVE_NETWORK", 0, SD_RESOLVED_NO_NETWORK
);
209 enum nss_status
_nss_resolve_gethostbyname4_r(
211 struct gaih_addrtuple
**pat
,
212 char *buffer
, size_t buflen
,
213 int *errnop
, int *h_errnop
,
216 _cleanup_(sd_varlink_unrefp
) sd_varlink
*link
= NULL
;
217 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*cparams
= NULL
;
218 _cleanup_(resolve_hostname_reply_destroy
) ResolveHostnameReply p
= {};
219 sd_json_variant
*rparams
, *entry
;
223 NSS_ENTRYPOINT_BEGIN
;
231 r
= connect_to_resolved(&link
);
237 SD_JSON_BUILD_PAIR("name", SD_JSON_BUILD_STRING(name
)),
238 SD_JSON_BUILD_PAIR("flags", SD_JSON_BUILD_UNSIGNED(query_flags())));
242 /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails, allowing falling
243 * back to other nss modules. Treat all other error conditions as NOTFOUND. This includes
244 * DNSSEC errors and suchlike. (We don't use UNAVAIL in this case so that the nsswitch.conf
245 * configuration can distinguish such executed but negative replies from complete failure to
246 * talk to resolved). */
247 const char *error_id
;
248 r
= sd_varlink_call(link
, "io.systemd.Resolve.ResolveHostname", cparams
, &rparams
, &error_id
);
251 if (!isempty(error_id
)) {
252 if (error_shall_try_again(error_id
))
254 if (error_shall_fallback(error_id
))
256 if (streq(error_id
, "io.systemd.Resolve.NoSuchResourceRecord"))
261 r
= sd_json_dispatch(rparams
, resolve_hostname_reply_dispatch_table
, json_dispatch_flags
, &p
);
264 if (sd_json_variant_is_blank_object(p
.addresses
))
267 size_t n_addresses
= 0;
268 JSON_VARIANT_ARRAY_FOREACH(entry
, p
.addresses
) {
269 AddressParameters q
= {};
271 r
= sd_json_dispatch(entry
, address_parameters_dispatch_table
, json_dispatch_flags
, &q
);
275 if (!IN_SET(q
.family
, AF_INET
, AF_INET6
))
278 if (q
.address_size
!= FAMILY_ADDRESS_SIZE(q
.family
)) {
286 const char *canonical
= p
.name
?: name
;
287 size_t l
= strlen(canonical
);
288 size_t idx
, ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * n_addresses
;
293 *h_errnop
= NETDB_INTERNAL
;
294 return NSS_STATUS_TRYAGAIN
;
297 /* First, append name */
298 char *r_name
= buffer
;
299 memcpy(r_name
, canonical
, l
+ 1);
302 /* Second, append addresses */
303 struct gaih_addrtuple
*r_tuple
= NULL
,
304 *r_tuple_first
= (struct gaih_addrtuple
*) (buffer
+ idx
);
306 JSON_VARIANT_ARRAY_FOREACH(entry
, p
.addresses
) {
307 AddressParameters q
= {};
309 r
= sd_json_dispatch(entry
, address_parameters_dispatch_table
, json_dispatch_flags
, &q
);
313 if (!IN_SET(q
.family
, AF_INET
, AF_INET6
))
316 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
317 r_tuple
->next
= (struct gaih_addrtuple
*) ((char*) r_tuple
+ ALIGN(sizeof(struct gaih_addrtuple
)));
318 r_tuple
->name
= r_name
;
319 r_tuple
->family
= q
.family
;
320 r_tuple
->scopeid
= ifindex_to_scopeid(q
.family
, &q
.address
, q
.ifindex
);
321 memcpy(r_tuple
->addr
, &q
.address
, q
.address_size
);
323 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
326 assert(r_tuple
); /* We had at least one address, so r_tuple must be set */
327 r_tuple
->next
= NULL
; /* Override last next pointer */
332 **pat
= *r_tuple_first
;
334 *pat
= r_tuple_first
;
339 /* Explicitly reset both *h_errnop and h_errno to work around
340 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
341 *h_errnop
= NETDB_SUCCESS
;
344 return NSS_STATUS_SUCCESS
;
349 *h_errnop
= NO_RECOVERY
;
350 return NSS_STATUS_UNAVAIL
;
353 *h_errnop
= HOST_NOT_FOUND
;
354 return NSS_STATUS_NOTFOUND
;
358 return NSS_STATUS_NOTFOUND
;
363 *h_errnop
= TRY_AGAIN
;
364 return NSS_STATUS_TRYAGAIN
;
367 enum nss_status
_nss_resolve_gethostbyname3_r(
370 struct hostent
*result
,
371 char *buffer
, size_t buflen
,
372 int *errnop
, int *h_errnop
,
376 _cleanup_(sd_varlink_unrefp
) sd_varlink
*link
= NULL
;
377 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*cparams
= NULL
;
378 _cleanup_(resolve_hostname_reply_destroy
) ResolveHostnameReply p
= {};
379 sd_json_variant
*rparams
, *entry
;
383 NSS_ENTRYPOINT_BEGIN
;
394 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
399 r
= connect_to_resolved(&link
);
405 SD_JSON_BUILD_PAIR("name", SD_JSON_BUILD_STRING(name
)),
406 SD_JSON_BUILD_PAIR("family", SD_JSON_BUILD_INTEGER(af
)),
407 SD_JSON_BUILD_PAIR("flags", SD_JSON_BUILD_UNSIGNED(query_flags())));
411 const char *error_id
;
412 r
= sd_varlink_call(link
, "io.systemd.Resolve.ResolveHostname", cparams
, &rparams
, &error_id
);
415 if (!isempty(error_id
)) {
416 if (error_shall_try_again(error_id
))
418 if (error_shall_fallback(error_id
))
420 if (streq(error_id
, "io.systemd.Resolve.NoSuchResourceRecord"))
425 r
= sd_json_dispatch(rparams
, resolve_hostname_reply_dispatch_table
, json_dispatch_flags
, &p
);
428 if (sd_json_variant_is_blank_object(p
.addresses
))
431 size_t n_addresses
= 0;
432 JSON_VARIANT_ARRAY_FOREACH(entry
, p
.addresses
) {
433 AddressParameters q
= {};
435 r
= sd_json_dispatch(entry
, address_parameters_dispatch_table
, json_dispatch_flags
, &q
);
439 if (!IN_SET(q
.family
, AF_INET
, AF_INET6
))
442 if (q
.address_size
!= FAMILY_ADDRESS_SIZE(q
.family
)) {
450 const char *canonical
= p
.name
?: name
;
452 size_t alen
= FAMILY_ADDRESS_SIZE(af
);
453 size_t l
= strlen(canonical
);
455 size_t idx
, ms
= ALIGN(l
+ 1) + n_addresses
* ALIGN(alen
) + (n_addresses
+ 2) * sizeof(char*);
460 *h_errnop
= NETDB_INTERNAL
;
461 return NSS_STATUS_TRYAGAIN
;
464 /* First, append name */
465 char *r_name
= buffer
;
466 memcpy(r_name
, canonical
, l
+1);
469 /* Second, create empty aliases array */
470 char *r_aliases
= buffer
+ idx
;
471 ((char**) r_aliases
)[0] = NULL
;
472 idx
+= sizeof(char*);
474 /* Third, append addresses */
475 char *r_addr
= buffer
+ idx
;
478 JSON_VARIANT_ARRAY_FOREACH(entry
, p
.addresses
) {
479 AddressParameters q
= {};
481 r
= sd_json_dispatch(entry
, address_parameters_dispatch_table
, json_dispatch_flags
, &q
);
488 if (q
.address_size
!= alen
) {
493 memcpy(r_addr
+ i
*ALIGN(alen
), &q
.address
, alen
);
497 assert(i
== n_addresses
);
498 idx
+= n_addresses
* ALIGN(alen
);
500 /* Fourth, append address pointer array */
501 char *r_addr_list
= buffer
+ idx
;
502 for (i
= 0; i
< n_addresses
; i
++)
503 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
505 ((char**) r_addr_list
)[i
] = NULL
;
506 idx
+= (n_addresses
+ 1) * sizeof(char*);
510 result
->h_name
= r_name
;
511 result
->h_aliases
= (char**) r_aliases
;
512 result
->h_addrtype
= af
;
513 result
->h_length
= alen
;
514 result
->h_addr_list
= (char**) r_addr_list
;
522 /* Explicitly reset both *h_errnop and h_errno to work around
523 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
524 *h_errnop
= NETDB_SUCCESS
;
527 return NSS_STATUS_SUCCESS
;
532 *h_errnop
= NO_RECOVERY
;
533 return NSS_STATUS_UNAVAIL
;
536 *h_errnop
= HOST_NOT_FOUND
;
537 return NSS_STATUS_NOTFOUND
;
541 return NSS_STATUS_NOTFOUND
;
546 *h_errnop
= TRY_AGAIN
;
547 return NSS_STATUS_TRYAGAIN
;
550 typedef struct ResolveAddressReply
{
551 sd_json_variant
*names
;
553 } ResolveAddressReply
;
555 static void resolve_address_reply_destroy(ResolveAddressReply
*p
) {
558 sd_json_variant_unref(p
->names
);
561 static const sd_json_dispatch_field resolve_address_reply_dispatch_table
[] = {
562 { "names", SD_JSON_VARIANT_ARRAY
, sd_json_dispatch_variant
, offsetof(ResolveAddressReply
, names
), SD_JSON_MANDATORY
},
563 { "flags", _SD_JSON_VARIANT_TYPE_INVALID
, sd_json_dispatch_uint64
, offsetof(ResolveAddressReply
, flags
), 0 },
567 typedef struct NameParameters
{
572 static void name_parameters_destroy(NameParameters
*p
) {
578 static const sd_json_dispatch_field name_parameters_dispatch_table
[] = {
579 { "ifindex", SD_JSON_VARIANT_INTEGER
, json_dispatch_ifindex
, offsetof(NameParameters
, ifindex
), 0 },
580 { "name", SD_JSON_VARIANT_STRING
, sd_json_dispatch_string
, offsetof(NameParameters
, name
), SD_JSON_MANDATORY
},
584 enum nss_status
_nss_resolve_gethostbyaddr2_r(
585 const void* addr
, socklen_t len
,
587 struct hostent
*result
,
588 char *buffer
, size_t buflen
,
589 int *errnop
, int *h_errnop
,
592 _cleanup_(sd_varlink_unrefp
) sd_varlink
*link
= NULL
;
593 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*cparams
= NULL
;
594 _cleanup_(resolve_address_reply_destroy
) ResolveAddressReply p
= {};
595 sd_json_variant
*rparams
, *entry
;
599 NSS_ENTRYPOINT_BEGIN
;
607 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
609 *errnop
= EAFNOSUPPORT
;
611 return NSS_STATUS_UNAVAIL
;
614 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
619 r
= connect_to_resolved(&link
);
625 SD_JSON_BUILD_PAIR("address", SD_JSON_BUILD_BYTE_ARRAY(addr
, len
)),
626 SD_JSON_BUILD_PAIR("family", SD_JSON_BUILD_INTEGER(af
)),
627 SD_JSON_BUILD_PAIR("flags", SD_JSON_BUILD_UNSIGNED(query_flags())));
631 const char* error_id
;
632 r
= sd_varlink_call(link
, "io.systemd.Resolve.ResolveAddress", cparams
, &rparams
, &error_id
);
635 if (!isempty(error_id
)) {
636 if (error_shall_try_again(error_id
))
638 if (error_shall_fallback(error_id
))
643 r
= sd_json_dispatch(rparams
, resolve_address_reply_dispatch_table
, json_dispatch_flags
, &p
);
646 if (sd_json_variant_is_blank_object(p
.names
))
651 JSON_VARIANT_ARRAY_FOREACH(entry
, p
.names
) {
652 _cleanup_(name_parameters_destroy
) NameParameters q
= {};
654 r
= sd_json_dispatch(entry
, name_parameters_dispatch_table
, json_dispatch_flags
, &q
);
658 ms
+= ALIGN(strlen(q
.name
) + 1);
661 size_t n_names
= sd_json_variant_elements(p
.names
);
662 ms
+= ALIGN(len
) + /* the address */
663 2 * sizeof(char*) + /* pointer to the address, plus trailing NULL */
664 n_names
* sizeof(char*); /* pointers to aliases, plus trailing NULL */
669 *h_errnop
= NETDB_INTERNAL
;
670 return NSS_STATUS_TRYAGAIN
;
673 /* First, place address */
674 char *r_addr
= buffer
;
675 memcpy(r_addr
, addr
, len
);
678 /* Second, place address list */
679 char *r_addr_list
= buffer
+ idx
;
680 ((char**) r_addr_list
)[0] = r_addr
;
681 ((char**) r_addr_list
)[1] = NULL
;
682 idx
+= sizeof(char*) * 2;
684 /* Third, reserve space for the aliases array, plus trailing NULL */
685 char *r_aliases
= buffer
+ idx
;
686 idx
+= sizeof(char*) * n_names
;
688 /* Fourth, place aliases */
689 char *r_name
= buffer
+ idx
;
692 JSON_VARIANT_ARRAY_FOREACH(entry
, p
.names
) {
693 _cleanup_(name_parameters_destroy
) NameParameters q
= {};
695 r
= sd_json_dispatch(entry
, name_parameters_dispatch_table
, json_dispatch_flags
, &q
);
699 size_t l
= strlen(q
.name
);
700 char *z
= buffer
+ idx
;
701 memcpy(z
, q
.name
, l
+ 1);
704 ((char**) r_aliases
)[i
- 1] = z
;
709 ((char**) r_aliases
)[n_names
- 1] = NULL
;
713 result
->h_name
= r_name
;
714 result
->h_aliases
= (char**) r_aliases
;
715 result
->h_addrtype
= af
;
716 result
->h_length
= len
;
717 result
->h_addr_list
= (char**) r_addr_list
;
722 /* Explicitly reset both *h_errnop and h_errno to work around
723 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
724 *h_errnop
= NETDB_SUCCESS
;
727 return NSS_STATUS_SUCCESS
;
732 *h_errnop
= NO_RECOVERY
;
733 return NSS_STATUS_UNAVAIL
;
736 *h_errnop
= HOST_NOT_FOUND
;
737 return NSS_STATUS_NOTFOUND
;
742 *h_errnop
= TRY_AGAIN
;
743 return NSS_STATUS_TRYAGAIN
;
746 NSS_GETHOSTBYNAME_FALLBACKS(resolve
);
747 NSS_GETHOSTBYADDR_FALLBACKS(resolve
);