diff --git a/cfg.mk b/cfg.mk index 33bf29c5b0..cc1f79a051 100644 --- a/cfg.mk +++ b/cfg.mk @@ -1226,10 +1226,10 @@ exclude_file_name_regexp--sc_prohibit_asprintf = \ ^(cfg\.mk|bootstrap.conf$$|examples/|src/util/virstring\.[ch]$$|tests/vircgroupmock\.c|tools/virt-login-shell\.c|tools/nss/libvirt_nss\.c$$) exclude_file_name_regexp--sc_prohibit_strdup = \ - ^(docs/|examples/|src/util/virstring\.c|tests/vir(netserverclient|cgroup)mock.c|tests/commandhelper\.c|tools/nss/libvirt_nss_macs\.c$$) + ^(docs/|examples/|src/util/virstring\.c|tests/vir(netserverclient|cgroup)mock.c|tests/commandhelper\.c|tools/nss/libvirt_nss_(leases|macs)\.c$$) exclude_file_name_regexp--sc_prohibit_close = \ - (\.p[yl]$$|\.spec\.in$$|^docs/|^(src/util/virfile\.c|src/libvirt-stream\.c|tests/(vir.+mock\.c|commandhelper\.c|qemusecuritymock\.c)|tools/nss/libvirt_nss_macs\.c)$$) + (\.p[yl]$$|\.spec\.in$$|^docs/|^(src/util/virfile\.c|src/libvirt-stream\.c|tests/(vir.+mock\.c|commandhelper\.c|qemusecuritymock\.c)|tools/nss/libvirt_nss_(leases|macs)\.c)$$) exclude_file_name_regexp--sc_prohibit_empty_lines_at_EOF = \ (^tests/(virhostcpu|virpcitest)data/|docs/js/.*\.js|docs/fonts/.*\.woff|\.diff|tests/virconfdata/no-newline\.conf$$) @@ -1259,7 +1259,7 @@ exclude_file_name_regexp--sc_prohibit_canonicalize_file_name = \ ^(cfg\.mk|tests/virfilemock\.c)$$ exclude_file_name_regexp--sc_prohibit_raw_allocation = \ - ^(docs/hacking\.html\.in|src/util/viralloc\.[ch]|examples/.*|tests/(securityselinuxhelper|(vircgroup|nss)mock|commandhelper)\.c|tools/wireshark/src/packet-libvirt\.c|tools/nss/libvirt_nss(_macs)?\.c)$$ + ^(docs/hacking\.html\.in|src/util/viralloc\.[ch]|examples/.*|tests/(securityselinuxhelper|(vircgroup|nss)mock|commandhelper)\.c|tools/wireshark/src/packet-libvirt\.c|tools/nss/libvirt_nss(_leases|_macs)?\.c)$$ exclude_file_name_regexp--sc_prohibit_readlink = \ ^src/(util/virutil|lxc/lxc_container)\.c$$ diff --git a/tools/Makefile.am b/tools/Makefile.am index eee4226231..61812a2cb1 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -476,7 +476,10 @@ endif ! WITH_BSD_NSS LIBVIRT_NSS_SOURCES = \ nss/libvirt_nss.c \ - nss/libvirt_nss.h + nss/libvirt_nss.h \ + nss/libvirt_nss_leases.c \ + nss/libvirt_nss_leases.h \ + $(NULL) if WITH_NSS noinst_LTLIBRARIES += nss/libnss_libvirt_impl.la @@ -485,6 +488,7 @@ nss_libnss_libvirt_impl_la_SOURCES = \ nss_libnss_libvirt_impl_la_CFLAGS = \ -DLIBVIRT_NSS \ + $(YAJL_CFLAGS) \ $(AM_CFLAGS) \ $(NULL) diff --git a/tools/nss/libvirt_nss.c b/tools/nss/libvirt_nss.c index b3756b984a..47d2ba9435 100644 --- a/tools/nss/libvirt_nss.c +++ b/tools/nss/libvirt_nss.c @@ -36,12 +36,13 @@ # include #endif -#include "virlease.h" #include "viralloc.h" #include "virtime.h" #include "virsocketaddr.h" #include "configmake.h" +#include "libvirt_nss_leases.h" + #if defined(LIBVIRT_NSS_GUEST) # include "libvirt_nss_macs.h" #endif /* !LIBVIRT_NSS_GUEST */ @@ -51,13 +52,6 @@ #define LIBVIRT_ALIGN(x) (((x) + __SIZEOF_POINTER__ - 1) & ~(__SIZEOF_POINTER__ - 1)) #define FAMILY_ADDRESS_SIZE(family) ((family) == AF_INET6 ? 16 : 4) -typedef struct { - unsigned char addr[16]; - int af; - long long expirytime; -} leaseAddress; - - static int leaseAddressSorter(const void *a, const void *b) @@ -77,147 +71,6 @@ sortAddr(leaseAddress *tmpAddress, } -static int -appendAddr(const char *name ATTRIBUTE_UNUSED, - leaseAddress **tmpAddress, - size_t *ntmpAddress, - virJSONValuePtr lease, - int af) -{ - const char *ipAddr; - virSocketAddr sa; - int family; - long long expirytime; - size_t i; - - if (!(ipAddr = virJSONValueObjectGetString(lease, "ip-address"))) { - ERROR("ip-address field missing for %s", name); - return -1; - } - - DEBUG("IP address: %s", ipAddr); - - if (virSocketAddrParse(&sa, ipAddr, AF_UNSPEC) < 0) { - ERROR("Unable to parse %s", ipAddr); - return -1; - } - - family = VIR_SOCKET_ADDR_FAMILY(&sa); - if (af != AF_UNSPEC && af != family) { - DEBUG("Skipping address which family is %d, %d requested", family, af); - return 0; - } - - if (virJSONValueObjectGetNumberLong(lease, "expiry-time", &expirytime) < 0) { - /* A lease cannot be present without expiry-time */ - ERROR("expiry-time field missing for %s", name); - return -1; - } - - for (i = 0; i < *ntmpAddress; i++) { - if (memcmp((*tmpAddress)[i].addr, - (family == AF_INET ? - (void *) &sa.data.inet4.sin_addr.s_addr : - (void *) &sa.data.inet6.sin6_addr.s6_addr), - FAMILY_ADDRESS_SIZE(family)) == 0) { - DEBUG("IP address already in the list"); - return 0; - } - } - - if (VIR_REALLOC_N_QUIET(*tmpAddress, *ntmpAddress + 1) < 0) { - ERROR("Out of memory"); - return -1; - } - - (*tmpAddress)[*ntmpAddress].expirytime = expirytime; - (*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)++; - return 0; -} - - -static int -findLeaseInJSON(leaseAddress **tmpAddress, - size_t *ntmpAddress, - virJSONValuePtr leases_array, - size_t nleases, - const char *name, - const char **macs, - size_t nmacs, - int af, - bool *found) -{ - size_t i; - size_t j; - long long expirytime; - time_t currtime; - - if ((currtime = time(NULL)) == (time_t) - 1) { - ERROR("Failed to get current system time"); - return -1; - } - - for (i = 0; i < nleases; i++) { - virJSONValuePtr lease = virJSONValueArrayGet(leases_array, i); - - if (!lease) { - /* This should never happen (TM) */ - ERROR("Unable to get element %zu of %zu", i, nleases); - return -1; - } - - if (macs) { - const char *macAddr; - bool match = false; - - macAddr = virJSONValueObjectGetString(lease, "mac-address"); - if (!macAddr) - continue; - - for (j = 0; j < nmacs && !match; j++) { - if (STREQ(macs[j], macAddr)) - match = true; - } - if (!match) - continue; - } else { - const char *lease_name; - - lease_name = virJSONValueObjectGetString(lease, "hostname"); - - if (STRNEQ_NULLABLE(name, lease_name)) - continue; - } - - if (virJSONValueObjectGetNumberLong(lease, "expiry-time", &expirytime) < 0) { - /* A lease cannot be present without expiry-time */ - ERROR("expiry-time field missing for %s", name); - return -1; - } - - /* Do not report expired lease */ - if (expirytime < (long long) currtime) { - DEBUG("Skipping expired lease for %s", name); - continue; - } - - DEBUG("Found record for %s", name); - *found = true; - - if (appendAddr(name, tmpAddress, ntmpAddress, lease, af) < 0) - return -1; - } - - return 0; -} - - /** * findLease: * @name: domain name to lookup @@ -250,13 +103,12 @@ findLease(const char *name, int ret = -1; const char *leaseDir = LEASEDIR; struct dirent *entry; - VIR_AUTOPTR(virJSONValue) leases_array = NULL; - ssize_t nleases; - VIR_AUTOFREE(leaseAddress *) tmpAddress = NULL; - size_t ntmpAddress = 0; + char **leaseFiles = NULL; + size_t nleaseFiles = 0; char **macs = NULL; size_t nmacs = 0; size_t i; + time_t now; *address = NULL; *naddress = 0; @@ -273,27 +125,21 @@ findLease(const char *name, goto cleanup; } - if (!(leases_array = virJSONValueNewArray())) { - ERROR("Failed to create json array"); - goto cleanup; - } - DEBUG("Dir: %s", leaseDir); while ((entry = readdir(dir)) != NULL) { char *path; size_t dlen = strlen(entry->d_name); if (dlen >= 7 && STREQ(entry->d_name + dlen - 7, ".status")) { + char **tmpLease; if (asprintf(&path, "%s/%s", leaseDir, entry->d_name) < 0) goto cleanup; - DEBUG("Processing %s", path); - if (virLeaseReadCustomLeaseFile(leases_array, path, NULL, NULL) < 0) { - ERROR("Unable to parse %s", path); - VIR_FREE(path); + tmpLease = realloc(leaseFiles, sizeof(char *) * (nleaseFiles + 1)); + if (!tmpLease) goto cleanup; - } - VIR_FREE(path); + leaseFiles = tmpLease; + leaseFiles[nleaseFiles++] = path; #if defined(LIBVIRT_NSS_GUEST) } else if (dlen >= 5 && STREQ(entry->d_name + dlen - 5, ".macs")) { if (asprintf(&path, "%s/%s", leaseDir, entry->d_name) < 0) @@ -313,9 +159,6 @@ findLease(const char *name, closedir(dir); dir = NULL; - nleases = virJSONValueArraySize(leases_array); - DEBUG("Read %zd leases", nleases); - #if defined(LIBVIRT_NSS_GUEST) DEBUG("Finding with %zu macs", nmacs); if (!nmacs) @@ -324,26 +167,38 @@ findLease(const char *name, DEBUG(" %s", macs[i]); #endif - if (findLeaseInJSON(&tmpAddress, &ntmpAddress, - leases_array, nleases, - name, (const char**)macs, nmacs, - af, found) < 0) + if ((now = time(NULL)) == (time_t)-1) { + DEBUG("Failed to get time"); goto cleanup; + } - DEBUG("Found %zu addresses", ntmpAddress); - sortAddr(tmpAddress, ntmpAddress); + for (i = 0; i < nleaseFiles; i++) { + if (findLeases(leaseFiles[i], + name, macs, nmacs, + af, now, + address, naddress, + found) < 0) + goto cleanup; + } - VIR_STEAL_PTR(*address, tmpAddress); - *naddress = ntmpAddress; - ntmpAddress = 0; + DEBUG("Found %zu addresses", *naddress); + sortAddr(*address, *naddress); ret = 0; cleanup: *errnop = errno; + for (i = 0; i < nleaseFiles; i++) + free(leaseFiles[i]); + free(leaseFiles); for (i = 0; i < nmacs; i++) free(macs[i]); free(macs); + if (ret < 0) { + free(*address); + *address = NULL; + *naddress = 0; + } if (dir) closedir(dir); return ret; diff --git a/tools/nss/libvirt_nss_leases.c b/tools/nss/libvirt_nss_leases.c new file mode 100644 index 0000000000..dec90fe0f3 --- /dev/null +++ b/tools/nss/libvirt_nss_leases.c @@ -0,0 +1,400 @@ +/* + * libvirt_nss_leases.c: Name Service Switch plugin lease file parser + * + * Copyright (C) 2019 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 + * . + */ + +#include + +#include +#include +#include +#include + +#include +#include + +#include "libvirt_nss_leases.h" +#include "libvirt_nss.h" +#include "virsocketaddr.h" +#include "viralloc.h" + +enum { + FIND_LEASES_STATE_START, + FIND_LEASES_STATE_LIST, + FIND_LEASES_STATE_ENTRY, +}; + + +typedef struct { + const char *name; + char **macs; + size_t nmacs; + int state; + unsigned long long now; + int af; + bool *found; + leaseAddress **addrs; + size_t *naddrs; + + char *key; + struct { + unsigned long long expiry; + char *ipaddr; + char *macaddr; + char *hostname; + } entry; +} findLeasesParser; + + +static int +appendAddr(const char *name ATTRIBUTE_UNUSED, + leaseAddress **tmpAddress, + size_t *ntmpAddress, + const char *ipAddr, + long long expirytime, + int af) +{ + virSocketAddr sa; + int family; + size_t i; + + DEBUG("IP address: %s", ipAddr); + if (virSocketAddrParse(&sa, ipAddr, AF_UNSPEC) < 0) { + ERROR("Unable to parse %s", ipAddr); + return -1; + } + + family = VIR_SOCKET_ADDR_FAMILY(&sa); + if (af != AF_UNSPEC && af != family) { + DEBUG("Skipping address which family is %d, %d requested", family, af); + return 0; + } + + for (i = 0; i < *ntmpAddress; i++) { + if (family == AF_INET) { + if (memcmp((*tmpAddress)[i].addr, + &sa.data.inet4.sin_addr.s_addr, + sizeof(sa.data.inet4.sin_addr.s_addr)) == 0) { + DEBUG("IP address already in the list"); + return 0; + } + } else { + if (memcmp((*tmpAddress)[i].addr, + &sa.data.inet6.sin6_addr.s6_addr, + sizeof(sa.data.inet6.sin6_addr.s6_addr)) == 0) { + DEBUG("IP address already in the list"); + return 0; + } + } + } + + if (VIR_REALLOC_N_QUIET(*tmpAddress, *ntmpAddress + 1) < 0) { + ERROR("Out of memory"); + return -1; + } + + (*tmpAddress)[*ntmpAddress].expirytime = expirytime; + (*tmpAddress)[*ntmpAddress].af = family; + if (family == AF_INET) + memcpy((*tmpAddress)[*ntmpAddress].addr, + &sa.data.inet4.sin_addr.s_addr, + sizeof(sa.data.inet4.sin_addr.s_addr)); + else + memcpy((*tmpAddress)[*ntmpAddress].addr, + &sa.data.inet6.sin6_addr.s6_addr, + sizeof(sa.data.inet6.sin6_addr.s6_addr)); + (*ntmpAddress)++; + return 0; +} + + +static int +findLeasesParserInteger(void *ctx, + long long val) +{ + findLeasesParser *parser = ctx; + + DEBUG("Parse int state=%d '%lld' (map key '%s')", + parser->state, val, NULLSTR(parser->key)); + if (!parser->key) + return 0; + + if (parser->state == FIND_LEASES_STATE_ENTRY) { + if (STRNEQ(parser->key, "expiry-time")) + return 0; + + parser->entry.expiry = val; + } else { + return 0; + } + return 1; +} + + +static int +findLeasesParserString(void *ctx, + const unsigned char *stringVal, + size_t stringLen) +{ + findLeasesParser *parser = ctx; + + DEBUG("Parse string state=%d '%.*s' (map key '%s')", + parser->state, (int)stringLen, (const char *)stringVal, + NULLSTR(parser->key)); + if (!parser->key) + return 0; + + if (parser->state == FIND_LEASES_STATE_ENTRY) { + if (STREQ(parser->key, "ip-address")) { + if (!(parser->entry.ipaddr = strndup((char *)stringVal, stringLen))) + return 0; + } else if (STREQ(parser->key, "mac-address")) { + if (!(parser->entry.macaddr = strndup((char *)stringVal, stringLen))) + return 0; + } else if (STREQ(parser->key, "hostname")) { + if (!(parser->entry.hostname = strndup((char *)stringVal, stringLen))) + return 0; + } else { + return 0; + } + } else { + return 0; + } + return 1; +} + + +static int +findLeasesParserMapKey(void *ctx, + const unsigned char *stringVal, + size_t stringLen) +{ + findLeasesParser *parser = ctx; + + DEBUG("Parse map key state=%d '%.*s'", + parser->state, (int)stringLen, (const char *)stringVal); + + free(parser->key); + if (!(parser->key = strndup((char *)stringVal, stringLen))) + return 0; + + return 1; +} + + +static int +findLeasesParserStartMap(void *ctx) +{ + findLeasesParser *parser = ctx; + + DEBUG("Parse start map state=%d", parser->state); + + if (parser->state != FIND_LEASES_STATE_LIST) + return 0; + + free(parser->key); + parser->key = NULL; + parser->state = FIND_LEASES_STATE_ENTRY; + + return 1; +} + + +static int +findLeasesParserEndMap(void *ctx) +{ + findLeasesParser *parser = ctx; + size_t i; + bool found = false; + + DEBUG("Parse end map state=%d", parser->state); + + if (parser->entry.macaddr == NULL) + return 0; + + if (parser->state != FIND_LEASES_STATE_ENTRY) + return 0; + + if (parser->nmacs) { + DEBUG("Check %zu macs", parser->nmacs); + for (i = 0; i < parser->nmacs && !found; i++) { + DEBUG("Check mac '%s' vs '%s'", parser->macs[i], NULLSTR(parser->entry.macaddr)); + if (STREQ_NULLABLE(parser->macs[i], parser->entry.macaddr)) + found = true; + } + } else { + DEBUG("Check name '%s' vs '%s'", parser->name, NULLSTR(parser->entry.hostname)); + if (STREQ_NULLABLE(parser->name, parser->entry.hostname)) + found = true; + } + DEBUG("Found %d", found); + if (parser->entry.expiry < parser->now) { + DEBUG("Entry expired at %llu vs now %llu", + parser->entry.expiry, parser->now); + found = false; + } + if (!parser->entry.ipaddr) + found = false; + + if (found) { + *parser->found = true; + + if (appendAddr(parser->name, + parser->addrs, parser->naddrs, + parser->entry.ipaddr, + parser->entry.expiry, + parser->af) < 0) + return 0; + } + + free(parser->entry.macaddr); + free(parser->entry.ipaddr); + free(parser->entry.hostname); + parser->entry.macaddr = NULL; + parser->entry.ipaddr = NULL; + parser->entry.hostname = NULL; + + parser->state = FIND_LEASES_STATE_LIST; + + return 1; +} + + +static int +findLeasesParserStartArray(void *ctx) +{ + findLeasesParser *parser = ctx; + + DEBUG("Parse start array state=%d", parser->state); + + if (parser->state == FIND_LEASES_STATE_START) { + parser->state = FIND_LEASES_STATE_LIST; + } else { + return 0; + } + + return 1; +} + + +static int +findLeasesParserEndArray(void *ctx) +{ + findLeasesParser *parser = ctx; + + DEBUG("Parse end array state=%d", parser->state); + + if (parser->state == FIND_LEASES_STATE_LIST) + parser->state = FIND_LEASES_STATE_START; + else + return 0; + + return 1; +} + + +int +findLeases(const char *file, + const char *name, + char **macs, + size_t nmacs, + int af, + time_t now, + leaseAddress **addrs, + size_t *naddrs, + bool *found) +{ + int fd = -1; + int ret = -1; + const yajl_callbacks parserCallbacks = { + NULL, /* null */ + NULL, /* bool */ + findLeasesParserInteger, + NULL, /* double */ + NULL, /* number */ + findLeasesParserString, + findLeasesParserStartMap, + findLeasesParserMapKey, + findLeasesParserEndMap, + findLeasesParserStartArray, + findLeasesParserEndArray, + }; + findLeasesParser parserState = { + .name = name, + .macs = macs, + .nmacs = nmacs, + .af = af, + .now = now, + .found = found, + .addrs = addrs, + .naddrs = naddrs, + }; + yajl_handle parser = NULL; + char line[1024]; + int rv; + + if ((fd = open(file, O_RDONLY)) < 0) { + ERROR("Cannot open %s", file); + goto cleanup; + } + + parser = yajl_alloc(&parserCallbacks, NULL, &parserState); + if (!parser) { + ERROR("Unable to create JSON parser"); + goto cleanup; + } + + while (1) { + rv = read(fd, line, sizeof(line)); + if (rv < 0) + goto cleanup; + if (rv == 0) + break; + + if (yajl_parse(parser, (const unsigned char *)line, rv) != + yajl_status_ok) { + ERROR("Parse failed %s", + yajl_get_error(parser, 1, + (const unsigned char*)line, rv)); + goto cleanup; + } + } + + if (yajl_complete_parse(parser) != yajl_status_ok) { + ERROR("Parse failed %s", + yajl_get_error(parser, 1, NULL, 0)); + goto cleanup; + } + + ret = 0; + + cleanup: + if (ret != 0) { + free(*addrs); + *addrs = NULL; + *naddrs = 0; + } + yajl_free(parser); + free(parserState.entry.ipaddr); + free(parserState.entry.macaddr); + free(parserState.entry.hostname); + free(parserState.key); + if (fd != -1) + close(fd); + return ret; +} diff --git a/tools/nss/libvirt_nss_leases.h b/tools/nss/libvirt_nss_leases.h new file mode 100644 index 0000000000..e213681e46 --- /dev/null +++ b/tools/nss/libvirt_nss_leases.h @@ -0,0 +1,40 @@ +/* + * libvirt_nss_leases.h: Name Service Switch plugin lease file parser + * + * Copyright (C) 2019 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 + * . + */ + +#pragma once + +#include "internal.h" + +typedef struct { + unsigned char addr[16]; + int af; + long long expirytime; +} leaseAddress; + +int +findLeases(const char *file, + const char *name, + char **macs, + size_t nmacs, + int af, + time_t now, + leaseAddress **addrs, + size_t *naddrs, + bool *found); diff --git a/tools/nss/libvirt_nss_macs.c b/tools/nss/libvirt_nss_macs.c index cd5ea7c620..fb5526bd7b 100644 --- a/tools/nss/libvirt_nss_macs.c +++ b/tools/nss/libvirt_nss_macs.c @@ -28,10 +28,8 @@ #include #include -#include "internal.h" - -#include "libvirt_nss.h" #include "libvirt_nss_macs.h" +#include "libvirt_nss.h" enum { FIND_MACS_STATE_START, diff --git a/tools/nss/libvirt_nss_macs.h b/tools/nss/libvirt_nss_macs.h index c504a8cf1f..64e291f549 100644 --- a/tools/nss/libvirt_nss_macs.h +++ b/tools/nss/libvirt_nss_macs.h @@ -20,6 +20,8 @@ #pragma once +#include "internal.h" + int findMACs(const char *file, const char *name,