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