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