1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "alloc-util.h"
9 #include "dlfcn-util.h"
10 #include "errno-list.h"
11 #include "format-util.h"
12 #include "hexdecoct.h"
13 #include "hostname-util.h"
14 #include "in-addr-util.h"
15 #include "local-addresses.h"
17 #include "main-func.h"
18 #include "nss-test-util.h"
20 #include "parse-util.h"
21 #include "path-util.h"
22 #include "stdio-util.h"
23 #include "string-util.h"
27 static size_t arg_bufsize
= 1024;
29 static const char* af_to_string(int family
, char *buf
, size_t buf_len
) {
32 if (family
== AF_UNSPEC
)
35 name
= af_to_name(family
);
39 snprintf(buf
, buf_len
, "%i", family
);
43 static int print_gaih_addrtuples(const struct gaih_addrtuple
*tuples
) {
46 for (const struct gaih_addrtuple
*it
= tuples
; it
; it
= it
->next
) {
47 _cleanup_free_
char *a
= NULL
;
48 union in_addr_union u
;
50 char family_name
[DECIMAL_STR_MAX(int)];
51 char ifname
[IF_NAMESIZE
+ 1];
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));
62 if (!format_ifname(it
->scopeid
, ifname
)) {
63 log_warning_errno(errno
, "if_indextoname(%d) failed: %m", it
->scopeid
);
65 xsprintf(ifname
, "%i", it
->scopeid
);
68 log_info(" \"%s\" %s %s %%%s",
70 af_to_string(it
->family
, family_name
, sizeof family_name
),
78 static void print_struct_hostent(struct hostent
*host
, const char *canon
) {
81 log_info(" \"%s\"", host
->h_name
);
82 STRV_FOREACH(s
, host
->h_aliases
)
83 log_info(" alias \"%s\"", *s
);
84 STRV_FOREACH(s
, host
->h_addr_list
) {
85 union in_addr_union u
;
86 _cleanup_free_
char *a
= NULL
;
87 char family_name
[DECIMAL_STR_MAX(int)];
90 assert_se((unsigned) host
->h_length
== FAMILY_ADDRESS_SIZE(host
->h_addrtype
));
91 memcpy(&u
, *s
, host
->h_length
);
92 r
= in_addr_to_string(host
->h_addrtype
, &u
, &a
);
95 af_to_string(host
->h_addrtype
, family_name
, sizeof family_name
),
99 log_info(" canonical: \"%s\"", canon
);
102 static void test_gethostbyname4_r(void *handle
, const char *module
, const char *name
) {
104 _nss_gethostbyname4_r_t f
;
105 char buffer
[arg_bufsize
];
106 struct gaih_addrtuple
*pat
= NULL
;
107 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
108 int32_t ttl
= INT32_MAX
; /* nss-dns wants to return the lowest ttl,
109 and will access this variable through *ttlp,
110 so we need to set it to something.
111 I'm not sure if this is a bug in nss-dns
113 enum nss_status status
;
114 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
117 fname
= strjoina("_nss_", module
, "_gethostbyname4_r");
118 f
= dlsym(handle
, fname
);
119 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
121 log_info("%s not defined", fname
);
125 status
= f(name
, &pat
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
);
126 if (status
== NSS_STATUS_SUCCESS
) {
127 log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%tx errno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
129 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
130 pat
? (char*) pat
- buffer
: 0,
131 errno1
, errno_to_name(errno1
) ?: "---",
132 errno2
, hstrerror(errno2
),
134 n
= print_gaih_addrtuples(pat
);
136 log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s",
138 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
140 errno1
, errno_to_name(errno1
) ?: "---",
141 errno2
, hstrerror(errno2
));
145 if (STR_IN_SET(module
, "resolve", "mymachines") && status
== NSS_STATUS_UNAVAIL
)
148 if (STR_IN_SET(module
, "myhostname", "resolve") && streq(name
, "localhost")) {
149 assert_se(status
== NSS_STATUS_SUCCESS
);
154 static void test_gethostbyname3_r(void *handle
, const char *module
, const char *name
, int af
) {
156 _nss_gethostbyname3_r_t f
;
157 char buffer
[arg_bufsize
];
158 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
159 int32_t ttl
= INT32_MAX
; /* nss-dns wants to return the lowest ttl,
160 and will access this variable through *ttlp,
161 so we need to set it to something.
162 I'm not sure if this is a bug in nss-dns
164 enum nss_status status
;
165 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
168 char family_name
[DECIMAL_STR_MAX(int)];
170 fname
= strjoina("_nss_", module
, "_gethostbyname3_r");
171 f
= dlsym(handle
, fname
);
172 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
174 log_info("%s not defined", fname
);
178 status
= f(name
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
, &canon
);
179 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
180 fname
, name
, af_to_string(af
, family_name
, sizeof family_name
),
181 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
182 errno1
, errno_to_name(errno1
) ?: "---",
183 errno2
, hstrerror(errno2
),
185 if (status
== NSS_STATUS_SUCCESS
)
186 print_struct_hostent(&host
, canon
);
189 static void test_gethostbyname2_r(void *handle
, const char *module
, const char *name
, int af
) {
191 _nss_gethostbyname2_r_t f
;
192 char buffer
[arg_bufsize
];
193 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
194 enum nss_status status
;
195 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
197 char family_name
[DECIMAL_STR_MAX(int)];
199 fname
= strjoina("_nss_", module
, "_gethostbyname2_r");
200 f
= dlsym(handle
, fname
);
201 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
203 log_info("%s not defined", fname
);
207 status
= f(name
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
208 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s",
209 fname
, name
, af_to_string(af
, family_name
, sizeof family_name
),
210 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
211 errno1
, errno_to_name(errno1
) ?: "---",
212 errno2
, hstrerror(errno2
));
213 if (status
== NSS_STATUS_SUCCESS
)
214 print_struct_hostent(&host
, NULL
);
217 static void test_gethostbyname_r(void *handle
, const char *module
, const char *name
) {
219 _nss_gethostbyname_r_t f
;
220 char buffer
[arg_bufsize
];
221 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
222 enum nss_status status
;
223 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
226 fname
= strjoina("_nss_", module
, "_gethostbyname_r");
227 f
= dlsym(handle
, fname
);
228 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
230 log_info("%s not defined", fname
);
234 status
= f(name
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
235 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
237 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
238 errno1
, errno_to_name(errno1
) ?: "---",
239 errno2
, hstrerror(errno2
));
240 if (status
== NSS_STATUS_SUCCESS
)
241 print_struct_hostent(&host
, NULL
);
244 static void test_gethostbyaddr2_r(void *handle
,
246 const void* addr
, socklen_t len
,
250 _nss_gethostbyaddr2_r_t f
;
251 char buffer
[arg_bufsize
];
252 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
253 enum nss_status status
;
254 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
256 int32_t ttl
= INT32_MAX
;
257 _cleanup_free_
char *addr_pretty
= NULL
;
259 fname
= strjoina("_nss_", module
, "_gethostbyaddr2_r");
260 f
= dlsym(handle
, fname
);
262 log_full_errno(f
? LOG_DEBUG
: LOG_INFO
, errno
,
263 "dlsym(0x%p, %s) → 0x%p: %m", handle
, fname
, f
);
265 log_info("%s not defined", fname
);
269 assert_se(in_addr_to_string(af
, addr
, &addr_pretty
) >= 0);
271 status
= f(addr
, len
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
);
272 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
274 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
275 errno1
, errno_to_name(errno1
) ?: "---",
276 errno2
, hstrerror(errno2
),
278 if (status
== NSS_STATUS_SUCCESS
)
279 print_struct_hostent(&host
, NULL
);
282 static void test_gethostbyaddr_r(void *handle
,
284 const void* addr
, socklen_t len
,
288 _nss_gethostbyaddr_r_t f
;
289 char buffer
[arg_bufsize
];
290 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
291 enum nss_status status
;
292 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
294 _cleanup_free_
char *addr_pretty
= NULL
;
296 fname
= strjoina("_nss_", module
, "_gethostbyaddr_r");
297 f
= dlsym(handle
, fname
);
299 log_full_errno(f
? LOG_DEBUG
: LOG_INFO
, errno
,
300 "dlsym(0x%p, %s) → 0x%p: %m", handle
, fname
, f
);
302 log_info("%s not defined", fname
);
306 assert_se(in_addr_to_string(af
, addr
, &addr_pretty
) >= 0);
308 status
= f(addr
, len
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
309 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
311 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
312 errno1
, errno_to_name(errno1
) ?: "---",
313 errno2
, hstrerror(errno2
));
314 if (status
== NSS_STATUS_SUCCESS
)
315 print_struct_hostent(&host
, NULL
);
318 static void test_byname(void *handle
, const char *module
, const char *name
) {
319 test_gethostbyname4_r(handle
, module
, name
);
322 test_gethostbyname3_r(handle
, module
, name
, AF_INET
);
324 test_gethostbyname3_r(handle
, module
, name
, AF_INET6
);
326 test_gethostbyname3_r(handle
, module
, name
, AF_UNSPEC
);
328 test_gethostbyname3_r(handle
, module
, name
, AF_LOCAL
);
331 test_gethostbyname2_r(handle
, module
, name
, AF_INET
);
333 test_gethostbyname2_r(handle
, module
, name
, AF_INET6
);
335 test_gethostbyname2_r(handle
, module
, name
, AF_UNSPEC
);
337 test_gethostbyname2_r(handle
, module
, name
, AF_LOCAL
);
340 test_gethostbyname_r(handle
, module
, name
);
344 static void test_byaddr(void *handle
,
346 const void* addr
, socklen_t len
,
348 test_gethostbyaddr2_r(handle
, module
, addr
, len
, af
);
351 test_gethostbyaddr_r(handle
, module
, addr
, len
, af
);
355 static int make_addresses(struct local_address
**addresses
) {
358 _cleanup_free_
struct local_address
*addrs
= NULL
;
360 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addrs
);
362 log_info_errno(n
, "Failed to query local addresses: %m");
364 n_alloc
= n
; /* we _can_ do that */
365 assert_se(GREEDY_REALLOC(addrs
, n_alloc
, n
+ 3));
367 addrs
[n
++] = (struct local_address
) { .family
= AF_INET
,
368 .address
.in
= { htobe32(0x7F000001) } };
369 addrs
[n
++] = (struct local_address
) { .family
= AF_INET
,
370 .address
.in
= { htobe32(0x7F000002) } };
371 addrs
[n
++] = (struct local_address
) { .family
= AF_INET6
,
372 .address
.in6
= in6addr_loopback
};
376 static int test_one_module(const char *dir
,
379 struct local_address
*addresses
,
382 log_info("======== %s ========", module
);
384 _cleanup_(dlclosep
) void *handle
= nss_open_handle(dir
, module
, RTLD_LAZY
|RTLD_NODELETE
);
389 STRV_FOREACH(name
, names
)
390 test_byname(handle
, module
, *name
);
392 for (int i
= 0; i
< n_addresses
; i
++)
393 test_byaddr(handle
, module
,
394 &addresses
[i
].address
,
395 FAMILY_ADDRESS_SIZE(addresses
[i
].family
),
396 addresses
[i
].family
);
402 static int parse_argv(int argc
, char **argv
,
405 struct local_address
**the_addresses
, int *n_addresses
) {
407 _cleanup_strv_free_
char **modules
= NULL
, **names
= NULL
;
408 _cleanup_free_
struct local_address
*addrs
= NULL
;
409 size_t n_allocated
= 0;
413 p
= getenv("SYSTEMD_TEST_NSS_BUFSIZE");
415 r
= safe_atozu(p
, &arg_bufsize
);
417 return log_error_errno(r
, "Failed to parse $SYSTEMD_TEST_NSS_BUFSIZE");
421 modules
= strv_new(argv
[1]);
424 #if ENABLE_NSS_MYHOSTNAME
427 #if ENABLE_NSS_RESOLVE
430 #if ENABLE_NSS_MYMACHINES
439 union in_addr_union address
;
441 STRV_FOREACH(name
, argv
+ 2) {
442 r
= in_addr_from_string_auto(*name
, &family
, &address
);
444 /* assume this is a name */
445 r
= strv_extend(&names
, *name
);
449 assert_se(GREEDY_REALLOC0(addrs
, n_allocated
, n
+ 1));
451 addrs
[n
++] = (struct local_address
) { .family
= family
,
452 .address
= address
};
456 _cleanup_free_
char *hostname
;
457 assert_se(hostname
= gethostname_malloc());
458 assert_se(names
= strv_new("localhost", "_gateway", "_outbound", "foo_no_such_host", hostname
));
460 n
= make_addresses(&addrs
);
464 *the_modules
= TAKE_PTR(modules
);
465 *the_names
= TAKE_PTR(names
);
466 *the_addresses
= TAKE_PTR(addrs
);
471 static int run(int argc
, char **argv
) {
472 _cleanup_free_
char *dir
= NULL
;
473 _cleanup_strv_free_
char **modules
= NULL
, **names
= NULL
;
474 _cleanup_free_
struct local_address
*addresses
= NULL
;
479 test_setup_logging(LOG_INFO
);
481 r
= parse_argv(argc
, argv
, &modules
, &names
, &addresses
, &n_addresses
);
483 return log_error_errno(r
, "Failed to parse arguments: %m");
485 assert_se(path_extract_directory(argv
[0], &dir
) >= 0);
487 STRV_FOREACH(module
, modules
) {
488 r
= test_one_module(dir
, *module
, names
, addresses
, n_addresses
);
496 DEFINE_MAIN_FUNCTION(run
);