* pfw/parser.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include "internal.h"
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
* Private Functions
****************************************************************************/
static pfw_interval_t* pfw_parse_interval(const char* word)
{
pfw_interval_t* itv;
itv = malloc(sizeof(pfw_interval_t));
if (!itv)
return NULL;
if (2 == sscanf(word, "[%" PRId32 ",%" PRId32 "]", &itv->left, &itv->right))
;
else if (1 == sscanf(word, "[%" PRId32 ",]", &itv->left))
itv->right = INT32_MAX;
else if (1 == sscanf(word, "[,%" PRId32 "]", &itv->right))
itv->left = INT32_MIN;
else
itv->left = itv->right = strtol(word, NULL, 0);
return itv;
}
static void pfw_free_rule(pfw_rule_t* rule)
{
pfw_rule_t* sub;
int i;
if (!rule)
return;
for (i = 0; (sub = pfw_vector_get(rule->branches, i)); i++)
pfw_free_rule(sub);
if (rule->predicate == PFW_PREDICATE_IN
|| rule->predicate == PFW_PREDICATE_NOTIN)
free(rule->state.itv);
pfw_vector_free(rule->branches);
free(rule);
}
static void pfw_free_ammends(pfw_vector_t* ammends)
{
pfw_ammend_t* ammend;
int i;
for (i = 0; (ammend = pfw_vector_get(ammends, i)); i++)
free(ammend);
pfw_vector_free(ammends);
}
static void pfw_free_act(pfw_act_t* act)
{
if (!act)
return;
pfw_free_ammends(act->param);
free(act);
}
static void pfw_free_config(pfw_config_t* config)
{
pfw_act_t* act;
int i;
pfw_free_rule(config->rules);
for (i = 0; (act = pfw_vector_get(config->acts, i)); i++)
pfw_free_act(act);
pfw_free_ammends(config->name);
pfw_vector_free(config->acts);
free(config->current);
free(config);
}
static void pfw_free_domain(pfw_domain_t* domain)
{
pfw_config_t* config;
int i;
for (i = 0; (config = pfw_vector_get(domain->configs, i)); i++)
pfw_free_config(config);
pfw_vector_free(domain->configs);
free(domain);
}
static void pfw_free_criterion(pfw_criterion_t* criterion)
{
pfw_listener_t *listener, *tmp;
pfw_interval_t* intervel;
int i;
if (!criterion)
return;
if (criterion->type == PFW_CRITERION_NUMERICAL) {
for (i = 0; (intervel = pfw_vector_get(criterion->ranges, i)); i++)
free(intervel);
}
LIST_FOREACH_SAFE(listener, &criterion->listeners, entry, tmp)
{
free(listener);
}
pfw_vector_free(criterion->ranges);
pfw_vector_free(criterion->names);
free(criterion);
}
static int pfw_parse_rule(pfw_context_t* ctx, pfw_rule_t** pr, int depth)
{
pfw_rule_t *rule, *sub;
char* word;
bool branch;
int ret, nb;
ret = pfw_context_get_depth(ctx);
if (ret != depth)
return ret < 0 ? ret : EOF;
rule = *pr = calloc(1, sizeof(pfw_rule_t));
if (!rule)
return -ENOMEM;
word = pfw_context_take_word(ctx);
if (!word) {
PFW_DEBUG("Rule starts with NULL\n");
ret = -EINVAL;
goto err;
} else if (!strcmp(word, "ALL")) {
rule->predicate = PFW_PREDICATE_ALL;
branch = true;
} else if (!strcmp(word, "ANY")) {
rule->predicate = PFW_PREDICATE_ANY;
branch = true;
} else {
rule->criterion.def = word;
branch = false;
}
if (branch) {
pfw_context_take_line(ctx);
for (nb = 0;; nb++) {
ret = pfw_parse_rule(ctx, &sub, depth + 1);
if (ret == EOF)
break;
else if (ret < 0) {
PFW_DEBUG("Rule branch %d invalid\n", nb);
goto err;
}
ret = pfw_vector_append(&rule->branches, sub);
if (ret < 0) {
goto err;
}
}
} else {
word = pfw_context_take_word(ctx);
if (!word) {
PFW_DEBUG("Rule has no predicate\n");
ret = -EINVAL;
goto err;
} else if (!strcmp(word, "Is")) {
rule->predicate = PFW_PREDICATE_IS;
} else if (!strcmp(word, "IsNot")) {
rule->predicate = PFW_PREDICATE_ISNOT;
} else if (!strcmp(word, "Excludes")) {
rule->predicate = PFW_PREDICATE_EXCLUDES;
} else if (!strcmp(word, "Includes")) {
rule->predicate = PFW_PREDICATE_INCLUDES;
} else if (!strcmp(word, "In")) {
rule->predicate = PFW_PREDICATE_IN;
} else if (!strcmp(word, "NotIn")) {
rule->predicate = PFW_PREDICATE_NOTIN;
} else {
PFW_DEBUG("Rule use invalid predicate '%s'\n", word);
ret = -EINVAL;
goto err;
}
word = pfw_context_take_word(ctx);
if (!word) {
PFW_DEBUG("Rule has no state\n");
ret = -EINVAL;
goto err;
}
if (rule->predicate == PFW_PREDICATE_IN
|| rule->predicate == PFW_PREDICATE_NOTIN) {
rule->state.itv = pfw_parse_interval(word);
if (!rule->state.itv) {
ret = -ENOMEM;
goto err;
}
} else {
rule->state.def = word;
}
pfw_context_take_line(ctx);
}
return 0;
err:
pfw_free_rule(rule);
return ret;
}
static int pfw_parse_ammends(pfw_context_t* ctx, pfw_vector_t** pv)
{
pfw_ammend_t* ammend;
char *word, *saveptr;
int ret;
word = pfw_context_take_line(ctx);
if (!word)
return -EINVAL;
word = strtok_r(word, "%", &saveptr);
while (word) {
ammend = calloc(1, sizeof(pfw_ammend_t));
if (!ammend)
return -ENOMEM;
ammend->u.raw = word;
ret = pfw_vector_append(pv, ammend);
if (ret < 0) {
free(ammend);
return ret;
}
word = strtok_r(NULL, "%", &saveptr);
}
return 0;
}
static int pfw_parse_act(pfw_context_t* ctx, pfw_act_t** pa)
{
pfw_act_t* act;
char* word;
int ret;
ret = pfw_context_get_depth(ctx);
if (ret != 2)
return ret < 0 ? ret : EOF;
act = *pa = calloc(1, sizeof(pfw_act_t));
if (!act)
return -ENOMEM;
word = pfw_context_take_word(ctx);
if (!word) {
PFW_DEBUG("Act has no plugin name\n");
ret = -EINVAL;
goto err;
}
act->plugin.def = word;
word = pfw_context_take_word(ctx);
if (!word || strcmp(word, "=")) {
PFW_DEBUG("Act should use '=' instead of '%s'\n", word);
ret = -EINVAL;
goto err;
}
ret = pfw_parse_ammends(ctx, &act->param);
if (ret < 0)
goto err;
return 0;
err:
pfw_free_act(act);
return ret;
}
static int pfw_parse_config(pfw_context_t* ctx, pfw_config_t** pc)
{
pfw_config_t* config;
pfw_rule_t* rule = NULL;
pfw_act_t* act;
char* word;
int ret;
ret = pfw_context_get_depth(ctx);
if (ret != 1)
return ret < 0 ? ret : EOF;
config = *pc = calloc(1, sizeof(pfw_config_t));
if (!config)
return -ENOMEM;
word = pfw_context_take_word(ctx);
if (!word || strcmp(word, "conf:")) {
PFW_DEBUG("Conf start with '%s'\n", word);
return -EINVAL;
}
ret = pfw_parse_ammends(ctx, &config->name);
if (ret < 0)
return ret;
ret = pfw_parse_rule(ctx, &rule, 2);
if (ret < 0 && ret != EOF) {
PFW_DEBUG("Conf uses invalid rules\n");
goto err;
}
config->rules = rule;
while (1) {
ret = pfw_parse_act(ctx, &act);
if (ret == EOF)
break;
else if (ret < 0) {
PFW_DEBUG("Conf uses invalid act\n");
goto err;
}
ret = pfw_vector_append(&config->acts, act);
if (ret < 0) {
pfw_free_act(act);
goto err;
}
}
return 0;
err:
pfw_free_config(config);
return ret;
}
static int pfw_parse_domain(pfw_context_t* ctx, pfw_domain_t** pd)
{
pfw_domain_t* domain;
pfw_config_t* config;
char* word;
int ret, nb;
ret = pfw_context_get_depth(ctx);
if (ret < 0)
return ret;
domain = *pd = calloc(1, sizeof(pfw_domain_t));
if (!domain)
return -ENOMEM;
word = pfw_context_take_word(ctx);
if (!word || strcmp(word, "domain:")) {
PFW_DEBUG("Domain start with '%s'\n", word);
ret = -EINVAL;
goto err;
}
word = pfw_context_take_word(ctx);
if (!word) {
PFW_DEBUG("Domain has no name\n");
ret = -EINVAL;
goto err;
}
domain->name = word;
pfw_context_take_line(ctx);
for (nb = 0;; nb++) {
ret = pfw_parse_config(ctx, &config);
if (ret == EOF)
break;
else if (ret < 0) {
PFW_DEBUG("Domain '%s' 's %dth config is invalid\n", domain->name, nb);
goto err;
}
ret = pfw_vector_append(&domain->configs, config);
if (ret < 0) {
pfw_free_config(config);
goto err;
}
}
return 0;
err:
pfw_free_domain(domain);
return ret;
}
static int pfw_parse_criterion(pfw_context_t* ctx, pfw_criterion_t** pc)
{
pfw_criterion_t* criterion;
char* word;
int ret, nb;
ret = pfw_context_get_depth(ctx);
if (ret < 0)
return ret;
criterion = *pc = calloc(1, sizeof(pfw_criterion_t));
if (!criterion)
return -ENOMEM;
word = pfw_context_take_word(ctx);
if (!word) {
PFW_DEBUG("Criterion starts with NULL\n");
ret = -EINVAL;
goto err;
}
if (!strcmp(word, "NumericalCriterion")) {
criterion->type = PFW_CRITERION_NUMERICAL;
} else if (!strcmp(word, "ExclusiveCriterion")) {
criterion->type = PFW_CRITERION_EXCLUSIVE;
} else if (!strcmp(word, "InclusiveCriterion")) {
criterion->type = PFW_CRITERION_INCLUSIVE;
} else {
PFW_DEBUG("Criterion has invalid type '%s'\n", word);
ret = -EINVAL;
goto err;
}
for (nb = 0;; nb++) {
word = pfw_context_take_word(ctx);
if (!word) {
PFW_DEBUG("Criterion has no ranges after %d names\n", nb);
ret = -EINVAL;
goto err;
} else if (!strcmp(word, ":"))
break;
ret = pfw_vector_append(&criterion->names, word);
if (ret < 0)
goto err;
}
for (nb = 0;; nb++) {
word = pfw_context_take_word(ctx);
if (!word) {
if (nb == 0) {
PFW_DEBUG("Criterion has no ranges after ':'\n");
ret = -EINVAL;
goto err;
}
break;
} else if (!strcmp(word, "=")) {
word = pfw_context_take_word(ctx);
if (!word) {
PFW_DEBUG("Criterion has no value after '='\n");
ret = -EINVAL;
goto err;
}
criterion->init.def = (void*)word;
break;
}
if (criterion->type == PFW_CRITERION_NUMERICAL) {
pfw_interval_t* itv;
itv = pfw_parse_interval(word);
if (!itv) {
ret = -ENOMEM;
goto err;
}
ret = pfw_vector_append(&criterion->ranges, itv);
if (ret < 0) {
free(itv);
goto err;
}
} else {
if (criterion->type == PFW_CRITERION_INCLUSIVE && nb > 31) {
PFW_DEBUG("InclusiveCriterion's ranges has %d over 31\n", nb);
goto err;
}
ret = pfw_vector_append(&criterion->ranges, word);
}
}
pfw_context_take_line(ctx);
return ret;
err:
pfw_free_criterion(criterion);
return ret;
}
* Public Functions
****************************************************************************/
void pfw_free_settings(pfw_vector_t* settings)
{
pfw_domain_t* domain;
int i;
for (i = 0; (domain = pfw_vector_get(settings, i)); i++)
pfw_free_domain(domain);
pfw_vector_free(settings);
}
int pfw_parse_settings(pfw_context_t* ctx, pfw_vector_t** p)
{
pfw_domain_t* domain;
int ret, nb;
for (nb = 0;; nb++) {
ret = pfw_parse_domain(ctx, &domain);
if (ret == EOF)
break;
else if (ret < 0) {
PFW_DEBUG("Invalid %dth domain\n", nb);
return ret;
}
ret = pfw_vector_append(p, domain);
if (ret < 0) {
pfw_free_domain(domain);
return ret;
}
}
return nb;
}
void pfw_free_criteria(pfw_vector_t* criteria)
{
pfw_criterion_t* criterion;
int i;
for (i = 0; (criterion = pfw_vector_get(criteria, i)); i++)
pfw_free_criterion(criterion);
pfw_vector_free(criteria);
}
int pfw_parse_criteria(pfw_context_t* ctx, pfw_vector_t** p)
{
pfw_criterion_t* criterion = NULL;
int ret, nb;
for (nb = 0;; nb++) {
ret = pfw_parse_criterion(ctx, &criterion);
if (ret == EOF)
break;
else if (ret < 0) {
PFW_DEBUG("Invalid %dth criterion\n", nb);
goto err;
}
LIST_INIT(&criterion->listeners);
ret = pfw_vector_append(p, criterion);
if (ret < 0) {
pfw_free_criterion(criterion);
goto err;
}
}
return nb;
err:
while (nb-- > 0) {
criterion = pfw_vector_get(*p, nb);
pfw_free_criterion(criterion);
}
pfw_vector_free(*p);
*p = NULL;
return ret;
}