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