1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "alloc-util.h"
9 #include "dlfcn-util.h"
11 #include "errno-list.h"
12 #include "format-util.h"
13 #include "hexdecoct.h"
14 #include "hostname-util.h"
15 #include "in-addr-util.h"
16 #include "local-addresses.h"
18 #include "main-func.h"
19 #include "nss-test-util.h"
21 #include "parse-util.h"
22 #include "path-util.h"
23 #include "socket-util.h"
24 #include "stdio-util.h"
25 #include "string-util.h"
29 static size_t arg_bufsize
= 1024;
31 static const char* af_to_string(int family
, char *buf
, size_t buf_len
) {
34 if (family
== AF_UNSPEC
)
37 name
= af_to_name(family
);
41 (void) snprintf(buf
, buf_len
, "%i", family
);
45 static int print_gaih_addrtuples(const struct gaih_addrtuple
*tuples
) {
48 for (const struct gaih_addrtuple
*it
= tuples
; it
; it
= it
->next
) {
49 _cleanup_free_
char *a
= NULL
;
50 union in_addr_union u
;
51 char family_name
[DECIMAL_STR_MAX(int)];
53 memcpy(&u
, it
->addr
, 16);
54 r
= in_addr_to_string(it
->family
, &u
, &a
);
55 assert_se(IN_SET(r
, 0, -EAFNOSUPPORT
));
56 if (r
== -EAFNOSUPPORT
)
57 assert_se(a
= hexmem(it
->addr
, 16));
59 log_info(" \"%s\" %s %s %s",
61 af_to_string(it
->family
, family_name
, sizeof family_name
),
63 FORMAT_IFNAME_FULL(it
->scopeid
, FORMAT_IFNAME_IFINDEX_WITH_PERCENT
));
70 static void print_struct_hostent(struct hostent
*host
, const char *canon
) {
71 log_info(" \"%s\"", host
->h_name
);
72 STRV_FOREACH(s
, host
->h_aliases
)
73 log_info(" alias \"%s\"", *s
);
74 STRV_FOREACH(s
, host
->h_addr_list
) {
75 union in_addr_union u
;
76 _cleanup_free_
char *a
= NULL
;
77 char family_name
[DECIMAL_STR_MAX(int)];
80 assert_se((unsigned) host
->h_length
== FAMILY_ADDRESS_SIZE(host
->h_addrtype
));
81 memcpy(&u
, *s
, host
->h_length
);
82 r
= in_addr_to_string(host
->h_addrtype
, &u
, &a
);
85 af_to_string(host
->h_addrtype
, family_name
, sizeof family_name
),
89 log_info(" canonical: \"%s\"", canon
);
92 static void test_gethostbyname4_r(void *handle
, const char *module
, const char *name
) {
94 _nss_gethostbyname4_r_t f
;
95 char buffer
[arg_bufsize
];
96 struct gaih_addrtuple
*pat
= NULL
;
97 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
98 int32_t ttl
= INT32_MAX
; /* nss-dns wants to return the lowest ttl,
99 and will access this variable through *ttlp,
100 so we need to set it to something.
101 I'm not sure if this is a bug in nss-dns
103 enum nss_status status
;
104 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
107 fname
= strjoina("_nss_", module
, "_gethostbyname4_r");
108 f
= dlsym(handle
, fname
);
109 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
111 log_info("%s not defined", fname
);
115 status
= f(name
, &pat
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
);
116 if (status
== NSS_STATUS_SUCCESS
) {
117 log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%"PRIxPTR
" errno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
119 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
120 pat
? (uintptr_t) pat
- (uintptr_t) buffer
: 0,
121 errno1
, errno_to_name(errno1
) ?: "---",
122 errno2
, hstrerror(errno2
),
124 n
= print_gaih_addrtuples(pat
);
126 log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s",
128 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
130 errno1
, errno_to_name(errno1
) ?: "---",
131 errno2
, hstrerror(errno2
));
135 if (STR_IN_SET(module
, "resolve", "mymachines") && status
== NSS_STATUS_UNAVAIL
)
138 if (streq(name
, "localhost")) {
139 if (streq(module
, "myhostname")) {
140 assert_se(status
== NSS_STATUS_SUCCESS
);
141 assert_se(n
== socket_ipv6_is_enabled() + 1);
143 } else if (streq(module
, "resolve") && getenv_bool_secure("SYSTEMD_NSS_RESOLVE_SYNTHESIZE") != 0) {
144 assert_se(status
== NSS_STATUS_SUCCESS
);
145 if (socket_ipv6_is_enabled())
148 assert_se(n
<= 2); /* Even if IPv6 is disabled, /etc/hosts may contain ::1. */
153 static void test_gethostbyname3_r(void *handle
, const char *module
, const char *name
, int af
) {
155 _nss_gethostbyname3_r_t f
;
156 char buffer
[arg_bufsize
];
157 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
158 int32_t ttl
= INT32_MAX
; /* nss-dns wants to return the lowest ttl,
159 and will access this variable through *ttlp,
160 so we need to set it to something.
161 I'm not sure if this is a bug in nss-dns
163 enum nss_status status
;
164 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
167 char family_name
[DECIMAL_STR_MAX(int)];
169 fname
= strjoina("_nss_", module
, "_gethostbyname3_r");
170 f
= dlsym(handle
, fname
);
171 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
173 log_info("%s not defined", fname
);
177 status
= f(name
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
, &canon
);
178 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
179 fname
, name
, af_to_string(af
, family_name
, sizeof family_name
),
180 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
181 errno1
, errno_to_name(errno1
) ?: "---",
182 errno2
, hstrerror(errno2
),
184 if (status
== NSS_STATUS_SUCCESS
)
185 print_struct_hostent(&host
, canon
);
188 static void test_gethostbyname2_r(void *handle
, const char *module
, const char *name
, int af
) {
190 _nss_gethostbyname2_r_t f
;
191 char buffer
[arg_bufsize
];
192 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
193 enum nss_status status
;
194 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
196 char family_name
[DECIMAL_STR_MAX(int)];
198 fname
= strjoina("_nss_", module
, "_gethostbyname2_r");
199 f
= dlsym(handle
, fname
);
200 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
202 log_info("%s not defined", fname
);
206 status
= f(name
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
207 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s",
208 fname
, name
, af_to_string(af
, family_name
, sizeof family_name
),
209 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
210 errno1
, errno_to_name(errno1
) ?: "---",
211 errno2
, hstrerror(errno2
));
212 if (status
== NSS_STATUS_SUCCESS
)
213 print_struct_hostent(&host
, NULL
);
216 static void test_gethostbyname_r(void *handle
, const char *module
, const char *name
) {
218 _nss_gethostbyname_r_t f
;
219 char buffer
[arg_bufsize
];
220 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
221 enum nss_status status
;
222 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
225 fname
= strjoina("_nss_", module
, "_gethostbyname_r");
226 f
= dlsym(handle
, fname
);
227 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
229 log_info("%s not defined", fname
);
233 status
= f(name
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
234 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
236 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
237 errno1
, errno_to_name(errno1
) ?: "---",
238 errno2
, hstrerror(errno2
));
239 if (status
== NSS_STATUS_SUCCESS
)
240 print_struct_hostent(&host
, NULL
);
243 static void test_gethostbyaddr2_r(void *handle
,
245 const void* addr
, socklen_t len
,
249 _nss_gethostbyaddr2_r_t f
;
250 char buffer
[arg_bufsize
];
251 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
252 enum nss_status status
;
253 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
255 int32_t ttl
= INT32_MAX
;
256 _cleanup_free_
char *addr_pretty
= NULL
;
258 fname
= strjoina("_nss_", module
, "_gethostbyaddr2_r");
259 f
= dlsym(handle
, fname
);
261 log_full_errno(f
? LOG_DEBUG
: LOG_INFO
, errno
,
262 "dlsym(0x%p, %s) → 0x%p: %m", handle
, fname
, f
);
264 log_info("%s not defined", fname
);
268 assert_se(in_addr_to_string(af
, addr
, &addr_pretty
) >= 0);
270 status
= f(addr
, len
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
);
271 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
273 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
274 errno1
, errno_to_name(errno1
) ?: "---",
275 errno2
, hstrerror(errno2
),
277 if (status
== NSS_STATUS_SUCCESS
)
278 print_struct_hostent(&host
, NULL
);
281 static void test_gethostbyaddr_r(void *handle
,
283 const void* addr
, socklen_t len
,
287 _nss_gethostbyaddr_r_t f
;
288 char buffer
[arg_bufsize
];
289 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
290 enum nss_status status
;
291 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
293 _cleanup_free_
char *addr_pretty
= NULL
;
295 fname
= strjoina("_nss_", module
, "_gethostbyaddr_r");
296 f
= dlsym(handle
, fname
);
298 log_full_errno(f
? LOG_DEBUG
: LOG_INFO
, errno
,
299 "dlsym(0x%p, %s) → 0x%p: %m", handle
, fname
, f
);
301 log_info("%s not defined", fname
);
305 assert_se(in_addr_to_string(af
, addr
, &addr_pretty
) >= 0);
307 status
= f(addr
, len
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
308 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
310 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
311 errno1
, errno_to_name(errno1
) ?: "---",
312 errno2
, hstrerror(errno2
));
313 if (status
== NSS_STATUS_SUCCESS
)
314 print_struct_hostent(&host
, NULL
);
317 static void test_byname(void *handle
, const char *module
, const char *name
) {
318 test_gethostbyname4_r(handle
, module
, name
);
321 test_gethostbyname3_r(handle
, module
, name
, AF_INET
);
323 test_gethostbyname3_r(handle
, module
, name
, AF_INET6
);
325 test_gethostbyname3_r(handle
, module
, name
, AF_UNSPEC
);
327 test_gethostbyname3_r(handle
, module
, name
, AF_UNIX
);
330 test_gethostbyname2_r(handle
, module
, name
, AF_INET
);
332 test_gethostbyname2_r(handle
, module
, name
, AF_INET6
);
334 test_gethostbyname2_r(handle
, module
, name
, AF_UNSPEC
);
336 test_gethostbyname2_r(handle
, module
, name
, AF_UNIX
);
339 test_gethostbyname_r(handle
, module
, name
);
343 static void test_byaddr(void *handle
,
345 const void* addr
, socklen_t len
,
347 test_gethostbyaddr2_r(handle
, module
, addr
, len
, af
);
350 test_gethostbyaddr_r(handle
, module
, addr
, len
, af
);
354 static int make_addresses(struct local_address
**addresses
) {
356 _cleanup_free_
struct local_address
*addrs
= NULL
;
358 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addrs
);
360 log_info_errno(n
, "Failed to query local addresses: %m");
362 assert_se(GREEDY_REALLOC(addrs
, n
+ 3));
364 addrs
[n
++] = (struct local_address
) { .family
= AF_INET
,
365 .address
.in
= { htobe32(0x7F000001) } };
366 addrs
[n
++] = (struct local_address
) { .family
= AF_INET
,
367 .address
.in
= { htobe32(0x7F000002) } };
368 addrs
[n
++] = (struct local_address
) { .family
= AF_INET6
,
369 .address
.in6
= in6addr_loopback
};
371 *addresses
= TAKE_PTR(addrs
);
375 static int test_one_module(const char *dir
,
378 struct local_address
*addresses
,
381 log_info("======== %s ========", module
);
383 _cleanup_(dlclosep
) void *handle
= nss_open_handle(dir
, module
, RTLD_LAZY
|RTLD_NODELETE
);
387 STRV_FOREACH(name
, names
)
388 test_byname(handle
, module
, *name
);
390 for (int i
= 0; i
< n_addresses
; i
++)
391 test_byaddr(handle
, module
,
392 &addresses
[i
].address
,
393 FAMILY_ADDRESS_SIZE(addresses
[i
].family
),
394 addresses
[i
].family
);
400 static int parse_argv(int argc
, char **argv
,
403 struct local_address
**the_addresses
, int *n_addresses
) {
405 _cleanup_strv_free_
char **modules
= NULL
, **names
= NULL
;
406 _cleanup_free_
struct local_address
*addrs
= NULL
;
410 p
= getenv("SYSTEMD_TEST_NSS_BUFSIZE");
412 r
= safe_atozu(p
, &arg_bufsize
);
414 return log_error_errno(r
, "Failed to parse $SYSTEMD_TEST_NSS_BUFSIZE");
418 modules
= strv_new(argv
[1]);
421 #if ENABLE_NSS_MYHOSTNAME
424 #if ENABLE_NSS_RESOLVE
427 #if ENABLE_NSS_MYMACHINES
435 union in_addr_union address
;
437 STRV_FOREACH(name
, argv
+ 2) {
438 r
= in_addr_from_string_auto(*name
, &family
, &address
);
440 /* assume this is a name */
441 r
= strv_extend(&names
, *name
);
445 assert_se(GREEDY_REALLOC0(addrs
, n
+ 1));
447 addrs
[n
++] = (struct local_address
) { .family
= family
,
448 .address
= address
};
452 _cleanup_free_
char *hostname
;
453 assert_se(hostname
= gethostname_malloc());
454 assert_se(names
= strv_new("localhost", "_gateway", "_outbound", "foo_no_such_host", hostname
));
456 n
= make_addresses(&addrs
);
460 *the_modules
= TAKE_PTR(modules
);
461 *the_names
= TAKE_PTR(names
);
462 *the_addresses
= TAKE_PTR(addrs
);
467 static int run(int argc
, char **argv
) {
468 _cleanup_free_
char *dir
= NULL
;
469 _cleanup_strv_free_
char **modules
= NULL
, **names
= NULL
;
470 _cleanup_free_
struct local_address
*addresses
= NULL
;
474 test_setup_logging(LOG_INFO
);
476 r
= parse_argv(argc
, argv
, &modules
, &names
, &addresses
, &n_addresses
);
478 return log_error_errno(r
, "Failed to parse arguments: %m");
480 assert_se(path_extract_directory(argv
[0], &dir
) >= 0);
482 STRV_FOREACH(module
, modules
) {
483 r
= test_one_module(dir
, *module
, names
, addresses
, n_addresses
);
491 DEFINE_MAIN_FUNCTION(run
);