]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-nss.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / test / test-nss.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
2abb5b3b
ZJS
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
39static 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
57static 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
71static void* open_handle(const char* dir, const char* module, int flags) {
72 const char *path;
73 void *handle;
74
3762b672
ZJS
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
2abb5b3b
ZJS
80 path = strjoina("libnss_", module, ".so.2");
81
82 handle = dlopen(path, flags);
9f7672b3
ZJS
83 if (!handle)
84 log_error("Failed to load module %s: %s", module, dlerror());
2abb5b3b
ZJS
85 return handle;
86}
87
88static 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);
4c701096 101 assert_se(IN_SET(r, 0, -EAFNOSUPPORT));
2abb5b3b
ZJS
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) {
25f027c5 109 log_warning_errno(errno, "if_indextoname(%d) failed: %m", it->scopeid);
2abb5b3b
ZJS
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
124static 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
148static 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
198static 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
230static 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
255static 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
279static 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
315static 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
349static 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
375static 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
9f7672b3
ZJS
386static 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
408static 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
440static 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(
08cf5b8d 454#if ENABLE_MYHOSTNAME
9f7672b3 455 "myhostname",
2abb5b3b 456#endif
1ec57f33 457#if ENABLE_RESOLVE
9f7672b3 458 "resolve",
2abb5b3b 459#endif
8e3e2f5f 460#if ENABLE_MACHINED
9f7672b3 461 "mymachines",
2abb5b3b 462#endif
9f7672b3
ZJS
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;
2abb5b3b 490
9f7672b3
ZJS
491 hostname = gethostname_malloc();
492 if (!hostname)
493 return -ENOMEM;
2abb5b3b 494
5248e7e1 495 names = strv_new("localhost", "_gateway", "foo_no_such_host", hostname, NULL);
9f7672b3
ZJS
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
513int main(int argc, char **argv) {
514 _cleanup_free_ char *dir = NULL;
515 _cleanup_strv_free_ char **modules = NULL, **names = NULL;
2abb5b3b 516 _cleanup_free_ struct local_address *addresses = NULL;
6a909d41 517 int n_addresses = 0;
9f7672b3
ZJS
518 char **module;
519 int r;
2abb5b3b
ZJS
520
521 log_set_max_level(LOG_INFO);
522 log_parse_environment();
523
9f7672b3
ZJS
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;
2abb5b3b
ZJS
528 }
529
9f7672b3
ZJS
530 dir = dirname_malloc(argv[0]);
531 if (!dir)
532 return EXIT_FAILURE;
2abb5b3b 533
9f7672b3
ZJS
534 STRV_FOREACH(module, modules) {
535 r = test_one_module(dir, *module, names, addresses, n_addresses);
536 if (r < 0)
537 return EXIT_FAILURE;
2abb5b3b
ZJS
538 }
539
540 return EXIT_SUCCESS;
541}