]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-nss.c
update TODO
[thirdparty/systemd.git] / src / test / test-nss.c
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
38 static 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
56 static 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
70 static 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);
80 assert_se(handle);
81 return handle;
82 }
83
84 static int print_gaih_addrtuples(const struct gaih_addrtuple *tuples) {
85 const struct gaih_addrtuple *it;
86 int n = 0;
87
88 for (it = tuples; it; it = it->next) {
89 _cleanup_free_ char *a = NULL;
90 union in_addr_union u;
91 int r;
92 char family_name[DECIMAL_STR_MAX(int)];
93 char ifname[IF_NAMESIZE];
94
95 memcpy(&u, it->addr, 16);
96 r = in_addr_to_string(it->family, &u, &a);
97 assert_se(r == 0 || r == -EAFNOSUPPORT);
98 if (r == -EAFNOSUPPORT)
99 assert_se((a = hexmem(it->addr, 16)));
100
101 if (it->scopeid == 0)
102 goto numerical_index;
103
104 if (if_indextoname(it->scopeid, ifname) == NULL) {
105 log_warning("if_indextoname(%d) failed: %m", it->scopeid);
106 numerical_index:
107 xsprintf(ifname, "%i", it->scopeid);
108 };
109
110 log_info(" \"%s\" %s %s %%%s",
111 it->name,
112 af_to_string(it->family, family_name, sizeof family_name),
113 a,
114 ifname);
115 n ++;
116 }
117 return n;
118 }
119
120 static void print_struct_hostent(struct hostent *host, const char *canon) {
121 char **s;
122
123 log_info(" \"%s\"", host->h_name);
124 STRV_FOREACH(s, host->h_aliases)
125 log_info(" alias \"%s\"", *s);
126 STRV_FOREACH(s, host->h_addr_list) {
127 union in_addr_union u;
128 _cleanup_free_ char *a = NULL;
129 char family_name[DECIMAL_STR_MAX(int)];
130 int r;
131
132 assert_se((unsigned) host->h_length == FAMILY_ADDRESS_SIZE(host->h_addrtype));
133 memcpy(&u, *s, host->h_length);
134 r = in_addr_to_string(host->h_addrtype, &u, &a);
135 assert_se(r == 0);
136 log_info(" %s %s",
137 af_to_string(host->h_addrtype, family_name, sizeof family_name),
138 a);
139 }
140 if (canon)
141 log_info(" canonical: \"%s\"", canon);
142 }
143
144 static void test_gethostbyname4_r(void *handle, const char *module, const char *name) {
145 const char *fname;
146 _nss_gethostbyname4_r_t f;
147 char buffer[2000];
148 struct gaih_addrtuple *pat = NULL;
149 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
150 int32_t ttl = INT32_MAX; /* nss-dns wants to return the lowest ttl,
151 and will access this variable through *ttlp,
152 so we need to set it to something.
153 I'm not sure if this is a bug in nss-dns
154 or not. */
155 enum nss_status status;
156 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
157 int n;
158
159 fname = strjoina("_nss_", module, "_gethostbyname4_r");
160 f = dlsym(handle, fname);
161 log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
162 assert_se(f);
163
164 status = f(name, &pat, buffer, sizeof buffer, &errno1, &errno2, &ttl);
165 if (status == NSS_STATUS_SUCCESS) {
166 log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%tx errno=%d/%s h_errno=%d/%s ttl=%"PRIi32,
167 fname, name,
168 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
169 pat ? (char*) pat - buffer : 0,
170 errno1, errno_to_name(errno1) ?: "---",
171 errno2, hstrerror(errno2),
172 ttl);
173 n = print_gaih_addrtuples(pat);
174 } else {
175 log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s",
176 fname, name,
177 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
178 pat,
179 errno1, errno_to_name(errno1) ?: "---",
180 errno2, hstrerror(errno2));
181 n = 0;
182 }
183
184 if (STR_IN_SET(module, "resolve", "mymachines") && status == NSS_STATUS_UNAVAIL)
185 return;
186
187 if (STR_IN_SET(module, "myhostname", "resolve") && streq(name, "localhost")) {
188 assert_se(status == NSS_STATUS_SUCCESS);
189 assert_se(n == 2);
190 }
191 }
192
193
194 static void test_gethostbyname3_r(void *handle, const char *module, const char *name, int af) {
195 const char *fname;
196 _nss_gethostbyname3_r_t f;
197 char buffer[2000];
198 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
199 int32_t ttl = INT32_MAX; /* nss-dns wants to return the lowest ttl,
200 and will access this variable through *ttlp,
201 so we need to set it to something.
202 I'm not sure if this is a bug in nss-dns
203 or not. */
204 enum nss_status status;
205 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
206 struct hostent host;
207 char *canon;
208 char family_name[DECIMAL_STR_MAX(int)];
209
210 fname = strjoina("_nss_", module, "_gethostbyname3_r");
211 f = dlsym(handle, fname);
212 log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
213 assert_se(f);
214
215 status = f(name, af, &host, buffer, sizeof buffer, &errno1, &errno2, &ttl, &canon);
216 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32,
217 fname, name, af_to_string(af, family_name, sizeof family_name),
218 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
219 errno1, errno_to_name(errno1) ?: "---",
220 errno2, hstrerror(errno2),
221 ttl);
222 if (status == NSS_STATUS_SUCCESS)
223 print_struct_hostent(&host, canon);
224 }
225
226 static void test_gethostbyname2_r(void *handle, const char *module, const char *name, int af) {
227 const char *fname;
228 _nss_gethostbyname2_r_t f;
229 char buffer[2000];
230 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
231 enum nss_status status;
232 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
233 struct hostent host;
234 char family_name[DECIMAL_STR_MAX(int)];
235
236 fname = strjoina("_nss_", module, "_gethostbyname2_r");
237 f = dlsym(handle, fname);
238 log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
239 assert_se(f);
240
241 status = f(name, af, &host, buffer, sizeof buffer, &errno1, &errno2);
242 log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s",
243 fname, name, af_to_string(af, family_name, sizeof family_name),
244 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
245 errno1, errno_to_name(errno1) ?: "---",
246 errno2, hstrerror(errno2));
247 if (status == NSS_STATUS_SUCCESS)
248 print_struct_hostent(&host, NULL);
249 }
250
251 static void test_gethostbyname_r(void *handle, const char *module, const char *name) {
252 const char *fname;
253 _nss_gethostbyname_r_t f;
254 char buffer[2000];
255 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
256 enum nss_status status;
257 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
258 struct hostent host;
259
260 fname = strjoina("_nss_", module, "_gethostbyname_r");
261 f = dlsym(handle, fname);
262 log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
263 assert_se(f);
264
265 status = f(name, &host, buffer, sizeof buffer, &errno1, &errno2);
266 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
267 fname, name,
268 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
269 errno1, errno_to_name(errno1) ?: "---",
270 errno2, hstrerror(errno2));
271 if (status == NSS_STATUS_SUCCESS)
272 print_struct_hostent(&host, NULL);
273 }
274
275 static void test_gethostbyaddr2_r(void *handle,
276 const char *module,
277 const void* addr, socklen_t len,
278 int af) {
279
280 const char *fname;
281 _nss_gethostbyaddr2_r_t f;
282 char buffer[2000];
283 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
284 enum nss_status status;
285 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
286 struct hostent host;
287 int32_t ttl = INT32_MAX;
288 _cleanup_free_ char *addr_pretty = NULL;
289
290 fname = strjoina("_nss_", module, "_gethostbyaddr2_r");
291 f = dlsym(handle, fname);
292
293 log_full_errno(f ? LOG_DEBUG : LOG_INFO, errno,
294 "dlsym(0x%p, %s) → 0x%p: %m", handle, fname, f);
295 if (!f)
296 return;
297
298 assert_se(in_addr_to_string(af, addr, &addr_pretty) >= 0);
299
300 status = f(addr, len, af, &host, buffer, sizeof buffer, &errno1, &errno2, &ttl);
301 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32,
302 fname, addr_pretty,
303 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
304 errno1, errno_to_name(errno1) ?: "---",
305 errno2, hstrerror(errno2),
306 ttl);
307 if (status == NSS_STATUS_SUCCESS)
308 print_struct_hostent(&host, NULL);
309 }
310
311 static void test_gethostbyaddr_r(void *handle,
312 const char *module,
313 const void* addr, socklen_t len,
314 int af) {
315
316 const char *fname;
317 _nss_gethostbyaddr_r_t f;
318 char buffer[2000];
319 int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
320 enum nss_status status;
321 char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
322 struct hostent host;
323 _cleanup_free_ char *addr_pretty = NULL;
324
325 fname = strjoina("_nss_", module, "_gethostbyaddr_r");
326 f = dlsym(handle, fname);
327
328 log_full_errno(f ? LOG_DEBUG : LOG_INFO, errno,
329 "dlsym(0x%p, %s) → 0x%p: %m", handle, fname, f);
330 if (!f)
331 return;
332
333 assert_se(in_addr_to_string(af, addr, &addr_pretty) >= 0);
334
335 status = f(addr, len, af, &host, buffer, sizeof buffer, &errno1, &errno2);
336 log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
337 fname, addr_pretty,
338 nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
339 errno1, errno_to_name(errno1) ?: "---",
340 errno2, hstrerror(errno2));
341 if (status == NSS_STATUS_SUCCESS)
342 print_struct_hostent(&host, NULL);
343 }
344
345 static void test_byname(void *handle, const char *module, const char *name) {
346 test_gethostbyname4_r(handle, module, name);
347 puts("");
348
349 test_gethostbyname3_r(handle, module, name, AF_INET);
350 puts("");
351 test_gethostbyname3_r(handle, module, name, AF_INET6);
352 puts("");
353 test_gethostbyname3_r(handle, module, name, AF_UNSPEC);
354 puts("");
355 test_gethostbyname3_r(handle, module, name, AF_LOCAL);
356 puts("");
357
358 test_gethostbyname2_r(handle, module, name, AF_INET);
359 puts("");
360 test_gethostbyname2_r(handle, module, name, AF_INET6);
361 puts("");
362 test_gethostbyname2_r(handle, module, name, AF_UNSPEC);
363 puts("");
364 test_gethostbyname2_r(handle, module, name, AF_LOCAL);
365 puts("");
366
367 test_gethostbyname_r(handle, module, name);
368 puts("");
369 }
370
371 static void test_byaddr(void *handle,
372 const char *module,
373 const void* addr, socklen_t len,
374 int af) {
375 test_gethostbyaddr2_r(handle, module, addr, len, af);
376 puts("");
377
378 test_gethostbyaddr_r(handle, module, addr, len, af);
379 puts("");
380 }
381
382 #ifdef HAVE_MYHOSTNAME
383 # define MODULE1 "myhostname\0"
384 #else
385 # define MODULE1
386 #endif
387 #ifdef HAVE_RESOLVED
388 # define MODULE2 "resolve\0"
389 #else
390 # define MODULE2
391 #endif
392 #ifdef HAVE_MACHINED
393 # define MODULE3 "mymachines\0"
394 #else
395 # define MODULE3
396 #endif
397 #define MODULE4 "dns\0"
398
399 int main(int argc, char **argv) {
400 _cleanup_free_ char *dir = NULL, *hostname = NULL;
401 const char *module;
402
403 const uint32_t local_address_ipv4 = htonl(0x7F000001);
404 const uint32_t local_address_ipv4_2 = htonl(0x7F000002);
405 _cleanup_free_ struct local_address *addresses = NULL;
406 int n_addresses;
407
408 log_set_max_level(LOG_INFO);
409 log_parse_environment();
410
411 dir = dirname_malloc(argv[0]);
412 assert_se(dir);
413
414 hostname = gethostname_malloc();
415 assert_se(hostname);
416
417 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
418 if (n_addresses < 0) {
419 log_info_errno(n_addresses, "Failed to query local addresses: %m");
420 n_addresses = 0;
421 }
422
423 NULSTR_FOREACH(module, MODULE1 MODULE2 MODULE3 MODULE4) {
424 void *handle;
425 const char *name;
426 int i;
427
428 log_info("======== %s ========", module);
429
430 handle = open_handle(streq(module, "dns") ? NULL : dir,
431 module,
432 RTLD_LAZY|RTLD_NODELETE);
433 NULSTR_FOREACH(name, "localhost\0" "gateway\0" "foo_no_such_host\0")
434 test_byname(handle, module, name);
435
436 test_byname(handle, module, hostname);
437
438 test_byaddr(handle, module, &local_address_ipv4, sizeof local_address_ipv4, AF_INET);
439 test_byaddr(handle, module, &local_address_ipv4_2, sizeof local_address_ipv4_2, AF_INET);
440 test_byaddr(handle, module, &in6addr_loopback, sizeof in6addr_loopback, AF_INET6);
441
442 for (i = 0; i < n_addresses; i++)
443 test_byaddr(handle, module,
444 &addresses[i].address,
445 FAMILY_ADDRESS_SIZE(addresses[i].family),
446 addresses[i].family);
447
448 dlclose(handle);
449
450 log_info(" ");
451 }
452
453 return EXIT_SUCCESS;
454 }