1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2016 Zbigniew Jędrzejewski-Szmek
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include "path-util.h"
28 #include "string-util.h"
29 #include "alloc-util.h"
30 #include "in-addr-util.h"
31 #include "hexdecoct.h"
33 #include "stdio-util.h"
35 #include "errno-list.h"
36 #include "hostname-util.h"
37 #include "local-addresses.h"
39 static const char* nss_status_to_string(enum nss_status status
, char *buf
, size_t buf_len
) {
41 case NSS_STATUS_TRYAGAIN
:
42 return "NSS_STATUS_TRYAGAIN";
43 case NSS_STATUS_UNAVAIL
:
44 return "NSS_STATUS_UNAVAIL";
45 case NSS_STATUS_NOTFOUND
:
46 return "NSS_STATUS_NOTFOUND";
47 case NSS_STATUS_SUCCESS
:
48 return "NSS_STATUS_SUCCESS";
49 case NSS_STATUS_RETURN
:
50 return "NSS_STATUS_RETURN";
52 snprintf(buf
, buf_len
, "%i", status
);
57 static const char* af_to_string(int family
, char *buf
, size_t buf_len
) {
60 if (family
== AF_UNSPEC
)
63 name
= af_to_name(family
);
67 snprintf(buf
, buf_len
, "%i", family
);
71 static void* open_handle(const char* dir
, const char* module
, int flags
) {
76 path
= strjoina(dir
, "/libnss_", module
, ".so.2");
77 if (access(path
, F_OK
) < 0)
78 path
= strjoina(dir
, "/.libs/libnss_", module
, ".so.2");
80 path
= strjoina("libnss_", module
, ".so.2");
82 handle
= dlopen(path
, flags
);
84 log_error("Failed to load module %s: %s", module
, dlerror());
88 static int print_gaih_addrtuples(const struct gaih_addrtuple
*tuples
) {
89 const struct gaih_addrtuple
*it
;
92 for (it
= tuples
; it
; it
= it
->next
) {
93 _cleanup_free_
char *a
= NULL
;
94 union in_addr_union u
;
96 char family_name
[DECIMAL_STR_MAX(int)];
97 char ifname
[IF_NAMESIZE
];
99 memcpy(&u
, it
->addr
, 16);
100 r
= in_addr_to_string(it
->family
, &u
, &a
);
101 assert_se(IN_SET(r
, 0, -EAFNOSUPPORT
));
102 if (r
== -EAFNOSUPPORT
)
103 assert_se((a
= hexmem(it
->addr
, 16)));
105 if (it
->scopeid
== 0)
106 goto numerical_index
;
108 if (if_indextoname(it
->scopeid
, ifname
) == NULL
) {
109 log_warning_errno(errno
, "if_indextoname(%d) failed: %m", it
->scopeid
);
111 xsprintf(ifname
, "%i", it
->scopeid
);
114 log_info(" \"%s\" %s %s %%%s",
116 af_to_string(it
->family
, family_name
, sizeof family_name
),
124 static void print_struct_hostent(struct hostent
*host
, const char *canon
) {
127 log_info(" \"%s\"", host
->h_name
);
128 STRV_FOREACH(s
, host
->h_aliases
)
129 log_info(" alias \"%s\"", *s
);
130 STRV_FOREACH(s
, host
->h_addr_list
) {
131 union in_addr_union u
;
132 _cleanup_free_
char *a
= NULL
;
133 char family_name
[DECIMAL_STR_MAX(int)];
136 assert_se((unsigned) host
->h_length
== FAMILY_ADDRESS_SIZE(host
->h_addrtype
));
137 memcpy(&u
, *s
, host
->h_length
);
138 r
= in_addr_to_string(host
->h_addrtype
, &u
, &a
);
141 af_to_string(host
->h_addrtype
, family_name
, sizeof family_name
),
145 log_info(" canonical: \"%s\"", canon
);
148 static void test_gethostbyname4_r(void *handle
, const char *module
, const char *name
) {
150 _nss_gethostbyname4_r_t f
;
152 struct gaih_addrtuple
*pat
= NULL
;
153 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
154 int32_t ttl
= INT32_MAX
; /* nss-dns wants to return the lowest ttl,
155 and will access this variable through *ttlp,
156 so we need to set it to something.
157 I'm not sure if this is a bug in nss-dns
159 enum nss_status status
;
160 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
163 fname
= strjoina("_nss_", module
, "_gethostbyname4_r");
164 f
= dlsym(handle
, fname
);
165 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
168 status
= f(name
, &pat
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
);
169 if (status
== NSS_STATUS_SUCCESS
) {
170 log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%tx errno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
172 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
173 pat
? (char*) pat
- buffer
: 0,
174 errno1
, errno_to_name(errno1
) ?: "---",
175 errno2
, hstrerror(errno2
),
177 n
= print_gaih_addrtuples(pat
);
179 log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s",
181 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
183 errno1
, errno_to_name(errno1
) ?: "---",
184 errno2
, hstrerror(errno2
));
188 if (STR_IN_SET(module
, "resolve", "mymachines") && status
== NSS_STATUS_UNAVAIL
)
191 if (STR_IN_SET(module
, "myhostname", "resolve") && streq(name
, "localhost")) {
192 assert_se(status
== NSS_STATUS_SUCCESS
);
198 static void test_gethostbyname3_r(void *handle
, const char *module
, const char *name
, int af
) {
200 _nss_gethostbyname3_r_t f
;
202 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
203 int32_t ttl
= INT32_MAX
; /* nss-dns wants to return the lowest ttl,
204 and will access this variable through *ttlp,
205 so we need to set it to something.
206 I'm not sure if this is a bug in nss-dns
208 enum nss_status status
;
209 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
212 char family_name
[DECIMAL_STR_MAX(int)];
214 fname
= strjoina("_nss_", module
, "_gethostbyname3_r");
215 f
= dlsym(handle
, fname
);
216 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
219 status
= f(name
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
, &canon
);
220 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
221 fname
, name
, af_to_string(af
, family_name
, sizeof family_name
),
222 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
223 errno1
, errno_to_name(errno1
) ?: "---",
224 errno2
, hstrerror(errno2
),
226 if (status
== NSS_STATUS_SUCCESS
)
227 print_struct_hostent(&host
, canon
);
230 static void test_gethostbyname2_r(void *handle
, const char *module
, const char *name
, int af
) {
232 _nss_gethostbyname2_r_t f
;
234 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
235 enum nss_status status
;
236 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
238 char family_name
[DECIMAL_STR_MAX(int)];
240 fname
= strjoina("_nss_", module
, "_gethostbyname2_r");
241 f
= dlsym(handle
, fname
);
242 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
245 status
= f(name
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
246 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s",
247 fname
, name
, af_to_string(af
, family_name
, sizeof family_name
),
248 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
249 errno1
, errno_to_name(errno1
) ?: "---",
250 errno2
, hstrerror(errno2
));
251 if (status
== NSS_STATUS_SUCCESS
)
252 print_struct_hostent(&host
, NULL
);
255 static void test_gethostbyname_r(void *handle
, const char *module
, const char *name
) {
257 _nss_gethostbyname_r_t f
;
259 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
260 enum nss_status status
;
261 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
264 fname
= strjoina("_nss_", module
, "_gethostbyname_r");
265 f
= dlsym(handle
, fname
);
266 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
269 status
= f(name
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
270 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
272 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
273 errno1
, errno_to_name(errno1
) ?: "---",
274 errno2
, hstrerror(errno2
));
275 if (status
== NSS_STATUS_SUCCESS
)
276 print_struct_hostent(&host
, NULL
);
279 static void test_gethostbyaddr2_r(void *handle
,
281 const void* addr
, socklen_t len
,
285 _nss_gethostbyaddr2_r_t f
;
287 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
288 enum nss_status status
;
289 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
291 int32_t ttl
= INT32_MAX
;
292 _cleanup_free_
char *addr_pretty
= NULL
;
294 fname
= strjoina("_nss_", module
, "_gethostbyaddr2_r");
295 f
= dlsym(handle
, fname
);
297 log_full_errno(f
? LOG_DEBUG
: LOG_INFO
, errno
,
298 "dlsym(0x%p, %s) → 0x%p: %m", handle
, fname
, f
);
302 assert_se(in_addr_to_string(af
, addr
, &addr_pretty
) >= 0);
304 status
= f(addr
, len
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
);
305 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
307 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
308 errno1
, errno_to_name(errno1
) ?: "---",
309 errno2
, hstrerror(errno2
),
311 if (status
== NSS_STATUS_SUCCESS
)
312 print_struct_hostent(&host
, NULL
);
315 static void test_gethostbyaddr_r(void *handle
,
317 const void* addr
, socklen_t len
,
321 _nss_gethostbyaddr_r_t f
;
323 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
324 enum nss_status status
;
325 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
327 _cleanup_free_
char *addr_pretty
= NULL
;
329 fname
= strjoina("_nss_", module
, "_gethostbyaddr_r");
330 f
= dlsym(handle
, fname
);
332 log_full_errno(f
? LOG_DEBUG
: LOG_INFO
, errno
,
333 "dlsym(0x%p, %s) → 0x%p: %m", handle
, fname
, f
);
337 assert_se(in_addr_to_string(af
, addr
, &addr_pretty
) >= 0);
339 status
= f(addr
, len
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
340 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
342 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
343 errno1
, errno_to_name(errno1
) ?: "---",
344 errno2
, hstrerror(errno2
));
345 if (status
== NSS_STATUS_SUCCESS
)
346 print_struct_hostent(&host
, NULL
);
349 static void test_byname(void *handle
, const char *module
, const char *name
) {
350 test_gethostbyname4_r(handle
, module
, name
);
353 test_gethostbyname3_r(handle
, module
, name
, AF_INET
);
355 test_gethostbyname3_r(handle
, module
, name
, AF_INET6
);
357 test_gethostbyname3_r(handle
, module
, name
, AF_UNSPEC
);
359 test_gethostbyname3_r(handle
, module
, name
, AF_LOCAL
);
362 test_gethostbyname2_r(handle
, module
, name
, AF_INET
);
364 test_gethostbyname2_r(handle
, module
, name
, AF_INET6
);
366 test_gethostbyname2_r(handle
, module
, name
, AF_UNSPEC
);
368 test_gethostbyname2_r(handle
, module
, name
, AF_LOCAL
);
371 test_gethostbyname_r(handle
, module
, name
);
375 static void test_byaddr(void *handle
,
377 const void* addr
, socklen_t len
,
379 test_gethostbyaddr2_r(handle
, module
, addr
, len
, af
);
382 test_gethostbyaddr_r(handle
, module
, addr
, len
, af
);
386 static int make_addresses(struct local_address
**addresses
) {
389 _cleanup_free_
struct local_address
*addrs
= NULL
;
391 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addrs
);
393 log_info_errno(n
, "Failed to query local addresses: %m");
395 n_alloc
= n
; /* we _can_ do that */
396 if (!GREEDY_REALLOC(addrs
, n_alloc
, n
+ 3))
399 addrs
[n
++] = (struct local_address
) { .family
= AF_INET
,
400 .address
.in
= { htobe32(0x7F000001) } };
401 addrs
[n
++] = (struct local_address
) { .family
= AF_INET
,
402 .address
.in
= { htobe32(0x7F000002) } };
403 addrs
[n
++] = (struct local_address
) { .family
= AF_INET6
,
404 .address
.in6
= in6addr_loopback
};
408 static int test_one_module(const char* dir
,
411 struct local_address
*addresses
,
418 log_info("======== %s ========", module
);
420 handle
= open_handle(streq(module
, "dns") ? NULL
: dir
,
422 RTLD_LAZY
|RTLD_NODELETE
);
426 STRV_FOREACH(name
, names
)
427 test_byname(handle
, module
, *name
);
429 for (i
= 0; i
< n_addresses
; i
++)
430 test_byaddr(handle
, module
,
431 &addresses
[i
].address
,
432 FAMILY_ADDRESS_SIZE(addresses
[i
].family
),
433 addresses
[i
].family
);
440 static int parse_argv(int argc
, char **argv
,
443 struct local_address
**the_addresses
, int *n_addresses
) {
446 _cleanup_strv_free_
char **modules
= NULL
, **names
= NULL
;
447 _cleanup_free_
struct local_address
*addrs
= NULL
;
448 size_t n_allocated
= 0;
451 modules
= strv_new(argv
[1], NULL
);
454 #if ENABLE_MYHOSTNAME
471 union in_addr_union address
;
473 STRV_FOREACH(name
, argv
+ 2) {
474 r
= in_addr_from_string_auto(*name
, &family
, &address
);
476 /* assume this is a name */
477 r
= strv_extend(&names
, *name
);
481 if (!GREEDY_REALLOC0(addrs
, n_allocated
, n
+ 1))
484 addrs
[n
++] = (struct local_address
) { .family
= family
,
485 .address
= address
};
489 _cleanup_free_
char *hostname
;
491 hostname
= gethostname_malloc();
495 names
= strv_new("localhost", "_gateway", "foo_no_such_host", hostname
, NULL
);
499 n
= make_addresses(&addrs
);
504 *the_modules
= modules
;
506 modules
= names
= NULL
;
507 *the_addresses
= addrs
;
513 int main(int argc
, char **argv
) {
514 _cleanup_free_
char *dir
= NULL
;
515 _cleanup_strv_free_
char **modules
= NULL
, **names
= NULL
;
516 _cleanup_free_
struct local_address
*addresses
= NULL
;
521 log_set_max_level(LOG_INFO
);
522 log_parse_environment();
524 r
= parse_argv(argc
, argv
, &modules
, &names
, &addresses
, &n_addresses
);
526 log_error_errno(r
, "Failed to parse arguments: %m");
530 dir
= dirname_malloc(argv
[0]);
534 STRV_FOREACH(module
, modules
) {
535 r
= test_one_module(dir
, *module
, names
, addresses
, n_addresses
);