diff --git a/oisf.yaml b/oisf.yaml index 88e3d6cf65..fb540d0d90 100644 --- a/oisf.yaml +++ b/oisf.yaml @@ -62,3 +62,7 @@ pfring: # All threads/processes that will participate need to have the same # clusterid. clusterid: 99 + +rule-files: + - netbios.rules + - x11.rules diff --git a/src/conf-yaml-loader.c b/src/conf-yaml-loader.c index 5caf80dca1..3a99e818d9 100644 --- a/src/conf-yaml-loader.c +++ b/src/conf-yaml-loader.c @@ -12,6 +12,7 @@ #include "eidps-common.h" #include "conf.h" #include "util-debug.h" +#include "util-unittest.h" /* Define to print the current YAML state. */ #undef PRINT_STATES @@ -30,6 +31,7 @@ enum conf_state { CONF_KEY = 0, CONF_VAL, + CONF_SEQ, }; /** @@ -67,6 +69,80 @@ GetKeyName(char **key, int level) return print_key; } +/** + * \brief Parse a YAML layer. + * + * This will eventually replace ConfYamlParse but for now its just + * used to load lists. + * + * \param parser A pointer to an active yaml_parser_t. + * \param parent The parent configuration node. + * \param init_state State to start off as. + */ +static void +ConfYamlParse2(yaml_parser_t *parser, ConfNode *parent, int init_state) +{ + ConfNode *node = parent; + yaml_event_t event; + int done = 0; + int state = init_state; + + while (!done) { + if (!yaml_parser_parse(parser, &event)) { + fprintf(stderr, "Failed to parse configuration file: %s\n", + parser->problem); + exit(EXIT_FAILURE); + } + + if (event.type == YAML_SCALAR_EVENT) { + if (state) { + ConfNode *new; + new = ConfNodeNew(); + if (new == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, + "Failed to allocate memory for new configuration node."); + exit(EXIT_FAILURE); + } + new->val = strdup((char *)event.data.scalar.value); + TAILQ_INSERT_TAIL(&node->head, new, next); + } + else { + node = ConfNodeNew(); + if (node == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, + "Failed to allocate memory for new configuration node."); + exit(EXIT_FAILURE); + } + node->val = strdup((char *)event.data.scalar.value); + TAILQ_INSERT_TAIL(&parent->head, node, next); + } + state ^= CONF_VAL; + } + else if (event.type == YAML_SEQUENCE_START_EVENT) { + state = CONF_SEQ; + } + else if (event.type == YAML_SEQUENCE_END_EVENT) { + state = CONF_KEY; + + /* Only b/c we called from the old parser... */ + if (init_state != CONF_KEY) + return; + } + else if (event.type == YAML_MAPPING_START_EVENT) { + ConfYamlParse2(parser, node, 0); + state ^= CONF_VAL; + } + else if (event.type == YAML_MAPPING_END_EVENT) { + done = 1; + } + else if (event.type == YAML_STREAM_END_EVENT) { + done = 1; + } + + yaml_event_delete(&event); + } +} + /** * \brief Process a YAML parser. * @@ -96,38 +172,39 @@ ConfYamlParse(yaml_parser_t *parser) parser->problem); exit(EXIT_FAILURE); } - if (level > -1) { - SCLogDebug("current key: %s", GetKeyName(key, level)); - } switch (event.type) { case YAML_STREAM_START_EVENT: - DPRINT_STATE(("YAML_STREAM_START_EVENT")); break; case YAML_STREAM_END_EVENT: - DPRINT_STATE(("YAML_STREAM_END_EVENT")); done = 1; break; case YAML_DOCUMENT_START_EVENT: - DPRINT_STATE(("YAML_STREAM_END_EVENT")); /* Ignored. */ break; case YAML_DOCUMENT_END_EVENT: - DPRINT_STATE(("YAML_DOCUMENT_END_EVENT")); /* Ignored. */ break; - case YAML_SEQUENCE_START_EVENT: - DPRINT_STATE(("YAML_SEQUENCE_START_EVENT")); - inseq = 1; + case YAML_SEQUENCE_START_EVENT: { + ConfNode *new; + new = ConfNodeNew(); + if (new == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, + "Failed to allocate new configuration node."); + exit(EXIT_FAILURE); + } + new->name = strdup(GetKeyName(key, level)); + ConfYamlParse2(parser, new, CONF_SEQ); + ConfSetNode(new); + state = CONF_KEY; break; + } case YAML_SEQUENCE_END_EVENT: - DPRINT_STATE(("YAML_SEQUENCE_END_EVENT")); - inseq = 0; break; case YAML_MAPPING_START_EVENT: - DPRINT_STATE(("YAML_MAPPING_START_EVENT")); level++; if (level == MAX_LEVELS) { - fprintf(stderr, "Reached maximum configuration nesting level.\n"); + fprintf(stderr, + "Reached maximum configuration nesting level.\n"); exit(EXIT_FAILURE); } @@ -136,7 +213,6 @@ ConfYamlParse(yaml_parser_t *parser) break; case YAML_MAPPING_END_EVENT: - DPRINT_STATE(("YAML_MAPPING_END_EVENT")); if (level > -1) { free(key[level]); key[level] = NULL; @@ -144,13 +220,6 @@ ConfYamlParse(yaml_parser_t *parser) level--; break; case YAML_SCALAR_EVENT: - DPRINT_STATE(("YAML_SCALAR_EVENT")); - if (inseq) { - if (level > -1) { - SCLogDebug("ignoring sequence value for %s", GetKeyName(key, level)); - } - break; - } if (state == CONF_KEY) { if (key[level] != NULL) free(key[level]); @@ -166,10 +235,8 @@ ConfYamlParse(yaml_parser_t *parser) } break; case YAML_ALIAS_EVENT: - DPRINT_STATE(("YAML_ALIAS_EVENT")); break; case YAML_NO_EVENT: - DPRINT_STATE(("YAML_NO_EVENT")); break; } yaml_event_delete(&event); @@ -218,3 +285,56 @@ ConfYamlLoadString(const u_char *string, size_t len) ConfYamlParse(&parser); yaml_parser_delete(&parser); } + +#ifdef UNITTESTS + +static int +ConfYamlRuleFileTest(void) +{ + char input[] = "\ +some-other-list:\n\ + - one\n\ + - two\n\ +rule-files:\n\ + - netbios.rules\n\ + - x11.rules\n\ +\n\ +default-log-dir: /tmp\n\ +"; + + ConfNode *node; + ConfYamlLoadString((u_char *)input, strlen(input)); + node = ConfGetNode("rule-files"); + if (node == NULL) + return 0; + if (TAILQ_EMPTY(&node->head)) + return 0; + int i = 0; + ConfNode *filename; + TAILQ_FOREACH(filename, &node->head, next) { + if (i == 0) { + if (strcmp(filename->val, "netbios.rules") != 0) + return 0; + } + else if (i == 1) { + if (strcmp(filename->val, "x11.rules") != 0) + return 0; + } + else { + return 0; + } + i++; + } + + return 1; +} + +#endif /* UNITTESTS */ + +void +ConfYamlRegisterTests(void) +{ +#ifdef UNITTESTS + UtRegisterTest("ConfYamlRuleFileTest", ConfYamlRuleFileTest, 1); +#endif /* UNITTESTS */ +} diff --git a/src/conf-yaml-loader.h b/src/conf-yaml-loader.h index 9e255dda30..0269163c6e 100644 --- a/src/conf-yaml-loader.h +++ b/src/conf-yaml-loader.h @@ -4,5 +4,6 @@ #define __CONF_YAML_LOADER_H__ void ConfYamlLoadFile(const char *); +void ConfYamlRegisterTests(void); #endif /* !__CONF_YAML_LOADER_H__ */ diff --git a/src/conf.c b/src/conf.c index e10f587e98..dcaa4e5840 100644 --- a/src/conf.c +++ b/src/conf.c @@ -9,7 +9,7 @@ * configuration data. Allowing run time changes to the configuration * will require some locks. * - * \author Endace Technology Limited + * \author Endace Technology Limited - Jason Ish * * \todo Consider using HashListTable to allow easy dumping of all data. */ @@ -24,16 +24,6 @@ static HashTable *conf_hash = NULL; -/** - * Structure of a configuration parameter. - */ -typedef struct ConfNode_ { - char *name; - char *val; - - int allow_override; -} ConfNode; - /** * \brief Function to generate the hash of a configuration value. * @@ -81,10 +71,7 @@ static void ConfHashFree(void *data) { ConfNode *cn = (ConfNode *)data; - SCLogDebug("freeing configuration parameter '%s'", cn->name); - free(cn->name); - free(cn->val); - free(cn); + ConfNodeFree(cn); } /** @@ -109,6 +96,91 @@ ConfInit(void) SCLogDebug("configuration module initialized"); } +/** + * \brief Allocate a new configuration node. + * + * \retval An allocated configuration node on success, NULL on failure. + */ +ConfNode * +ConfNodeNew(void) +{ + ConfNode *new; + + new = calloc(1, sizeof(*new)); + if (new == NULL) + return NULL; + TAILQ_INIT(&new->head); + + return new; +} + +/** + * \brief Free a ConfNode and all of its children. + * + * \param node The configuration node to free. + */ +void +ConfNodeFree(ConfNode *node) +{ + ConfNode *tmp; + + TAILQ_FOREACH(tmp, &node->head, next) + ConfNodeFree(tmp); + + if (node->name != NULL) + free(node->name); + if (node->val != NULL) + free(node->val); + free(node); +} + +/** + * \brief Set a configuration node. + * + * \retval 1 on success, 0 on failure. + */ +int +ConfSetNode(ConfNode *node) +{ + ConfNode lookup; + ConfNode *pnode; + + lookup.name = node->name; + pnode = HashTableLookup(conf_hash, &lookup, sizeof(lookup)); + if (pnode != NULL) { + if (!pnode->allow_override) { + return 0; + } + HashTableRemove(conf_hash, pnode, sizeof(*pnode)); + } + + if (HashTableAdd(conf_hash, node, sizeof(*node)) != 0) { + SCLogError(SC_ERR_MEM_ALLOC, "Failed to add new configuration node."); + exit(EXIT_FAILURE); + } + + return 1; +} + +/** + * \brief Get a ConfNode by key. + * + * \param key The lookup key of the node to find. + * + * \retval The node matching the key or NULL if not found. + */ +ConfNode * +ConfGetNode(char *key) +{ + ConfNode lookup; + ConfNode *node; + + lookup.name = key; + + node = HashTableLookup(conf_hash, &lookup, sizeof(lookup)); + return node; +} + /** * \brief Set a configuration value. * @@ -132,7 +204,7 @@ ConfSet(char *name, char *val, int allow_override) HashTableRemove(conf_hash, conf_node, sizeof(*conf_node)); } - conf_node = calloc(1, sizeof(*conf_node)); + conf_node = ConfNodeNew(); if (conf_node == NULL) { return 0; } @@ -280,6 +352,12 @@ ConfDump(void) while (b != NULL) { cn = (ConfNode *)b->data; printf("%s=%s\n", cn->name, cn->val); + if (!TAILQ_EMPTY(&cn->head)) { + ConfNode *n0; + TAILQ_FOREACH(n0, &cn->head, next) { + printf(".%s\n", n0->val); + } + } b = b->next; } } @@ -323,6 +401,38 @@ ConfTestSetAndGet(void) return 1; } +static int +ConfTestSetGetNode(void) +{ + ConfNode *set; + ConfNode *get; + const char key[] = "some-key"; + const char val[] = "some-val"; + + set = ConfNodeNew(); + if (set == NULL) + return 0; + set->name = strdup(key); + set->val = strdup(val); + if (ConfSetNode(set) != 1) + return 0; + + get = ConfGetNode(key); + if (get == NULL) + return 0; + if (strcmp(get->name, key) != 0) + return 0; + if (strcmp(get->val, val) != 0) + return 0; + + ConfRemove(key); + get = ConfGetNode(key); + if (get != NULL) + return 0; + + return 1; +} + /** * Test that overriding a value is allowed provided allow_override is * true and that the config parameter gets the new value. @@ -465,6 +575,7 @@ void ConfRegisterTests(void) { UtRegisterTest("ConfTestGetNonExistant", ConfTestGetNonExistant, 1); + UtRegisterTest("ConfTestSetGetNode", ConfTestSetGetNode, 1); UtRegisterTest("ConfTestSetAndGet", ConfTestSetAndGet, 1); UtRegisterTest("ConfTestOverrideValue1", ConfTestOverrideValue1, 1); UtRegisterTest("ConfTestOverrideValue2", ConfTestOverrideValue2, 1); diff --git a/src/conf.h b/src/conf.h index 623bbaf659..91c86efdcb 100644 --- a/src/conf.h +++ b/src/conf.h @@ -7,6 +7,22 @@ #ifndef __CONF_H__ #define __CONF_H__ +#include "queue.h" + +/** + * Structure of a configuration parameter. + */ +typedef struct ConfNode_ { + char *name; + char *val; + + int allow_override; + + TAILQ_HEAD(, ConfNode_) head; + TAILQ_ENTRY(ConfNode_) next; +} ConfNode; + + /** * The default log directory. */ @@ -16,9 +32,13 @@ void ConfInit(void); int ConfGet(char *name, char **vptr); int ConfGetInt(char *name, intmax_t *val); int ConfGetBool(char *name, int *val); - int ConfSet(char *name, char *val, int allow_override); void ConfDump(void); +void ConfNodeDump(ConfNode *node); +ConfNode *ConfNodeNew(void); +void ConfNodeFree(ConfNode *); +int ConfSetNode(ConfNode *node); +ConfNode *ConfGetNode(char *key); void ConfRegisterTests(); #endif /* ! __CONF_H__ */ diff --git a/src/eidps.c b/src/eidps.c index a81c07e9b0..729da53132 100644 --- a/src/eidps.c +++ b/src/eidps.c @@ -453,6 +453,7 @@ int main(int argc, char **argv) DecodeGRERegisterTests(); AlpDetectRegisterTests(); ConfRegisterTests(); + ConfYamlRegisterTests(); TmqhFlowRegisterTests(); FlowRegisterTests(); SCSigRegisterSignatureOrderingTests();