From 38e32d4ac134aa6da411b0093e788bce35775d09 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Mon, 15 Feb 2016 14:02:05 +0100 Subject: [PATCH] nss: Introduce a test A small test to see how is the nss module working. Signed-off-by: Michal Privoznik --- cfg.mk | 2 +- tests/Makefile.am | 18 ++++ tests/nssdata/virbr0.status | 20 ++++ tests/nssdata/virbr1.status | 20 ++++ tests/nssmock.c | 140 ++++++++++++++++++++++++ tests/nsstest.c | 208 ++++++++++++++++++++++++++++++++++++ 6 files changed, 407 insertions(+), 1 deletion(-) create mode 100644 tests/nssdata/virbr0.status create mode 100644 tests/nssdata/virbr1.status create mode 100644 tests/nssmock.c create mode 100644 tests/nsstest.c diff --git a/cfg.mk b/cfg.mk index 5b864afb3c..6f28eef29c 100644 --- a/cfg.mk +++ b/cfg.mk @@ -1139,7 +1139,7 @@ exclude_file_name_regexp--sc_copyright_usage = \ ^COPYING(|\.LESSER)$$ exclude_file_name_regexp--sc_flags_usage = \ - ^(docs/|src/util/virnetdevtap\.c$$|tests/vir(cgroup|pci|usb)mock\.c$$) + ^(docs/|src/util/virnetdevtap\.c$$|tests/(vir(cgroup|pci|usb)|nss)mock\.c$$) exclude_file_name_regexp--sc_libvirt_unmarked_diagnostics = \ ^(src/rpc/gendispatch\.pl$$|tests/) diff --git a/tests/Makefile.am b/tests/Makefile.am index a1d61f1cb8..19bb40c9cb 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -108,6 +108,7 @@ EXTRA_DIST = \ nodedevschemadata \ nodedevschematest \ nodeinfodata \ + nssdata \ nwfilterschematest \ nwfilterxml2firewalldata \ nwfilterxml2xmlin \ @@ -190,6 +191,7 @@ test_programs = virshtest sockettest \ vircaps2xmltest \ virnetdevtest \ virtypedparamtest \ + nsstest \ $(NULL) if WITH_REMOTE @@ -421,6 +423,7 @@ test_libraries = libshunload.la \ virpcimock.la \ virnetdevmock.la \ nodeinfomock.la \ + nssmock.la \ $(NULL) if WITH_QEMU test_libraries += libqemumonitortestutils.la \ @@ -1064,6 +1067,21 @@ nodeinfomock_la_CFLAGS = $(AM_CFLAGS) nodeinfomock_la_LDFLAGS = $(MOCKLIBS_LDFLAGS) nodeinfomock_la_LIBADD = $(MOCKLIBS_LIBS) +nsstest_SOURCES = \ + nsstest.c testutils.h testutils.c +nsstest_CFLAGS = \ + $(AM_CFLAGS) \ + -I$(top_srcdir)/tools/nss +nsstest_LDADD = \ + $(LDADDS) \ + ../tools/nss/libnss_libvirt_impl.la + +nssmock_la_SOURCES = \ + nssmock.c +nssmock_la_CFLAGS = $(AM_CFLAGS) +nssmock_la_LDFLAGS = $(MOCKLIBS_LDFLAGS) +nssmock_la_LIBADD = $(MOCKLIBS_LIBS) + virnetdevtest_SOURCES = \ virnetdevtest.c testutils.h testutils.c virnetdevtest_CFLAGS = $(AM_CFLAGS) $(LIBNL_CFLAGS) diff --git a/tests/nssdata/virbr0.status b/tests/nssdata/virbr0.status new file mode 100644 index 0000000000..6ebe954547 --- /dev/null +++ b/tests/nssdata/virbr0.status @@ -0,0 +1,20 @@ +[ + { + "ip-address": "192.168.122.197", + "mac-address": "52:54:00:a4:6f:91", + "hostname": "fedora", + "expiry-time": 1900000000 + }, + { + "ip-address": "192.168.122.198", + "mac-address": "52:54:00:a4:6f:92", + "hostname": "fedora", + "expiry-time": 1900000000 + }, + { + "ip-address": "192.168.122.254", + "mac-address": "52:54:00:3a:b5:0c", + "hostname": "gentoo", + "expiry-time": 2000000000 + } +] diff --git a/tests/nssdata/virbr1.status b/tests/nssdata/virbr1.status new file mode 100644 index 0000000000..f8a9157f34 --- /dev/null +++ b/tests/nssdata/virbr1.status @@ -0,0 +1,20 @@ +[ + { + "ip-address": "192.168.122.199", + "mac-address": "52:54:00:a4:6f:93", + "hostname": "fedora", + "expiry-time": 1900000000 + }, + { + "ip-address": "2001:1234:dead:beef::2", + "mac-address": "52:54:00:04:be:64", + "hostname": "gentoo", + "expiry-time": 1900000000 + }, + { + "ip-address": "192.168.122.200", + "mac-address": "52:54:00:a4:6f:94", + "hostname": "fedora", + "expiry-time": 1 + } +] diff --git a/tests/nssmock.c b/tests/nssmock.c new file mode 100644 index 0000000000..b4a42604cb --- /dev/null +++ b/tests/nssmock.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Michal Privoznik + */ + +#include + +#ifdef __linux__ +# include +# include +# include +# include +# include +# include +# include + +# include "configmake.h" +# include "internal.h" +# include "virstring.h" +# include "viralloc.h" + +static int (*realopen)(const char *path, int flags, ...); +static DIR * (*realopendir)(const char *name); + +# define LEASEDIR LOCALSTATEDIR "/lib/libvirt/dnsmasq/" + +# define STDERR(...) \ + fprintf(stderr, "%s %zu: ", __FUNCTION__, (size_t) __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + +# define ABORT(...) \ + do { \ + STDERR(__VA_ARGS__); \ + abort(); \ + } while (0) + +# define ABORT_OOM() \ + ABORT("Out of memory") + +/* + * Functions to load the symbols and init the environment + */ +static void +init_syms(void) +{ + if (realopen) + return; + +# define LOAD_SYM(name) \ + do { \ + if (!(real ## name = dlsym(RTLD_NEXT, #name))) \ + ABORT("Cannot find real '%s' symbol\n", #name); \ + } while (0) + + LOAD_SYM(open); + LOAD_SYM(opendir); +} + +static int +getrealpath(char **newpath, + const char *path) +{ + if (STRPREFIX(path, LEASEDIR)) { + if (virAsprintfQuiet(newpath, "%s/nssdata/%s", + abs_srcdir, + path + strlen(LEASEDIR)) < 0) { + errno = ENOMEM; + return -1; + } + } else { + if (VIR_STRDUP_QUIET(*newpath, path) < 0) + return -1; + } + + return 0; +} + +int +open(const char *path, int flags, ...) +{ + int ret; + char *newpath = NULL; + + init_syms(); + + if (STRPREFIX(path, LEASEDIR) && + getrealpath(&newpath, path) < 0) + return -1; + + if (flags & O_CREAT) { + va_list ap; + mode_t mode; + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + ret = realopen(newpath ? newpath : path, flags, mode); + } else { + ret = realopen(newpath ? newpath : path, flags); + } + + VIR_FREE(newpath); + return ret; +} + +DIR * +opendir(const char *path) +{ + DIR *ret; + char *newpath = NULL; + + init_syms(); + + if (STRPREFIX(path, LEASEDIR) && + getrealpath(&newpath, path) < 0) + return NULL; + + ret = realopendir(newpath ? newpath : path); + + VIR_FREE(newpath); + return ret; +} +#else +/* Nothing to override on non-__linux__ platforms */ +#endif diff --git a/tests/nsstest.c b/tests/nsstest.c new file mode 100644 index 0000000000..340f313616 --- /dev/null +++ b/tests/nsstest.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Michal Privoznik + */ + +#include + +#include "testutils.h" + +#ifdef __linux__ + +# include +# include +# include "libvirt_nss.h" +# include "virsocketaddr.h" + +# define VIR_FROM_THIS VIR_FROM_NONE + +# define BUF_SIZE 1024 + +struct testNSSData { + const char *hostname; + const char *const *ipAddr; + int af; +}; + +static int +testGetHostByName(const void *opaque) +{ + const struct testNSSData *data = opaque; + const bool existent = data->hostname && data->ipAddr && data->ipAddr[0]; + int ret = -1; + struct hostent resolved; + char buf[BUF_SIZE] = { 0 }; + char **addrList; + int rv, tmp_errno = 0, tmp_herrno = 0; + size_t i = 0; + + if (!data) + goto cleanup; + + memset(&resolved, 0, sizeof(resolved)); + + rv = _nss_libvirt_gethostbyname2_r(data->hostname, + data->af, + &resolved, + buf, sizeof(buf), + &tmp_errno, + &tmp_herrno); + + if (rv == NSS_STATUS_TRYAGAIN || + rv == NSS_STATUS_UNAVAIL || + rv == NSS_STATUS_RETURN) { + /* Resolving failed in unexpected fashion. */ + virReportError(VIR_ERR_INTERNAL_ERROR, + "Resolving of %s failed due to internal error", + data->hostname); + goto cleanup; + } else if (rv == NSS_STATUS_NOTFOUND) { + /* Resolving failed. Should it? */ + if (!existent) + ret = 0; + else + virReportError(VIR_ERR_INTERNAL_ERROR, + "Resolving of %s failed", + data->hostname); + goto cleanup; + } + + /* Resolving succeeded. Should it? */ + if (!existent) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Resolving of %s succeeded but was expected to fail", + data->hostname); + goto cleanup; + } + + /* Now lets see if resolved address match our expectations. */ + + if (!resolved.h_name) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "resolved.h_name empty"); + goto cleanup; + } + + if (data->af != AF_UNSPEC && + resolved.h_addrtype != data->af) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Expected AF_INET (%d) got %d", + data->af, resolved.h_addrtype); + goto cleanup; + } + + if ((resolved.h_addrtype == AF_INET && resolved.h_length != 4) || + (resolved.h_addrtype == AF_INET6 && resolved.h_length != 16)) { + /* IPv4 addresses are encoded into 4 bytes */ + virReportError(VIR_ERR_INTERNAL_ERROR, + "Expected %d bytes long address, got %d", + resolved.h_addrtype == AF_INET ? 4 : 16, + resolved.h_length); + goto cleanup; + } + + if (!resolved.h_addr_list) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "resolved.h_addr_list empty"); + goto cleanup; + } + + addrList = resolved.h_addr_list; + while (*addrList) { + virSocketAddr sa; + char *ipAddr; + + memset(&sa, 0, sizeof(sa)); + + if (resolved.h_addrtype == AF_INET) { + /* For some reason, virSocketAddrSetIPv4Addr does htonl() conversion. + * But the data we already have is in network order. */ + virSocketAddrSetIPv4Addr(&sa, ntohl(*((uint32_t *) *addrList))); + } else { + virSocketAddrSetIPv6Addr(&sa, (uint32_t *) *addrList); + } + + if (!(ipAddr = virSocketAddrFormat(&sa))) { + /* error reported by helper */ + goto cleanup; + } + + if (!data->ipAddr[i]) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Unexpected address %s", ipAddr); + VIR_FREE(ipAddr); + goto cleanup; + } + + if (STRNEQ(data->ipAddr[i], ipAddr)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Address mismatch. Expected %s got %s", + data->ipAddr[i], ipAddr); + VIR_FREE(ipAddr); + goto cleanup; + } + VIR_FREE(ipAddr); + + addrList++; + i++; + } + + if (data->ipAddr[i]) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Address mismatch. Expected %s got nothing", + data->ipAddr[i]); + goto cleanup; + } + + ret = 0; + cleanup: + return ret; +} + +static int +mymain(void) +{ + int ret = 0; + +# define DO_TEST(name, family, ...) \ + do { \ + const char *addr[] = { __VA_ARGS__, NULL}; \ + struct testNSSData data = { \ + .hostname = name, .ipAddr = addr, .af = family, \ + }; \ + if (virtTestRun(name, testGetHostByName, &data) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST("fedora", AF_INET, "192.168.122.197", "192.168.122.198", "192.168.122.199"); + DO_TEST("gentoo", AF_INET, "192.168.122.254"); + DO_TEST("gentoo", AF_INET6, "2001:1234:dead:beef::2"); + DO_TEST("gentoo", AF_UNSPEC, "192.168.122.254"); + DO_TEST("non-existent", AF_UNSPEC, NULL); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/nssmock.so") +#else +int +main(void) +{ + return EXIT_AM_SKIP; +} +#endif