1 /* SPDX-License-Identifier: LGPL-2.1+ */
9 #include "alloc-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"
19 #include "path-util.h"
20 #include "stdio-util.h"
21 #include "string-util.h"
25 static const char* nss_status_to_string(enum nss_status status
, char *buf
, size_t buf_len
) {
27 case NSS_STATUS_TRYAGAIN
:
28 return "NSS_STATUS_TRYAGAIN";
29 case NSS_STATUS_UNAVAIL
:
30 return "NSS_STATUS_UNAVAIL";
31 case NSS_STATUS_NOTFOUND
:
32 return "NSS_STATUS_NOTFOUND";
33 case NSS_STATUS_SUCCESS
:
34 return "NSS_STATUS_SUCCESS";
35 case NSS_STATUS_RETURN
:
36 return "NSS_STATUS_RETURN";
38 snprintf(buf
, buf_len
, "%i", status
);
43 static const char* af_to_string(int family
, char *buf
, size_t buf_len
) {
46 if (family
== AF_UNSPEC
)
49 name
= af_to_name(family
);
53 snprintf(buf
, buf_len
, "%i", family
);
57 static void* open_handle(const char* dir
, const char* module
, int flags
) {
58 const char *path
= NULL
;
62 path
= strjoina(dir
, "/libnss_", module
, ".so.2");
63 if (!path
|| access(path
, F_OK
) < 0)
64 path
= strjoina("libnss_", module
, ".so.2");
66 handle
= dlopen(path
, flags
);
68 log_error("Failed to load module %s: %s", module
, dlerror());
72 static int print_gaih_addrtuples(const struct gaih_addrtuple
*tuples
) {
73 const struct gaih_addrtuple
*it
;
76 for (it
= tuples
; it
; it
= it
->next
) {
77 _cleanup_free_
char *a
= NULL
;
78 union in_addr_union u
;
80 char family_name
[DECIMAL_STR_MAX(int)];
81 char ifname
[IF_NAMESIZE
+ 1];
83 memcpy(&u
, it
->addr
, 16);
84 r
= in_addr_to_string(it
->family
, &u
, &a
);
85 assert_se(IN_SET(r
, 0, -EAFNOSUPPORT
));
86 if (r
== -EAFNOSUPPORT
)
87 assert_se(a
= hexmem(it
->addr
, 16));
92 if (!format_ifname(it
->scopeid
, ifname
)) {
93 log_warning_errno(errno
, "if_indextoname(%d) failed: %m", it
->scopeid
);
95 xsprintf(ifname
, "%i", it
->scopeid
);
98 log_info(" \"%s\" %s %s %%%s",
100 af_to_string(it
->family
, family_name
, sizeof family_name
),
108 static void print_struct_hostent(struct hostent
*host
, const char *canon
) {
111 log_info(" \"%s\"", host
->h_name
);
112 STRV_FOREACH(s
, host
->h_aliases
)
113 log_info(" alias \"%s\"", *s
);
114 STRV_FOREACH(s
, host
->h_addr_list
) {
115 union in_addr_union u
;
116 _cleanup_free_
char *a
= NULL
;
117 char family_name
[DECIMAL_STR_MAX(int)];
120 assert_se((unsigned) host
->h_length
== FAMILY_ADDRESS_SIZE(host
->h_addrtype
));
121 memcpy(&u
, *s
, host
->h_length
);
122 r
= in_addr_to_string(host
->h_addrtype
, &u
, &a
);
125 af_to_string(host
->h_addrtype
, family_name
, sizeof family_name
),
129 log_info(" canonical: \"%s\"", canon
);
132 static void test_gethostbyname4_r(void *handle
, const char *module
, const char *name
) {
134 _nss_gethostbyname4_r_t f
;
136 struct gaih_addrtuple
*pat
= NULL
;
137 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
138 int32_t ttl
= INT32_MAX
; /* nss-dns wants to return the lowest ttl,
139 and will access this variable through *ttlp,
140 so we need to set it to something.
141 I'm not sure if this is a bug in nss-dns
143 enum nss_status status
;
144 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
147 fname
= strjoina("_nss_", module
, "_gethostbyname4_r");
148 f
= dlsym(handle
, fname
);
149 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
152 status
= f(name
, &pat
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
);
153 if (status
== NSS_STATUS_SUCCESS
) {
154 log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%tx errno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
156 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
157 pat
? (char*) pat
- buffer
: 0,
158 errno1
, errno_to_name(errno1
) ?: "---",
159 errno2
, hstrerror(errno2
),
161 n
= print_gaih_addrtuples(pat
);
163 log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s",
165 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
167 errno1
, errno_to_name(errno1
) ?: "---",
168 errno2
, hstrerror(errno2
));
172 if (STR_IN_SET(module
, "resolve", "mymachines") && status
== NSS_STATUS_UNAVAIL
)
175 if (STR_IN_SET(module
, "myhostname", "resolve") && streq(name
, "localhost")) {
176 assert_se(status
== NSS_STATUS_SUCCESS
);
181 static void test_gethostbyname3_r(void *handle
, const char *module
, const char *name
, int af
) {
183 _nss_gethostbyname3_r_t f
;
185 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
186 int32_t ttl
= INT32_MAX
; /* nss-dns wants to return the lowest ttl,
187 and will access this variable through *ttlp,
188 so we need to set it to something.
189 I'm not sure if this is a bug in nss-dns
191 enum nss_status status
;
192 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
195 char family_name
[DECIMAL_STR_MAX(int)];
197 fname
= strjoina("_nss_", module
, "_gethostbyname3_r");
198 f
= dlsym(handle
, fname
);
199 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
202 status
= f(name
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
, &canon
);
203 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
204 fname
, name
, af_to_string(af
, family_name
, sizeof family_name
),
205 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
206 errno1
, errno_to_name(errno1
) ?: "---",
207 errno2
, hstrerror(errno2
),
209 if (status
== NSS_STATUS_SUCCESS
)
210 print_struct_hostent(&host
, canon
);
213 static void test_gethostbyname2_r(void *handle
, const char *module
, const char *name
, int af
) {
215 _nss_gethostbyname2_r_t f
;
217 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
218 enum nss_status status
;
219 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
221 char family_name
[DECIMAL_STR_MAX(int)];
223 fname
= strjoina("_nss_", module
, "_gethostbyname2_r");
224 f
= dlsym(handle
, fname
);
225 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
228 status
= f(name
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
229 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s",
230 fname
, name
, af_to_string(af
, family_name
, sizeof family_name
),
231 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
232 errno1
, errno_to_name(errno1
) ?: "---",
233 errno2
, hstrerror(errno2
));
234 if (status
== NSS_STATUS_SUCCESS
)
235 print_struct_hostent(&host
, NULL
);
238 static void test_gethostbyname_r(void *handle
, const char *module
, const char *name
) {
240 _nss_gethostbyname_r_t f
;
242 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
243 enum nss_status status
;
244 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
247 fname
= strjoina("_nss_", module
, "_gethostbyname_r");
248 f
= dlsym(handle
, fname
);
249 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
252 status
= f(name
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
253 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
255 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
256 errno1
, errno_to_name(errno1
) ?: "---",
257 errno2
, hstrerror(errno2
));
258 if (status
== NSS_STATUS_SUCCESS
)
259 print_struct_hostent(&host
, NULL
);
262 static void test_gethostbyaddr2_r(void *handle
,
264 const void* addr
, socklen_t len
,
268 _nss_gethostbyaddr2_r_t f
;
270 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
271 enum nss_status status
;
272 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
274 int32_t ttl
= INT32_MAX
;
275 _cleanup_free_
char *addr_pretty
= NULL
;
277 fname
= strjoina("_nss_", module
, "_gethostbyaddr2_r");
278 f
= dlsym(handle
, fname
);
280 log_full_errno(f
? LOG_DEBUG
: LOG_INFO
, errno
,
281 "dlsym(0x%p, %s) → 0x%p: %m", handle
, fname
, f
);
285 assert_se(in_addr_to_string(af
, addr
, &addr_pretty
) >= 0);
287 status
= f(addr
, len
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
);
288 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
290 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
291 errno1
, errno_to_name(errno1
) ?: "---",
292 errno2
, hstrerror(errno2
),
294 if (status
== NSS_STATUS_SUCCESS
)
295 print_struct_hostent(&host
, NULL
);
298 static void test_gethostbyaddr_r(void *handle
,
300 const void* addr
, socklen_t len
,
304 _nss_gethostbyaddr_r_t f
;
306 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
307 enum nss_status status
;
308 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
310 _cleanup_free_
char *addr_pretty
= NULL
;
312 fname
= strjoina("_nss_", module
, "_gethostbyaddr_r");
313 f
= dlsym(handle
, fname
);
315 log_full_errno(f
? LOG_DEBUG
: LOG_INFO
, errno
,
316 "dlsym(0x%p, %s) → 0x%p: %m", handle
, fname
, f
);
320 assert_se(in_addr_to_string(af
, addr
, &addr_pretty
) >= 0);
322 status
= f(addr
, len
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
323 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
325 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
326 errno1
, errno_to_name(errno1
) ?: "---",
327 errno2
, hstrerror(errno2
));
328 if (status
== NSS_STATUS_SUCCESS
)
329 print_struct_hostent(&host
, NULL
);
332 static void test_byname(void *handle
, const char *module
, const char *name
) {
333 test_gethostbyname4_r(handle
, module
, name
);
336 test_gethostbyname3_r(handle
, module
, name
, AF_INET
);
338 test_gethostbyname3_r(handle
, module
, name
, AF_INET6
);
340 test_gethostbyname3_r(handle
, module
, name
, AF_UNSPEC
);
342 test_gethostbyname3_r(handle
, module
, name
, AF_LOCAL
);
345 test_gethostbyname2_r(handle
, module
, name
, AF_INET
);
347 test_gethostbyname2_r(handle
, module
, name
, AF_INET6
);
349 test_gethostbyname2_r(handle
, module
, name
, AF_UNSPEC
);
351 test_gethostbyname2_r(handle
, module
, name
, AF_LOCAL
);
354 test_gethostbyname_r(handle
, module
, name
);
358 static void test_byaddr(void *handle
,
360 const void* addr
, socklen_t len
,
362 test_gethostbyaddr2_r(handle
, module
, addr
, len
, af
);
365 test_gethostbyaddr_r(handle
, module
, addr
, len
, af
);
369 static int make_addresses(struct local_address
**addresses
) {
372 _cleanup_free_
struct local_address
*addrs
= NULL
;
374 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addrs
);
376 log_info_errno(n
, "Failed to query local addresses: %m");
378 n_alloc
= n
; /* we _can_ do that */
379 if (!GREEDY_REALLOC(addrs
, n_alloc
, n
+ 3))
382 addrs
[n
++] = (struct local_address
) { .family
= AF_INET
,
383 .address
.in
= { htobe32(0x7F000001) } };
384 addrs
[n
++] = (struct local_address
) { .family
= AF_INET
,
385 .address
.in
= { htobe32(0x7F000002) } };
386 addrs
[n
++] = (struct local_address
) { .family
= AF_INET6
,
387 .address
.in6
= in6addr_loopback
};
391 static int test_one_module(const char* dir
,
394 struct local_address
*addresses
,
400 log_info("======== %s ========", module
);
402 handle
= open_handle(dir
, module
, RTLD_LAZY
|RTLD_NODELETE
);
406 STRV_FOREACH(name
, names
)
407 test_byname(handle
, module
, *name
);
409 for (i
= 0; i
< n_addresses
; i
++)
410 test_byaddr(handle
, module
,
411 &addresses
[i
].address
,
412 FAMILY_ADDRESS_SIZE(addresses
[i
].family
),
413 addresses
[i
].family
);
420 static int parse_argv(int argc
, char **argv
,
423 struct local_address
**the_addresses
, int *n_addresses
) {
426 _cleanup_strv_free_
char **modules
= NULL
, **names
= NULL
;
427 _cleanup_free_
struct local_address
*addrs
= NULL
;
428 size_t n_allocated
= 0;
431 modules
= strv_new(argv
[1]);
434 #if ENABLE_NSS_MYHOSTNAME
437 #if ENABLE_NSS_RESOLVE
440 #if ENABLE_NSS_MYMACHINES
450 union in_addr_union address
;
452 STRV_FOREACH(name
, argv
+ 2) {
453 r
= in_addr_from_string_auto(*name
, &family
, &address
);
455 /* assume this is a name */
456 r
= strv_extend(&names
, *name
);
460 if (!GREEDY_REALLOC0(addrs
, n_allocated
, n
+ 1))
463 addrs
[n
++] = (struct local_address
) { .family
= family
,
464 .address
= address
};
468 _cleanup_free_
char *hostname
;
470 hostname
= gethostname_malloc();
474 names
= strv_new("localhost", "_gateway", "foo_no_such_host", hostname
);
478 n
= make_addresses(&addrs
);
483 *the_modules
= modules
;
485 modules
= names
= NULL
;
486 *the_addresses
= addrs
;
492 static int run(int argc
, char **argv
) {
493 _cleanup_free_
char *dir
= NULL
;
494 _cleanup_strv_free_
char **modules
= NULL
, **names
= NULL
;
495 _cleanup_free_
struct local_address
*addresses
= NULL
;
500 test_setup_logging(LOG_INFO
);
502 r
= parse_argv(argc
, argv
, &modules
, &names
, &addresses
, &n_addresses
);
504 log_error_errno(r
, "Failed to parse arguments: %m");
508 dir
= dirname_malloc(argv
[0]);
512 STRV_FOREACH(module
, modules
) {
513 r
= test_one_module(dir
, *module
, names
, addresses
, n_addresses
);
521 DEFINE_MAIN_FUNCTION(run
);