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