]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-nss.c
dns-domain: add helper that checks whether domain is dot suffixed
[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
890e5a4d 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
890e5a4d 66 log_debug("Using %s", path);
2abb5b3b 67 handle = dlopen(path, flags);
9f7672b3
ZJS
68 if (!handle)
69 log_error("Failed to load module %s: %s", module, dlerror());
2abb5b3b
ZJS
70 return handle;
71}
72
73static int print_gaih_addrtuples(const struct gaih_addrtuple *tuples) {
2abb5b3b
ZJS
74 int n = 0;
75
890e5a4d 76 for (const struct gaih_addrtuple *it = tuples; it; it = it->next) {
2abb5b3b
ZJS
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);
08554d47
ZJS
150 if (!f) {
151 log_info("%s not defined", fname);
152 return;
153 }
2abb5b3b
ZJS
154
155 status = f(name, &pat, buffer, sizeof buffer, &errno1, &errno2, &ttl);
156 if (status == NSS_STATUS_SUCCESS) {
157 log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%tx errno=%d/%s h_errno=%d/%s ttl=%"PRIi32,
158 fname, name,
159 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
160 pat ? (char*) pat - buffer : 0,
161 errno1, errno_to_name(errno1) ?: "---",
162 errno2, hstrerror(errno2),
163 ttl);
164 n = print_gaih_addrtuples(pat);
165 } else {
166 log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s",
167 fname, name,
168 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
169 pat,
170 errno1, errno_to_name(errno1) ?: "---",
171 errno2, hstrerror(errno2));
172 n = 0;
173 }
174
175 if (STR_IN_SET(module, "resolve", "mymachines") && status == NSS_STATUS_UNAVAIL)
176 return;
177
178 if (STR_IN_SET(module, "myhostname", "resolve") && streq(name, "localhost")) {
179 assert_se(status == NSS_STATUS_SUCCESS);
180 assert_se(n == 2);
181 }
182}
183
2abb5b3b
ZJS
184static void test_gethostbyname3_r(void *handle, const char *module, const char *name, int af) {
185 const char *fname;
186 _nss_gethostbyname3_r_t f;
187 char buffer[2000];
188 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
189 int32_t ttl = INT32_MAX; /* nss-dns wants to return the lowest ttl,
190 and will access this variable through *ttlp,
191 so we need to set it to something.
192 I'm not sure if this is a bug in nss-dns
193 or not. */
194 enum nss_status status;
195 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
196 struct hostent host;
197 char *canon;
198 char family_name[DECIMAL_STR_MAX(int)];
199
200 fname = strjoina("_nss_", module, "_gethostbyname3_r");
201 f = dlsym(handle, fname);
202 log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
08554d47
ZJS
203 if (!f) {
204 log_info("%s not defined", fname);
205 return;
206 }
2abb5b3b
ZJS
207
208 status = f(name, af, &host, buffer, sizeof buffer, &errno1, &errno2, &ttl, &canon);
209 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32,
210 fname, name, af_to_string(af, family_name, sizeof family_name),
211 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
212 errno1, errno_to_name(errno1) ?: "---",
213 errno2, hstrerror(errno2),
214 ttl);
215 if (status == NSS_STATUS_SUCCESS)
216 print_struct_hostent(&host, canon);
217}
218
219static void test_gethostbyname2_r(void *handle, const char *module, const char *name, int af) {
220 const char *fname;
221 _nss_gethostbyname2_r_t f;
222 char buffer[2000];
223 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
224 enum nss_status status;
225 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
226 struct hostent host;
227 char family_name[DECIMAL_STR_MAX(int)];
228
229 fname = strjoina("_nss_", module, "_gethostbyname2_r");
230 f = dlsym(handle, fname);
231 log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
08554d47
ZJS
232 if (!f) {
233 log_info("%s not defined", fname);
234 return;
235 }
2abb5b3b
ZJS
236
237 status = f(name, af, &host, buffer, sizeof buffer, &errno1, &errno2);
238 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s",
239 fname, name, af_to_string(af, family_name, sizeof family_name),
240 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
241 errno1, errno_to_name(errno1) ?: "---",
242 errno2, hstrerror(errno2));
243 if (status == NSS_STATUS_SUCCESS)
244 print_struct_hostent(&host, NULL);
245}
246
247static void test_gethostbyname_r(void *handle, const char *module, const char *name) {
248 const char *fname;
249 _nss_gethostbyname_r_t f;
250 char buffer[2000];
251 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
252 enum nss_status status;
253 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
254 struct hostent host;
255
256 fname = strjoina("_nss_", module, "_gethostbyname_r");
257 f = dlsym(handle, fname);
258 log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
08554d47
ZJS
259 if (!f) {
260 log_info("%s not defined", fname);
261 return;
262 }
2abb5b3b
ZJS
263
264 status = f(name, &host, buffer, sizeof buffer, &errno1, &errno2);
265 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
266 fname, name,
267 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
268 errno1, errno_to_name(errno1) ?: "---",
269 errno2, hstrerror(errno2));
270 if (status == NSS_STATUS_SUCCESS)
271 print_struct_hostent(&host, NULL);
272}
273
274static void test_gethostbyaddr2_r(void *handle,
275 const char *module,
276 const void* addr, socklen_t len,
277 int af) {
278
279 const char *fname;
280 _nss_gethostbyaddr2_r_t f;
281 char buffer[2000];
282 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
283 enum nss_status status;
284 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
285 struct hostent host;
286 int32_t ttl = INT32_MAX;
287 _cleanup_free_ char *addr_pretty = NULL;
288
289 fname = strjoina("_nss_", module, "_gethostbyaddr2_r");
290 f = dlsym(handle, fname);
291
292 log_full_errno(f ? LOG_DEBUG : LOG_INFO, errno,
293 "dlsym(0x%p, %s) → 0x%p: %m", handle, fname, f);
08554d47
ZJS
294 if (!f) {
295 log_info("%s not defined", fname);
2abb5b3b 296 return;
08554d47 297 }
2abb5b3b
ZJS
298
299 assert_se(in_addr_to_string(af, addr, &addr_pretty) >= 0);
300
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,
303 fname, addr_pretty,
304 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
305 errno1, errno_to_name(errno1) ?: "---",
306 errno2, hstrerror(errno2),
307 ttl);
308 if (status == NSS_STATUS_SUCCESS)
309 print_struct_hostent(&host, NULL);
310}
311
312static void test_gethostbyaddr_r(void *handle,
313 const char *module,
314 const void* addr, socklen_t len,
315 int af) {
316
317 const char *fname;
318 _nss_gethostbyaddr_r_t f;
319 char buffer[2000];
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)];
323 struct hostent host;
324 _cleanup_free_ char *addr_pretty = NULL;
325
326 fname = strjoina("_nss_", module, "_gethostbyaddr_r");
327 f = dlsym(handle, fname);
328
329 log_full_errno(f ? LOG_DEBUG : LOG_INFO, errno,
330 "dlsym(0x%p, %s) → 0x%p: %m", handle, fname, f);
08554d47
ZJS
331 if (!f) {
332 log_info("%s not defined", fname);
2abb5b3b 333 return;
08554d47 334 }
2abb5b3b
ZJS
335
336 assert_se(in_addr_to_string(af, addr, &addr_pretty) >= 0);
337
338 status = f(addr, len, af, &host, buffer, sizeof buffer, &errno1, &errno2);
339 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
340 fname, addr_pretty,
341 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
342 errno1, errno_to_name(errno1) ?: "---",
343 errno2, hstrerror(errno2));
344 if (status == NSS_STATUS_SUCCESS)
345 print_struct_hostent(&host, NULL);
346}
347
348static void test_byname(void *handle, const char *module, const char *name) {
349 test_gethostbyname4_r(handle, module, name);
350 puts("");
351
352 test_gethostbyname3_r(handle, module, name, AF_INET);
353 puts("");
354 test_gethostbyname3_r(handle, module, name, AF_INET6);
355 puts("");
356 test_gethostbyname3_r(handle, module, name, AF_UNSPEC);
357 puts("");
358 test_gethostbyname3_r(handle, module, name, AF_LOCAL);
359 puts("");
360
361 test_gethostbyname2_r(handle, module, name, AF_INET);
362 puts("");
363 test_gethostbyname2_r(handle, module, name, AF_INET6);
364 puts("");
365 test_gethostbyname2_r(handle, module, name, AF_UNSPEC);
366 puts("");
367 test_gethostbyname2_r(handle, module, name, AF_LOCAL);
368 puts("");
369
370 test_gethostbyname_r(handle, module, name);
371 puts("");
372}
373
374static void test_byaddr(void *handle,
375 const char *module,
376 const void* addr, socklen_t len,
377 int af) {
378 test_gethostbyaddr2_r(handle, module, addr, len, af);
379 puts("");
380
381 test_gethostbyaddr_r(handle, module, addr, len, af);
382 puts("");
383}
384
9f7672b3
ZJS
385static int make_addresses(struct local_address **addresses) {
386 int n;
387 size_t n_alloc;
388 _cleanup_free_ struct local_address *addrs = NULL;
389
390 n = local_addresses(NULL, 0, AF_UNSPEC, &addrs);
391 if (n < 0)
392 log_info_errno(n, "Failed to query local addresses: %m");
393
394 n_alloc = n; /* we _can_ do that */
395 if (!GREEDY_REALLOC(addrs, n_alloc, n + 3))
396 return log_oom();
397
398 addrs[n++] = (struct local_address) { .family = AF_INET,
399 .address.in = { htobe32(0x7F000001) } };
400 addrs[n++] = (struct local_address) { .family = AF_INET,
401 .address.in = { htobe32(0x7F000002) } };
402 addrs[n++] = (struct local_address) { .family = AF_INET6,
403 .address.in6 = in6addr_loopback };
404 return 0;
405}
406
890e5a4d 407static int test_one_module(const char *dir,
9f7672b3
ZJS
408 const char *module,
409 char **names,
410 struct local_address *addresses,
411 int n_addresses) {
412 void *handle;
413 char **name;
9f7672b3 414
9f7672b3
ZJS
415 log_info("======== %s ========", module);
416
8d732e2f 417 handle = open_handle(dir, module, RTLD_LAZY|RTLD_NODELETE);
9f7672b3
ZJS
418 if (!handle)
419 return -EINVAL;
420
421 STRV_FOREACH(name, names)
422 test_byname(handle, module, *name);
423
890e5a4d 424 for (int i = 0; i < n_addresses; i++)
9f7672b3
ZJS
425 test_byaddr(handle, module,
426 &addresses[i].address,
427 FAMILY_ADDRESS_SIZE(addresses[i].family),
428 addresses[i].family);
429
430 log_info(" ");
431 dlclose(handle);
432 return 0;
433}
434
435static int parse_argv(int argc, char **argv,
436 char ***the_modules,
437 char ***the_names,
438 struct local_address **the_addresses, int *n_addresses) {
439
440 int r, n = 0;
441 _cleanup_strv_free_ char **modules = NULL, **names = NULL;
442 _cleanup_free_ struct local_address *addrs = NULL;
443 size_t n_allocated = 0;
444
445 if (argc > 1)
bea1a013 446 modules = strv_new(argv[1]);
9f7672b3
ZJS
447 else
448 modules = strv_new(
08540a95 449#if ENABLE_NSS_MYHOSTNAME
9f7672b3 450 "myhostname",
2abb5b3b 451#endif
08540a95 452#if ENABLE_NSS_RESOLVE
9f7672b3 453 "resolve",
2abb5b3b 454#endif
08540a95 455#if ENABLE_NSS_MYMACHINES
9f7672b3 456 "mymachines",
2abb5b3b 457#endif
bea1a013 458 "dns");
9f7672b3
ZJS
459 if (!modules)
460 return -ENOMEM;
461
462 if (argc > 2) {
463 char **name;
464 int family;
465 union in_addr_union address;
466
467 STRV_FOREACH(name, argv + 2) {
468 r = in_addr_from_string_auto(*name, &family, &address);
469 if (r < 0) {
470 /* assume this is a name */
471 r = strv_extend(&names, *name);
472 if (r < 0)
473 return r;
474 } else {
475 if (!GREEDY_REALLOC0(addrs, n_allocated, n + 1))
476 return -ENOMEM;
477
478 addrs[n++] = (struct local_address) { .family = family,
479 .address = address };
480 }
481 }
482 } else {
483 _cleanup_free_ char *hostname;
2abb5b3b 484
9f7672b3
ZJS
485 hostname = gethostname_malloc();
486 if (!hostname)
487 return -ENOMEM;
2abb5b3b 488
bea1a013 489 names = strv_new("localhost", "_gateway", "foo_no_such_host", hostname);
9f7672b3
ZJS
490 if (!names)
491 return -ENOMEM;
492
493 n = make_addresses(&addrs);
494 if (n < 0)
495 return n;
496 }
497
498 *the_modules = modules;
499 *the_names = names;
500 modules = names = NULL;
501 *the_addresses = addrs;
502 *n_addresses = n;
503 addrs = NULL;
504 return 0;
505}
506
f68a2622 507static int run(int argc, char **argv) {
9f7672b3
ZJS
508 _cleanup_free_ char *dir = NULL;
509 _cleanup_strv_free_ char **modules = NULL, **names = NULL;
2abb5b3b 510 _cleanup_free_ struct local_address *addresses = NULL;
6a909d41 511 int n_addresses = 0;
9f7672b3
ZJS
512 char **module;
513 int r;
2abb5b3b 514
f68a2622 515 test_setup_logging(LOG_INFO);
2abb5b3b 516
9f7672b3
ZJS
517 r = parse_argv(argc, argv, &modules, &names, &addresses, &n_addresses);
518 if (r < 0) {
519 log_error_errno(r, "Failed to parse arguments: %m");
520 return EXIT_FAILURE;
2abb5b3b
ZJS
521 }
522
9f7672b3
ZJS
523 dir = dirname_malloc(argv[0]);
524 if (!dir)
f68a2622 525 return log_oom();
2abb5b3b 526
9f7672b3
ZJS
527 STRV_FOREACH(module, modules) {
528 r = test_one_module(dir, *module, names, addresses, n_addresses);
529 if (r < 0)
f68a2622 530 return r;
2abb5b3b
ZJS
531 }
532
f68a2622 533 return 0;
2abb5b3b 534}
f68a2622
ZJS
535
536DEFINE_MAIN_FUNCTION(run);