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 <mpriv...@redhat.com> --- tools/Makefile.am | 8 +- tools/nss/libvirt_nss.c | 322 ++++++++++++++++++++++++++++++++++++++++++++- tools/nss/libvirt_nss.h | 14 +- tools/nss/libvirt_nss.syms | 4 +- 4 files changed, 342 insertions(+), 6 deletions(-) diff --git a/tools/Makefile.am b/tools/Makefile.am index a850adb..c6ef201 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) \ + ../src/libvirt_util.la \ + ../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..f044074 100644 --- a/tools/nss/libvirt_nss.c +++ b/tools/nss/libvirt_nss.c @@ -29,8 +29,324 @@ #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 + +/** + * findLease: + * @name: domain name to lookup + * @af: address family + * @addr: address + * @alen: address length + * @errnop: errno pointer + * + * Lookup @name in libvirt's IP database, parse it and store in + * @addr. Callers can choose which address family (@af) should be + * returned, however only AT_INET (IPv4) is currently supported. + * + * Returns -1 on error + * 0 if no matching record was found + * 1 if host found, but no matching IP address found + * 2 on success + */ +static int +findLease(const char *name, + int af, + unsigned char addr[16], + int *alen, + int *errnop) { - return c; + DIR *dir = NULL; + int ret = -1; + const char *leaseDir = LEASEDIR; + struct dirent *entry; + virJSONValuePtr leases_array = NULL; + virJSONValue const *lease = NULL; + char *server_duid = NULL; + ssize_t i, nleases; + const char *ipAddr; + virSocketAddr sa; + + if (af == AF_UNSPEC) + af = AF_INET; + + /* No IPv6 support yet since we don't store those in status file. */ + if (af != AF_INET) { + 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++) { + const char *lease_name; + long long expirytime; + + 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); + + 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; + } + + /* Hooray! Found. */ + break; + } + + if (i == nleases) { + /* NOT found */ + ret = errno = 0; + goto cleanup; + } + + /* found */ + + ret = 1; + DEBUG("Validating record for %s", name); + + if (!(ipAddr = virJSONValueObjectGetString(lease, "ip-address"))) { + ERROR("ip-address field missing for %s", name); + goto cleanup; + } + + if (virSocketAddrParse(&sa, ipAddr, af) < 0) { + ERROR("Unable to parse %s", ipAddr); + goto cleanup; + } + + *alen = 4; /* IPv4 addresses are 4 bytes long */ + memcpy(addr, &sa.data.inet4.sin_addr.s_addr, *alen); + + DEBUG("Validated"); + + /* Explicitly reset errno variable */ + errno = 0; + ret = 2; + + cleanup: + *errnop = errno; + 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; + unsigned char addr[16]; + int alen; + size_t nameLen, need, idx; + int r; + + if ((r = findLease(name, af, addr, &alen, errnop)) < 0) { + /* Error occurred. Return immediately. */ + if (*errnop == EAGAIN) { + *herrnop = TRY_AGAIN; + return NSS_STATUS_TRYAGAIN; + } else { + *herrnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } + } else if (r == 0) { + /* NOT found */ + *errnop = ESRCH; + *herrnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } else if (r == 1) { + /* Found, but no data */ + *errnop = ENXIO; + *herrnop = NO_DATA; + return NSS_STATUS_UNAVAIL; + } + + /* Found and have data */ + + nameLen = strlen(name); + /* We need space for: + * a) name + * b) alias + * c) address + * d) NULL stem */ + need = ALIGN(nameLen + 1) + ALIGN(alen) + 3 * 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; + memcpy(r_addr, addr, alen); + idx += ALIGN(alen); + + /* Third, append address pointer array */ + r_addr_list = buffer + idx; + ((char**) r_addr_list)[0] = r_addr; + ((char**) r_addr_list)[1] = NULL; + idx += 2 * 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: + 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@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list