[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH v3 5/7] nss: Implement _nss_libvirt_gethostbyname3_r



The implementation is pretty straightforward. Moreover, because
of the nature of things, gethostbyname_r and gethostbyname2_r can
be implemented at the same time too.

Signed-off-by: Michal Privoznik <mprivozn redhat com>
---
 config-post.h              |  21 +++
 src/Makefile.am            |  66 +++++++++
 src/util/virfile.c         |   2 +-
 tests/Makefile.am          |   2 +-
 tools/Makefile.am          |   8 +-
 tools/nss/libvirt_nss.c    | 354 ++++++++++++++++++++++++++++++++++++++++++++-
 tools/nss/libvirt_nss.h    |  14 +-
 tools/nss/libvirt_nss.syms |   4 +-
 8 files changed, 463 insertions(+), 8 deletions(-)

diff --git a/config-post.h b/config-post.h
index 8367200..d61ac57 100644
--- a/config-post.h
+++ b/config-post.h
@@ -43,3 +43,24 @@
 # undef WITH_YAJL
 # undef WITH_YAJL2
 #endif
+
+/*
+ * With the NSS module it's the same story as virt-login-shell. See the
+ * explanation above.
+ */
+#ifdef LIBVIRT_NSS
+# undef HAVE_LIBDEVMAPPER_H
+# undef HAVE_LIBNL
+# undef HAVE_LIBNL3
+# undef HAVE_LIBSASL2
+# undef WITH_CAPNG
+# undef WITH_CURL
+# undef WITH_DTRACE_PROBES
+# undef WITH_GNUTLS
+# undef WITH_GNUTLS_GCRYPT
+# undef WITH_MACVTAP
+# undef WITH_NUMACTL
+# undef WITH_SASL
+# undef WITH_SSH2
+# undef WITH_VIRTUALPORT
+#endif /* LIBVIRT_NSS */
diff --git a/src/Makefile.am b/src/Makefile.am
index 7d31839..b773108 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2942,6 +2942,72 @@ endif WITH_LIBVIRTD
 endif WITH_SECDRIVER_APPARMOR
 EXTRA_DIST += $(SECURITY_DRIVER_APPARMOR_HELPER_SOURCES)
 
+if WITH_NSS
+noinst_LTLIBRARIES += libvirt-nss.la
+
+libvirt_nss_la_SOURCES =		\
+		util/viralloc.c			\
+		util/viralloc.h			\
+		util/virbitmap.c		\
+		util/virbitmap.h		\
+		util/virbuffer.c		\
+		util/virbuffer.h		\
+		util/vircommand.c		\
+		util/vircommand.h		\
+		util/virerror.c			\
+		util/virerror.h			\
+		util/virfile.c			\
+		util/virfile.h			\
+		util/virjson.c			\
+		util/virjson.h			\
+		util/virkmod.c			\
+		util/virkmod.h			\
+		util/virlease.c			\
+		util/virlease.h			\
+		util/virlog.c			\
+		util/virlog.h			\
+		util/virobject.c		\
+		util/virobject.h		\
+		util/virpidfile.c		\
+		util/virpidfile.h		\
+		util/virprocess.c		\
+		util/virprocess.h		\
+		util/virsocketaddr.c	\
+		util/virsocketaddr.h	\
+		util/virstring.c		\
+		util/virstring.h		\
+		util/virthread.c		\
+		util/virthread.h		\
+		util/virthreadjob.c		\
+		util/virthreadjob.h		\
+		util/virtime.c			\
+		util/virtime.h			\
+		util/virutil.c			\
+		util/virutil.h			\
+		$(NULL)
+
+libvirt_nss_la_CFLAGS =			\
+		-DLIBVIRT_NSS			\
+		$(AM_CFLAGS)			\
+		$(CAPNG_CFLAGS)			\
+		$(LIBXML_CFLAGS)		\
+		$(SECDRIVER_CFLAGS)		\
+		$(YAJL_CFLAGS)			\
+		$(NULL)
+libvirt_nss_la_LDFLAGS =		\
+		$(AM_LDFLAGS)			\
+		$(NULL)
+
+libvirt_nss_la_LIBADD =			\
+		$(CAPNG_LIBS)			\
+		$(LIBXML_LIBS)			\
+		$(SECDRIVER_LIBS)		\
+		$(THREAD_LIBS)			\
+		$(YAJL_LIBS)			\
+		$(NULL)
+endif WITH_NSS
+
+
 install-data-local: install-init install-systemd
 if WITH_LIBVIRTD
 	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/lockd"
diff --git a/src/util/virfile.c b/src/util/virfile.c
index f45e18f..0fae0f5 100644
--- a/src/util/virfile.c
+++ b/src/util/virfile.c
@@ -554,7 +554,7 @@ int virFileUpdatePerm(const char *path,
 
 
 #if defined(__linux__) && HAVE_DECL_LO_FLAGS_AUTOCLEAR && \
-    !defined(LIBVIRT_SETUID_RPC_CLIENT)
+    !defined(LIBVIRT_SETUID_RPC_CLIENT) && !defined(LIBVIRT_NSS)
 
 # if HAVE_DECL_LOOP_CTL_GET_FREE
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 90981dc..55e8432 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -65,7 +65,7 @@ GNULIB_LIBS = \
        ../gnulib/lib/libgnu.la
 
 LDADDS = \
-        $(WARN_CFLAGS) \
+	$(WARN_CFLAGS) \
 	$(NO_INDIRECT_LDFLAGS) \
 	$(PROBES_O) \
 	$(GNULIB_LIBS) \
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 9754e42..8312111 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -433,7 +433,13 @@ nss_libnss_libvirt_impl_la_CFLAGS = \
 	$(AM_CFLAGS)		\
 	$(WARN_CFLAGS)		\
 	$(PIE_CFLAGS)		\
-	$(COVERAGE_CFLAGS)
+	$(COVERAGE_CFLAGS)	\
+	$(LIBXML_CFLAGS)
+
+nss_libnss_libvirt_impl_la_LIBADD = \
+	$(LIBXML_LIBS)				\
+	../gnulib/lib/libgnu.la		\
+	../src/libvirt-nss.la
 
 nss_libnss_libvirt_la_SOURCES =
 nss_libnss_libvirt_la_LDFLAGS = \
diff --git a/tools/nss/libvirt_nss.c b/tools/nss/libvirt_nss.c
index 461d8ca..2f9cd34 100644
--- a/tools/nss/libvirt_nss.c
+++ b/tools/nss/libvirt_nss.c
@@ -29,8 +29,356 @@
 
 #include "libvirt_nss.h"
 
-int
-blah(int c)
+#include <resolv.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include "virlease.h"
+#include "viralloc.h"
+#include "virfile.h"
+#include "virerror.h"
+#include "virstring.h"
+#include "virsocketaddr.h"
+#include "configmake.h"
+
+#if 0
+# define ERROR(...)                                             \
+do {                                                            \
+    char ebuf[1024];                                            \
+    fprintf(stderr, "ERROR %s:%d : ", __FUNCTION__, __LINE__);  \
+    fprintf(stderr, __VA_ARGS__);                               \
+    fprintf(stderr, " : %s\n", virStrerror(errno, ebuf, sizeof(ebuf))); \
+    fprintf(stderr, "\n");                                      \
+} while (0)
+
+# define DEBUG(...)                                             \
+do {                                                            \
+    fprintf(stderr, "DEBUG %s:%d : ", __FUNCTION__, __LINE__);  \
+    fprintf(stderr, __VA_ARGS__);                               \
+    fprintf(stderr, "\n");                                      \
+} while (0)
+#else
+# define ERROR(...) do { } while (0)
+# define DEBUG(...) do { } while (0)
+#endif
+
+#define LEASEDIR LOCALSTATEDIR "/lib/libvirt/dnsmasq/"
+
+#define ALIGN4(l) (((l) + 3) & ~3)
+#define ALIGN8(l) (((l) + 7) & ~7)
+
+#if __SIZEOF_POINTER__ == 8
+# define ALIGN(l) ALIGN8(l)
+#elif __SIZEOF_POINTER__ == 4
+# define ALIGN(l) ALIGN4(l)
+#else
+# error "Wut? Pointers are neither 4 nor 8 bytes long?"
+#endif
+
+#define FAMILY_ADDRESS_SIZE(family) ((family) == AF_INET6 ? 16 : 4)
+
+typedef struct {
+    unsigned char addr[16];
+    int af;
+} leaseAddress;
+
+/**
+ * findLease:
+ * @name: domain name to lookup
+ * @af: address family
+ * @address: all the addresses found for selected @af
+ * @naddress: number of elements in @address array
+ * @found: whether @name has been found
+ * @errnop: errno pointer
+ *
+ * Lookup @name in libvirt's IP database, parse it and store all
+ * addresses found in @address array. Callers can choose which
+ * address family (@af) should be returned. Currently only
+ * AF_INET (IPv4) and AF_INET6 (IPv6) are supported. As a corner
+ * case, AF_UNSPEC may be passed to @af in which case no address
+ * filtering is done and addresses from both families are
+ * returned.
+ *
+ * Returns -1 on error
+ *          0 on success
+ */
+static int
+findLease(const char *name,
+          int af,
+          leaseAddress **address,
+          size_t *naddress,
+          bool *found,
+          int *errnop)
 {
-    return c;
+    DIR *dir = NULL;
+    int ret = -1;
+    const char *leaseDir = LEASEDIR;
+    struct dirent *entry;
+    virJSONValuePtr leases_array = NULL;
+    char *server_duid = NULL;
+    ssize_t i, nleases;
+    leaseAddress *tmpAddress = NULL;
+    size_t ntmpAddress = 0;
+
+    *address = NULL;
+    *naddress = 0;
+    *found = false;
+
+    if (af != AF_UNSPEC && af != AF_INET && af != AF_INET6) {
+        errno = EAFNOSUPPORT;
+        goto cleanup;
+    }
+
+
+    if (!(dir = opendir(leaseDir))) {
+        ERROR("Failed to open dir '%s'", leaseDir);
+        goto cleanup;
+    }
+
+    if (!(leases_array = virJSONValueNewArray())) {
+        ERROR("Failed to create json array");
+        goto cleanup;
+    }
+
+    DEBUG("Dir: %s", leaseDir);
+    while ((ret = virDirRead(dir, &entry, leaseDir)) > 0) {
+        char *path;
+
+        if (entry->d_name[0] == '.')
+            continue;
+
+        if (!virFileHasSuffix(entry->d_name, ".status"))
+            continue;
+
+        if (!(path = virFileBuildPath(leaseDir, entry->d_name, NULL)))
+            goto cleanup;
+
+        DEBUG("Processing %s", path);
+
+        if (virLeaseReadCustomLeaseFile(leases_array, path, NULL, &server_duid) < 0) {
+            ERROR("Unable to parse %s", path);
+            VIR_FREE(path);
+            goto cleanup;
+        }
+
+        VIR_FREE(path);
+        VIR_FREE(server_duid);
+    }
+
+    closedir(dir);
+    dir = NULL;
+
+    nleases = virJSONValueArraySize(leases_array);
+    DEBUG("Read %zd leases", nleases);
+
+    for (i = 0; i < nleases; i++) {
+        virJSONValue const *lease;
+        const char *lease_name;
+        long long expirytime;
+        virSocketAddr sa;
+        const char *ipAddr;
+        int family;
+
+        lease = virJSONValueArrayGet(leases_array, i);
+
+        if (!lease) {
+            /* This should never happen (TM) */
+            ERROR("Unable to get element %zd of %zd", i, nleases);
+            goto cleanup;
+        }
+
+        lease_name = virJSONValueObjectGetString(lease, "hostname");
+
+        if (STRNEQ_NULLABLE(name, lease_name))
+            continue;
+
+        DEBUG("Found record for %s", lease_name);
+        *found = true;
+
+        if (virJSONValueObjectGetNumberLong(lease, "expiry-time", &expirytime) < 0) {
+            ERROR("Unable to read expiry-time on %s", lease_name);
+            goto cleanup;
+        }
+
+        /* Check whether lease has expired or not */
+        if (expirytime < (long long) time(NULL)) {
+            DEBUG("Lease for %s expired", lease_name);
+            continue;
+        }
+
+        if (!(ipAddr = virJSONValueObjectGetString(lease, "ip-address"))) {
+            ERROR("ip-address field missing for %s", name);
+            goto cleanup;
+        }
+
+        DEBUG("IP address: %s", ipAddr);
+
+        if (virSocketAddrParse(&sa, ipAddr, AF_UNSPEC) < 0) {
+            ERROR("Unable to parse %s", ipAddr);
+            goto cleanup;
+        }
+
+        family = VIR_SOCKET_ADDR_FAMILY(&sa);
+        if (af != AF_UNSPEC && af != family) {
+            DEBUG("Skipping address which family is %d, %d requested", family, af);
+            continue;
+        }
+
+        if (VIR_REALLOC_N_QUIET(tmpAddress, ntmpAddress + 1) < 0) {
+            ERROR("Out of memory");
+            goto cleanup;
+        }
+
+        tmpAddress[ntmpAddress].af = family;
+        memcpy(tmpAddress[ntmpAddress].addr,
+               (family == AF_INET ?
+                (void *) &sa.data.inet4.sin_addr.s_addr :
+                (void *) &sa.data.inet6.sin6_addr.s6_addr),
+               FAMILY_ADDRESS_SIZE(family));
+        ntmpAddress++;
+    }
+
+    *address = tmpAddress;
+    *naddress = ntmpAddress;
+    tmpAddress = NULL;
+    ntmpAddress = 0;
+
+    ret = 0;
+
+ cleanup:
+    *errnop = errno;
+    VIR_FREE(tmpAddress);
+    virJSONValueFree(leases_array);
+    if (dir)
+        closedir(dir);
+    return ret;
+}
+
+
+enum nss_status
+_nss_libvirt_gethostbyname_r(const char *name, struct hostent *result,
+                             char *buffer, size_t buflen, int *errnop,
+                             int *herrnop)
+{
+    int af = ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET);
+
+    return _nss_libvirt_gethostbyname3_r(name, af, result, buffer, buflen,
+                                         errnop, herrnop, NULL, NULL);
+}
+
+enum nss_status
+_nss_libvirt_gethostbyname2_r(const char *name, int af, struct hostent *result,
+                              char *buffer, size_t buflen, int *errnop,
+                              int *herrnop)
+{
+    return _nss_libvirt_gethostbyname3_r(name, af, result, buffer, buflen,
+                                         errnop, herrnop, NULL, NULL);
+}
+
+enum nss_status
+_nss_libvirt_gethostbyname3_r(const char *name, int af, struct hostent *result,
+                              char *buffer, size_t buflen, int *errnop,
+                              int *herrnop, int32_t *ttlp, char **canonp)
+{
+    enum nss_status ret = NSS_STATUS_UNAVAIL;
+    char *r_name, *r_aliases, *r_addr, *r_addr_list;
+    leaseAddress *addr = NULL;
+    size_t naddr, i;
+    bool found = false;
+    size_t nameLen, need, idx;
+    int alen;
+    int r;
+
+    if ((r = findLease(name, af, &addr, &naddr, &found, errnop)) < 0) {
+        /* Error occurred. Return immediately. */
+        if (*errnop == EAGAIN) {
+            *herrnop = TRY_AGAIN;
+            return NSS_STATUS_TRYAGAIN;
+        } else {
+            *herrnop = NO_RECOVERY;
+            return NSS_STATUS_UNAVAIL;
+        }
+    }
+
+    if (!found) {
+        /* NOT found */
+        *errnop = ESRCH;
+        *herrnop = HOST_NOT_FOUND;
+        return NSS_STATUS_NOTFOUND;
+    } else if (!naddr) {
+        /* Found, but no data */
+        *errnop = ENXIO;
+        *herrnop = NO_DATA;
+        return NSS_STATUS_UNAVAIL;
+    }
+
+    /* Found and have data */
+
+    af = addr[0].af;
+    alen = FAMILY_ADDRESS_SIZE(af);
+
+    nameLen = strlen(name);
+    /* We need space for:
+     * a) name
+     * b) alias
+     * c) addresses
+     * d) NULL stem */
+    need = ALIGN(nameLen + 1) + naddr * ALIGN(alen) + (naddr + 2) * sizeof(char*);
+
+    if (buflen < need) {
+        *errnop = ENOMEM;
+        *herrnop = TRY_AGAIN;
+        ret = NSS_STATUS_TRYAGAIN;
+        goto cleanup;
+    }
+
+    /* First, append name */
+    r_name = buffer;
+    memcpy(r_name, name, nameLen + 1);
+    idx = ALIGN(nameLen + 1);
+
+    /* Second, create aliases array */
+    r_aliases = buffer + idx;
+    ((char**) r_aliases)[0] = NULL;
+    idx += sizeof(char*);
+
+    /* Third, append address */
+    r_addr = buffer + idx;
+    for (i = 0; i < naddr; i++)
+        memcpy(r_addr + i * ALIGN(alen), addr[i].addr, alen);
+    idx += naddr * ALIGN(alen);
+
+    /* Third, append address pointer array */
+    r_addr_list = buffer + idx;
+    for (i = 0; i < naddr; i++)
+        ((char**) r_addr_list)[i] = r_addr + i * ALIGN(alen);
+    ((char**) r_addr_list)[i] = NULL;
+    idx += (naddr + 1) * sizeof(char*);
+
+    /* At this point, idx == need */
+    DEBUG("Done idx:%zd need:%zd", idx, need);
+
+    result->h_name = r_name;
+    result->h_aliases = (char**) r_aliases;
+    result->h_addrtype = af;
+    result->h_length = alen;
+    result->h_addr_list = (char**) r_addr_list;
+
+    if (ttlp)
+        *ttlp = 0;
+
+    if (canonp)
+        *canonp = r_name;
+
+    /* Explicitly reset all error variables */
+    *errnop = 0;
+    *herrnop = NETDB_SUCCESS;
+    h_errno = 0;
+
+    ret = NSS_STATUS_SUCCESS;
+ cleanup:
+    VIR_FREE(addr);
+    return ret;
 }
diff --git a/tools/nss/libvirt_nss.h b/tools/nss/libvirt_nss.h
index b54e5e3..dd037f5 100644
--- a/tools/nss/libvirt_nss.h
+++ b/tools/nss/libvirt_nss.h
@@ -32,5 +32,17 @@
 # include <nss.h>
 # include <netdb.h>
 
-int blah(int c);
+enum nss_status
+_nss_libvirt_gethostbyname_r(const char *name, struct hostent *result,
+                             char *buffer, size_t buflen, int *errnop,
+                             int *herrnop);
+
+enum nss_status
+_nss_libvirt_gethostbyname2_r(const char *name, int af, struct hostent *result,
+                              char *buffer, size_t buflen, int *errnop,
+                              int *herrnop);
+enum nss_status
+_nss_libvirt_gethostbyname3_r(const char *name, int af, struct hostent *result,
+                              char *buffer, size_t buflen, int *errnop,
+                              int *herrnop, int32_t *ttlp, char **canonp);
 #endif /* __LIBVIRT_NSS_H__ */
diff --git a/tools/nss/libvirt_nss.syms b/tools/nss/libvirt_nss.syms
index 3246213..b88b8be 100644
--- a/tools/nss/libvirt_nss.syms
+++ b/tools/nss/libvirt_nss.syms
@@ -4,6 +4,8 @@
 
 {
 global:
-    blah;
+    _nss_libvirt_gethostbyname_r;
+    _nss_libvirt_gethostbyname2_r;
+    _nss_libvirt_gethostbyname3_r;
 local: *;
 };
-- 
2.4.10


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]