2 This file is part of systemd.
4 Copyright 2016 Zbigniew Jędrzejewski-Szmek
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include "path-util.h"
27 #include "string-util.h"
28 #include "alloc-util.h"
29 #include "in-addr-util.h"
30 #include "hexdecoct.h"
32 #include "stdio-util.h"
34 #include "errno-list.h"
35 #include "hostname-util.h"
36 #include "local-addresses.h"
38 static const char* nss_status_to_string(enum nss_status status
, char *buf
, size_t buf_len
) {
40 case NSS_STATUS_TRYAGAIN
:
41 return "NSS_STATUS_TRYAGAIN";
42 case NSS_STATUS_UNAVAIL
:
43 return "NSS_STATUS_UNAVAIL";
44 case NSS_STATUS_NOTFOUND
:
45 return "NSS_STATUS_NOTFOUND";
46 case NSS_STATUS_SUCCESS
:
47 return "NSS_STATUS_SUCCESS";
48 case NSS_STATUS_RETURN
:
49 return "NSS_STATUS_RETURN";
51 snprintf(buf
, buf_len
, "%i", status
);
56 static const char* af_to_string(int family
, char *buf
, size_t buf_len
) {
59 if (family
== AF_UNSPEC
)
62 name
= af_to_name(family
);
66 snprintf(buf
, buf_len
, "%i", family
);
70 static void* open_handle(const char* dir
, const char* module
, int flags
) {
75 path
= strjoina(dir
, "/.libs/libnss_", module
, ".so.2");
77 path
= strjoina("libnss_", module
, ".so.2");
79 handle
= dlopen(path
, flags
);
81 log_error("Failed to load module %s: %s", module
, dlerror());
85 static int print_gaih_addrtuples(const struct gaih_addrtuple
*tuples
) {
86 const struct gaih_addrtuple
*it
;
89 for (it
= tuples
; it
; it
= it
->next
) {
90 _cleanup_free_
char *a
= NULL
;
91 union in_addr_union u
;
93 char family_name
[DECIMAL_STR_MAX(int)];
94 char ifname
[IF_NAMESIZE
];
96 memcpy(&u
, it
->addr
, 16);
97 r
= in_addr_to_string(it
->family
, &u
, &a
);
98 assert_se(r
== 0 || r
== -EAFNOSUPPORT
);
99 if (r
== -EAFNOSUPPORT
)
100 assert_se((a
= hexmem(it
->addr
, 16)));
102 if (it
->scopeid
== 0)
103 goto numerical_index
;
105 if (if_indextoname(it
->scopeid
, ifname
) == NULL
) {
106 log_warning("if_indextoname(%d) failed: %m", it
->scopeid
);
108 xsprintf(ifname
, "%i", it
->scopeid
);
111 log_info(" \"%s\" %s %s %%%s",
113 af_to_string(it
->family
, family_name
, sizeof family_name
),
121 static void print_struct_hostent(struct hostent
*host
, const char *canon
) {
124 log_info(" \"%s\"", host
->h_name
);
125 STRV_FOREACH(s
, host
->h_aliases
)
126 log_info(" alias \"%s\"", *s
);
127 STRV_FOREACH(s
, host
->h_addr_list
) {
128 union in_addr_union u
;
129 _cleanup_free_
char *a
= NULL
;
130 char family_name
[DECIMAL_STR_MAX(int)];
133 assert_se((unsigned) host
->h_length
== FAMILY_ADDRESS_SIZE(host
->h_addrtype
));
134 memcpy(&u
, *s
, host
->h_length
);
135 r
= in_addr_to_string(host
->h_addrtype
, &u
, &a
);
138 af_to_string(host
->h_addrtype
, family_name
, sizeof family_name
),
142 log_info(" canonical: \"%s\"", canon
);
145 static void test_gethostbyname4_r(void *handle
, const char *module
, const char *name
) {
147 _nss_gethostbyname4_r_t f
;
149 struct gaih_addrtuple
*pat
= NULL
;
150 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
151 int32_t ttl
= INT32_MAX
; /* nss-dns wants to return the lowest ttl,
152 and will access this variable through *ttlp,
153 so we need to set it to something.
154 I'm not sure if this is a bug in nss-dns
156 enum nss_status status
;
157 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
160 fname
= strjoina("_nss_", module
, "_gethostbyname4_r");
161 f
= dlsym(handle
, fname
);
162 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
165 status
= f(name
, &pat
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
);
166 if (status
== NSS_STATUS_SUCCESS
) {
167 log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%tx errno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
169 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
170 pat
? (char*) pat
- buffer
: 0,
171 errno1
, errno_to_name(errno1
) ?: "---",
172 errno2
, hstrerror(errno2
),
174 n
= print_gaih_addrtuples(pat
);
176 log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s",
178 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
180 errno1
, errno_to_name(errno1
) ?: "---",
181 errno2
, hstrerror(errno2
));
185 if (STR_IN_SET(module
, "resolve", "mymachines") && status
== NSS_STATUS_UNAVAIL
)
188 if (STR_IN_SET(module
, "myhostname", "resolve") && streq(name
, "localhost")) {
189 assert_se(status
== NSS_STATUS_SUCCESS
);
195 static void test_gethostbyname3_r(void *handle
, const char *module
, const char *name
, int af
) {
197 _nss_gethostbyname3_r_t f
;
199 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
200 int32_t ttl
= INT32_MAX
; /* nss-dns wants to return the lowest ttl,
201 and will access this variable through *ttlp,
202 so we need to set it to something.
203 I'm not sure if this is a bug in nss-dns
205 enum nss_status status
;
206 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
209 char family_name
[DECIMAL_STR_MAX(int)];
211 fname
= strjoina("_nss_", module
, "_gethostbyname3_r");
212 f
= dlsym(handle
, fname
);
213 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
216 status
= f(name
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
, &canon
);
217 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
218 fname
, name
, af_to_string(af
, family_name
, sizeof family_name
),
219 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
220 errno1
, errno_to_name(errno1
) ?: "---",
221 errno2
, hstrerror(errno2
),
223 if (status
== NSS_STATUS_SUCCESS
)
224 print_struct_hostent(&host
, canon
);
227 static void test_gethostbyname2_r(void *handle
, const char *module
, const char *name
, int af
) {
229 _nss_gethostbyname2_r_t f
;
231 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
232 enum nss_status status
;
233 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
235 char family_name
[DECIMAL_STR_MAX(int)];
237 fname
= strjoina("_nss_", module
, "_gethostbyname2_r");
238 f
= dlsym(handle
, fname
);
239 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
242 status
= f(name
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
243 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s",
244 fname
, name
, af_to_string(af
, family_name
, sizeof family_name
),
245 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
246 errno1
, errno_to_name(errno1
) ?: "---",
247 errno2
, hstrerror(errno2
));
248 if (status
== NSS_STATUS_SUCCESS
)
249 print_struct_hostent(&host
, NULL
);
252 static void test_gethostbyname_r(void *handle
, const char *module
, const char *name
) {
254 _nss_gethostbyname_r_t f
;
256 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
257 enum nss_status status
;
258 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
261 fname
= strjoina("_nss_", module
, "_gethostbyname_r");
262 f
= dlsym(handle
, fname
);
263 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
266 status
= f(name
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
267 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
269 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
270 errno1
, errno_to_name(errno1
) ?: "---",
271 errno2
, hstrerror(errno2
));
272 if (status
== NSS_STATUS_SUCCESS
)
273 print_struct_hostent(&host
, NULL
);
276 static void test_gethostbyaddr2_r(void *handle
,
278 const void* addr
, socklen_t len
,
282 _nss_gethostbyaddr2_r_t f
;
284 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
285 enum nss_status status
;
286 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
288 int32_t ttl
= INT32_MAX
;
289 _cleanup_free_
char *addr_pretty
= NULL
;
291 fname
= strjoina("_nss_", module
, "_gethostbyaddr2_r");
292 f
= dlsym(handle
, fname
);
294 log_full_errno(f
? LOG_DEBUG
: LOG_INFO
, errno
,
295 "dlsym(0x%p, %s) → 0x%p: %m", handle
, fname
, f
);
299 assert_se(in_addr_to_string(af
, addr
, &addr_pretty
) >= 0);
301 status
= f(addr
, len
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
);
302 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
304 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
305 errno1
, errno_to_name(errno1
) ?: "---",
306 errno2
, hstrerror(errno2
),
308 if (status
== NSS_STATUS_SUCCESS
)
309 print_struct_hostent(&host
, NULL
);
312 static void test_gethostbyaddr_r(void *handle
,
314 const void* addr
, socklen_t len
,
318 _nss_gethostbyaddr_r_t f
;
320 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
321 enum nss_status status
;
322 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
324 _cleanup_free_
char *addr_pretty
= NULL
;
326 fname
= strjoina("_nss_", module
, "_gethostbyaddr_r");
327 f
= dlsym(handle
, fname
);
329 log_full_errno(f
? LOG_DEBUG
: LOG_INFO
, errno
,
330 "dlsym(0x%p, %s) → 0x%p: %m", handle
, fname
, f
);
334 assert_se(in_addr_to_string(af
, addr
, &addr_pretty
) >= 0);
336 status
= f(addr
, len
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
337 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
339 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
340 errno1
, errno_to_name(errno1
) ?: "---",
341 errno2
, hstrerror(errno2
));
342 if (status
== NSS_STATUS_SUCCESS
)
343 print_struct_hostent(&host
, NULL
);
346 static void test_byname(void *handle
, const char *module
, const char *name
) {
347 test_gethostbyname4_r(handle
, module
, name
);
350 test_gethostbyname3_r(handle
, module
, name
, AF_INET
);
352 test_gethostbyname3_r(handle
, module
, name
, AF_INET6
);
354 test_gethostbyname3_r(handle
, module
, name
, AF_UNSPEC
);
356 test_gethostbyname3_r(handle
, module
, name
, AF_LOCAL
);
359 test_gethostbyname2_r(handle
, module
, name
, AF_INET
);
361 test_gethostbyname2_r(handle
, module
, name
, AF_INET6
);
363 test_gethostbyname2_r(handle
, module
, name
, AF_UNSPEC
);
365 test_gethostbyname2_r(handle
, module
, name
, AF_LOCAL
);
368 test_gethostbyname_r(handle
, module
, name
);
372 static void test_byaddr(void *handle
,
374 const void* addr
, socklen_t len
,
376 test_gethostbyaddr2_r(handle
, module
, addr
, len
, af
);
379 test_gethostbyaddr_r(handle
, module
, addr
, len
, af
);
383 static int make_addresses(struct local_address
**addresses
) {
386 _cleanup_free_
struct local_address
*addrs
= NULL
;
388 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addrs
);
390 log_info_errno(n
, "Failed to query local addresses: %m");
392 n_alloc
= n
; /* we _can_ do that */
393 if (!GREEDY_REALLOC(addrs
, n_alloc
, n
+ 3))
396 addrs
[n
++] = (struct local_address
) { .family
= AF_INET
,
397 .address
.in
= { htobe32(0x7F000001) } };
398 addrs
[n
++] = (struct local_address
) { .family
= AF_INET
,
399 .address
.in
= { htobe32(0x7F000002) } };
400 addrs
[n
++] = (struct local_address
) { .family
= AF_INET6
,
401 .address
.in6
= in6addr_loopback
};
405 static int test_one_module(const char* dir
,
408 struct local_address
*addresses
,
415 log_info("======== %s ========", module
);
417 handle
= open_handle(streq(module
, "dns") ? NULL
: dir
,
419 RTLD_LAZY
|RTLD_NODELETE
);
423 STRV_FOREACH(name
, names
)
424 test_byname(handle
, module
, *name
);
426 for (i
= 0; i
< n_addresses
; i
++)
427 test_byaddr(handle
, module
,
428 &addresses
[i
].address
,
429 FAMILY_ADDRESS_SIZE(addresses
[i
].family
),
430 addresses
[i
].family
);
437 static int parse_argv(int argc
, char **argv
,
440 struct local_address
**the_addresses
, int *n_addresses
) {
443 _cleanup_strv_free_
char **modules
= NULL
, **names
= NULL
;
444 _cleanup_free_
struct local_address
*addrs
= NULL
;
445 size_t n_allocated
= 0;
448 modules
= strv_new(argv
[1], NULL
);
451 #ifdef HAVE_MYHOSTNAME
468 union in_addr_union address
;
470 STRV_FOREACH(name
, argv
+ 2) {
471 r
= in_addr_from_string_auto(*name
, &family
, &address
);
473 /* assume this is a name */
474 r
= strv_extend(&names
, *name
);
478 if (!GREEDY_REALLOC0(addrs
, n_allocated
, n
+ 1))
481 addrs
[n
++] = (struct local_address
) { .family
= family
,
482 .address
= address
};
486 _cleanup_free_
char *hostname
;
488 hostname
= gethostname_malloc();
492 names
= strv_new("localhost", "gateway", "foo_no_such_host", hostname
, NULL
);
496 n
= make_addresses(&addrs
);
501 *the_modules
= modules
;
503 modules
= names
= NULL
;
504 *the_addresses
= addrs
;
510 int main(int argc
, char **argv
) {
511 _cleanup_free_
char *dir
= NULL
;
512 _cleanup_strv_free_
char **modules
= NULL
, **names
= NULL
;
513 _cleanup_free_
struct local_address
*addresses
= NULL
;
518 log_set_max_level(LOG_INFO
);
519 log_parse_environment();
521 r
= parse_argv(argc
, argv
, &modules
, &names
, &addresses
, &n_addresses
);
523 log_error_errno(r
, "Failed to parse arguments: %m");
527 dir
= dirname_malloc(argv
[0]);
531 STRV_FOREACH(module
, modules
) {
532 r
= test_one_module(dir
, *module
, names
, addresses
, n_addresses
);