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