1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2016 Zbigniew Jędrzejewski-Szmek
14 #include "path-util.h"
15 #include "string-util.h"
16 #include "alloc-util.h"
17 #include "in-addr-util.h"
18 #include "hexdecoct.h"
20 #include "stdio-util.h"
22 #include "errno-list.h"
23 #include "hostname-util.h"
24 #include "local-addresses.h"
26 static const char* nss_status_to_string(enum nss_status status
, char *buf
, size_t buf_len
) {
28 case NSS_STATUS_TRYAGAIN
:
29 return "NSS_STATUS_TRYAGAIN";
30 case NSS_STATUS_UNAVAIL
:
31 return "NSS_STATUS_UNAVAIL";
32 case NSS_STATUS_NOTFOUND
:
33 return "NSS_STATUS_NOTFOUND";
34 case NSS_STATUS_SUCCESS
:
35 return "NSS_STATUS_SUCCESS";
36 case NSS_STATUS_RETURN
:
37 return "NSS_STATUS_RETURN";
39 snprintf(buf
, buf_len
, "%i", status
);
44 static const char* af_to_string(int family
, char *buf
, size_t buf_len
) {
47 if (family
== AF_UNSPEC
)
50 name
= af_to_name(family
);
54 snprintf(buf
, buf_len
, "%i", family
);
58 static void* open_handle(const char* dir
, const char* module
, int flags
) {
63 path
= strjoina(dir
, "/libnss_", module
, ".so.2");
64 if (access(path
, F_OK
) < 0)
65 path
= strjoina(dir
, "/.libs/libnss_", module
, ".so.2");
67 path
= strjoina("libnss_", module
, ".so.2");
69 handle
= dlopen(path
, flags
);
71 log_error("Failed to load module %s: %s", module
, dlerror());
75 static int print_gaih_addrtuples(const struct gaih_addrtuple
*tuples
) {
76 const struct gaih_addrtuple
*it
;
79 for (it
= tuples
; it
; it
= it
->next
) {
80 _cleanup_free_
char *a
= NULL
;
81 union in_addr_union u
;
83 char family_name
[DECIMAL_STR_MAX(int)];
84 char ifname
[IF_NAMESIZE
];
86 memcpy(&u
, it
->addr
, 16);
87 r
= in_addr_to_string(it
->family
, &u
, &a
);
88 assert_se(IN_SET(r
, 0, -EAFNOSUPPORT
));
89 if (r
== -EAFNOSUPPORT
)
90 assert_se((a
= hexmem(it
->addr
, 16)));
95 if (if_indextoname(it
->scopeid
, ifname
) == NULL
) {
96 log_warning_errno(errno
, "if_indextoname(%d) failed: %m", it
->scopeid
);
98 xsprintf(ifname
, "%i", it
->scopeid
);
101 log_info(" \"%s\" %s %s %%%s",
103 af_to_string(it
->family
, family_name
, sizeof family_name
),
111 static void print_struct_hostent(struct hostent
*host
, const char *canon
) {
114 log_info(" \"%s\"", host
->h_name
);
115 STRV_FOREACH(s
, host
->h_aliases
)
116 log_info(" alias \"%s\"", *s
);
117 STRV_FOREACH(s
, host
->h_addr_list
) {
118 union in_addr_union u
;
119 _cleanup_free_
char *a
= NULL
;
120 char family_name
[DECIMAL_STR_MAX(int)];
123 assert_se((unsigned) host
->h_length
== FAMILY_ADDRESS_SIZE(host
->h_addrtype
));
124 memcpy(&u
, *s
, host
->h_length
);
125 r
= in_addr_to_string(host
->h_addrtype
, &u
, &a
);
128 af_to_string(host
->h_addrtype
, family_name
, sizeof family_name
),
132 log_info(" canonical: \"%s\"", canon
);
135 static void test_gethostbyname4_r(void *handle
, const char *module
, const char *name
) {
137 _nss_gethostbyname4_r_t f
;
139 struct gaih_addrtuple
*pat
= NULL
;
140 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
141 int32_t ttl
= INT32_MAX
; /* nss-dns wants to return the lowest ttl,
142 and will access this variable through *ttlp,
143 so we need to set it to something.
144 I'm not sure if this is a bug in nss-dns
146 enum nss_status status
;
147 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
150 fname
= strjoina("_nss_", module
, "_gethostbyname4_r");
151 f
= dlsym(handle
, fname
);
152 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
155 status
= f(name
, &pat
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
);
156 if (status
== NSS_STATUS_SUCCESS
) {
157 log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%tx errno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
159 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
160 pat
? (char*) pat
- buffer
: 0,
161 errno1
, errno_to_name(errno1
) ?: "---",
162 errno2
, hstrerror(errno2
),
164 n
= print_gaih_addrtuples(pat
);
166 log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s",
168 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
170 errno1
, errno_to_name(errno1
) ?: "---",
171 errno2
, hstrerror(errno2
));
175 if (STR_IN_SET(module
, "resolve", "mymachines") && status
== NSS_STATUS_UNAVAIL
)
178 if (STR_IN_SET(module
, "myhostname", "resolve") && streq(name
, "localhost")) {
179 assert_se(status
== NSS_STATUS_SUCCESS
);
184 static void test_gethostbyname3_r(void *handle
, const char *module
, const char *name
, int af
) {
186 _nss_gethostbyname3_r_t f
;
188 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
189 int32_t ttl
= INT32_MAX
; /* nss-dns wants to return the lowest ttl,
190 and will access this variable through *ttlp,
191 so we need to set it to something.
192 I'm not sure if this is a bug in nss-dns
194 enum nss_status status
;
195 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
198 char family_name
[DECIMAL_STR_MAX(int)];
200 fname
= strjoina("_nss_", module
, "_gethostbyname3_r");
201 f
= dlsym(handle
, fname
);
202 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
205 status
= f(name
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
, &canon
);
206 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
207 fname
, name
, af_to_string(af
, family_name
, sizeof family_name
),
208 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
209 errno1
, errno_to_name(errno1
) ?: "---",
210 errno2
, hstrerror(errno2
),
212 if (status
== NSS_STATUS_SUCCESS
)
213 print_struct_hostent(&host
, canon
);
216 static void test_gethostbyname2_r(void *handle
, const char *module
, const char *name
, int af
) {
218 _nss_gethostbyname2_r_t f
;
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
)];
224 char family_name
[DECIMAL_STR_MAX(int)];
226 fname
= strjoina("_nss_", module
, "_gethostbyname2_r");
227 f
= dlsym(handle
, fname
);
228 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
231 status
= f(name
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
232 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s",
233 fname
, name
, af_to_string(af
, family_name
, sizeof family_name
),
234 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
235 errno1
, errno_to_name(errno1
) ?: "---",
236 errno2
, hstrerror(errno2
));
237 if (status
== NSS_STATUS_SUCCESS
)
238 print_struct_hostent(&host
, NULL
);
241 static void test_gethostbyname_r(void *handle
, const char *module
, const char *name
) {
243 _nss_gethostbyname_r_t f
;
245 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
246 enum nss_status status
;
247 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
250 fname
= strjoina("_nss_", module
, "_gethostbyname_r");
251 f
= dlsym(handle
, fname
);
252 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
255 status
= f(name
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
256 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
258 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
259 errno1
, errno_to_name(errno1
) ?: "---",
260 errno2
, hstrerror(errno2
));
261 if (status
== NSS_STATUS_SUCCESS
)
262 print_struct_hostent(&host
, NULL
);
265 static void test_gethostbyaddr2_r(void *handle
,
267 const void* addr
, socklen_t len
,
271 _nss_gethostbyaddr2_r_t f
;
273 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
274 enum nss_status status
;
275 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
277 int32_t ttl
= INT32_MAX
;
278 _cleanup_free_
char *addr_pretty
= NULL
;
280 fname
= strjoina("_nss_", module
, "_gethostbyaddr2_r");
281 f
= dlsym(handle
, fname
);
283 log_full_errno(f
? LOG_DEBUG
: LOG_INFO
, errno
,
284 "dlsym(0x%p, %s) → 0x%p: %m", handle
, fname
, f
);
288 assert_se(in_addr_to_string(af
, addr
, &addr_pretty
) >= 0);
290 status
= f(addr
, len
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
);
291 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
293 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
294 errno1
, errno_to_name(errno1
) ?: "---",
295 errno2
, hstrerror(errno2
),
297 if (status
== NSS_STATUS_SUCCESS
)
298 print_struct_hostent(&host
, NULL
);
301 static void test_gethostbyaddr_r(void *handle
,
303 const void* addr
, socklen_t len
,
307 _nss_gethostbyaddr_r_t f
;
309 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
310 enum nss_status status
;
311 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
313 _cleanup_free_
char *addr_pretty
= NULL
;
315 fname
= strjoina("_nss_", module
, "_gethostbyaddr_r");
316 f
= dlsym(handle
, fname
);
318 log_full_errno(f
? LOG_DEBUG
: LOG_INFO
, errno
,
319 "dlsym(0x%p, %s) → 0x%p: %m", handle
, fname
, f
);
323 assert_se(in_addr_to_string(af
, addr
, &addr_pretty
) >= 0);
325 status
= f(addr
, len
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
326 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
328 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
329 errno1
, errno_to_name(errno1
) ?: "---",
330 errno2
, hstrerror(errno2
));
331 if (status
== NSS_STATUS_SUCCESS
)
332 print_struct_hostent(&host
, NULL
);
335 static void test_byname(void *handle
, const char *module
, const char *name
) {
336 test_gethostbyname4_r(handle
, module
, name
);
339 test_gethostbyname3_r(handle
, module
, name
, AF_INET
);
341 test_gethostbyname3_r(handle
, module
, name
, AF_INET6
);
343 test_gethostbyname3_r(handle
, module
, name
, AF_UNSPEC
);
345 test_gethostbyname3_r(handle
, module
, name
, AF_LOCAL
);
348 test_gethostbyname2_r(handle
, module
, name
, AF_INET
);
350 test_gethostbyname2_r(handle
, module
, name
, AF_INET6
);
352 test_gethostbyname2_r(handle
, module
, name
, AF_UNSPEC
);
354 test_gethostbyname2_r(handle
, module
, name
, AF_LOCAL
);
357 test_gethostbyname_r(handle
, module
, name
);
361 static void test_byaddr(void *handle
,
363 const void* addr
, socklen_t len
,
365 test_gethostbyaddr2_r(handle
, module
, addr
, len
, af
);
368 test_gethostbyaddr_r(handle
, module
, addr
, len
, af
);
372 static int make_addresses(struct local_address
**addresses
) {
375 _cleanup_free_
struct local_address
*addrs
= NULL
;
377 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addrs
);
379 log_info_errno(n
, "Failed to query local addresses: %m");
381 n_alloc
= n
; /* we _can_ do that */
382 if (!GREEDY_REALLOC(addrs
, n_alloc
, n
+ 3))
385 addrs
[n
++] = (struct local_address
) { .family
= AF_INET
,
386 .address
.in
= { htobe32(0x7F000001) } };
387 addrs
[n
++] = (struct local_address
) { .family
= AF_INET
,
388 .address
.in
= { htobe32(0x7F000002) } };
389 addrs
[n
++] = (struct local_address
) { .family
= AF_INET6
,
390 .address
.in6
= in6addr_loopback
};
394 static int test_one_module(const char* dir
,
397 struct local_address
*addresses
,
403 log_info("======== %s ========", module
);
405 handle
= open_handle(streq(module
, "dns") ? NULL
: dir
,
407 RTLD_LAZY
|RTLD_NODELETE
);
411 STRV_FOREACH(name
, names
)
412 test_byname(handle
, module
, *name
);
414 for (i
= 0; i
< n_addresses
; i
++)
415 test_byaddr(handle
, module
,
416 &addresses
[i
].address
,
417 FAMILY_ADDRESS_SIZE(addresses
[i
].family
),
418 addresses
[i
].family
);
425 static int parse_argv(int argc
, char **argv
,
428 struct local_address
**the_addresses
, int *n_addresses
) {
431 _cleanup_strv_free_
char **modules
= NULL
, **names
= NULL
;
432 _cleanup_free_
struct local_address
*addrs
= NULL
;
433 size_t n_allocated
= 0;
436 modules
= strv_new(argv
[1], NULL
);
439 #if ENABLE_MYHOSTNAME
456 union in_addr_union address
;
458 STRV_FOREACH(name
, argv
+ 2) {
459 r
= in_addr_from_string_auto(*name
, &family
, &address
);
461 /* assume this is a name */
462 r
= strv_extend(&names
, *name
);
466 if (!GREEDY_REALLOC0(addrs
, n_allocated
, n
+ 1))
469 addrs
[n
++] = (struct local_address
) { .family
= family
,
470 .address
= address
};
474 _cleanup_free_
char *hostname
;
476 hostname
= gethostname_malloc();
480 names
= strv_new("localhost", "_gateway", "foo_no_such_host", hostname
, NULL
);
484 n
= make_addresses(&addrs
);
489 *the_modules
= modules
;
491 modules
= names
= NULL
;
492 *the_addresses
= addrs
;
498 int main(int argc
, char **argv
) {
499 _cleanup_free_
char *dir
= NULL
;
500 _cleanup_strv_free_
char **modules
= NULL
, **names
= NULL
;
501 _cleanup_free_
struct local_address
*addresses
= NULL
;
506 log_set_max_level(LOG_INFO
);
507 log_parse_environment();
509 r
= parse_argv(argc
, argv
, &modules
, &names
, &addresses
, &n_addresses
);
511 log_error_errno(r
, "Failed to parse arguments: %m");
515 dir
= dirname_malloc(argv
[0]);
519 STRV_FOREACH(module
, modules
) {
520 r
= test_one_module(dir
, *module
, names
, addresses
, n_addresses
);