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