]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-nss.c
test-nss-files: simplify module name handling
[thirdparty/systemd.git] / src / test / test-nss.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
2abb5b3b
ZJS
2
3#include <dlfcn.h>
4#include <stdlib.h>
5#include <net/if.h>
6
7#include "log.h"
8#include "nss-util.h"
9#include "path-util.h"
10#include "string-util.h"
11#include "alloc-util.h"
12#include "in-addr-util.h"
13#include "hexdecoct.h"
14#include "af-list.h"
15#include "stdio-util.h"
16#include "strv.h"
17#include "errno-list.h"
18#include "hostname-util.h"
19#include "local-addresses.h"
20
21static const char* nss_status_to_string(enum nss_status status, char *buf, size_t buf_len) {
22 switch (status) {
23 case NSS_STATUS_TRYAGAIN:
24 return "NSS_STATUS_TRYAGAIN";
25 case NSS_STATUS_UNAVAIL:
26 return "NSS_STATUS_UNAVAIL";
27 case NSS_STATUS_NOTFOUND:
28 return "NSS_STATUS_NOTFOUND";
29 case NSS_STATUS_SUCCESS:
30 return "NSS_STATUS_SUCCESS";
31 case NSS_STATUS_RETURN:
32 return "NSS_STATUS_RETURN";
33 default:
34 snprintf(buf, buf_len, "%i", status);
35 return buf;
36 }
37};
38
39static const char* af_to_string(int family, char *buf, size_t buf_len) {
40 const char *name;
41
42 if (family == AF_UNSPEC)
43 return "*";
44
45 name = af_to_name(family);
46 if (name)
47 return name;
48
49 snprintf(buf, buf_len, "%i", family);
50 return buf;
51}
52
53static void* open_handle(const char* dir, const char* module, int flags) {
8d732e2f 54 const char *path = NULL;
2abb5b3b
ZJS
55 void *handle;
56
8d732e2f 57 if (dir)
3762b672 58 path = strjoina(dir, "/libnss_", module, ".so.2");
8d732e2f 59 if (!path || access(path, F_OK) < 0)
2abb5b3b
ZJS
60 path = strjoina("libnss_", module, ".so.2");
61
62 handle = dlopen(path, flags);
9f7672b3
ZJS
63 if (!handle)
64 log_error("Failed to load module %s: %s", module, dlerror());
2abb5b3b
ZJS
65 return handle;
66}
67
68static int print_gaih_addrtuples(const struct gaih_addrtuple *tuples) {
69 const struct gaih_addrtuple *it;
70 int n = 0;
71
72 for (it = tuples; it; it = it->next) {
73 _cleanup_free_ char *a = NULL;
74 union in_addr_union u;
75 int r;
76 char family_name[DECIMAL_STR_MAX(int)];
77 char ifname[IF_NAMESIZE];
78
79 memcpy(&u, it->addr, 16);
80 r = in_addr_to_string(it->family, &u, &a);
4c701096 81 assert_se(IN_SET(r, 0, -EAFNOSUPPORT));
2abb5b3b 82 if (r == -EAFNOSUPPORT)
dcd6361e 83 assert_se(a = hexmem(it->addr, 16));
2abb5b3b
ZJS
84
85 if (it->scopeid == 0)
86 goto numerical_index;
87
88 if (if_indextoname(it->scopeid, ifname) == NULL) {
25f027c5 89 log_warning_errno(errno, "if_indextoname(%d) failed: %m", it->scopeid);
2abb5b3b
ZJS
90 numerical_index:
91 xsprintf(ifname, "%i", it->scopeid);
92 };
93
94 log_info(" \"%s\" %s %s %%%s",
95 it->name,
96 af_to_string(it->family, family_name, sizeof family_name),
97 a,
98 ifname);
99 n ++;
100 }
101 return n;
102}
103
104static void print_struct_hostent(struct hostent *host, const char *canon) {
105 char **s;
106
107 log_info(" \"%s\"", host->h_name);
108 STRV_FOREACH(s, host->h_aliases)
109 log_info(" alias \"%s\"", *s);
110 STRV_FOREACH(s, host->h_addr_list) {
111 union in_addr_union u;
112 _cleanup_free_ char *a = NULL;
113 char family_name[DECIMAL_STR_MAX(int)];
114 int r;
115
116 assert_se((unsigned) host->h_length == FAMILY_ADDRESS_SIZE(host->h_addrtype));
117 memcpy(&u, *s, host->h_length);
118 r = in_addr_to_string(host->h_addrtype, &u, &a);
119 assert_se(r == 0);
120 log_info(" %s %s",
121 af_to_string(host->h_addrtype, family_name, sizeof family_name),
122 a);
123 }
124 if (canon)
125 log_info(" canonical: \"%s\"", canon);
126}
127
128static void test_gethostbyname4_r(void *handle, const char *module, const char *name) {
129 const char *fname;
130 _nss_gethostbyname4_r_t f;
131 char buffer[2000];
132 struct gaih_addrtuple *pat = NULL;
133 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
134 int32_t ttl = INT32_MAX; /* nss-dns wants to return the lowest ttl,
135 and will access this variable through *ttlp,
136 so we need to set it to something.
137 I'm not sure if this is a bug in nss-dns
138 or not. */
139 enum nss_status status;
140 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
141 int n;
142
143 fname = strjoina("_nss_", module, "_gethostbyname4_r");
144 f = dlsym(handle, fname);
145 log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
146 assert_se(f);
147
148 status = f(name, &pat, buffer, sizeof buffer, &errno1, &errno2, &ttl);
149 if (status == NSS_STATUS_SUCCESS) {
150 log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%tx errno=%d/%s h_errno=%d/%s ttl=%"PRIi32,
151 fname, name,
152 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
153 pat ? (char*) pat - buffer : 0,
154 errno1, errno_to_name(errno1) ?: "---",
155 errno2, hstrerror(errno2),
156 ttl);
157 n = print_gaih_addrtuples(pat);
158 } else {
159 log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s",
160 fname, name,
161 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
162 pat,
163 errno1, errno_to_name(errno1) ?: "---",
164 errno2, hstrerror(errno2));
165 n = 0;
166 }
167
168 if (STR_IN_SET(module, "resolve", "mymachines") && status == NSS_STATUS_UNAVAIL)
169 return;
170
171 if (STR_IN_SET(module, "myhostname", "resolve") && streq(name, "localhost")) {
172 assert_se(status == NSS_STATUS_SUCCESS);
173 assert_se(n == 2);
174 }
175}
176
2abb5b3b
ZJS
177static void test_gethostbyname3_r(void *handle, const char *module, const char *name, int af) {
178 const char *fname;
179 _nss_gethostbyname3_r_t f;
180 char buffer[2000];
181 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
182 int32_t ttl = INT32_MAX; /* nss-dns wants to return the lowest ttl,
183 and will access this variable through *ttlp,
184 so we need to set it to something.
185 I'm not sure if this is a bug in nss-dns
186 or not. */
187 enum nss_status status;
188 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
189 struct hostent host;
190 char *canon;
191 char family_name[DECIMAL_STR_MAX(int)];
192
193 fname = strjoina("_nss_", module, "_gethostbyname3_r");
194 f = dlsym(handle, fname);
195 log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
196 assert_se(f);
197
198 status = f(name, af, &host, buffer, sizeof buffer, &errno1, &errno2, &ttl, &canon);
199 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32,
200 fname, name, af_to_string(af, family_name, sizeof family_name),
201 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
202 errno1, errno_to_name(errno1) ?: "---",
203 errno2, hstrerror(errno2),
204 ttl);
205 if (status == NSS_STATUS_SUCCESS)
206 print_struct_hostent(&host, canon);
207}
208
209static void test_gethostbyname2_r(void *handle, const char *module, const char *name, int af) {
210 const char *fname;
211 _nss_gethostbyname2_r_t f;
212 char buffer[2000];
213 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
214 enum nss_status status;
215 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
216 struct hostent host;
217 char family_name[DECIMAL_STR_MAX(int)];
218
219 fname = strjoina("_nss_", module, "_gethostbyname2_r");
220 f = dlsym(handle, fname);
221 log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
222 assert_se(f);
223
224 status = f(name, af, &host, buffer, sizeof buffer, &errno1, &errno2);
225 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s",
226 fname, name, af_to_string(af, family_name, sizeof family_name),
227 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
228 errno1, errno_to_name(errno1) ?: "---",
229 errno2, hstrerror(errno2));
230 if (status == NSS_STATUS_SUCCESS)
231 print_struct_hostent(&host, NULL);
232}
233
234static void test_gethostbyname_r(void *handle, const char *module, const char *name) {
235 const char *fname;
236 _nss_gethostbyname_r_t f;
237 char buffer[2000];
238 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
239 enum nss_status status;
240 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
241 struct hostent host;
242
243 fname = strjoina("_nss_", module, "_gethostbyname_r");
244 f = dlsym(handle, fname);
245 log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
246 assert_se(f);
247
248 status = f(name, &host, buffer, sizeof buffer, &errno1, &errno2);
249 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
250 fname, name,
251 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
252 errno1, errno_to_name(errno1) ?: "---",
253 errno2, hstrerror(errno2));
254 if (status == NSS_STATUS_SUCCESS)
255 print_struct_hostent(&host, NULL);
256}
257
258static void test_gethostbyaddr2_r(void *handle,
259 const char *module,
260 const void* addr, socklen_t len,
261 int af) {
262
263 const char *fname;
264 _nss_gethostbyaddr2_r_t f;
265 char buffer[2000];
266 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
267 enum nss_status status;
268 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
269 struct hostent host;
270 int32_t ttl = INT32_MAX;
271 _cleanup_free_ char *addr_pretty = NULL;
272
273 fname = strjoina("_nss_", module, "_gethostbyaddr2_r");
274 f = dlsym(handle, fname);
275
276 log_full_errno(f ? LOG_DEBUG : LOG_INFO, errno,
277 "dlsym(0x%p, %s) → 0x%p: %m", handle, fname, f);
278 if (!f)
279 return;
280
281 assert_se(in_addr_to_string(af, addr, &addr_pretty) >= 0);
282
283 status = f(addr, len, af, &host, buffer, sizeof buffer, &errno1, &errno2, &ttl);
284 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32,
285 fname, addr_pretty,
286 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
287 errno1, errno_to_name(errno1) ?: "---",
288 errno2, hstrerror(errno2),
289 ttl);
290 if (status == NSS_STATUS_SUCCESS)
291 print_struct_hostent(&host, NULL);
292}
293
294static void test_gethostbyaddr_r(void *handle,
295 const char *module,
296 const void* addr, socklen_t len,
297 int af) {
298
299 const char *fname;
300 _nss_gethostbyaddr_r_t f;
301 char buffer[2000];
302 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
303 enum nss_status status;
304 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
305 struct hostent host;
306 _cleanup_free_ char *addr_pretty = NULL;
307
308 fname = strjoina("_nss_", module, "_gethostbyaddr_r");
309 f = dlsym(handle, fname);
310
311 log_full_errno(f ? LOG_DEBUG : LOG_INFO, errno,
312 "dlsym(0x%p, %s) → 0x%p: %m", handle, fname, f);
313 if (!f)
314 return;
315
316 assert_se(in_addr_to_string(af, addr, &addr_pretty) >= 0);
317
318 status = f(addr, len, af, &host, buffer, sizeof buffer, &errno1, &errno2);
319 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
320 fname, addr_pretty,
321 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
322 errno1, errno_to_name(errno1) ?: "---",
323 errno2, hstrerror(errno2));
324 if (status == NSS_STATUS_SUCCESS)
325 print_struct_hostent(&host, NULL);
326}
327
328static void test_byname(void *handle, const char *module, const char *name) {
329 test_gethostbyname4_r(handle, module, name);
330 puts("");
331
332 test_gethostbyname3_r(handle, module, name, AF_INET);
333 puts("");
334 test_gethostbyname3_r(handle, module, name, AF_INET6);
335 puts("");
336 test_gethostbyname3_r(handle, module, name, AF_UNSPEC);
337 puts("");
338 test_gethostbyname3_r(handle, module, name, AF_LOCAL);
339 puts("");
340
341 test_gethostbyname2_r(handle, module, name, AF_INET);
342 puts("");
343 test_gethostbyname2_r(handle, module, name, AF_INET6);
344 puts("");
345 test_gethostbyname2_r(handle, module, name, AF_UNSPEC);
346 puts("");
347 test_gethostbyname2_r(handle, module, name, AF_LOCAL);
348 puts("");
349
350 test_gethostbyname_r(handle, module, name);
351 puts("");
352}
353
354static void test_byaddr(void *handle,
355 const char *module,
356 const void* addr, socklen_t len,
357 int af) {
358 test_gethostbyaddr2_r(handle, module, addr, len, af);
359 puts("");
360
361 test_gethostbyaddr_r(handle, module, addr, len, af);
362 puts("");
363}
364
9f7672b3
ZJS
365static int make_addresses(struct local_address **addresses) {
366 int n;
367 size_t n_alloc;
368 _cleanup_free_ struct local_address *addrs = NULL;
369
370 n = local_addresses(NULL, 0, AF_UNSPEC, &addrs);
371 if (n < 0)
372 log_info_errno(n, "Failed to query local addresses: %m");
373
374 n_alloc = n; /* we _can_ do that */
375 if (!GREEDY_REALLOC(addrs, n_alloc, n + 3))
376 return log_oom();
377
378 addrs[n++] = (struct local_address) { .family = AF_INET,
379 .address.in = { htobe32(0x7F000001) } };
380 addrs[n++] = (struct local_address) { .family = AF_INET,
381 .address.in = { htobe32(0x7F000002) } };
382 addrs[n++] = (struct local_address) { .family = AF_INET6,
383 .address.in6 = in6addr_loopback };
384 return 0;
385}
386
387static int test_one_module(const char* dir,
388 const char *module,
389 char **names,
390 struct local_address *addresses,
391 int n_addresses) {
392 void *handle;
393 char **name;
394 int i;
395
9f7672b3
ZJS
396 log_info("======== %s ========", module);
397
8d732e2f 398 handle = open_handle(dir, module, RTLD_LAZY|RTLD_NODELETE);
9f7672b3
ZJS
399 if (!handle)
400 return -EINVAL;
401
402 STRV_FOREACH(name, names)
403 test_byname(handle, module, *name);
404
405 for (i = 0; i < n_addresses; i++)
406 test_byaddr(handle, module,
407 &addresses[i].address,
408 FAMILY_ADDRESS_SIZE(addresses[i].family),
409 addresses[i].family);
410
411 log_info(" ");
412 dlclose(handle);
413 return 0;
414}
415
416static int parse_argv(int argc, char **argv,
417 char ***the_modules,
418 char ***the_names,
419 struct local_address **the_addresses, int *n_addresses) {
420
421 int r, n = 0;
422 _cleanup_strv_free_ char **modules = NULL, **names = NULL;
423 _cleanup_free_ struct local_address *addrs = NULL;
424 size_t n_allocated = 0;
425
426 if (argc > 1)
427 modules = strv_new(argv[1], NULL);
428 else
429 modules = strv_new(
08540a95 430#if ENABLE_NSS_MYHOSTNAME
9f7672b3 431 "myhostname",
2abb5b3b 432#endif
08540a95 433#if ENABLE_NSS_RESOLVE
9f7672b3 434 "resolve",
2abb5b3b 435#endif
08540a95 436#if ENABLE_NSS_MYMACHINES
9f7672b3 437 "mymachines",
2abb5b3b 438#endif
9f7672b3
ZJS
439 "dns",
440 NULL);
441 if (!modules)
442 return -ENOMEM;
443
444 if (argc > 2) {
445 char **name;
446 int family;
447 union in_addr_union address;
448
449 STRV_FOREACH(name, argv + 2) {
450 r = in_addr_from_string_auto(*name, &family, &address);
451 if (r < 0) {
452 /* assume this is a name */
453 r = strv_extend(&names, *name);
454 if (r < 0)
455 return r;
456 } else {
457 if (!GREEDY_REALLOC0(addrs, n_allocated, n + 1))
458 return -ENOMEM;
459
460 addrs[n++] = (struct local_address) { .family = family,
461 .address = address };
462 }
463 }
464 } else {
465 _cleanup_free_ char *hostname;
2abb5b3b 466
9f7672b3
ZJS
467 hostname = gethostname_malloc();
468 if (!hostname)
469 return -ENOMEM;
2abb5b3b 470
5248e7e1 471 names = strv_new("localhost", "_gateway", "foo_no_such_host", hostname, NULL);
9f7672b3
ZJS
472 if (!names)
473 return -ENOMEM;
474
475 n = make_addresses(&addrs);
476 if (n < 0)
477 return n;
478 }
479
480 *the_modules = modules;
481 *the_names = names;
482 modules = names = NULL;
483 *the_addresses = addrs;
484 *n_addresses = n;
485 addrs = NULL;
486 return 0;
487}
488
489int main(int argc, char **argv) {
490 _cleanup_free_ char *dir = NULL;
491 _cleanup_strv_free_ char **modules = NULL, **names = NULL;
2abb5b3b 492 _cleanup_free_ struct local_address *addresses = NULL;
6a909d41 493 int n_addresses = 0;
9f7672b3
ZJS
494 char **module;
495 int r;
2abb5b3b
ZJS
496
497 log_set_max_level(LOG_INFO);
498 log_parse_environment();
499
9f7672b3
ZJS
500 r = parse_argv(argc, argv, &modules, &names, &addresses, &n_addresses);
501 if (r < 0) {
502 log_error_errno(r, "Failed to parse arguments: %m");
503 return EXIT_FAILURE;
2abb5b3b
ZJS
504 }
505
9f7672b3
ZJS
506 dir = dirname_malloc(argv[0]);
507 if (!dir)
508 return EXIT_FAILURE;
2abb5b3b 509
9f7672b3
ZJS
510 STRV_FOREACH(module, modules) {
511 r = test_one_module(dir, *module, names, addresses, n_addresses);
512 if (r < 0)
513 return EXIT_FAILURE;
2abb5b3b
ZJS
514 }
515
516 return EXIT_SUCCESS;
517}