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@xxxxxxxxxx> --- tools/Makefile.am | 7 +- tools/nss/libvirt_nss.c | 354 ++++++++++++++++++++++++++++++++++++++++++++- tools/nss/libvirt_nss.h | 14 +- tools/nss/libvirt_nss.syms | 4 +- 4 files changed, 373 insertions(+), 6 deletions(-) diff --git a/tools/Makefile.am b/tools/Makefile.am index a850adb..e938e80 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -433,7 +433,12 @@ 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) \ + ../src/libvirt.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..8a3dee3 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 1 +# 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); + } + + 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); + VIR_FREE(server_duid); + 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list