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