A small test to see how is the nss module working.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
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 5b864af..6f28eef 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 55e8432..2bb9d28 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 \
@@ -1067,6 +1070,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 0000000..6ebe954
--- /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 0000000..f8a9157
--- /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 0000000..b4a4260
--- /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
+ * <
http://www.gnu.org/licenses/>.
+ *
+ * Author: Michal Privoznik <mprivozn(a)redhat.com>
+ */
+
+#include <config.h>
+
+#ifdef __linux__
+# include <stdio.h>
+# include <stdlib.h>
+# include <dlfcn.h>
+# include <sys/types.h>
+# include <dirent.h>
+# include <sys/stat.h>
+# include <fcntl.h>
+
+# 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 0000000..340f313
--- /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
+ * <
http://www.gnu.org/licenses/>.
+ *
+ * Author: Michal Privoznik <mprivozn(a)redhat.com>
+ */
+
+#include <config.h>
+
+#include "testutils.h"
+
+#ifdef __linux__
+
+# include <stdbool.h>
+# include <arpa/inet.h>
+# 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
--
2.4.10