Blame src/v1_samp.c

Packit 1422b7
/* samp.c -- code for ln_v1_samp objects.
Packit 1422b7
 *
Packit 1422b7
 * Copyright 2010-2015 by Rainer Gerhards and Adiscon GmbH.
Packit 1422b7
 *
Packit 1422b7
 * Modified by Pavel Levshin (pavel@levshin.spb.ru) in 2013
Packit 1422b7
 *
Packit 1422b7
 * This file is part of liblognorm.
Packit 1422b7
 *
Packit 1422b7
 * This library is free software; you can redistribute it and/or
Packit 1422b7
 * modify it under the terms of the GNU Lesser General Public
Packit 1422b7
 * License as published by the Free Software Foundation; either
Packit 1422b7
 * version 2.1 of the License, or (at your option) any later version.
Packit 1422b7
 *
Packit 1422b7
 * This library is distributed in the hope that it will be useful,
Packit 1422b7
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 1422b7
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 1422b7
 * Lesser General Public License for more details.
Packit 1422b7
 *
Packit 1422b7
 * You should have received a copy of the GNU Lesser General Public
Packit 1422b7
 * License along with this library; if not, write to the Free Software
Packit 1422b7
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 1422b7
 *
Packit 1422b7
 * A copy of the LGPL v2.1 can be found in the file "COPYING" in this distribution.
Packit 1422b7
 */
Packit 1422b7
#include "config.h"
Packit 1422b7
#include <stdlib.h>
Packit 1422b7
#include <stdio.h>
Packit 1422b7
#include <stdarg.h>
Packit 1422b7
#include <string.h>
Packit 1422b7
#include <assert.h>
Packit 1422b7
#include <ctype.h>
Packit 1422b7
Packit 1422b7
#define LOGNORM_V1_SUBSYSTEM /* indicate we are old cruft */
Packit 1422b7
#include "v1_liblognorm.h"
Packit 1422b7
#include "internal.h"
Packit 1422b7
#include "lognorm.h"
Packit 1422b7
#include "samp.h"
Packit 1422b7
#include "v1_ptree.h"
Packit 1422b7
#include "v1_samp.h"
Packit 1422b7
#include "v1_parser.h"
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Construct a sample object.
Packit 1422b7
 */
Packit 1422b7
struct ln_v1_samp*
Packit 1422b7
ln_v1_sampCreate(ln_ctx __attribute__((unused)) ctx)
Packit 1422b7
{
Packit 1422b7
	struct ln_v1_samp* samp;
Packit 1422b7
Packit 1422b7
	if((samp = calloc(1, sizeof(struct ln_v1_samp))) == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* place specific init code here (none at this time) */
Packit 1422b7
Packit 1422b7
done:	return samp;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void
Packit 1422b7
ln_v1_sampFree(ln_ctx __attribute__((unused)) ctx, struct ln_v1_samp *samp)
Packit 1422b7
{
Packit 1422b7
	free(samp);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Extract a field description from a sample.
Packit 1422b7
 * The field description is added to the tail of the current
Packit 1422b7
 * subtree's field list. The parse buffer must be position on the
Packit 1422b7
 * leading '%' that starts a field definition. It is a program error
Packit 1422b7
 * if this condition is not met.
Packit 1422b7
 *
Packit 1422b7
 * Note that we break up the object model and access ptree members
Packit 1422b7
 * directly. Let's consider us a friend of ptree. This is necessary
Packit 1422b7
 * to optimize the structure for a high-speed parsing process.
Packit 1422b7
 *
Packit 1422b7
 * @param[in] str a temporary work string. This is passed in to save the
Packit 1422b7
 * 		  creation overhead
Packit 1422b7
 * @returns 0 on success, something else otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
addFieldDescr(ln_ctx ctx, struct ln_ptree **subtree, es_str_t *rule,
Packit 1422b7
	        es_size_t *bufOffs, es_str_t **str)
Packit 1422b7
{
Packit 1422b7
	int r;
Packit 1422b7
	ln_fieldList_t *node = ln_v1_parseFieldDescr(ctx, rule, bufOffs, str, &r);
Packit 1422b7
	assert(subtree != NULL);
Packit 1422b7
Packit 1422b7
	if (node != NULL) CHKR(ln_addFDescrToPTree(subtree, node));
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
ln_fieldList_t*
Packit 1422b7
ln_v1_parseFieldDescr(ln_ctx ctx, es_str_t *rule, es_size_t *bufOffs, es_str_t **str, int* ret)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	ln_fieldList_t *node;
Packit 1422b7
	es_size_t i = *bufOffs;
Packit 1422b7
	char *cstr;	/* for debug mode strings */
Packit 1422b7
	unsigned char *buf;
Packit 1422b7
	es_size_t lenBuf;
Packit 1422b7
	void* (*constructor_fn)(ln_fieldList_t *, ln_ctx) = NULL;
Packit 1422b7
Packit 1422b7
	buf = es_getBufAddr(rule);
Packit 1422b7
	lenBuf = es_strlen(rule);
Packit 1422b7
	assert(buf[i] == '%');
Packit 1422b7
	++i;	/* "eat" ':' */
Packit 1422b7
	CHKN(node = calloc(1, sizeof(ln_fieldList_t)));
Packit 1422b7
	node->subtree = NULL;
Packit 1422b7
	node->next = NULL;
Packit 1422b7
	node->data = NULL;
Packit 1422b7
	node->raw_data = NULL;
Packit 1422b7
	node->parser_data = NULL;
Packit 1422b7
	node->parser_data_destructor = NULL;
Packit 1422b7
	CHKN(node->name = es_newStr(16));
Packit 1422b7
Packit 1422b7
	/* skip leading whitespace in field name */
Packit 1422b7
	while(i < lenBuf && isspace(buf[i]))
Packit 1422b7
		++i;
Packit 1422b7
	while(i < lenBuf && buf[i] != ':') {
Packit 1422b7
		CHKR(es_addChar(&node->name, buf[i++]));
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(es_strlen(node->name) == 0) {
Packit 1422b7
		FAIL(LN_INVLDFDESCR);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(ctx->debug) {
Packit 1422b7
		cstr = es_str2cstr(node->name, NULL);
Packit 1422b7
		ln_dbgprintf(ctx, "parsed field: '%s'", cstr);
Packit 1422b7
		free(cstr);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(buf[i] != ':') {
Packit 1422b7
		/* may be valid later if we have a loaded CEE dictionary
Packit 1422b7
		 * and the name is present inside it.
Packit 1422b7
		 */
Packit 1422b7
		FAIL(LN_INVLDFDESCR);
Packit 1422b7
	}
Packit 1422b7
	++i; /* skip ':' */
Packit 1422b7
Packit 1422b7
	/* parse and process type (trailing whitespace must be trimmed) */
Packit 1422b7
	es_emptyStr(*str);
Packit 1422b7
	size_t j = i;
Packit 1422b7
	/* scan for terminator */
Packit 1422b7
	while(j < lenBuf && buf[j] != ':' && buf[j] != '%')
Packit 1422b7
		++j;
Packit 1422b7
	/* now trim trailing space backwards */
Packit 1422b7
	size_t next = j;
Packit 1422b7
	--j;
Packit 1422b7
	while(j >= i && isspace(buf[j]))
Packit 1422b7
		--j;
Packit 1422b7
	/* now copy */
Packit 1422b7
	while(i <= j) {
Packit 1422b7
		CHKR(es_addChar(str, buf[i++]));
Packit 1422b7
	}
Packit 1422b7
	/* finally move i to consumed position */
Packit 1422b7
	i = next;
Packit 1422b7
Packit 1422b7
	if(i == lenBuf) {
Packit 1422b7
		FAIL(LN_INVLDFDESCR);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	node->isIPTables = 0; /* first assume no special parser is used */
Packit 1422b7
	if(!es_strconstcmp(*str, "date-rfc3164")) {
Packit 1422b7
		node->parser = ln_parseRFC3164Date;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "date-rfc5424")) {
Packit 1422b7
		node->parser = ln_parseRFC5424Date;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "number")) {
Packit 1422b7
		node->parser = ln_parseNumber;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "float")) {
Packit 1422b7
		node->parser = ln_parseFloat;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "hexnumber")) {
Packit 1422b7
		node->parser = ln_parseHexNumber;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "kernel-timestamp")) {
Packit 1422b7
		node->parser = ln_parseKernelTimestamp;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "whitespace")) {
Packit 1422b7
		node->parser = ln_parseWhitespace;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "ipv4")) {
Packit 1422b7
		node->parser = ln_parseIPv4;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "ipv6")) {
Packit 1422b7
		node->parser = ln_parseIPv6;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "word")) {
Packit 1422b7
		node->parser = ln_parseWord;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "alpha")) {
Packit 1422b7
		node->parser = ln_parseAlpha;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "rest")) {
Packit 1422b7
		node->parser = ln_parseRest;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "op-quoted-string")) {
Packit 1422b7
		node->parser = ln_parseOpQuotedString;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "quoted-string")) {
Packit 1422b7
		node->parser = ln_parseQuotedString;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "date-iso")) {
Packit 1422b7
		node->parser = ln_parseISODate;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "time-24hr")) {
Packit 1422b7
		node->parser = ln_parseTime24hr;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "time-12hr")) {
Packit 1422b7
		node->parser = ln_parseTime12hr;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "duration")) {
Packit 1422b7
		node->parser = ln_parseDuration;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "cisco-interface-spec")) {
Packit 1422b7
		node->parser = ln_parseCiscoInterfaceSpec;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "json")) {
Packit 1422b7
		node->parser = ln_parseJSON;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "cee-syslog")) {
Packit 1422b7
		node->parser = ln_parseCEESyslog;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "mac48")) {
Packit 1422b7
		node->parser = ln_parseMAC48;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "name-value-list")) {
Packit 1422b7
		node->parser = ln_parseNameValue;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "cef")) {
Packit 1422b7
		node->parser = ln_parseCEF;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "checkpoint-lea")) {
Packit 1422b7
		node->parser = ln_parseCheckpointLEA;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "v2-iptables")) {
Packit 1422b7
		node->parser = ln_parsev2IPTables;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "iptables")) {
Packit 1422b7
		node->parser = NULL;
Packit 1422b7
		node->isIPTables = 1;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "string-to")) {
Packit 1422b7
		/* TODO: check extra data!!!! (very important) */
Packit 1422b7
		node->parser = ln_parseStringTo;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "char-to")) {
Packit 1422b7
		/* TODO: check extra data!!!! (very important) */
Packit 1422b7
		node->parser = ln_parseCharTo;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "char-sep")) {
Packit 1422b7
		/* TODO: check extra data!!!! (very important) */
Packit 1422b7
		node->parser = ln_parseCharSeparated;
Packit 1422b7
	} else if(!es_strconstcmp(*str, "tokenized")) {
Packit 1422b7
		node->parser = ln_parseTokenized;
Packit 1422b7
		constructor_fn = tokenized_parser_data_constructor;
Packit 1422b7
		node->parser_data_destructor = tokenized_parser_data_destructor;
Packit 1422b7
	}
Packit 1422b7
#ifdef FEATURE_REGEXP
Packit 1422b7
	else if(!es_strconstcmp(*str, "regex")) {
Packit 1422b7
		node->parser = ln_parseRegex;
Packit 1422b7
		constructor_fn = regex_parser_data_constructor;
Packit 1422b7
		node->parser_data_destructor = regex_parser_data_destructor;
Packit 1422b7
	}
Packit 1422b7
#endif
Packit 1422b7
	else if (!es_strconstcmp(*str, "recursive")) {
Packit 1422b7
		node->parser = ln_parseRecursive;
Packit 1422b7
		constructor_fn = recursive_parser_data_constructor;
Packit 1422b7
		node->parser_data_destructor = recursive_parser_data_destructor;
Packit 1422b7
	} else if (!es_strconstcmp(*str, "descent")) {
Packit 1422b7
		node->parser = ln_parseRecursive;
Packit 1422b7
		constructor_fn = descent_parser_data_constructor;
Packit 1422b7
		node->parser_data_destructor = recursive_parser_data_destructor;
Packit 1422b7
	} else if (!es_strconstcmp(*str, "interpret")) {
Packit 1422b7
		node->parser = ln_parseInterpret;
Packit 1422b7
		constructor_fn = interpret_parser_data_constructor;
Packit 1422b7
		node->parser_data_destructor = interpret_parser_data_destructor;
Packit 1422b7
	} else if (!es_strconstcmp(*str, "suffixed")) {
Packit 1422b7
		node->parser = ln_parseSuffixed;
Packit 1422b7
		constructor_fn = suffixed_parser_data_constructor;
Packit 1422b7
		node->parser_data_destructor = suffixed_parser_data_destructor;
Packit 1422b7
	} else if (!es_strconstcmp(*str, "named_suffixed")) {
Packit 1422b7
		node->parser = ln_parseSuffixed;
Packit 1422b7
		constructor_fn = named_suffixed_parser_data_constructor;
Packit 1422b7
		node->parser_data_destructor = suffixed_parser_data_destructor;
Packit 1422b7
	} else {
Packit 1422b7
		cstr = es_str2cstr(*str, NULL);
Packit 1422b7
		ln_errprintf(ctx, 0, "invalid field type '%s'", cstr);
Packit 1422b7
		free(cstr);
Packit 1422b7
		FAIL(LN_INVLDFDESCR);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(buf[i] == '%') {
Packit 1422b7
		i++;
Packit 1422b7
	} else {
Packit 1422b7
		/* parse extra data */
Packit 1422b7
		CHKN(node->data = es_newStr(8));
Packit 1422b7
		i++;
Packit 1422b7
		while(i < lenBuf) {
Packit 1422b7
			if(buf[i] == '%') {
Packit 1422b7
				++i;
Packit 1422b7
				break; /* end of field */
Packit 1422b7
			}
Packit 1422b7
			CHKR(es_addChar(&node->data, buf[i++]));
Packit 1422b7
		}
Packit 1422b7
		node->raw_data = es_strdup(node->data);
Packit 1422b7
		es_unescapeStr(node->data);
Packit 1422b7
		if(ctx->debug) {
Packit 1422b7
			cstr = es_str2cstr(node->data, NULL);
Packit 1422b7
			ln_dbgprintf(ctx, "parsed extra data: '%s'", cstr);
Packit 1422b7
			free(cstr);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if (constructor_fn) node->parser_data = constructor_fn(node, ctx);
Packit 1422b7
Packit 1422b7
Packit 1422b7
	*bufOffs = i;
Packit 1422b7
done:
Packit 1422b7
	if (r != 0) {
Packit 1422b7
		if (node->name != NULL) es_deleteStr(node->name);
Packit 1422b7
		free(node);
Packit 1422b7
		node = NULL;
Packit 1422b7
	}
Packit 1422b7
	*ret = r;
Packit 1422b7
	return node;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a Literal string out of the template and add it to the tree.
Packit 1422b7
 * @param[in] ctx the context
Packit 1422b7
 * @param[in/out] subtree on entry, current subtree, on exist newest
Packit 1422b7
 *    		deepest subtree
Packit 1422b7
 * @param[in] rule string with current rule
Packit 1422b7
 * @param[in/out] bufOffs parse pointer, up to which offset is parsed
Packit 1422b7
 * 		(is updated so that it points to first char after consumed
Packit 1422b7
 * 		string on exit).
Packit 1422b7
 * @param[out] str literal extracted (is empty, when no litral could be found)
Packit 1422b7
 * @return 0 on success, something else otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
parseLiteral(ln_ctx ctx, struct ln_ptree **subtree, es_str_t *rule,
Packit 1422b7
	     es_size_t *bufOffs, es_str_t **str)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	es_size_t i = *bufOffs;
Packit 1422b7
	unsigned char *buf;
Packit 1422b7
	es_size_t lenBuf;
Packit 1422b7
Packit 1422b7
	es_emptyStr(*str);
Packit 1422b7
	buf = es_getBufAddr(rule);
Packit 1422b7
	lenBuf = es_strlen(rule);
Packit 1422b7
	/* extract maximum length literal */
Packit 1422b7
	while(i < lenBuf) {
Packit 1422b7
		if(buf[i] == '%') {
Packit 1422b7
			if(i+1 < lenBuf && buf[i+1] != '%') {
Packit 1422b7
				break; /* field start is end of literal */
Packit 1422b7
			}
Packit 1422b7
			if (++i == lenBuf) break;
Packit 1422b7
		}
Packit 1422b7
		CHKR(es_addChar(str, buf[i]));
Packit 1422b7
		++i;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	es_unescapeStr(*str);
Packit 1422b7
	if(ctx->debug) {
Packit 1422b7
		char *cstr = es_str2cstr(*str, NULL);
Packit 1422b7
		ln_dbgprintf(ctx, "parsed literal: '%s'", cstr);
Packit 1422b7
		free(cstr);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	*subtree = ln_buildPTree(*subtree, *str, 0);
Packit 1422b7
	*bufOffs = i;
Packit 1422b7
	r = 0;
Packit 1422b7
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* Implementation note:
Packit 1422b7
 * We read in the sample, and split it into chunks of literal text and
Packit 1422b7
 * fields. Each literal text is added as whole to the tree, as is each
Packit 1422b7
 * field individually. To do so, we keep track of our current subtree
Packit 1422b7
 * root, which changes whenever a new part of the tree is build. It is
Packit 1422b7
 * set to the then-lowest part of the tree, where the next step sample
Packit 1422b7
 * data is to be added.
Packit 1422b7
 *
Packit 1422b7
 * This function processes the whole string or returns an error.
Packit 1422b7
 *
Packit 1422b7
 * format: literal1%field:type:extra-data%literal2
Packit 1422b7
 *
Packit 1422b7
 * @returns the new subtree root (or NULL in case of error)
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
addSampToTree(ln_ctx ctx, es_str_t *rule, struct json_object *tagBucket)
Packit 1422b7
{
Packit 1422b7
	int r = -1;
Packit 1422b7
	struct ln_ptree* subtree;
Packit 1422b7
	es_str_t *str = NULL;
Packit 1422b7
	es_size_t i;
Packit 1422b7
Packit 1422b7
	subtree = ctx->ptree;
Packit 1422b7
	CHKN(str = es_newStr(256));
Packit 1422b7
	i = 0;
Packit 1422b7
	while(i < es_strlen(rule)) {
Packit 1422b7
		ln_dbgprintf(ctx, "addSampToTree %d of %d", i, es_strlen(rule));
Packit 1422b7
		CHKR(parseLiteral(ctx, &subtree, rule, &i, &str);;
Packit 1422b7
		/* After the literal there can be field only*/
Packit 1422b7
		if (i < es_strlen(rule)) {
Packit 1422b7
			CHKR(addFieldDescr(ctx, &subtree, rule, &i, &str);;
Packit 1422b7
			if (i == es_strlen(rule)) {
Packit 1422b7
				/* finish the tree with empty literal to avoid false merging*/
Packit 1422b7
				CHKR(parseLiteral(ctx, &subtree, rule, &i, &str);;
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	ln_dbgprintf(ctx, "end addSampToTree %d of %d", i, es_strlen(rule));
Packit 1422b7
	/* we are at the end of rule processing, so this node is a terminal */
Packit 1422b7
	subtree->flags.isTerminal = 1;
Packit 1422b7
	subtree->tags = tagBucket;
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	if(str != NULL)
Packit 1422b7
		es_deleteStr(str);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * get the initial word of a rule line that tells us the type of the
Packit 1422b7
 * line.
Packit 1422b7
 * @param[in] buf line buffer
Packit 1422b7
 * @param[in] len length of buffer
Packit 1422b7
 * @param[out] offs offset after "="
Packit 1422b7
 * @param[out] str string with "linetype-word" (newly created)
Packit 1422b7
 * @returns 0 on success, something else otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
getLineType(const char *buf, es_size_t lenBuf, es_size_t *offs, es_str_t **str)
Packit 1422b7
{
Packit 1422b7
	int r = -1;
Packit 1422b7
	es_size_t i;
Packit 1422b7
Packit 1422b7
	*str = es_newStr(16);
Packit 1422b7
	for(i = 0 ; i < lenBuf && buf[i] != '=' ; ++i) {
Packit 1422b7
		CHKR(es_addChar(str, buf[i]));
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(i < lenBuf)
Packit 1422b7
		++i; /* skip over '=' */
Packit 1422b7
	*offs = i;
Packit 1422b7
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Get a new common prefix from the config file. That is actually everything from
Packit 1422b7
 * the current offset to the end of line.
Packit 1422b7
 *
Packit 1422b7
 * @param[in] buf line buffer
Packit 1422b7
 * @param[in] len length of buffer
Packit 1422b7
 * @param[in] offs offset after "="
Packit 1422b7
 * @param[in/out] str string to store common offset. If NULL, it is created,
Packit 1422b7
 * 	 	otherwise it is emptied.
Packit 1422b7
 * @returns 0 on success, something else otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
getPrefix(const char *buf, es_size_t lenBuf, es_size_t offs, es_str_t **str)
Packit 1422b7
{
Packit 1422b7
	int r;
Packit 1422b7
Packit 1422b7
	if(*str == NULL) {
Packit 1422b7
		CHKN(*str = es_newStr(lenBuf - offs));
Packit 1422b7
	} else {
Packit 1422b7
		es_emptyStr(*str);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	r = es_addBuf(str, (char*)buf + offs, lenBuf - offs);
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Extend the common prefix. This means that the line is concatenated
Packit 1422b7
 * to the prefix. This is useful if the same rulebase is to be used with
Packit 1422b7
 * different prefixes (well, not strictly necessary, but probably useful).
Packit 1422b7
 *
Packit 1422b7
 * @param[in] ctx current context
Packit 1422b7
 * @param[in] buf line buffer
Packit 1422b7
 * @param[in] len length of buffer
Packit 1422b7
 * @param[in] offs offset to-be-added text starts
Packit 1422b7
 * @returns 0 on success, something else otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
extendPrefix(ln_ctx ctx, const char *buf, es_size_t lenBuf, es_size_t offs)
Packit 1422b7
{
Packit 1422b7
	return es_addBuf(&ctx->rulePrefix, (char*)buf+offs, lenBuf - offs);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Add a tag to the tag bucket. Helper to processTags.
Packit 1422b7
 * @param[in] ctx current context
Packit 1422b7
 * @param[in] tagname string with tag name
Packit 1422b7
 * @param[out] tagBucket tagbucket to which new tags shall be added
Packit 1422b7
 *                       the tagbucket is created if it is NULL
Packit 1422b7
 * @returns 0 on success, something else otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
addTagStrToBucket(ln_ctx ctx, es_str_t *tagname, struct json_object **tagBucket)
Packit 1422b7
{
Packit 1422b7
	int r = -1;
Packit 1422b7
	char *cstr;
Packit 1422b7
	struct json_object *tag;
Packit 1422b7
Packit 1422b7
	if(*tagBucket == NULL) {
Packit 1422b7
		CHKN(*tagBucket = json_object_new_array());
Packit 1422b7
	}
Packit 1422b7
	cstr = es_str2cstr(tagname, NULL);
Packit 1422b7
	ln_dbgprintf(ctx, "tag found: '%s'", cstr);
Packit 1422b7
	CHKN(tag = json_object_new_string(cstr));
Packit 1422b7
	json_object_array_add(*tagBucket, tag);
Packit 1422b7
	free(cstr);
Packit 1422b7
	r = 0;
Packit 1422b7
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Extract the tags and create a tag bucket out of them
Packit 1422b7
 *
Packit 1422b7
 * @param[in] ctx current context
Packit 1422b7
 * @param[in] buf line buffer
Packit 1422b7
 * @param[in] len length of buffer
Packit 1422b7
 * @param[in,out] poffs offset where tags start, on exit and success
Packit 1422b7
 *                      offset after tag part (excluding ':')
Packit 1422b7
 * @param[out] tagBucket tagbucket to which new tags shall be added
Packit 1422b7
 *                       the tagbucket is created if it is NULL
Packit 1422b7
 * @returns 0 on success, something else otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
processTags(ln_ctx ctx, const char *buf, es_size_t lenBuf, es_size_t *poffs, struct json_object **tagBucket)
Packit 1422b7
{
Packit 1422b7
	int r = -1;
Packit 1422b7
	es_str_t *str = NULL;
Packit 1422b7
	es_size_t i;
Packit 1422b7
Packit 1422b7
	assert(poffs != NULL);
Packit 1422b7
	i = *poffs;
Packit 1422b7
	while(i < lenBuf && buf[i] != ':') {
Packit 1422b7
		if(buf[i] == ',') {
Packit 1422b7
			/* end of this tag */
Packit 1422b7
			CHKR(addTagStrToBucket(ctx, str, tagBucket));
Packit 1422b7
			es_deleteStr(str);
Packit 1422b7
			str = NULL;
Packit 1422b7
		} else {
Packit 1422b7
			if(str == NULL) {
Packit 1422b7
				CHKN(str = es_newStr(32));
Packit 1422b7
			}
Packit 1422b7
			CHKR(es_addChar(&str, buf[i]));
Packit 1422b7
		}
Packit 1422b7
		++i;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(buf[i] != ':')
Packit 1422b7
		goto done;
Packit 1422b7
	++i; /* skip ':' */
Packit 1422b7
Packit 1422b7
	if(str != NULL) {
Packit 1422b7
		CHKR(addTagStrToBucket(ctx, str, tagBucket));
Packit 1422b7
		es_deleteStr(str);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	*poffs = i;
Packit 1422b7
	r = 0;
Packit 1422b7
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Process a new rule and add it to tree.
Packit 1422b7
 *
Packit 1422b7
 * @param[in] ctx current context
Packit 1422b7
 * @param[in] buf line buffer
Packit 1422b7
 * @param[in] len length of buffer
Packit 1422b7
 * @param[in] offs offset where rule starts
Packit 1422b7
 * @returns 0 on success, something else otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
processRule(ln_ctx ctx, const char *buf, es_size_t lenBuf, es_size_t offs)
Packit 1422b7
{
Packit 1422b7
	int r = -1;
Packit 1422b7
	es_str_t *str;
Packit 1422b7
	struct json_object *tagBucket = NULL;
Packit 1422b7
Packit 1422b7
	ln_dbgprintf(ctx, "sample line to add: '%s'\n", buf+offs);
Packit 1422b7
	CHKR(processTags(ctx, buf, lenBuf, &offs, &tagBucket));
Packit 1422b7
Packit 1422b7
	if(offs == lenBuf) {
Packit 1422b7
		ln_dbgprintf(ctx, "error, actual message sample part is missing");
Packit 1422b7
		// TODO: provide some error indicator to app? We definitely must do (a callback?)
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	if(ctx->rulePrefix == NULL) {
Packit 1422b7
		CHKN(str = es_newStr(lenBuf));
Packit 1422b7
	} else {
Packit 1422b7
		CHKN(str = es_strdup(ctx->rulePrefix));
Packit 1422b7
	}
Packit 1422b7
	CHKR(es_addBuf(&str, (char*)buf + offs, lenBuf - offs));
Packit 1422b7
	addSampToTree(ctx, str, tagBucket);
Packit 1422b7
	es_deleteStr(str);
Packit 1422b7
	r = 0;
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Obtain a field name from a rule base line.
Packit 1422b7
 *
Packit 1422b7
 * @param[in] ctx current context
Packit 1422b7
 * @param[in] buf line buffer
Packit 1422b7
 * @param[in] len length of buffer
Packit 1422b7
 * @param[in/out] offs on entry: offset where tag starts,
Packit 1422b7
 * 		       on exit: updated offset AFTER TAG and (':')
Packit 1422b7
 * @param [out] strTag obtained tag, if successful
Packit 1422b7
 * @returns 0 on success, something else otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
getFieldName(ln_ctx __attribute__((unused)) ctx, const char *buf, es_size_t lenBuf, es_size_t *offs,
Packit 1422b7
es_str_t **strTag)
Packit 1422b7
{
Packit 1422b7
	int r = -1;
Packit 1422b7
	es_size_t i;
Packit 1422b7
Packit 1422b7
	i = *offs;
Packit 1422b7
	while(i < lenBuf &&
Packit 1422b7
	       (isalnum(buf[i]) || buf[i] == '_' || buf[i] == '.')) {
Packit 1422b7
		if(*strTag == NULL) {
Packit 1422b7
			CHKN(*strTag = es_newStr(32));
Packit 1422b7
		}
Packit 1422b7
		CHKR(es_addChar(strTag, buf[i]));
Packit 1422b7
		++i;
Packit 1422b7
	}
Packit 1422b7
	*offs = i;
Packit 1422b7
	r = 0;
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Skip over whitespace.
Packit 1422b7
 * Skips any whitespace present at the offset.
Packit 1422b7
 *
Packit 1422b7
 * @param[in] ctx current context
Packit 1422b7
 * @param[in] buf line buffer
Packit 1422b7
 * @param[in] len length of buffer
Packit 1422b7
 * @param[in/out] offs on entry: offset first unprocessed position
Packit 1422b7
 */
Packit 1422b7
static void
Packit 1422b7
skipWhitespace(ln_ctx __attribute__((unused)) ctx, const char *buf, es_size_t lenBuf, es_size_t *offs)
Packit 1422b7
{
Packit 1422b7
	while(*offs < lenBuf && isspace(buf[*offs])) {
Packit 1422b7
		(*offs)++;
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Obtain an annotation (field) operation.
Packit 1422b7
 * This usually is a plus or minus sign followed by a field name
Packit 1422b7
 * followed (if plus) by an equal sign and the field value. On entry,
Packit 1422b7
 * offs must be positioned on the first unprocessed field (after ':' for
Packit 1422b7
 * the initial field!). Extra whitespace is detected and, if present,
Packit 1422b7
 * skipped. The obtained operation is added to the annotation set provided.
Packit 1422b7
 * Note that extracted string objects are passed to the annotation; thus it
Packit 1422b7
 * is vital NOT to free them (most importantly, this is *not* a memory leak).
Packit 1422b7
 *
Packit 1422b7
 * @param[in] ctx current context
Packit 1422b7
 * @param[in] annot active annotation set to which the operation is to be added
Packit 1422b7
 * @param[in] buf line buffer
Packit 1422b7
 * @param[in] len length of buffer
Packit 1422b7
 * @param[in/out] offs on entry: offset where tag starts,
Packit 1422b7
 * 		       on exit: updated offset AFTER TAG and (':')
Packit 1422b7
 * @param [out] strTag obtained tag, if successful
Packit 1422b7
 * @returns 0 on success, something else otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
getAnnotationOp(ln_ctx ctx, ln_annot *annot, const char *buf, es_size_t lenBuf, es_size_t *offs)
Packit 1422b7
{
Packit 1422b7
	int r = -1;
Packit 1422b7
	es_size_t i;
Packit 1422b7
	es_str_t *fieldName = NULL;
Packit 1422b7
	es_str_t *fieldVal = NULL;
Packit 1422b7
	ln_annot_opcode opc;
Packit 1422b7
Packit 1422b7
	i = *offs;
Packit 1422b7
	skipWhitespace(ctx, buf, lenBuf, &i);
Packit 1422b7
	if(i == lenBuf) {
Packit 1422b7
		r = 0;
Packit 1422b7
		goto done; /* nothing left to process (no error!) */
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(buf[i] == '+') {
Packit 1422b7
		opc = ln_annot_ADD;
Packit 1422b7
	} else if(buf[i] == '-') {
Packit 1422b7
		ln_dbgprintf(ctx, "annotate op '-' not yet implemented - failing");
Packit 1422b7
		goto fail;
Packit 1422b7
	} else {
Packit 1422b7
		ln_dbgprintf(ctx, "invalid annotate opcode '%c' - failing" , buf[i]);
Packit 1422b7
		goto fail;
Packit 1422b7
	}
Packit 1422b7
	i++;
Packit 1422b7
Packit 1422b7
	if(i == lenBuf) goto fail; /* nothing left to process */
Packit 1422b7
Packit 1422b7
	CHKR(getFieldName(ctx, buf, lenBuf, &i, &fieldName));
Packit 1422b7
	if(i == lenBuf) goto fail; /* nothing left to process */
Packit 1422b7
	if(buf[i] != '=') goto fail; /* format error */
Packit 1422b7
	i++;
Packit 1422b7
Packit 1422b7
	skipWhitespace(ctx, buf, lenBuf, &i);
Packit 1422b7
	if(buf[i] != '"') goto fail; /* format error */
Packit 1422b7
	++i;
Packit 1422b7
Packit 1422b7
	while(i < lenBuf && buf[i] != '"') {
Packit 1422b7
		if(fieldVal == NULL) {
Packit 1422b7
			CHKN(fieldVal = es_newStr(32));
Packit 1422b7
		}
Packit 1422b7
		CHKR(es_addChar(&fieldVal, buf[i]));
Packit 1422b7
		++i;
Packit 1422b7
	}
Packit 1422b7
	*offs = (i == lenBuf) ? i : i+1;
Packit 1422b7
	CHKR(ln_addAnnotOp(annot, opc, fieldName, fieldVal));
Packit 1422b7
	r = 0;
Packit 1422b7
done:	return r;
Packit 1422b7
fail:	return -1;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Process a new annotation and add it to the annotation set.
Packit 1422b7
 *
Packit 1422b7
 * @param[in] ctx current context
Packit 1422b7
 * @param[in] buf line buffer
Packit 1422b7
 * @param[in] len length of buffer
Packit 1422b7
 * @param[in] offs offset where annotation starts
Packit 1422b7
 * @returns 0 on success, something else otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
processAnnotate(ln_ctx ctx, const char *buf, es_size_t lenBuf, es_size_t offs)
Packit 1422b7
{
Packit 1422b7
	int r;
Packit 1422b7
	es_str_t *tag = NULL;
Packit 1422b7
	ln_annot *annot;
Packit 1422b7
Packit 1422b7
	ln_dbgprintf(ctx, "sample annotation to add: '%s'", buf+offs);
Packit 1422b7
	CHKR(getFieldName(ctx, buf, lenBuf, &offs, &tag));
Packit 1422b7
	skipWhitespace(ctx, buf, lenBuf, &offs);
Packit 1422b7
	if(buf[offs] != ':' || tag == NULL) {
Packit 1422b7
		ln_dbgprintf(ctx, "invalid tag field in annotation, line is '%s'", buf);
Packit 1422b7
		r=-1;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	++offs;
Packit 1422b7
Packit 1422b7
	/* we got an annotation! */
Packit 1422b7
	CHKN(annot = ln_newAnnot(tag));
Packit 1422b7
Packit 1422b7
	while(offs < lenBuf) {
Packit 1422b7
		CHKR(getAnnotationOp(ctx, annot, buf, lenBuf, &offs));
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	r = ln_addAnnotToSet(ctx->pas, annot);
Packit 1422b7
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
struct ln_v1_samp *
Packit 1422b7
ln_v1_processSamp(ln_ctx ctx, const char *buf, es_size_t lenBuf)
Packit 1422b7
{
Packit 1422b7
	struct ln_v1_samp *samp = NULL;
Packit 1422b7
	es_str_t *typeStr = NULL;
Packit 1422b7
	es_size_t offs;
Packit 1422b7
Packit 1422b7
	if(getLineType(buf, lenBuf, &offs, &typeStr) != 0)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(!es_strconstcmp(typeStr, "prefix")) {
Packit 1422b7
		if(getPrefix(buf, lenBuf, offs, &ctx->rulePrefix) != 0) goto done;
Packit 1422b7
	} else if(!es_strconstcmp(typeStr, "extendprefix")) {
Packit 1422b7
		if(extendPrefix(ctx, buf, lenBuf, offs) != 0) goto done;
Packit 1422b7
	} else if(!es_strconstcmp(typeStr, "rule")) {
Packit 1422b7
		if(processRule(ctx, buf, lenBuf, offs) != 0) goto done;
Packit 1422b7
	} else if(!es_strconstcmp(typeStr, "annotate")) {
Packit 1422b7
		if(processAnnotate(ctx, buf, lenBuf, offs) != 0) goto done;
Packit 1422b7
	} else {
Packit 1422b7
		/* TODO error reporting */
Packit 1422b7
		char *str;
Packit 1422b7
		str = es_str2cstr(typeStr, NULL);
Packit 1422b7
		ln_dbgprintf(ctx, "invalid record type detected: '%s'", str);
Packit 1422b7
		free(str);
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	if(typeStr != NULL)
Packit 1422b7
		es_deleteStr(typeStr);
Packit 1422b7
Packit 1422b7
	return samp;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
struct ln_v1_samp *
Packit 1422b7
ln_v1_sampRead(ln_ctx ctx, FILE *const __restrict__ repo, int *const __restrict__ isEof)
Packit 1422b7
{
Packit 1422b7
	struct ln_v1_samp *samp = NULL;
Packit 1422b7
	char buf[10*1024]; /**< max size of rule - TODO: make configurable */
Packit 1422b7
Packit 1422b7
	size_t i = 0;
Packit 1422b7
	int inParser = 0;
Packit 1422b7
	int done = 0;
Packit 1422b7
	while(!done) {
Packit 1422b7
		int c = fgetc(repo);
Packit 1422b7
		if(c == EOF) {
Packit 1422b7
			*isEof = 1;
Packit 1422b7
			if(i == 0)
Packit 1422b7
				goto done;
Packit 1422b7
			else
Packit 1422b7
				done = 1; /* last line missing LF, still process it! */
Packit 1422b7
		} else if(c == '\n') {
Packit 1422b7
			++ctx->conf_ln_nbr;
Packit 1422b7
			if(inParser) {
Packit 1422b7
				if(ln_sampChkRunawayRule(ctx, repo, NULL)) {
Packit 1422b7
					/* ignore previous rule */
Packit 1422b7
					inParser = 0;
Packit 1422b7
					i = 0;
Packit 1422b7
				}
Packit 1422b7
			}
Packit 1422b7
			if(!inParser && i != 0)
Packit 1422b7
				done = 1;
Packit 1422b7
		} else if(c == '#' && i == 0) {
Packit 1422b7
			ln_sampSkipCommentLine(ctx, repo, NULL);
Packit 1422b7
			i = 0; /* back to beginning */
Packit 1422b7
		} else {
Packit 1422b7
			if(c == '%')
Packit 1422b7
				inParser = (inParser) ? 0 : 1;
Packit 1422b7
			buf[i++] = c;
Packit 1422b7
			if(i >= sizeof(buf)) {
Packit 1422b7
				ln_errprintf(ctx, 0, "line is too long");
Packit 1422b7
				goto done;
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	buf[i] = '\0';
Packit 1422b7
Packit 1422b7
	ln_dbgprintf(ctx, "read rulebase line[~%d]: '%s'", ctx->conf_ln_nbr, buf);
Packit 1422b7
	ln_v1_processSamp(ctx, buf, i);
Packit 1422b7
Packit 1422b7
ln_dbgprintf(ctx, "---------------------------------------");
Packit 1422b7
ln_displayPTree(ctx->ptree, 0);
Packit 1422b7
ln_dbgprintf(ctx, "=======================================");
Packit 1422b7
done:
Packit 1422b7
	return samp;
Packit 1422b7
}