/* * libvirt_nss_macs.c: Name Service Switch plugin MAC 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_macs.h" #include "libvirt_nss.h" enum { FIND_MACS_STATE_START, FIND_MACS_STATE_LIST, FIND_MACS_STATE_ENTRY, FIND_MACS_STATE_ENTRY_MACS, }; typedef struct { const char *name; char ***macs; size_t *nmacs; int state; char *key; struct { char *name; char **macs; size_t nmacs; } entry; } findMACsParser; static int findMACsParserString(void *ctx, const unsigned char *stringVal, size_t stringLen) { findMACsParser *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_MACS_STATE_ENTRY) { if (strcmp(parser->key, "domain")) return 1; free(parser->entry.name); if (!(parser->entry.name = strndup((char *)stringVal, stringLen))) return 0; } else if (parser->state == FIND_MACS_STATE_ENTRY_MACS) { char **macs; if (strcmp(parser->key, "macs")) return 1; if (!(macs = realloc(parser->entry.macs, sizeof(char *) * (parser->entry.nmacs + 1)))) return 0; parser->entry.macs = macs; if (!(macs[parser->entry.nmacs++] = strndup((char *)stringVal, stringLen))) return 0; } else { return 0; } return 1; } static int findMACsParserMapKey(void *ctx, const unsigned char *stringVal, size_t stringLen) { findMACsParser *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 findMACsParserStartMap(void *ctx) { findMACsParser *parser = ctx; DEBUG("Parse start map state=%d", parser->state); if (parser->state != FIND_MACS_STATE_LIST) return 0; free(parser->key); parser->key = NULL; parser->state = FIND_MACS_STATE_ENTRY; return 1; } static int findMACsParserEndMap(void *ctx) { findMACsParser *parser = ctx; size_t i; DEBUG("Parse end map state=%d", parser->state); if (parser->entry.name == NULL) return 0; if (parser->state != FIND_MACS_STATE_ENTRY) return 0; if (!strcasecmp(parser->entry.name, parser->name)) { char **macs = realloc(*parser->macs, sizeof(char *) * ((*parser->nmacs) + parser->entry.nmacs)); if (!macs) return 0; *parser->macs = macs; for (i = 0; i < parser->entry.nmacs; i++) (*parser->macs)[(*parser->nmacs)++] = parser->entry.macs[i]; } else { for (i = 0; i < parser->entry.nmacs; i++) free(parser->entry.macs[i]); } free(parser->entry.macs); parser->entry.macs = NULL; parser->entry.nmacs = 0; parser->state = FIND_MACS_STATE_LIST; return 1; } static int findMACsParserStartArray(void *ctx) { findMACsParser *parser = ctx; DEBUG("Parse start array state=%d", parser->state); if (parser->state == FIND_MACS_STATE_START) parser->state = FIND_MACS_STATE_LIST; else if (parser->state == FIND_MACS_STATE_ENTRY) parser->state = FIND_MACS_STATE_ENTRY_MACS; else return 0; return 1; } static int findMACsParserEndArray(void *ctx) { findMACsParser *parser = ctx; DEBUG("Parse end array state=%d", parser->state); if (parser->state == FIND_MACS_STATE_LIST) parser->state = FIND_MACS_STATE_START; else if (parser->state == FIND_MACS_STATE_ENTRY_MACS) parser->state = FIND_MACS_STATE_ENTRY; else return 0; return 1; } int findMACs(const char *file, const char *name, char ***macs, size_t *nmacs) { int fd = -1; int ret = -1; const yajl_callbacks parserCallbacks = { NULL, /* null */ NULL, /* bool */ NULL, /* integer */ NULL, /* double */ NULL, /* number */ findMACsParserString, findMACsParserStartMap, findMACsParserMapKey, findMACsParserEndMap, findMACsParserStartArray, findMACsParserEndArray, }; findMACsParser parserState = { .name = name, .macs = macs, .nmacs = nmacs, }; yajl_handle parser = NULL; char line[1024]; size_t i; 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) { unsigned char *err = yajl_get_error(parser, 1, (const unsigned char*)line, rv); ERROR("Parse failed %s", (const char *) err); yajl_free_error(parser, err); 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) { for (i = 0; i < *nmacs; i++) { char *mac = (*macs)[i]; free(mac); } free(*macs); *macs = NULL; *nmacs = 0; } if (parser) yajl_free(parser); for (i = 0; i < parserState.entry.nmacs; i++) free(parserState.entry.macs[i]); free(parserState.entry.macs); free(parserState.entry.name); free(parserState.key); if (fd != -1) close(fd); return ret; }