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
);
84 static int print_gaih_addrtuples(const struct gaih_addrtuple
*tuples
) {
85 const struct gaih_addrtuple
*it
;
88 for (it
= tuples
; it
; it
= it
->next
) {
89 _cleanup_free_
char *a
= NULL
;
90 union in_addr_union u
;
92 char family_name
[DECIMAL_STR_MAX(int)];
93 char ifname
[IF_NAMESIZE
];
95 memcpy(&u
, it
->addr
, 16);
96 r
= in_addr_to_string(it
->family
, &u
, &a
);
97 assert_se(r
== 0 || r
== -EAFNOSUPPORT
);
98 if (r
== -EAFNOSUPPORT
)
99 assert_se((a
= hexmem(it
->addr
, 16)));
101 if (it
->scopeid
== 0)
102 goto numerical_index
;
104 if (if_indextoname(it
->scopeid
, ifname
) == NULL
) {
105 log_warning("if_indextoname(%d) failed: %m", it
->scopeid
);
107 xsprintf(ifname
, "%i", it
->scopeid
);
110 log_info(" \"%s\" %s %s %%%s",
112 af_to_string(it
->family
, family_name
, sizeof family_name
),
120 static void print_struct_hostent(struct hostent
*host
, const char *canon
) {
123 log_info(" \"%s\"", host
->h_name
);
124 STRV_FOREACH(s
, host
->h_aliases
)
125 log_info(" alias \"%s\"", *s
);
126 STRV_FOREACH(s
, host
->h_addr_list
) {
127 union in_addr_union u
;
128 _cleanup_free_
char *a
= NULL
;
129 char family_name
[DECIMAL_STR_MAX(int)];
132 assert_se((unsigned) host
->h_length
== FAMILY_ADDRESS_SIZE(host
->h_addrtype
));
133 memcpy(&u
, *s
, host
->h_length
);
134 r
= in_addr_to_string(host
->h_addrtype
, &u
, &a
);
137 af_to_string(host
->h_addrtype
, family_name
, sizeof family_name
),
141 log_info(" canonical: \"%s\"", canon
);
144 static void test_gethostbyname4_r(void *handle
, const char *module
, const char *name
) {
146 _nss_gethostbyname4_r_t f
;
148 struct gaih_addrtuple
*pat
= NULL
;
149 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
150 int32_t ttl
= INT32_MAX
; /* nss-dns wants to return the lowest ttl,
151 and will access this variable through *ttlp,
152 so we need to set it to something.
153 I'm not sure if this is a bug in nss-dns
155 enum nss_status status
;
156 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
159 fname
= strjoina("_nss_", module
, "_gethostbyname4_r");
160 f
= dlsym(handle
, fname
);
161 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
164 status
= f(name
, &pat
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
);
165 if (status
== NSS_STATUS_SUCCESS
) {
166 log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%tx errno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
168 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
169 pat
? (char*) pat
- buffer
: 0,
170 errno1
, errno_to_name(errno1
) ?: "---",
171 errno2
, hstrerror(errno2
),
173 n
= print_gaih_addrtuples(pat
);
175 log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s",
177 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
179 errno1
, errno_to_name(errno1
) ?: "---",
180 errno2
, hstrerror(errno2
));
184 if (STR_IN_SET(module
, "resolve", "mymachines") && status
== NSS_STATUS_UNAVAIL
)
187 if (STR_IN_SET(module
, "myhostname", "resolve") && streq(name
, "localhost")) {
188 assert_se(status
== NSS_STATUS_SUCCESS
);
194 static void test_gethostbyname3_r(void *handle
, const char *module
, const char *name
, int af
) {
196 _nss_gethostbyname3_r_t f
;
198 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
199 int32_t ttl
= INT32_MAX
; /* nss-dns wants to return the lowest ttl,
200 and will access this variable through *ttlp,
201 so we need to set it to something.
202 I'm not sure if this is a bug in nss-dns
204 enum nss_status status
;
205 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
208 char family_name
[DECIMAL_STR_MAX(int)];
210 fname
= strjoina("_nss_", module
, "_gethostbyname3_r");
211 f
= dlsym(handle
, fname
);
212 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
215 status
= f(name
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
, &canon
);
216 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
217 fname
, name
, af_to_string(af
, family_name
, sizeof family_name
),
218 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
219 errno1
, errno_to_name(errno1
) ?: "---",
220 errno2
, hstrerror(errno2
),
222 if (status
== NSS_STATUS_SUCCESS
)
223 print_struct_hostent(&host
, canon
);
226 static void test_gethostbyname2_r(void *handle
, const char *module
, const char *name
, int af
) {
228 _nss_gethostbyname2_r_t f
;
230 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
231 enum nss_status status
;
232 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
234 char family_name
[DECIMAL_STR_MAX(int)];
236 fname
= strjoina("_nss_", module
, "_gethostbyname2_r");
237 f
= dlsym(handle
, fname
);
238 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
241 status
= f(name
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
242 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s",
243 fname
, name
, af_to_string(af
, family_name
, sizeof family_name
),
244 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
245 errno1
, errno_to_name(errno1
) ?: "---",
246 errno2
, hstrerror(errno2
));
247 if (status
== NSS_STATUS_SUCCESS
)
248 print_struct_hostent(&host
, NULL
);
251 static void test_gethostbyname_r(void *handle
, const char *module
, const char *name
) {
253 _nss_gethostbyname_r_t f
;
255 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
256 enum nss_status status
;
257 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
260 fname
= strjoina("_nss_", module
, "_gethostbyname_r");
261 f
= dlsym(handle
, fname
);
262 log_debug("dlsym(0x%p, %s) → 0x%p", handle
, fname
, f
);
265 status
= f(name
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
266 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
268 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
269 errno1
, errno_to_name(errno1
) ?: "---",
270 errno2
, hstrerror(errno2
));
271 if (status
== NSS_STATUS_SUCCESS
)
272 print_struct_hostent(&host
, NULL
);
275 static void test_gethostbyaddr2_r(void *handle
,
277 const void* addr
, socklen_t len
,
281 _nss_gethostbyaddr2_r_t f
;
283 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
284 enum nss_status status
;
285 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
287 int32_t ttl
= INT32_MAX
;
288 _cleanup_free_
char *addr_pretty
= NULL
;
290 fname
= strjoina("_nss_", module
, "_gethostbyaddr2_r");
291 f
= dlsym(handle
, fname
);
293 log_full_errno(f
? LOG_DEBUG
: LOG_INFO
, errno
,
294 "dlsym(0x%p, %s) → 0x%p: %m", handle
, fname
, f
);
298 assert_se(in_addr_to_string(af
, addr
, &addr_pretty
) >= 0);
300 status
= f(addr
, len
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
, &ttl
);
301 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32
,
303 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
304 errno1
, errno_to_name(errno1
) ?: "---",
305 errno2
, hstrerror(errno2
),
307 if (status
== NSS_STATUS_SUCCESS
)
308 print_struct_hostent(&host
, NULL
);
311 static void test_gethostbyaddr_r(void *handle
,
313 const void* addr
, socklen_t len
,
317 _nss_gethostbyaddr_r_t f
;
319 int errno1
= 999, errno2
= 999; /* nss-dns doesn't set those */
320 enum nss_status status
;
321 char pretty_status
[DECIMAL_STR_MAX(enum nss_status
)];
323 _cleanup_free_
char *addr_pretty
= NULL
;
325 fname
= strjoina("_nss_", module
, "_gethostbyaddr_r");
326 f
= dlsym(handle
, fname
);
328 log_full_errno(f
? LOG_DEBUG
: LOG_INFO
, errno
,
329 "dlsym(0x%p, %s) → 0x%p: %m", handle
, fname
, f
);
333 assert_se(in_addr_to_string(af
, addr
, &addr_pretty
) >= 0);
335 status
= f(addr
, len
, af
, &host
, buffer
, sizeof buffer
, &errno1
, &errno2
);
336 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
338 nss_status_to_string(status
, pretty_status
, sizeof pretty_status
), "\n",
339 errno1
, errno_to_name(errno1
) ?: "---",
340 errno2
, hstrerror(errno2
));
341 if (status
== NSS_STATUS_SUCCESS
)
342 print_struct_hostent(&host
, NULL
);
345 static void test_byname(void *handle
, const char *module
, const char *name
) {
346 test_gethostbyname4_r(handle
, module
, name
);
349 test_gethostbyname3_r(handle
, module
, name
, AF_INET
);
351 test_gethostbyname3_r(handle
, module
, name
, AF_INET6
);
353 test_gethostbyname3_r(handle
, module
, name
, AF_UNSPEC
);
355 test_gethostbyname3_r(handle
, module
, name
, AF_LOCAL
);
358 test_gethostbyname2_r(handle
, module
, name
, AF_INET
);
360 test_gethostbyname2_r(handle
, module
, name
, AF_INET6
);
362 test_gethostbyname2_r(handle
, module
, name
, AF_UNSPEC
);
364 test_gethostbyname2_r(handle
, module
, name
, AF_LOCAL
);
367 test_gethostbyname_r(handle
, module
, name
);
371 static void test_byaddr(void *handle
,
373 const void* addr
, socklen_t len
,
375 test_gethostbyaddr2_r(handle
, module
, addr
, len
, af
);
378 test_gethostbyaddr_r(handle
, module
, addr
, len
, af
);
382 #ifdef HAVE_MYHOSTNAME
383 # define MODULE1 "myhostname\0"
388 # define MODULE2 "resolve\0"
393 # define MODULE3 "mymachines\0"
397 #define MODULE4 "dns\0"
399 int main(int argc
, char **argv
) {
400 _cleanup_free_
char *dir
= NULL
, *hostname
= NULL
;
403 const uint32_t local_address_ipv4
= htonl(0x7F000001);
404 const uint32_t local_address_ipv4_2
= htonl(0x7F000002);
405 _cleanup_free_
struct local_address
*addresses
= NULL
;
408 log_set_max_level(LOG_INFO
);
409 log_parse_environment();
411 dir
= dirname_malloc(argv
[0]);
414 hostname
= gethostname_malloc();
417 n_addresses
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
418 if (n_addresses
< 0) {
419 log_info_errno(n_addresses
, "Failed to query local addresses: %m");
423 NULSTR_FOREACH(module
, MODULE1 MODULE2 MODULE3 MODULE4
) {
428 log_info("======== %s ========", module
);
430 handle
= open_handle(streq(module
, "dns") ? NULL
: dir
,
432 RTLD_LAZY
|RTLD_NODELETE
);
433 NULSTR_FOREACH(name
, "localhost\0" "gateway\0" "foo_no_such_host\0")
434 test_byname(handle
, module
, name
);
436 test_byname(handle
, module
, hostname
);
438 test_byaddr(handle
, module
, &local_address_ipv4
, sizeof local_address_ipv4
, AF_INET
);
439 test_byaddr(handle
, module
, &local_address_ipv4_2
, sizeof local_address_ipv4_2
, AF_INET
);
440 test_byaddr(handle
, module
, &in6addr_loopback
, sizeof in6addr_loopback
, AF_INET6
);
442 for (i
= 0; i
< n_addresses
; i
++)
443 test_byaddr(handle
, module
,
444 &addresses
[i
].address
,
445 FAMILY_ADDRESS_SIZE(addresses
[i
].family
),
446 addresses
[i
].family
);