Blame src/v1_parser.c

Packit 1422b7
/*
Packit 1422b7
 * liblognorm - a fast samples-based log normalization library
Packit 1422b7
 * Copyright 2010-2018 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 <assert.h>
Packit 1422b7
#include <ctype.h>
Packit 1422b7
#include <sys/types.h>
Packit 1422b7
#include <string.h>
Packit 1422b7
#include <strings.h>
Packit 1422b7
Packit 1422b7
#include "v1_liblognorm.h"
Packit 1422b7
#include "internal.h"
Packit 1422b7
#include "lognorm.h"
Packit 1422b7
#include "v1_parser.h"
Packit 1422b7
#include "v1_samp.h"
Packit 1422b7
Packit 1422b7
#ifdef FEATURE_REGEXP
Packit 1422b7
#include <pcre.h>
Packit 1422b7
#include <errno.h>
Packit 1422b7
#endif
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* some helpers */
Packit 1422b7
static inline int
Packit 1422b7
hParseInt(const unsigned char **buf, size_t *lenBuf)
Packit 1422b7
{
Packit 1422b7
	const unsigned char *p = *buf;
Packit 1422b7
	size_t len = *lenBuf;
Packit 1422b7
	int i = 0;
Packit 1422b7
Packit 1422b7
	while(len > 0 && isdigit(*p)) {
Packit 1422b7
		i = i * 10 + *p - '0';
Packit 1422b7
		++p;
Packit 1422b7
		--len;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	*buf = p;
Packit 1422b7
	*lenBuf = len;
Packit 1422b7
	return i;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* parsers for the primitive types
Packit 1422b7
 *
Packit 1422b7
 * All parsers receive
Packit 1422b7
 *
Packit 1422b7
 * @param[in] str the to-be-parsed string
Packit 1422b7
 * @param[in] strLen length of the to-be-parsed string
Packit 1422b7
 * @param[in] offs an offset into the string
Packit 1422b7
 * @param[in] node fieldlist with additional data; for simple
Packit 1422b7
 *            parsers, this sets variable "ed", which just is
Packit 1422b7
 *            string data.
Packit 1422b7
 * @param[out] parsed bytes
Packit 1422b7
 * @param[out] value ptr to json object containing parsed data
Packit 1422b7
 *             (can be unused, but if used *value MUST be NULL on entry)
Packit 1422b7
 *
Packit 1422b7
 * They will try to parse out "their" object from the string. If they
Packit 1422b7
 * succeed, they:
Packit 1422b7
 *
Packit 1422b7
 * return 0 on success and LN_WRONGPARSER if this parser could
Packit 1422b7
 *           not successfully parse (but all went well otherwise) and something
Packit 1422b7
 *           else in case of an error.
Packit 1422b7
 */
Packit 1422b7
#define PARSER(ParserName) \
Packit 1422b7
int ln_parse##ParserName(const char *const str, const size_t strLen, \
Packit 1422b7
	size_t *const offs,       \
Packit 1422b7
	__attribute__((unused)) const ln_fieldList_t *node,  \
Packit 1422b7
	size_t *parsed,                                      \
Packit 1422b7
	__attribute__((unused)) struct json_object **value) \
Packit 1422b7
{ \
Packit 1422b7
	int r = LN_WRONGPARSER; \
Packit 1422b7
	__attribute__((unused)) es_str_t *ed = node->data;  \
Packit 1422b7
	*parsed = 0;
Packit 1422b7
Packit 1422b7
#define FAILParser \
Packit 1422b7
	goto parserdone; /* suppress warnings */ \
Packit 1422b7
parserdone: \
Packit 1422b7
	r = 0; \
Packit 1422b7
	goto done; /* suppress warnings */ \
Packit 1422b7
done:
Packit 1422b7
Packit 1422b7
#define ENDFailParser \
Packit 1422b7
	return r; \
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Utilities to allow constructors of complex parser's to
Packit 1422b7
 *  easily process field-declaration arguments.
Packit 1422b7
 */
Packit 1422b7
#define FIELD_ARG_SEPERATOR ":"
Packit 1422b7
#define MAX_FIELD_ARGS 10
Packit 1422b7
Packit 1422b7
struct pcons_args_s {
Packit 1422b7
	int argc;
Packit 1422b7
	char *argv[MAX_FIELD_ARGS];
Packit 1422b7
};
Packit 1422b7
Packit 1422b7
typedef struct pcons_args_s pcons_args_t;
Packit 1422b7
Packit 1422b7
static void free_pcons_args(pcons_args_t** dat_p) {
Packit 1422b7
	pcons_args_t *dat = *dat_p;
Packit 1422b7
	*dat_p = NULL;
Packit 1422b7
	if (! dat) {
Packit 1422b7
	  return;
Packit 1422b7
	}
Packit 1422b7
	while((--(dat->argc)) >= 0) {
Packit 1422b7
		if (dat->argv[dat->argc] != NULL) free(dat->argv[dat->argc]);
Packit 1422b7
	}
Packit 1422b7
	free(dat);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static pcons_args_t* pcons_args(es_str_t *args, int expected_argc) {
Packit 1422b7
	pcons_args_t *dat = NULL;
Packit 1422b7
	char* orig_str = NULL;
Packit 1422b7
	if ((dat = malloc(sizeof(pcons_args_t))) == NULL) goto fail;
Packit 1422b7
	dat->argc = 0;
Packit 1422b7
	if (args != NULL) {
Packit 1422b7
		orig_str = es_str2cstr(args, NULL);
Packit 1422b7
		char *str = orig_str;
Packit 1422b7
		while (dat->argc < MAX_FIELD_ARGS) {
Packit 1422b7
			int i = dat->argc++;
Packit 1422b7
			char *next = (dat->argc == expected_argc) ? NULL : strstr(str, FIELD_ARG_SEPERATOR);
Packit 1422b7
			if (next == NULL) {
Packit 1422b7
				if ((dat->argv[i] = strdup(str)) == NULL) goto fail;
Packit 1422b7
				break;
Packit 1422b7
			} else {
Packit 1422b7
				if ((dat->argv[i] = strndup(str, next - str)) == NULL) goto fail;
Packit 1422b7
				next++;
Packit 1422b7
			}
Packit 1422b7
			str = next;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	goto done;
Packit 1422b7
fail:
Packit 1422b7
	if (dat != NULL) free_pcons_args(&dat;;
Packit 1422b7
done:
Packit 1422b7
	if (orig_str != NULL) free(orig_str);
Packit 1422b7
	return dat;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static const char* pcons_arg(pcons_args_t *dat, int i, const char* dflt_val) {
Packit 1422b7
	if (i >= dat->argc) return dflt_val;
Packit 1422b7
	return dat->argv[i];
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static char* pcons_arg_copy(pcons_args_t *dat, int i, const char* dflt_val) {
Packit 1422b7
	const char *str = pcons_arg(dat, i, dflt_val);
Packit 1422b7
	return (str == NULL) ? NULL : strdup(str);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static void pcons_unescape_arg(pcons_args_t *dat, int i) {
Packit 1422b7
	char *arg = (char*) pcons_arg(dat, i, NULL);
Packit 1422b7
	es_str_t *str = NULL;
Packit 1422b7
	if (arg != NULL) {
Packit 1422b7
		str = es_newStrFromCStr(arg, strlen(arg));
Packit 1422b7
		if (str != NULL) {
Packit 1422b7
			es_unescapeStr(str);
Packit 1422b7
			free(arg);
Packit 1422b7
			dat->argv[i] = es_str2cstr(str, NULL);
Packit 1422b7
			es_deleteStr(str);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a TIMESTAMP as specified in RFC5424 (subset of RFC3339).
Packit 1422b7
 */
Packit 1422b7
PARSER(RFC5424Date)
Packit 1422b7
	const unsigned char *pszTS;
Packit 1422b7
	/* variables to temporarily hold time information while we parse */
Packit 1422b7
	__attribute__((unused)) int year;
Packit 1422b7
	int month;
Packit 1422b7
	int day;
Packit 1422b7
	int hour; /* 24 hour clock */
Packit 1422b7
	int minute;
Packit 1422b7
	int second;
Packit 1422b7
	__attribute__((unused)) int secfrac;	/* fractional seconds (must be 32 bit!) */
Packit 1422b7
	__attribute__((unused)) int secfracPrecision;
Packit 1422b7
	int OffsetHour;		/* UTC offset in hours */
Packit 1422b7
	int OffsetMinute;	/* UTC offset in minutes */
Packit 1422b7
	size_t len;
Packit 1422b7
	size_t orglen;
Packit 1422b7
	/* end variables to temporarily hold time information while we parse */
Packit 1422b7
Packit 1422b7
	pszTS = (unsigned char*) str + *offs;
Packit 1422b7
	len = orglen = strLen - *offs;
Packit 1422b7
Packit 1422b7
	year = hParseInt(&pszTS, &len;;
Packit 1422b7
Packit 1422b7
	/* We take the liberty to accept slightly malformed timestamps e.g. in
Packit 1422b7
	 * the format of 2003-9-1T1:0:0.  */
Packit 1422b7
	if(len == 0 || *pszTS++ != '-') goto done;
Packit 1422b7
	--len;
Packit 1422b7
	month = hParseInt(&pszTS, &len;;
Packit 1422b7
	if(month < 1 || month > 12) goto done;
Packit 1422b7
Packit 1422b7
	if(len == 0 || *pszTS++ != '-')
Packit 1422b7
		goto done;
Packit 1422b7
	--len;
Packit 1422b7
	day = hParseInt(&pszTS, &len;;
Packit 1422b7
	if(day < 1 || day > 31) goto done;
Packit 1422b7
Packit 1422b7
	if(len == 0 || *pszTS++ != 'T') goto done;
Packit 1422b7
	--len;
Packit 1422b7
Packit 1422b7
	hour = hParseInt(&pszTS, &len;;
Packit 1422b7
	if(hour < 0 || hour > 23) goto done;
Packit 1422b7
Packit 1422b7
	if(len == 0 || *pszTS++ != ':')
Packit 1422b7
		goto done;
Packit 1422b7
	--len;
Packit 1422b7
	minute = hParseInt(&pszTS, &len;;
Packit 1422b7
	if(minute < 0 || minute > 59) goto done;
Packit 1422b7
Packit 1422b7
	if(len == 0 || *pszTS++ != ':') goto done;
Packit 1422b7
	--len;
Packit 1422b7
	second = hParseInt(&pszTS, &len;;
Packit 1422b7
	if(second < 0 || second > 60) goto done;
Packit 1422b7
Packit 1422b7
	/* Now let's see if we have secfrac */
Packit 1422b7
	if(len > 0 && *pszTS == '.') {
Packit 1422b7
		--len;
Packit 1422b7
		const unsigned char *pszStart = ++pszTS;
Packit 1422b7
		secfrac = hParseInt(&pszTS, &len;;
Packit 1422b7
		secfracPrecision = (int) (pszTS - pszStart);
Packit 1422b7
	} else {
Packit 1422b7
		secfracPrecision = 0;
Packit 1422b7
		secfrac = 0;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* check the timezone */
Packit 1422b7
	if(len == 0) goto done;
Packit 1422b7
Packit 1422b7
	if(*pszTS == 'Z') {
Packit 1422b7
		--len;
Packit 1422b7
		pszTS++; /* eat Z */
Packit 1422b7
	} else if((*pszTS == '+') || (*pszTS == '-')) {
Packit 1422b7
		--len;
Packit 1422b7
		pszTS++;
Packit 1422b7
Packit 1422b7
		OffsetHour = hParseInt(&pszTS, &len;;
Packit 1422b7
		if(OffsetHour < 0 || OffsetHour > 23)
Packit 1422b7
			goto done;
Packit 1422b7
Packit 1422b7
		if(len == 0 || *pszTS++ != ':')
Packit 1422b7
			goto done;
Packit 1422b7
		--len;
Packit 1422b7
		OffsetMinute = hParseInt(&pszTS, &len;;
Packit 1422b7
		if(OffsetMinute < 0 || OffsetMinute > 59)
Packit 1422b7
			goto done;
Packit 1422b7
	} else {
Packit 1422b7
		/* there MUST be TZ information */
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(len > 0) {
Packit 1422b7
		if(*pszTS != ' ') /* if it is not a space, it can not be a "good" time */
Packit 1422b7
			goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* we had success, so update parse pointer */
Packit 1422b7
	*parsed = orglen - len;
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a RFC3164 Date.
Packit 1422b7
 */
Packit 1422b7
PARSER(RFC3164Date)
Packit 1422b7
	const unsigned char *p;
Packit 1422b7
	size_t len, orglen;
Packit 1422b7
	/* variables to temporarily hold time information while we parse */
Packit 1422b7
	__attribute__((unused)) int month;
Packit 1422b7
	int day;
Packit 1422b7
#if 0 /* TODO: why does this still exist? */
Packit 1422b7
	int year = 0; /* 0 means no year provided */
Packit 1422b7
#endif
Packit 1422b7
	int hour; /* 24 hour clock */
Packit 1422b7
	int minute;
Packit 1422b7
	int second;
Packit 1422b7
Packit 1422b7
	p = (unsigned char*) str + *offs;
Packit 1422b7
	orglen = len = strLen - *offs;
Packit 1422b7
	/* If we look at the month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec),
Packit 1422b7
	 * we may see the following character sequences occur:
Packit 1422b7
	 *
Packit 1422b7
	 * J(an/u(n/l)), Feb, Ma(r/y), A(pr/ug), Sep, Oct, Nov, Dec
Packit 1422b7
	 *
Packit 1422b7
	 * We will use this for parsing, as it probably is the
Packit 1422b7
	 * fastest way to parse it.
Packit 1422b7
	 */
Packit 1422b7
	if(len < 3)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	switch(*p++)
Packit 1422b7
	{
Packit 1422b7
	case 'j':
Packit 1422b7
	case 'J':
Packit 1422b7
		if(*p == 'a' || *p == 'A') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'n' || *p == 'N') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 1;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else if(*p == 'u' || *p == 'U') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'n' || *p == 'N') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 6;
Packit 1422b7
			} else if(*p == 'l' || *p == 'L') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 7;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	case 'f':
Packit 1422b7
	case 'F':
Packit 1422b7
		if(*p == 'e' || *p == 'E') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'b' || *p == 'B') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 2;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	case 'm':
Packit 1422b7
	case 'M':
Packit 1422b7
		if(*p == 'a' || *p == 'A') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'r' || *p == 'R') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 3;
Packit 1422b7
			} else if(*p == 'y' || *p == 'Y') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 5;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	case 'a':
Packit 1422b7
	case 'A':
Packit 1422b7
		if(*p == 'p' || *p == 'P') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'r' || *p == 'R') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 4;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else if(*p == 'u' || *p == 'U') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'g' || *p == 'G') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 8;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	case 's':
Packit 1422b7
	case 'S':
Packit 1422b7
		if(*p == 'e' || *p == 'E') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'p' || *p == 'P') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 9;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	case 'o':
Packit 1422b7
	case 'O':
Packit 1422b7
		if(*p == 'c' || *p == 'C') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 't' || *p == 'T') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 10;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	case 'n':
Packit 1422b7
	case 'N':
Packit 1422b7
		if(*p == 'o' || *p == 'O') {
Packit 1422b7
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'v' || *p == 'V') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 11;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	case 'd':
Packit 1422b7
	case 'D':
Packit 1422b7
		if(*p == 'e' || *p == 'E') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'c' || *p == 'C') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 12;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	default:
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	len -= 3;
Packit 1422b7
	
Packit 1422b7
	/* done month */
Packit 1422b7
Packit 1422b7
	if(len == 0 || *p++ != ' ')
Packit 1422b7
		goto done;
Packit 1422b7
	--len;
Packit 1422b7
Packit 1422b7
	/* we accept a slightly malformed timestamp with one-digit days. */
Packit 1422b7
	if(*p == ' ') {
Packit 1422b7
		--len;
Packit 1422b7
		++p;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	day = hParseInt(&p, &len;;
Packit 1422b7
	if(day < 1 || day > 31)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(len == 0 || *p++ != ' ')
Packit 1422b7
		goto done;
Packit 1422b7
	--len;
Packit 1422b7
Packit 1422b7
	/* time part */
Packit 1422b7
	hour = hParseInt(&p, &len;;
Packit 1422b7
	if(hour > 1970 && hour < 2100) {
Packit 1422b7
		/* if so, we assume this actually is a year. This is a format found
Packit 1422b7
		 * e.g. in Cisco devices.
Packit 1422b7
		 *
Packit 1422b7
		year = hour;
Packit 1422b7
		*/
Packit 1422b7
Packit 1422b7
		/* re-query the hour, this time it must be valid */
Packit 1422b7
		if(len == 0 || *p++ != ' ')
Packit 1422b7
			goto done;
Packit 1422b7
		--len;
Packit 1422b7
		hour = hParseInt(&p, &len;;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(hour < 0 || hour > 23)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(len == 0 || *p++ != ':')
Packit 1422b7
		goto done;
Packit 1422b7
	--len;
Packit 1422b7
	minute = hParseInt(&p, &len;;
Packit 1422b7
	if(minute < 0 || minute > 59)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(len == 0 || *p++ != ':')
Packit 1422b7
		goto done;
Packit 1422b7
	--len;
Packit 1422b7
	second = hParseInt(&p, &len;;
Packit 1422b7
	if(second < 0 || second > 60)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* we provide support for an extra ":" after the date. While this is an
Packit 1422b7
	 * invalid format, it occurs frequently enough (e.g. with Cisco devices)
Packit 1422b7
	 * to permit it as a valid case. -- rgerhards, 2008-09-12
Packit 1422b7
	 */
Packit 1422b7
	if(len > 0 && *p == ':') {
Packit 1422b7
		++p; /* just skip past it */
Packit 1422b7
		--len;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* we had success, so update parse pointer */
Packit 1422b7
	*parsed = orglen - len;
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a Number.
Packit 1422b7
 * Note that a number is an abstracted concept. We always represent it
Packit 1422b7
 * as 64 bits (but may later change our mind if performance dictates so).
Packit 1422b7
 */
Packit 1422b7
PARSER(Number)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = str;
Packit 1422b7
Packit 1422b7
	for (i = *offs; i < strLen && isdigit(c[i]); i++);
Packit 1422b7
	if (i == *offs)
Packit 1422b7
		goto done;
Packit 1422b7
	
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a Real-number in floating-pt form.
Packit 1422b7
 */
Packit 1422b7
PARSER(Float)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = str;
Packit 1422b7
Packit 1422b7
	int seen_point = 0;
Packit 1422b7
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	if (c[i] == '-') i++;
Packit 1422b7
Packit 1422b7
	for (; i < strLen; i++) {
Packit 1422b7
		if (c[i] == '.') {
Packit 1422b7
			if (seen_point != 0) break;
Packit 1422b7
			seen_point = 1;
Packit 1422b7
		} else if (! isdigit(c[i])) {
Packit 1422b7
			break;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	if (i == *offs)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a hex Number.
Packit 1422b7
 * A hex number begins with 0x and contains only hex digits until the terminating
Packit 1422b7
 * whitespace. Note that if a non-hex character is deteced inside the number string,
Packit 1422b7
 * this is NOT considered to be a number.
Packit 1422b7
 */
Packit 1422b7
PARSER(HexNumber)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = str;
Packit 1422b7
Packit 1422b7
	if(c[i] != '0' || c[i+1] != 'x')
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	for (i += 2 ; i < strLen && isxdigit(c[i]); i++);
Packit 1422b7
	if (i == *offs || !isspace(c[i]))
Packit 1422b7
		goto done;
Packit 1422b7
	
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a kernel timestamp.
Packit 1422b7
 * This is a fixed format, see
Packit 1422b7
 * https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/printk/printk.c?id=refs/tags/v4.0#n1011
Packit 1422b7
 * This is the code that generates it:
Packit 1422b7
 * sprintf(buf, "[%5lu.%06lu] ",  (unsigned long)ts, rem_nsec / 1000);
Packit 1422b7
 * We accept up to 12 digits for ts, everything above that for sure is
Packit 1422b7
 * no timestamp.
Packit 1422b7
 */
Packit 1422b7
#define LEN_KERNEL_TIMESTAMP 14
Packit 1422b7
PARSER(KernelTimestamp)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = str;
Packit 1422b7
Packit 1422b7
	i = *offs;
Packit 1422b7
	if(c[i] != '[' || i+LEN_KERNEL_TIMESTAMP > strLen
Packit 1422b7
	   || !isdigit(c[i+1])
Packit 1422b7
	   || !isdigit(c[i+2])
Packit 1422b7
	   || !isdigit(c[i+3])
Packit 1422b7
	   || !isdigit(c[i+4])
Packit 1422b7
	   || !isdigit(c[i+5])
Packit 1422b7
	   )
Packit 1422b7
		goto done;
Packit 1422b7
	i += 6;
Packit 1422b7
	for(int j = 0 ; j < 7 && i < strLen && isdigit(c[i]) ; )
Packit 1422b7
		++i, ++j;	/* just scan */
Packit 1422b7
Packit 1422b7
	if(i >= strLen || c[i] != '.')
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	++i; /* skip over '.' */
Packit 1422b7
Packit 1422b7
	if( i+7 > strLen
Packit 1422b7
	   || !isdigit(c[i+0])
Packit 1422b7
	   || !isdigit(c[i+1])
Packit 1422b7
	   || !isdigit(c[i+2])
Packit 1422b7
	   || !isdigit(c[i+3])
Packit 1422b7
	   || !isdigit(c[i+4])
Packit 1422b7
	   || !isdigit(c[i+5])
Packit 1422b7
	   || c[i+6] != ']'
Packit 1422b7
	   )
Packit 1422b7
		goto done;
Packit 1422b7
	i += 7;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse whitespace.
Packit 1422b7
 * This parses all whitespace until the first non-whitespace character
Packit 1422b7
 * is found. This is primarily a tool to skip to the next "word" if
Packit 1422b7
 * the exact number of whitspace characters (and type of whitespace)
Packit 1422b7
 * is not known. The current parsing position MUST be on a whitspace,
Packit 1422b7
 * else the parser does not match.
Packit 1422b7
 * This parser is also a forward-compatibility tool for the upcoming
Packit 1422b7
 * slsa (simple log structure analyser) tool.
Packit 1422b7
 */
Packit 1422b7
PARSER(Whitespace)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = str;
Packit 1422b7
Packit 1422b7
	if(!isspace(c[i]))
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	for (i++ ; i < strLen && isspace(c[i]); i++);
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a word.
Packit 1422b7
 * A word is a SP-delimited entity. The parser always works, except if
Packit 1422b7
 * the offset is position on a space upon entry.
Packit 1422b7
 */
Packit 1422b7
PARSER(Word)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	/* search end of word */
Packit 1422b7
	while(i < strLen && c[i] != ' ')
Packit 1422b7
		i++;
Packit 1422b7
Packit 1422b7
	if(i == *offs)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse everything up to a specific string.
Packit 1422b7
 * swisskid, 2015-01-21
Packit 1422b7
 */
Packit 1422b7
PARSER(StringTo)
Packit 1422b7
	const char *c;
Packit 1422b7
	char *toFind = NULL;
Packit 1422b7
	size_t i, j, k, m;
Packit 1422b7
	int chkstr;
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	assert(ed != NULL);
Packit 1422b7
	k = es_strlen(ed) - 1;
Packit 1422b7
	toFind = es_str2cstr(ed, NULL);
Packit 1422b7
	c = str;
Packit 1422b7
	i = *offs;
Packit 1422b7
	chkstr = 0;
Packit 1422b7
Packit 1422b7
	/* Total hunt for letter */
Packit 1422b7
	while(chkstr == 0 && i < strLen ) {
Packit 1422b7
	    i++;
Packit 1422b7
	    if(c[i] == toFind[0]) {
Packit 1422b7
		/* Found the first letter, now find the rest of the string */
Packit 1422b7
		j = 0;
Packit 1422b7
		m = i;
Packit 1422b7
		while(m < strLen && j < k ) {
Packit 1422b7
		    m++;
Packit 1422b7
		    j++;
Packit 1422b7
		    if(c[m] != toFind[j])
Packit 1422b7
			break;
Packit 1422b7
		    if (j == k)
Packit 1422b7
			chkstr = 1;
Packit 1422b7
		}
Packit 1422b7
	    }
Packit 1422b7
	}
Packit 1422b7
	if(i == *offs || i == strLen || c[i] != toFind[0])
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	if(toFind != NULL) free(toFind);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a alphabetic word.
Packit 1422b7
 * A alpha word is composed of characters for which isalpha returns true.
Packit 1422b7
 * The parser dones if there is no alpha character at all.
Packit 1422b7
 */
Packit 1422b7
PARSER(Alpha)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	/* search end of word */
Packit 1422b7
	while(i < strLen && isalpha(c[i]))
Packit 1422b7
		i++;
Packit 1422b7
Packit 1422b7
	if(i == *offs) {
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse everything up to a specific character.
Packit 1422b7
 * The character must be the only char inside extra data passed to the parser.
Packit 1422b7
 * It is a program error if strlen(ed) != 1. It is considered a format error if
Packit 1422b7
 * a) the to-be-parsed buffer is already positioned on the terminator character
Packit 1422b7
 * b) there is no terminator until the end of the buffer
Packit 1422b7
 * In those cases, the parsers declares itself as not being successful, in all
Packit 1422b7
 * other cases a string is extracted.
Packit 1422b7
 */
Packit 1422b7
PARSER(CharTo)
Packit 1422b7
	const char *c;
Packit 1422b7
	unsigned char cTerm;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	assert(es_strlen(ed) == 1);
Packit 1422b7
	cTerm = *(es_getBufAddr(ed));
Packit 1422b7
	c = str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	/* search end of word */
Packit 1422b7
	while(i < strLen && c[i] != cTerm)
Packit 1422b7
		i++;
Packit 1422b7
Packit 1422b7
	if(i == *offs || i == strLen || c[i] != cTerm)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse everything up to a specific character, or up to the end of string.
Packit 1422b7
 * The character must be the only char inside extra data passed to the parser.
Packit 1422b7
 * It is a program error if strlen(ed) != 1.
Packit 1422b7
 * This parser always returns success.
Packit 1422b7
 * By nature of the parser, it is required that end of string or the separator
Packit 1422b7
 * follows this field in rule.
Packit 1422b7
 */
Packit 1422b7
PARSER(CharSeparated)
Packit 1422b7
	const char *c;
Packit 1422b7
	unsigned char cTerm;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	assert(es_strlen(ed) == 1);
Packit 1422b7
	cTerm = *(es_getBufAddr(ed));
Packit 1422b7
	c = str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	/* search end of word */
Packit 1422b7
	while(i < strLen && c[i] != cTerm)
Packit 1422b7
		i++;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse yet-to-be-matched portion of string by re-applying
Packit 1422b7
 * top-level rules again.
Packit 1422b7
 */
Packit 1422b7
#define DEFAULT_REMAINING_FIELD_NAME "tail"
Packit 1422b7
Packit 1422b7
struct recursive_parser_data_s {
Packit 1422b7
	ln_ctx ctx;
Packit 1422b7
	char* remaining_field;
Packit 1422b7
	int free_ctx;
Packit 1422b7
};
Packit 1422b7
Packit 1422b7
PARSER(Recursive)
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
Packit 1422b7
	struct recursive_parser_data_s* pData = (struct recursive_parser_data_s*) node->parser_data;
Packit 1422b7
Packit 1422b7
	if (pData != NULL) {
Packit 1422b7
		int remaining_len = strLen - *offs;
Packit 1422b7
		const char *remaining_str = str + *offs;
Packit 1422b7
		json_object *unparsed = NULL;
Packit 1422b7
		CHKN(*value = json_object_new_object());
Packit 1422b7
Packit 1422b7
		ln_normalize(pData->ctx, remaining_str, remaining_len, value);
Packit 1422b7
Packit 1422b7
		if (json_object_object_get_ex(*value, UNPARSED_DATA_KEY, &unparsed)) {
Packit 1422b7
			json_object_put(*value);
Packit 1422b7
			*value = NULL;
Packit 1422b7
			*parsed = 0;
Packit 1422b7
		} else if (pData->remaining_field != NULL
Packit 1422b7
			&& json_object_object_get_ex(*value, pData->remaining_field, &unparsed)) {
Packit 1422b7
			*parsed = strLen - *offs - json_object_get_string_len(unparsed);
Packit 1422b7
			json_object_object_del(*value, pData->remaining_field);
Packit 1422b7
		} else {
Packit 1422b7
			*parsed = strLen - *offs;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
typedef ln_ctx (ctx_constructor)(ln_ctx, pcons_args_t*, const char*);
Packit 1422b7
Packit 1422b7
static void*
Packit 1422b7
_recursive_parser_data_constructor(ln_fieldList_t *node,
Packit 1422b7
	ln_ctx ctx,
Packit 1422b7
	int no_of_args,
Packit 1422b7
	int remaining_field_arg_idx,
Packit 1422b7
	int free_ctx, ctx_constructor *fn)
Packit 1422b7
{
Packit 1422b7
	int r = LN_BADCONFIG;
Packit 1422b7
	char* name = NULL;
Packit 1422b7
	struct recursive_parser_data_s *pData = NULL;
Packit 1422b7
	pcons_args_t *args = NULL;
Packit 1422b7
	CHKN(name = es_str2cstr(node->name, NULL));
Packit 1422b7
	CHKN(pData = calloc(1, sizeof(struct recursive_parser_data_s)));
Packit 1422b7
	pData->free_ctx = free_ctx;
Packit 1422b7
	pData->remaining_field = NULL;
Packit 1422b7
	CHKN(args = pcons_args(node->raw_data, no_of_args));
Packit 1422b7
	CHKN(pData->ctx = fn(ctx, args, name));
Packit 1422b7
	CHKN(pData->remaining_field = pcons_arg_copy(args, remaining_field_arg_idx, DEFAULT_REMAINING_FIELD_NAME));
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	if (r != 0) {
Packit 1422b7
		if (name == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for recursive/descent field name");
Packit 1422b7
		else if (pData == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for parser-data for field: %s", name);
Packit 1422b7
		else if (args == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for argument-parsing for field: %s", name);
Packit 1422b7
		else if (pData->ctx == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "recursive/descent normalizer context creation "
Packit 1422b7
		"doneed for field: %s", name);
Packit 1422b7
		else if (pData->remaining_field == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for remaining-field name for "
Packit 1422b7
				"recursive/descent field: %s", name);
Packit 1422b7
Packit 1422b7
		recursive_parser_data_destructor((void**) &pData);
Packit 1422b7
	}
Packit 1422b7
	free(name);
Packit 1422b7
	free_pcons_args(&args);
Packit 1422b7
	return pData;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static ln_ctx identity_recursive_parse_ctx_constructor(ln_ctx parent_ctx,
Packit 1422b7
	__attribute__((unused)) pcons_args_t* args,
Packit 1422b7
	__attribute__((unused)) const char* field_name) {
Packit 1422b7
	return parent_ctx;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void* recursive_parser_data_constructor(ln_fieldList_t *node, ln_ctx ctx) {
Packit 1422b7
	return _recursive_parser_data_constructor(node, ctx, 1, 0, 0, identity_recursive_parse_ctx_constructor);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static ln_ctx child_recursive_parse_ctx_constructor(ln_ctx parent_ctx, pcons_args_t* args, const char* field_name) {
Packit 1422b7
	int r = LN_BADCONFIG;
Packit 1422b7
	const char* rb = NULL;
Packit 1422b7
	ln_ctx ctx = NULL;
Packit 1422b7
	pcons_unescape_arg(args, 0);
Packit 1422b7
	CHKN(rb = pcons_arg(args, 0, NULL));
Packit 1422b7
	CHKN(ctx = ln_v1_inherittedCtx(parent_ctx));
Packit 1422b7
	CHKR(ln_v1_loadSamples(ctx, rb));
Packit 1422b7
done:
Packit 1422b7
	if (r != 0) {
Packit 1422b7
		if (rb == NULL)
Packit 1422b7
			ln_dbgprintf(parent_ctx, "file-name for descent rulebase not provided for field: %s",
Packit 1422b7
				field_name);
Packit 1422b7
		else if (ctx == NULL)
Packit 1422b7
			ln_dbgprintf(parent_ctx, "couldn't allocate memory to create descent-field normalizer "
Packit 1422b7
				"context for field: %s", field_name);
Packit 1422b7
		else
Packit 1422b7
			ln_dbgprintf(parent_ctx, "couldn't load samples into descent context for field: %s",
Packit 1422b7
				field_name);
Packit 1422b7
		if (ctx != NULL) ln_exitCtx(ctx);
Packit 1422b7
		ctx = NULL;
Packit 1422b7
	}
Packit 1422b7
	return ctx;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void* descent_parser_data_constructor(ln_fieldList_t *node, ln_ctx ctx) {
Packit 1422b7
	return _recursive_parser_data_constructor(node, ctx, 2, 1, 1, child_recursive_parse_ctx_constructor);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void recursive_parser_data_destructor(void** dataPtr) {
Packit 1422b7
	if (*dataPtr != NULL) {
Packit 1422b7
		struct recursive_parser_data_s *pData = (struct recursive_parser_data_s*) *dataPtr;
Packit 1422b7
		if (pData->free_ctx && pData->ctx != NULL) {
Packit 1422b7
			ln_exitCtx(pData->ctx);
Packit 1422b7
			pData->ctx = NULL;
Packit 1422b7
		}
Packit 1422b7
		if (pData->remaining_field != NULL) free(pData->remaining_field);
Packit 1422b7
		free(pData);
Packit 1422b7
		*dataPtr = NULL;
Packit 1422b7
	}
Packit 1422b7
};
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse string tokenized by given char-sequence
Packit 1422b7
 * The sequence may appear 0 or more times, but zero times means 1 token.
Packit 1422b7
 * NOTE: its not 0 tokens, but 1 token.
Packit 1422b7
 *
Packit 1422b7
 * The token found is parsed according to the field-type provided after
Packit 1422b7
 *  tokenizer char-seq.
Packit 1422b7
 */
Packit 1422b7
#define DEFAULT_MATCHED_FIELD_NAME "default"
Packit 1422b7
Packit 1422b7
struct tokenized_parser_data_s {
Packit 1422b7
	es_str_t *tok_str;
Packit 1422b7
	ln_ctx ctx;
Packit 1422b7
	char *remaining_field;
Packit 1422b7
	int use_default_field;
Packit 1422b7
	int free_ctx;
Packit 1422b7
};
Packit 1422b7
Packit 1422b7
typedef struct tokenized_parser_data_s tokenized_parser_data_t;
Packit 1422b7
Packit 1422b7
PARSER(Tokenized)
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
Packit 1422b7
	tokenized_parser_data_t *pData = (tokenized_parser_data_t*) node->parser_data;
Packit 1422b7
Packit 1422b7
	if (pData != NULL ) {
Packit 1422b7
		json_object *json_p = NULL;
Packit 1422b7
		if (pData->use_default_field) CHKN(json_p = json_object_new_object());
Packit 1422b7
		json_object *matches = NULL;
Packit 1422b7
		CHKN(matches = json_object_new_array());
Packit 1422b7
Packit 1422b7
		int remaining_len = strLen - *offs;
Packit 1422b7
		const char *remaining_str = str + *offs;
Packit 1422b7
		json_object *remaining = NULL;
Packit 1422b7
		json_object *match = NULL;
Packit 1422b7
Packit 1422b7
		while (remaining_len > 0) {
Packit 1422b7
			if (! pData->use_default_field) {
Packit 1422b7
				json_object_put(json_p);
Packit 1422b7
				json_p = json_object_new_object();
Packit 1422b7
			} /*TODO: handle null condition gracefully*/
Packit 1422b7
Packit 1422b7
			ln_normalize(pData->ctx, remaining_str, remaining_len, &json_p);
Packit 1422b7
Packit 1422b7
			if (remaining) json_object_put(remaining);
Packit 1422b7
Packit 1422b7
			if (pData->use_default_field
Packit 1422b7
				&& json_object_object_get_ex(json_p, DEFAULT_MATCHED_FIELD_NAME, &match)) {
Packit 1422b7
				json_object_array_add(matches, json_object_get(match));
Packit 1422b7
			} else if (! (pData->use_default_field
Packit 1422b7
				|| json_object_object_get_ex(json_p, UNPARSED_DATA_KEY, &match))) {
Packit 1422b7
				json_object_array_add(matches, json_object_get(json_p));
Packit 1422b7
			} else {
Packit 1422b7
				if (json_object_array_length(matches) > 0) {
Packit 1422b7
					remaining_len += es_strlen(pData->tok_str);
Packit 1422b7
					break;
Packit 1422b7
				} else {
Packit 1422b7
					json_object_put(json_p);
Packit 1422b7
					json_object_put(matches);
Packit 1422b7
					FAIL(LN_WRONGPARSER);
Packit 1422b7
				}
Packit 1422b7
			}
Packit 1422b7
Packit 1422b7
			if (json_object_object_get_ex(json_p, pData->remaining_field, &remaining)) {
Packit 1422b7
				remaining_len = json_object_get_string_len(remaining);
Packit 1422b7
				if (remaining_len > 0) {
Packit 1422b7
					remaining_str = json_object_get_string(json_object_get(remaining));
Packit 1422b7
					json_object_object_del(json_p, pData->remaining_field);
Packit 1422b7
					if (es_strbufcmp(pData->tok_str, (const unsigned char *)remaining_str,
Packit 1422b7
					es_strlen(pData->tok_str))) {
Packit 1422b7
						json_object_put(remaining);
Packit 1422b7
						break;
Packit 1422b7
					} else {
Packit 1422b7
						remaining_str += es_strlen(pData->tok_str);
Packit 1422b7
						remaining_len -= es_strlen(pData->tok_str);
Packit 1422b7
					}
Packit 1422b7
				}
Packit 1422b7
			} else {
Packit 1422b7
				remaining_len = 0;
Packit 1422b7
				break;
Packit 1422b7
			}
Packit 1422b7
Packit 1422b7
			if (pData->use_default_field) json_object_object_del(json_p, DEFAULT_MATCHED_FIELD_NAME);
Packit 1422b7
		}
Packit 1422b7
		json_object_put(json_p);
Packit 1422b7
Packit 1422b7
		/* success, persist */
Packit 1422b7
		*parsed = (strLen - *offs) - remaining_len;
Packit 1422b7
		*value =  matches;
Packit 1422b7
	} else {
Packit 1422b7
		FAIL(LN_BADPARSERSTATE);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void tokenized_parser_data_destructor(void** dataPtr) {
Packit 1422b7
	tokenized_parser_data_t *data = (tokenized_parser_data_t*) *dataPtr;
Packit 1422b7
	if (data->tok_str != NULL) es_deleteStr(data->tok_str);
Packit 1422b7
	if (data->free_ctx && (data->ctx != NULL)) ln_exitCtx(data->ctx);
Packit 1422b7
	if (data->remaining_field != NULL) free(data->remaining_field);
Packit 1422b7
	free(data);
Packit 1422b7
	*dataPtr = NULL;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static void load_generated_parser_samples(ln_ctx ctx,
Packit 1422b7
	const char* const field_descr, const int field_descr_len,
Packit 1422b7
	const char* const suffix, const int length) {
Packit 1422b7
	static const char* const RULE_PREFIX = "rule=:%"DEFAULT_MATCHED_FIELD_NAME":";/*TODO: extract nice constants*/
Packit 1422b7
	static const int RULE_PREFIX_LEN = 15;
Packit 1422b7
Packit 1422b7
	char *sample_str = NULL;
Packit 1422b7
	es_str_t *field_decl = es_newStrFromCStr(RULE_PREFIX, RULE_PREFIX_LEN);
Packit 1422b7
	if (! field_decl) goto free;
Packit 1422b7
Packit 1422b7
	if (es_addBuf(&field_decl, field_descr, field_descr_len)
Packit 1422b7
		|| es_addBuf(&field_decl, "%", 1)
Packit 1422b7
		|| es_addBuf(&field_decl, suffix, length)) {
Packit 1422b7
		ln_dbgprintf(ctx, "couldn't prepare field for tokenized field-picking: '%s'", field_descr);
Packit 1422b7
		goto free;
Packit 1422b7
	}
Packit 1422b7
	sample_str = es_str2cstr(field_decl, NULL);
Packit 1422b7
	if (! sample_str) {
Packit 1422b7
		ln_dbgprintf(ctx, "couldn't prepare sample-string for: '%s'", field_descr);
Packit 1422b7
		goto free;
Packit 1422b7
	}
Packit 1422b7
	ln_v1_loadSample(ctx, sample_str);
Packit 1422b7
free:
Packit 1422b7
	if (sample_str) free(sample_str);
Packit 1422b7
	if (field_decl) es_deleteStr(field_decl);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static ln_ctx generate_context_with_field_as_prefix(ln_ctx parent, const char* field_descr, int field_descr_len) {
Packit 1422b7
	int r = LN_BADCONFIG;
Packit 1422b7
	const char* remaining_field = "%"DEFAULT_REMAINING_FIELD_NAME":rest%";
Packit 1422b7
	ln_ctx ctx = NULL;
Packit 1422b7
	CHKN(ctx = ln_v1_inherittedCtx(parent));
Packit 1422b7
	load_generated_parser_samples(ctx, field_descr, field_descr_len, remaining_field, strlen(remaining_field));
Packit 1422b7
	load_generated_parser_samples(ctx, field_descr, field_descr_len, "", 0);
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	if (r != 0) {
Packit 1422b7
		ln_exitCtx(ctx);
Packit 1422b7
		ctx = NULL;
Packit 1422b7
	}
Packit 1422b7
	return ctx;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static ln_fieldList_t* parse_tokenized_content_field(ln_ctx ctx, const char* field_descr, size_t field_descr_len) {
Packit 1422b7
	es_str_t* tmp = NULL;
Packit 1422b7
	es_str_t* descr = NULL;
Packit 1422b7
	ln_fieldList_t *node = NULL;
Packit 1422b7
	int r = 0;
Packit 1422b7
	CHKN(tmp = es_newStr(80));
Packit 1422b7
	CHKN(descr = es_newStr(80));
Packit 1422b7
	const char* field_prefix = "%" DEFAULT_MATCHED_FIELD_NAME ":";
Packit 1422b7
	CHKR(es_addBuf(&descr, field_prefix, strlen(field_prefix)));
Packit 1422b7
	CHKR(es_addBuf(&descr, field_descr, field_descr_len));
Packit 1422b7
	CHKR(es_addChar(&descr, '%'));
Packit 1422b7
	es_size_t offset = 0;
Packit 1422b7
	CHKN(node = ln_v1_parseFieldDescr(ctx, descr, &offset, &tmp, &r);;
Packit 1422b7
	if (offset != es_strlen(descr)) FAIL(LN_BADPARSERSTATE);
Packit 1422b7
done:
Packit 1422b7
	if (r != 0) {
Packit 1422b7
		if (node != NULL) ln_deletePTreeNode(node);
Packit 1422b7
		node = NULL;
Packit 1422b7
	}
Packit 1422b7
	if (descr != NULL) es_deleteStr(descr);
Packit 1422b7
	if (tmp != NULL) es_deleteStr(tmp);
Packit 1422b7
	return node;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void* tokenized_parser_data_constructor(ln_fieldList_t *node, ln_ctx ctx) {
Packit 1422b7
	int r = LN_BADCONFIG;
Packit 1422b7
	char* name = es_str2cstr(node->name, NULL);
Packit 1422b7
	pcons_args_t *args = NULL;
Packit 1422b7
	tokenized_parser_data_t *pData = NULL;
Packit 1422b7
	const char *field_descr = NULL;
Packit 1422b7
	ln_fieldList_t* field = NULL;
Packit 1422b7
	const char *tok = NULL;
Packit 1422b7
Packit 1422b7
	CHKN(args = pcons_args(node->raw_data, 2));
Packit 1422b7
Packit 1422b7
	CHKN(pData = calloc(1, sizeof(tokenized_parser_data_t)));
Packit 1422b7
	pcons_unescape_arg(args, 0);
Packit 1422b7
	CHKN(tok = pcons_arg(args, 0, NULL));
Packit 1422b7
	CHKN(pData->tok_str = es_newStrFromCStr(tok, strlen(tok)));
Packit 1422b7
	es_unescapeStr(pData->tok_str);
Packit 1422b7
	CHKN(field_descr = pcons_arg(args, 1, NULL));
Packit 1422b7
	const int field_descr_len = strlen(field_descr);
Packit 1422b7
	pData->free_ctx = 1;
Packit 1422b7
	CHKN(field = parse_tokenized_content_field(ctx, field_descr, field_descr_len));
Packit 1422b7
	if (field->parser == ln_parseRecursive) {
Packit 1422b7
		pData->use_default_field = 0;
Packit 1422b7
		struct recursive_parser_data_s *dat = (struct recursive_parser_data_s*) field->parser_data;
Packit 1422b7
		if (dat != NULL) {
Packit 1422b7
			CHKN(pData->remaining_field = strdup(dat->remaining_field));
Packit 1422b7
			pData->free_ctx = dat->free_ctx;
Packit 1422b7
			pData->ctx = dat->ctx;
Packit 1422b7
			dat->free_ctx = 0;
Packit 1422b7
		}
Packit 1422b7
	} else {
Packit 1422b7
		pData->use_default_field = 1;
Packit 1422b7
		CHKN(pData->ctx = generate_context_with_field_as_prefix(ctx, field_descr, field_descr_len));
Packit 1422b7
	}
Packit 1422b7
	if (pData->remaining_field == NULL) CHKN(pData->remaining_field = strdup(DEFAULT_REMAINING_FIELD_NAME));
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	if (r != 0) {
Packit 1422b7
		if (name == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for tokenized-field name");
Packit 1422b7
		else if (args == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for argument-parsing for field: %s", name);
Packit 1422b7
		else if (pData == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for parser-data for field: %s", name);
Packit 1422b7
		else if (tok == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "token-separator not provided for field: %s", name);
Packit 1422b7
		else if (pData->tok_str == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for token-separator "
Packit 1422b7
			"for field: %s", name);
Packit 1422b7
		else if (field_descr == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "field-type not provided for field: %s", name);
Packit 1422b7
		else if (field == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't resolve single-token field-type for tokenized field: %s", name);
Packit 1422b7
		else if (pData->ctx == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't initialize normalizer-context for field: %s", name);
Packit 1422b7
		else if (pData->remaining_field == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for "
Packit 1422b7
				"remaining-field-name for field: %s", name);
Packit 1422b7
		if (pData) tokenized_parser_data_destructor((void**) &pData);
Packit 1422b7
	}
Packit 1422b7
	if (name != NULL) free(name);
Packit 1422b7
	if (field != NULL) ln_deletePTreeNode(field);
Packit 1422b7
	if (args) free_pcons_args(&args);
Packit 1422b7
	return pData;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
#ifdef FEATURE_REGEXP
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse string matched by provided posix extended regex.
Packit 1422b7
 *
Packit 1422b7
 * Please note that using regex field in most cases will be
Packit 1422b7
 * significantly slower than other field-types.
Packit 1422b7
 */
Packit 1422b7
struct regex_parser_data_s {
Packit 1422b7
	pcre *re;
Packit 1422b7
	int consume_group;
Packit 1422b7
	int return_group;
Packit 1422b7
	int max_groups;
Packit 1422b7
};
Packit 1422b7
Packit 1422b7
PARSER(Regex)
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	unsigned int* ovector = NULL;
Packit 1422b7
Packit 1422b7
	struct regex_parser_data_s *pData = (struct regex_parser_data_s*) node->parser_data;
Packit 1422b7
	if (pData != NULL) {
Packit 1422b7
		ovector = calloc(pData->max_groups, sizeof(unsigned int) * 3);
Packit 1422b7
		if (ovector == NULL) FAIL(LN_NOMEM);
Packit 1422b7
Packit 1422b7
		int result = pcre_exec(pData->re, NULL,	str, strLen, *offs, 0, (int*) ovector, pData->max_groups * 3);
Packit 1422b7
		if (result == 0) result = pData->max_groups;
Packit 1422b7
		if (result > pData->consume_group) {
Packit 1422b7
			/*please check 'man 3 pcreapi' for cryptic '2 * n' and '2 * n + 1' magic*/
Packit 1422b7
			if (ovector[2 * pData->consume_group] == *offs) {
Packit 1422b7
				*parsed = ovector[2 * pData->consume_group + 1] - ovector[2 * pData->consume_group];
Packit 1422b7
				if (pData->consume_group != pData->return_group) {
Packit 1422b7
					char* val = NULL;
Packit 1422b7
					if((val = strndup(str + ovector[2 * pData->return_group],
Packit 1422b7
						ovector[2 * pData->return_group + 1] -
Packit 1422b7
						ovector[2 * pData->return_group])) == NULL) {
Packit 1422b7
						free(ovector);
Packit 1422b7
						FAIL(LN_NOMEM);
Packit 1422b7
					}
Packit 1422b7
					*value = json_object_new_string(val);
Packit 1422b7
					free(val);
Packit 1422b7
					if (*value == NULL) {
Packit 1422b7
						free(ovector);
Packit 1422b7
						FAIL(LN_NOMEM);
Packit 1422b7
					}
Packit 1422b7
				}
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
		free(ovector);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static const char* regex_parser_configure_consume_and_return_group(pcons_args_t* args,
Packit 1422b7
struct regex_parser_data_s *pData) {
Packit 1422b7
	const char* consume_group_parse_error = "couldn't parse consume-group number";
Packit 1422b7
	const char* return_group_parse_error = "couldn't parse return-group number";
Packit 1422b7
Packit 1422b7
	char* tmp = NULL;
Packit 1422b7
Packit 1422b7
	const char* consume_grp_str = NULL;
Packit 1422b7
	const char* return_grp_str = NULL;
Packit 1422b7
Packit 1422b7
	if ((consume_grp_str = pcons_arg(args, 1, "0")) == NULL ||
Packit 1422b7
		strlen(consume_grp_str) == 0) return consume_group_parse_error;
Packit 1422b7
	if ((return_grp_str = pcons_arg(args, 2, consume_grp_str)) == NULL ||
Packit 1422b7
		strlen(return_grp_str) == 0) return return_group_parse_error;
Packit 1422b7
Packit 1422b7
	errno = 0;
Packit 1422b7
	pData->consume_group = strtol(consume_grp_str, &tmp, 10);
Packit 1422b7
	if (errno != 0 || strlen(tmp) != 0) return consume_group_parse_error;
Packit 1422b7
Packit 1422b7
	pData->return_group = strtol(return_grp_str, &tmp, 10);
Packit 1422b7
	if (errno != 0 || strlen(tmp) != 0) return return_group_parse_error;
Packit 1422b7
Packit 1422b7
	return NULL;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void* regex_parser_data_constructor(ln_fieldList_t *node, ln_ctx ctx) {
Packit 1422b7
	int r = LN_BADCONFIG;
Packit 1422b7
	char* exp = NULL;
Packit 1422b7
	const char* grp_parse_err = NULL;
Packit 1422b7
	pcons_args_t* args = NULL;
Packit 1422b7
	char* name = NULL;
Packit 1422b7
	struct regex_parser_data_s *pData = NULL;
Packit 1422b7
	const char *unescaped_exp = NULL;
Packit 1422b7
	const char *error = NULL;
Packit 1422b7
	int erroffset = 0;
Packit 1422b7
Packit 1422b7
Packit 1422b7
	CHKN(name = es_str2cstr(node->name, NULL));
Packit 1422b7
Packit 1422b7
	if (! ctx->opts & LN_CTXOPT_ALLOW_REGEX) FAIL(LN_BADCONFIG);
Packit 1422b7
	CHKN(pData = malloc(sizeof(struct regex_parser_data_s)));
Packit 1422b7
	pData->re = NULL;
Packit 1422b7
Packit 1422b7
	CHKN(args = pcons_args(node->raw_data, 3));
Packit 1422b7
	pData->consume_group = pData->return_group = 0;
Packit 1422b7
	CHKN(unescaped_exp = pcons_arg(args, 0, NULL));
Packit 1422b7
	pcons_unescape_arg(args, 0);
Packit 1422b7
	CHKN(exp = pcons_arg_copy(args, 0, NULL));
Packit 1422b7
Packit 1422b7
	if ((grp_parse_err = regex_parser_configure_consume_and_return_group(args, pData)) != NULL)
Packit 1422b7
		FAIL(LN_BADCONFIG);
Packit 1422b7
Packit 1422b7
	CHKN(pData->re = pcre_compile(exp, 0, &error, &erroffset, NULL));
Packit 1422b7
Packit 1422b7
	pData->max_groups = ((pData->consume_group > pData->return_group) ? pData->consume_group :
Packit 1422b7
					pData->return_group) + 1;
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	if (r != 0) {
Packit 1422b7
		if (name == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory regex-field name");
Packit 1422b7
		else if (! ctx->opts & LN_CTXOPT_ALLOW_REGEX)
Packit 1422b7
			ln_dbgprintf(ctx, "regex support is not enabled for: '%s' "
Packit 1422b7
				"(please check lognorm context initialization)", name);
Packit 1422b7
		else if (pData == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for parser-data for field: %s", name);
Packit 1422b7
		else if (args == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for argument-parsing for field: %s", name);
Packit 1422b7
		else if (unescaped_exp == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "regular-expression missing for field: '%s'", name);
Packit 1422b7
		else if (exp == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for regex-string for field: '%s'", name);
Packit 1422b7
		else if (grp_parse_err != NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "%s for: '%s'", grp_parse_err, name);
Packit 1422b7
		else if (pData->re == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't compile regex(encountered error '%s' at char '%d' in pattern) "
Packit 1422b7
				 "for regex-matched field: '%s'", error, erroffset, name);
Packit 1422b7
		regex_parser_data_destructor((void**)&pData);
Packit 1422b7
	}
Packit 1422b7
	if (exp != NULL) free(exp);
Packit 1422b7
	if (args != NULL) free_pcons_args(&args);
Packit 1422b7
	if (name != NULL) free(name);
Packit 1422b7
	return pData;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void regex_parser_data_destructor(void** dataPtr) {
Packit 1422b7
	if ((*dataPtr) != NULL) {
Packit 1422b7
		struct regex_parser_data_s *pData = (struct regex_parser_data_s*) *dataPtr;
Packit 1422b7
		if (pData->re != NULL) pcre_free(pData->re);
Packit 1422b7
		free(pData);
Packit 1422b7
		*dataPtr = NULL;
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
#endif
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse yet-to-be-matched portion of string by re-applying
Packit 1422b7
 * top-level rules again.
Packit 1422b7
 */
Packit 1422b7
typedef enum interpret_type {
Packit 1422b7
	/* If you change this, be sure to update json_type_to_name() too */
Packit 1422b7
	it_b10int,
Packit 1422b7
	it_b16int,
Packit 1422b7
	it_floating_pt,
Packit 1422b7
	it_boolean
Packit 1422b7
} interpret_type;
Packit 1422b7
Packit 1422b7
struct interpret_parser_data_s {
Packit 1422b7
	ln_ctx ctx;
Packit 1422b7
	enum interpret_type intrprt;
Packit 1422b7
};
Packit 1422b7
Packit 1422b7
static json_object* interpret_as_int(json_object *value, int base) {
Packit 1422b7
	if (json_object_is_type(value, json_type_string)) {
Packit 1422b7
		return json_object_new_int64(strtol(json_object_get_string(value), NULL, base));
Packit 1422b7
	} else if (json_object_is_type(value, json_type_int)) {
Packit 1422b7
		return value;
Packit 1422b7
	} else {
Packit 1422b7
		return NULL;
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static json_object* interpret_as_double(json_object *value) {
Packit 1422b7
	double val = json_object_get_double(value);
Packit 1422b7
	return json_object_new_double(val);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static json_object* interpret_as_boolean(json_object *value) {
Packit 1422b7
	json_bool val;
Packit 1422b7
	if (json_object_is_type(value, json_type_string)) {
Packit 1422b7
		const char* str = json_object_get_string(value);
Packit 1422b7
		val = (strcasecmp(str, "false") == 0 || strcasecmp(str, "no") == 0) ? 0 : 1;
Packit 1422b7
	} else {
Packit 1422b7
		val = json_object_get_boolean(value);
Packit 1422b7
	}
Packit 1422b7
	return json_object_new_boolean(val);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static int reinterpret_value(json_object **value, enum interpret_type to_type) {
Packit 1422b7
	switch(to_type) {
Packit 1422b7
	case it_b10int:
Packit 1422b7
		*value = interpret_as_int(*value, 10);
Packit 1422b7
		break;
Packit 1422b7
	case it_b16int:
Packit 1422b7
		*value = interpret_as_int(*value, 16);
Packit 1422b7
		break;
Packit 1422b7
	case it_floating_pt:
Packit 1422b7
		*value = interpret_as_double(*value);
Packit 1422b7
		break;
Packit 1422b7
	case it_boolean:
Packit 1422b7
		*value = interpret_as_boolean(*value);
Packit 1422b7
		break;
Packit 1422b7
	default:
Packit 1422b7
		return 0;
Packit 1422b7
	}
Packit 1422b7
	return 1;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
PARSER(Interpret)
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	json_object *unparsed = NULL;
Packit 1422b7
	json_object *parsed_raw = NULL;
Packit 1422b7
Packit 1422b7
	struct interpret_parser_data_s* pData = (struct interpret_parser_data_s*) node->parser_data;
Packit 1422b7
Packit 1422b7
	if (pData != NULL) {
Packit 1422b7
		int remaining_len = strLen - *offs;
Packit 1422b7
		const char *remaining_str = str + *offs;
Packit 1422b7
Packit 1422b7
		CHKN(parsed_raw = json_object_new_object());
Packit 1422b7
Packit 1422b7
		ln_normalize(pData->ctx, remaining_str, remaining_len, &parsed_raw);
Packit 1422b7
Packit 1422b7
		if (json_object_object_get_ex(parsed_raw, UNPARSED_DATA_KEY, NULL)) {
Packit 1422b7
			*parsed = 0;
Packit 1422b7
		} else {
Packit 1422b7
			json_object_object_get_ex(parsed_raw, DEFAULT_MATCHED_FIELD_NAME, value);
Packit 1422b7
			json_object_object_get_ex(parsed_raw, DEFAULT_REMAINING_FIELD_NAME, &unparsed);
Packit 1422b7
			if (reinterpret_value(value, pData->intrprt)) {
Packit 1422b7
				*parsed = strLen - *offs - json_object_get_string_len(unparsed);
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
		json_object_put(parsed_raw);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void* interpret_parser_data_constructor(ln_fieldList_t *node, ln_ctx ctx) {
Packit 1422b7
	int r = LN_BADCONFIG;
Packit 1422b7
	char* name = NULL;
Packit 1422b7
	struct interpret_parser_data_s *pData = NULL;
Packit 1422b7
	pcons_args_t *args = NULL;
Packit 1422b7
	int bad_interpret = 0;
Packit 1422b7
	const char* type_str = NULL;
Packit 1422b7
	const char *field_type = NULL;
Packit 1422b7
	CHKN(name = es_str2cstr(node->name, NULL));
Packit 1422b7
	CHKN(pData = calloc(1, sizeof(struct interpret_parser_data_s)));
Packit 1422b7
	CHKN(args = pcons_args(node->raw_data, 2));
Packit 1422b7
	CHKN(type_str = pcons_arg(args, 0, NULL));
Packit 1422b7
	if (strcmp(type_str, "int") == 0 || strcmp(type_str, "base10int") == 0) {
Packit 1422b7
		pData->intrprt = it_b10int;
Packit 1422b7
	} else if (strcmp(type_str, "base16int") == 0) {
Packit 1422b7
		pData->intrprt = it_b16int;
Packit 1422b7
	} else if (strcmp(type_str, "float") == 0) {
Packit 1422b7
		pData->intrprt = it_floating_pt;
Packit 1422b7
	} else if (strcmp(type_str, "bool") == 0) {
Packit 1422b7
		pData->intrprt = it_boolean;
Packit 1422b7
	} else {
Packit 1422b7
		bad_interpret = 1;
Packit 1422b7
		FAIL(LN_BADCONFIG);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	CHKN(field_type = pcons_arg(args, 1, NULL));
Packit 1422b7
	CHKN(pData->ctx = generate_context_with_field_as_prefix(ctx, field_type, strlen(field_type)));
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	if (r != 0) {
Packit 1422b7
		if (name == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for interpret-field name");
Packit 1422b7
		else if (pData == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for parser-data for field: %s", name);
Packit 1422b7
		else if (args == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for argument-parsing for field: %s", name);
Packit 1422b7
		else if (type_str == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "no type provided for interpretation of field: %s", name);
Packit 1422b7
		else if (bad_interpret != 0)
Packit 1422b7
			ln_dbgprintf(ctx, "interpretation to unknown type '%s' requested for field: %s",
Packit 1422b7
				type_str, name);
Packit 1422b7
		else if (field_type == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "field-type to actually match the content not provided for "
Packit 1422b7
				"field: %s", name);
Packit 1422b7
		else if (pData->ctx == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't instantiate the normalizer context for matching "
Packit 1422b7
				"field: %s", name);
Packit 1422b7
Packit 1422b7
		interpret_parser_data_destructor((void**) &pData);
Packit 1422b7
	}
Packit 1422b7
	free(name);
Packit 1422b7
	free_pcons_args(&args);
Packit 1422b7
	return pData;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void interpret_parser_data_destructor(void** dataPtr) {
Packit 1422b7
	if (*dataPtr != NULL) {
Packit 1422b7
		struct interpret_parser_data_s *pData = (struct interpret_parser_data_s*) *dataPtr;
Packit 1422b7
		if (pData->ctx != NULL) ln_exitCtx(pData->ctx);
Packit 1422b7
		free(pData);
Packit 1422b7
		*dataPtr = NULL;
Packit 1422b7
	}
Packit 1422b7
};
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse suffixed char-sequence, where suffix is one of many possible suffixes.
Packit 1422b7
 */
Packit 1422b7
struct suffixed_parser_data_s {
Packit 1422b7
	int nsuffix;
Packit 1422b7
	int *suffix_offsets;
Packit 1422b7
	int *suffix_lengths;
Packit 1422b7
	char* suffixes_str;
Packit 1422b7
	ln_ctx ctx;
Packit 1422b7
	char* value_field_name;
Packit 1422b7
	char* suffix_field_name;
Packit 1422b7
};
Packit 1422b7
Packit 1422b7
PARSER(Suffixed) {
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	json_object *unparsed = NULL;
Packit 1422b7
	json_object *parsed_raw = NULL;
Packit 1422b7
	json_object *parsed_value = NULL;
Packit 1422b7
	json_object *result = NULL;
Packit 1422b7
	json_object *suffix = NULL;
Packit 1422b7
Packit 1422b7
	struct suffixed_parser_data_s *pData = (struct suffixed_parser_data_s*) node->parser_data;
Packit 1422b7
Packit 1422b7
	if (pData != NULL) {
Packit 1422b7
		int remaining_len = strLen - *offs;
Packit 1422b7
		const char *remaining_str = str + *offs;
Packit 1422b7
		int i;
Packit 1422b7
Packit 1422b7
		CHKN(parsed_raw = json_object_new_object());
Packit 1422b7
Packit 1422b7
		ln_normalize(pData->ctx, remaining_str, remaining_len, &parsed_raw);
Packit 1422b7
Packit 1422b7
		if (json_object_object_get_ex(parsed_raw, UNPARSED_DATA_KEY, NULL)) {
Packit 1422b7
			*parsed = 0;
Packit 1422b7
		} else {
Packit 1422b7
			json_object_object_get_ex(parsed_raw, DEFAULT_MATCHED_FIELD_NAME, &parsed_value);
Packit 1422b7
			json_object_object_get_ex(parsed_raw, DEFAULT_REMAINING_FIELD_NAME, &unparsed);
Packit 1422b7
		const char* unparsed_frag = json_object_get_string(unparsed);
Packit 1422b7
			for(i = 0; i < pData->nsuffix; i++) {
Packit 1422b7
		const char* possible_suffix = pData->suffixes_str + pData->suffix_offsets[i];
Packit 1422b7
				int len = pData->suffix_lengths[i];
Packit 1422b7
				if (strncmp(possible_suffix, unparsed_frag, len) == 0) {
Packit 1422b7
				CHKN(result = json_object_new_object());
Packit 1422b7
				CHKN(suffix = json_object_new_string(possible_suffix));
Packit 1422b7
					json_object_get(parsed_value);
Packit 1422b7
				json_object_object_add(result, pData->value_field_name, parsed_value);
Packit 1422b7
				json_object_object_add(result, pData->suffix_field_name, suffix);
Packit 1422b7
					*parsed = strLen - *offs - json_object_get_string_len(unparsed) + len;
Packit 1422b7
				break;
Packit 1422b7
				}
Packit 1422b7
			}
Packit 1422b7
			if (result != NULL) {
Packit 1422b7
				*value = result;
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
FAILParser
Packit 1422b7
	if (r != 0) {
Packit 1422b7
		if (result != NULL) json_object_put(result);
Packit 1422b7
	}
Packit 1422b7
	if (parsed_raw != NULL) json_object_put(parsed_raw);
Packit 1422b7
} ENDFailParser
Packit 1422b7
Packit 1422b7
static struct suffixed_parser_data_s* _suffixed_parser_data_constructor(ln_fieldList_t *node,
Packit 1422b7
	ln_ctx ctx,
Packit 1422b7
	es_str_t* raw_args,
Packit 1422b7
	const char* value_field,
Packit 1422b7
	const char* suffix_field) {
Packit 1422b7
	int r = LN_BADCONFIG;
Packit 1422b7
	pcons_args_t* args = NULL;
Packit 1422b7
	char* name = NULL;
Packit 1422b7
	struct suffixed_parser_data_s *pData = NULL;
Packit 1422b7
	const char *escaped_tokenizer = NULL;
Packit 1422b7
	const char *uncopied_suffixes_str = NULL;
Packit 1422b7
	const char *tokenizer = NULL;
Packit 1422b7
	char *suffixes_str = NULL;
Packit 1422b7
	const char *field_type = NULL;
Packit 1422b7
Packit 1422b7
	char *tok_saveptr = NULL;
Packit 1422b7
	char *tok_input = NULL;
Packit 1422b7
	int i = 0;
Packit 1422b7
	char *tok = NULL;
Packit 1422b7
Packit 1422b7
	CHKN(name = es_str2cstr(node->name, NULL));
Packit 1422b7
Packit 1422b7
	CHKN(pData = calloc(1, sizeof(struct suffixed_parser_data_s)));
Packit 1422b7
Packit 1422b7
	if (value_field == NULL) value_field = "value";
Packit 1422b7
	if (suffix_field == NULL) suffix_field = "suffix";
Packit 1422b7
	pData->value_field_name = strdup(value_field);
Packit 1422b7
	pData->suffix_field_name = strdup(suffix_field);
Packit 1422b7
Packit 1422b7
	CHKN(args = pcons_args(raw_args, 3));
Packit 1422b7
	CHKN(escaped_tokenizer = pcons_arg(args, 0, NULL));
Packit 1422b7
	pcons_unescape_arg(args, 0);
Packit 1422b7
	CHKN(tokenizer = pcons_arg(args, 0, NULL));
Packit 1422b7
Packit 1422b7
	CHKN(uncopied_suffixes_str = pcons_arg(args, 1, NULL));
Packit 1422b7
	pcons_unescape_arg(args, 1);
Packit 1422b7
	CHKN(suffixes_str = pcons_arg_copy(args, 1, NULL));
Packit 1422b7
Packit 1422b7
	tok_input = suffixes_str;
Packit 1422b7
	while (strtok_r(tok_input, tokenizer, &tok_saveptr) != NULL) {
Packit 1422b7
		tok_input = NULL;
Packit 1422b7
		pData->nsuffix++;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if (pData->nsuffix == 0) {
Packit 1422b7
		FAIL(LN_INVLDFDESCR);
Packit 1422b7
	}
Packit 1422b7
	CHKN(pData->suffix_offsets = calloc(pData->nsuffix, sizeof(int)));
Packit 1422b7
	CHKN(pData->suffix_lengths = calloc(pData->nsuffix, sizeof(int)));
Packit 1422b7
	CHKN(pData->suffixes_str = pcons_arg_copy(args, 1, NULL));
Packit 1422b7
Packit 1422b7
	tok_input = pData->suffixes_str;
Packit 1422b7
	while ((tok = strtok_r(tok_input, tokenizer, &tok_saveptr)) != NULL) {
Packit 1422b7
		tok_input = NULL;
Packit 1422b7
		pData->suffix_offsets[i] = tok - pData->suffixes_str;
Packit 1422b7
	pData->suffix_lengths[i++] = strlen(tok);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	CHKN(field_type = pcons_arg(args, 2, NULL));
Packit 1422b7
	CHKN(pData->ctx = generate_context_with_field_as_prefix(ctx, field_type, strlen(field_type)));
Packit 1422b7
	
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	if (r != 0) {
Packit 1422b7
		if (name == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory suffixed-field name");
Packit 1422b7
		else if (pData == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for parser-data for field: %s", name);
Packit 1422b7
		else if (pData->value_field_name == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for value-field's name for field: %s", name);
Packit 1422b7
		else if (pData->suffix_field_name == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for suffix-field's name for field: %s", name);
Packit 1422b7
		else if (args == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for argument-parsing for field: %s", name);
Packit 1422b7
		else if (escaped_tokenizer == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "suffix token-string missing for field: '%s'", name);
Packit 1422b7
		else if (tokenizer == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for unescaping token-string for field: '%s'",
Packit 1422b7
				name);
Packit 1422b7
		else if (uncopied_suffixes_str == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "suffix-list missing for field: '%s'", name);
Packit 1422b7
		else if (suffixes_str == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for suffix-list for field: '%s'", name);
Packit 1422b7
		else if (pData->nsuffix == 0)
Packit 1422b7
			ln_dbgprintf(ctx, "could't read suffix-value(s) for field: '%s'", name);
Packit 1422b7
		else if (pData->suffix_offsets == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for suffix-list element references for field: "
Packit 1422b7
				"'%s'", name);
Packit 1422b7
		else if (pData->suffix_lengths == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for suffix-list element lengths for field: '%s'",
Packit 1422b7
				name);
Packit 1422b7
		else if (pData->suffixes_str == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for suffix-list for field: '%s'", name);
Packit 1422b7
		else if (field_type == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "field-type declaration missing for field: '%s'", name);
Packit 1422b7
		else if (pData->ctx == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for normalizer-context for field: '%s'", name);
Packit 1422b7
		suffixed_parser_data_destructor((void**)&pData);
Packit 1422b7
	}
Packit 1422b7
	free_pcons_args(&args);
Packit 1422b7
	if (suffixes_str != NULL) free(suffixes_str);
Packit 1422b7
	if (name != NULL) free(name);
Packit 1422b7
	return pData;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void* suffixed_parser_data_constructor(ln_fieldList_t *node, ln_ctx ctx) {
Packit 1422b7
	return _suffixed_parser_data_constructor(node, ctx, node->raw_data, NULL, NULL);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void* named_suffixed_parser_data_constructor(ln_fieldList_t *node, ln_ctx ctx) {
Packit 1422b7
	int r = LN_BADCONFIG;
Packit 1422b7
	pcons_args_t* args = NULL;
Packit 1422b7
	char* name = NULL;
Packit 1422b7
	const char* value_field_name = NULL;
Packit 1422b7
	const char* suffix_field_name = NULL;
Packit 1422b7
	const char* remaining_args = NULL;
Packit 1422b7
	es_str_t* unnamed_suffix_args = NULL;
Packit 1422b7
	struct suffixed_parser_data_s* pData = NULL;
Packit 1422b7
	CHKN(name = es_str2cstr(node->name, NULL));
Packit 1422b7
	CHKN(args = pcons_args(node->raw_data, 3));
Packit 1422b7
	CHKN(value_field_name = pcons_arg(args, 0, NULL));
Packit 1422b7
	CHKN(suffix_field_name = pcons_arg(args, 1, NULL));
Packit 1422b7
	CHKN(remaining_args = pcons_arg(args, 2, NULL));
Packit 1422b7
	CHKN(unnamed_suffix_args = es_newStrFromCStr(remaining_args, strlen(remaining_args)));
Packit 1422b7
	
Packit 1422b7
	CHKN(pData = _suffixed_parser_data_constructor(node, ctx, unnamed_suffix_args, value_field_name,
Packit 1422b7
		suffix_field_name));
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	if (r != 0) {
Packit 1422b7
		if (name == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory named_suffixed-field name");
Packit 1422b7
		else if (args == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for argument-parsing for field: %s", name);
Packit 1422b7
		else if (value_field_name == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "key-name for value not provided for field: %s", name);
Packit 1422b7
		else if (suffix_field_name == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "key-name for suffix not provided for field: %s", name);
Packit 1422b7
		else if (unnamed_suffix_args == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't allocate memory for unnamed-suffix-field args for field: %s",
Packit 1422b7
				name);
Packit 1422b7
		else if (pData == NULL)
Packit 1422b7
			ln_dbgprintf(ctx, "couldn't create parser-data for field: %s", name);
Packit 1422b7
		suffixed_parser_data_destructor((void**)&pData);
Packit 1422b7
	}
Packit 1422b7
	if (unnamed_suffix_args != NULL) free(unnamed_suffix_args);
Packit 1422b7
	if (args != NULL) free_pcons_args(&args);
Packit 1422b7
	if (name != NULL) free(name);
Packit 1422b7
	return pData;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
void suffixed_parser_data_destructor(void** dataPtr) {
Packit 1422b7
	if ((*dataPtr) != NULL) {
Packit 1422b7
		struct suffixed_parser_data_s *pData = (struct suffixed_parser_data_s*) *dataPtr;
Packit 1422b7
		if (pData->suffixes_str != NULL) free(pData->suffixes_str);
Packit 1422b7
		if (pData->suffix_offsets != NULL) free(pData->suffix_offsets);
Packit 1422b7
		if (pData->suffix_lengths != NULL) free(pData->suffix_lengths);
Packit 1422b7
		if (pData->value_field_name != NULL) free(pData->value_field_name);
Packit 1422b7
		if (pData->suffix_field_name != NULL) free(pData->suffix_field_name);
Packit 1422b7
		if (pData->ctx != NULL)	ln_exitCtx(pData->ctx);
Packit 1422b7
		free(pData);
Packit 1422b7
		*dataPtr = NULL;
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Just get everything till the end of string.
Packit 1422b7
 */
Packit 1422b7
PARSER(Rest)
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
Packit 1422b7
	/* silence the warning about unused variable */
Packit 1422b7
	(void)str;
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = strLen - *offs;
Packit 1422b7
	r = 0;
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a possibly quoted string. In this initial implementation, escaping of the quote
Packit 1422b7
 * char is not supported. A quoted string is one start starts with a double quote,
Packit 1422b7
 * has some text (not containing double quotes) and ends with the first double
Packit 1422b7
 * quote character seen. The extracted string does NOT include the quote characters.
Packit 1422b7
 * swisskid, 2015-01-21
Packit 1422b7
 */
Packit 1422b7
PARSER(OpQuotedString)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
	char *cstr = NULL;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	if(c[i] != '"') {
Packit 1422b7
		while(i < strLen && c[i] != ' ')
Packit 1422b7
			i++;
Packit 1422b7
Packit 1422b7
		if(i == *offs)
Packit 1422b7
			goto done;
Packit 1422b7
Packit 1422b7
		/* success, persist */
Packit 1422b7
		*parsed = i - *offs;
Packit 1422b7
		/* create JSON value to save quoted string contents */
Packit 1422b7
		CHKN(cstr = strndup((char*)c + *offs, *parsed));
Packit 1422b7
	} else {
Packit 1422b7
	    ++i;
Packit 1422b7
Packit 1422b7
	    /* search end of string */
Packit 1422b7
	    while(i < strLen && c[i] != '"')
Packit 1422b7
		    i++;
Packit 1422b7
Packit 1422b7
	    if(i == strLen || c[i] != '"')
Packit 1422b7
		    goto done;
Packit 1422b7
	    /* success, persist */
Packit 1422b7
	    *parsed = i + 1 - *offs; /* "eat" terminal double quote */
Packit 1422b7
	    /* create JSON value to save quoted string contents */
Packit 1422b7
	    CHKN(cstr = strndup((char*)c + *offs + 1, *parsed - 2));
Packit 1422b7
	}
Packit 1422b7
	CHKN(*value = json_object_new_string(cstr));
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	free(cstr);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a quoted string. In this initial implementation, escaping of the quote
Packit 1422b7
 * char is not supported. A quoted string is one start starts with a double quote,
Packit 1422b7
 * has some text (not containing double quotes) and ends with the first double
Packit 1422b7
 * quote character seen. The extracted string does NOT include the quote characters.
Packit 1422b7
 * rgerhards, 2011-01-14
Packit 1422b7
 */
Packit 1422b7
PARSER(QuotedString)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
	char *cstr = NULL;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = str;
Packit 1422b7
	i = *offs;
Packit 1422b7
	if(i + 2 > strLen)
Packit 1422b7
		goto done;	/* needs at least 2 characters */
Packit 1422b7
Packit 1422b7
	if(c[i] != '"')
Packit 1422b7
		goto done;
Packit 1422b7
	++i;
Packit 1422b7
Packit 1422b7
	/* search end of string */
Packit 1422b7
	while(i < strLen && c[i] != '"')
Packit 1422b7
		i++;
Packit 1422b7
Packit 1422b7
	if(i == strLen || c[i] != '"')
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i + 1 - *offs; /* "eat" terminal double quote */
Packit 1422b7
	/* create JSON value to save quoted string contents */
Packit 1422b7
	CHKN(cstr = strndup((char*)c + *offs + 1, *parsed - 2));
Packit 1422b7
	CHKN(*value = json_object_new_string(cstr));
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	free(cstr);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse an ISO date, that is YYYY-MM-DD (exactly this format).
Packit 1422b7
 * Note: we do manual loop unrolling -- this is fast AND efficient.
Packit 1422b7
 * rgerhards, 2011-01-14
Packit 1422b7
 */
Packit 1422b7
PARSER(ISODate)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	if(*offs+10 > strLen)
Packit 1422b7
		goto done;	/* if it is not 10 chars, it can't be an ISO date */
Packit 1422b7
Packit 1422b7
	/* year */
Packit 1422b7
	if(!isdigit(c[i])) goto done;
Packit 1422b7
	if(!isdigit(c[i+1])) goto done;
Packit 1422b7
	if(!isdigit(c[i+2])) goto done;
Packit 1422b7
	if(!isdigit(c[i+3])) goto done;
Packit 1422b7
	if(c[i+4] != '-') goto done;
Packit 1422b7
	/* month */
Packit 1422b7
	if(c[i+5] == '0') {
Packit 1422b7
		if(c[i+6] < '1' || c[i+6] > '9') goto done;
Packit 1422b7
	} else if(c[i+5] == '1') {
Packit 1422b7
		if(c[i+6] < '0' || c[i+6] > '2') goto done;
Packit 1422b7
	} else {
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	if(c[i+7] != '-') goto done;
Packit 1422b7
	/* day */
Packit 1422b7
	if(c[i+8] == '0') {
Packit 1422b7
		if(c[i+9] < '1' || c[i+9] > '9') goto done;
Packit 1422b7
	} else if(c[i+8] == '1' || c[i+8] == '2') {
Packit 1422b7
		if(!isdigit(c[i+9])) goto done;
Packit 1422b7
	} else if(c[i+8] == '3') {
Packit 1422b7
		if(c[i+9] != '0' && c[i+9] != '1') goto done;
Packit 1422b7
	} else {
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = 10;
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a Cisco interface spec. Sample for such a spec are:
Packit 1422b7
 *   outside:192.168.52.102/50349
Packit 1422b7
 *   inside:192.168.1.15/56543 (192.168.1.112/54543)
Packit 1422b7
 *   outside:192.168.1.13/50179 (192.168.1.13/50179)(LOCAL\some.user)
Packit 1422b7
 *   outside:192.168.1.25/41850(LOCAL\RG-867G8-DEL88D879BBFFC8)
Packit 1422b7
 *   inside:192.168.1.25/53 (192.168.1.25/53) (some.user)
Packit 1422b7
 *   192.168.1.15/0(LOCAL\RG-867G8-DEL88D879BBFFC8)
Packit 1422b7
 * From this, we conclude the format is:
Packit 1422b7
 *   [interface:]ip/port [SP (ip2/port2)] [[SP](username)]
Packit 1422b7
 * In order to match, this syntax must start on a non-whitespace char
Packit 1422b7
 * other than colon.
Packit 1422b7
 */
Packit 1422b7
PARSER(CiscoInterfaceSpec)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	if(c[i] == ':' || isspace(c[i])) goto done;
Packit 1422b7
Packit 1422b7
	/* first, check if we have an interface. We do this by trying
Packit 1422b7
	 * to detect if we have an IP. If we have, obviously no interface
Packit 1422b7
	 * is present. Otherwise, we check if we have a valid interface.
Packit 1422b7
	 */
Packit 1422b7
	int bHaveInterface = 0;
Packit 1422b7
	size_t idxInterface = 0;
Packit 1422b7
	size_t lenInterface = 0;
Packit 1422b7
	int bHaveIP = 0;
Packit 1422b7
	size_t lenIP;
Packit 1422b7
	size_t idxIP = i;
Packit 1422b7
	if(ln_parseIPv4(str, strLen, &i, node, &lenIP, NULL) == 0) {
Packit 1422b7
		bHaveIP = 1;
Packit 1422b7
		i += lenIP - 1; /* position on delimiter */
Packit 1422b7
	} else {
Packit 1422b7
		idxInterface = i;
Packit 1422b7
		while(i < strLen) {
Packit 1422b7
			if(isspace(c[i])) goto done;
Packit 1422b7
			if(c[i] == ':')
Packit 1422b7
				break;
Packit 1422b7
			++i;
Packit 1422b7
		}
Packit 1422b7
		lenInterface = i - idxInterface;
Packit 1422b7
		bHaveInterface = 1;
Packit 1422b7
	}
Packit 1422b7
	if(i == strLen) goto done;
Packit 1422b7
	++i; /* skip over colon */
Packit 1422b7
Packit 1422b7
	/* we now utilize our other parser helpers */
Packit 1422b7
	if(!bHaveIP) {
Packit 1422b7
		idxIP = i;
Packit 1422b7
		if(ln_parseIPv4(str, strLen, &i, node, &lenIP, NULL) != 0) goto done;
Packit 1422b7
		i += lenIP;
Packit 1422b7
	}
Packit 1422b7
	if(i == strLen || c[i] != '/') goto done;
Packit 1422b7
	++i; /* skip slash */
Packit 1422b7
	const size_t idxPort = i;
Packit 1422b7
	size_t lenPort;
Packit 1422b7
	if(ln_parseNumber(str, strLen, &i, node, &lenPort, NULL) != 0) goto done;
Packit 1422b7
	i += lenPort;
Packit 1422b7
	if(i == strLen) goto success;
Packit 1422b7
Packit 1422b7
	/* check if optional second ip/port is present
Packit 1422b7
	 * We assume we must at least have 5 chars [" (::1)"]
Packit 1422b7
	 */
Packit 1422b7
	int bHaveIP2 = 0;
Packit 1422b7
	size_t idxIP2 = 0, lenIP2 = 0;
Packit 1422b7
	size_t idxPort2 = 0, lenPort2 = 0;
Packit 1422b7
	if(i+5 < strLen && c[i] == ' ' && c[i+1] == '(') {
Packit 1422b7
		size_t iTmp = i+2; /* skip over " (" */
Packit 1422b7
		idxIP2 = iTmp;
Packit 1422b7
		if(ln_parseIPv4(str, strLen, &iTmp, node, &lenIP2, NULL) == 0) {
Packit 1422b7
			iTmp += lenIP2;
Packit 1422b7
			if(i < strLen || c[iTmp] == '/') {
Packit 1422b7
				++iTmp; /* skip slash */
Packit 1422b7
				idxPort2 = iTmp;
Packit 1422b7
				if(ln_parseNumber(str, strLen, &iTmp, node, &lenPort2, NULL) == 0) {
Packit 1422b7
					iTmp += lenPort2;
Packit 1422b7
					if(iTmp < strLen && c[iTmp] == ')') {
Packit 1422b7
						i = iTmp + 1; /* match, so use new index */
Packit 1422b7
						bHaveIP2 = 1;
Packit 1422b7
					}
Packit 1422b7
				}
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* check if optional username is present
Packit 1422b7
	 * We assume we must at least have 3 chars ["(n)"]
Packit 1422b7
	 */
Packit 1422b7
	int bHaveUser = 0;
Packit 1422b7
	size_t idxUser = 0;
Packit 1422b7
	size_t lenUser = 0;
Packit 1422b7
	if(   (i+2 < strLen && c[i] == '(' && !isspace(c[i+1]) )
Packit 1422b7
	   || (i+3 < strLen && c[i] == ' ' && c[i+1] == '(' && !isspace(c[i+2])) ) {
Packit 1422b7
		idxUser = i + ((c[i] == ' ') ? 2 : 1); /* skip [SP]'(' */
Packit 1422b7
		size_t iTmp = idxUser;
Packit 1422b7
		while(iTmp < strLen && !isspace(c[iTmp]) && c[iTmp] != ')')
Packit 1422b7
			++iTmp; /* just scan */
Packit 1422b7
		if(iTmp < strLen && c[iTmp] == ')') {
Packit 1422b7
			i = iTmp + 1; /* we have a match, so use new index */
Packit 1422b7
			bHaveUser = 1;
Packit 1422b7
			lenUser = iTmp - idxUser;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* all done, save data */
Packit 1422b7
	if(value == NULL)
Packit 1422b7
		goto success;
Packit 1422b7
Packit 1422b7
	CHKN(*value = json_object_new_object());
Packit 1422b7
	json_object *json;
Packit 1422b7
	if(bHaveInterface) {
Packit 1422b7
		CHKN(json = json_object_new_string_len(c+idxInterface, lenInterface));
Packit 1422b7
		json_object_object_add_ex(*value, "interface", json,
Packit 1422b7
			JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
	}
Packit 1422b7
	CHKN(json = json_object_new_string_len(c+idxIP, lenIP));
Packit 1422b7
	json_object_object_add_ex(*value, "ip", json, JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
	CHKN(json = json_object_new_string_len(c+idxPort, lenPort));
Packit 1422b7
	json_object_object_add_ex(*value, "port", json, JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
	if(bHaveIP2) {
Packit 1422b7
		CHKN(json = json_object_new_string_len(c+idxIP2, lenIP2));
Packit 1422b7
		json_object_object_add_ex(*value, "ip2", json,
Packit 1422b7
			JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
		CHKN(json = json_object_new_string_len(c+idxPort2, lenPort2));
Packit 1422b7
		json_object_object_add_ex(*value, "port2", json,
Packit 1422b7
			JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
	}
Packit 1422b7
	if(bHaveUser) {
Packit 1422b7
		CHKN(json = json_object_new_string_len(c+idxUser, lenUser));
Packit 1422b7
		json_object_object_add_ex(*value, "user", json,
Packit 1422b7
			JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
success: /* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	if(r != 0 && value != NULL && *value != NULL) {
Packit 1422b7
		json_object_put(*value);
Packit 1422b7
		*value = NULL; /* to be on the save side */
Packit 1422b7
	}
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a duration. A duration is similar to a timestamp, except that
Packit 1422b7
 * it tells about time elapsed. As such, hours can be larger than 23
Packit 1422b7
 * and hours may also be specified by a single digit (this, for example,
Packit 1422b7
 * is commonly done in Cisco software).
Packit 1422b7
 * Note: we do manual loop unrolling -- this is fast AND efficient.
Packit 1422b7
 */
Packit 1422b7
PARSER(Duration)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	/* hour is a bit tricky */
Packit 1422b7
	if(!isdigit(c[i])) goto done;
Packit 1422b7
	++i;
Packit 1422b7
	if(isdigit(c[i]))
Packit 1422b7
		++i;
Packit 1422b7
	if(c[i] == ':')
Packit 1422b7
		++i;
Packit 1422b7
	else
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(i+5 > strLen)
Packit 1422b7
		goto done;/* if it is not 5 chars from here, it can't be us */
Packit 1422b7
Packit 1422b7
	if(c[i] < '0' || c[i] > '5') goto done;
Packit 1422b7
	if(!isdigit(c[i+1])) goto done;
Packit 1422b7
	if(c[i+2] != ':') goto done;
Packit 1422b7
	if(c[i+3] < '0' || c[i+3] > '5') goto done;
Packit 1422b7
	if(!isdigit(c[i+4])) goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = (i + 5) - *offs;
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a timestamp in 24hr format (exactly HH:MM:SS).
Packit 1422b7
 * Note: we do manual loop unrolling -- this is fast AND efficient.
Packit 1422b7
 * rgerhards, 2011-01-14
Packit 1422b7
 */
Packit 1422b7
PARSER(Time24hr)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	if(*offs+8 > strLen)
Packit 1422b7
		goto done;	/* if it is not 8 chars, it can't be us */
Packit 1422b7
Packit 1422b7
	/* hour */
Packit 1422b7
	if(c[i] == '0' || c[i] == '1') {
Packit 1422b7
		if(!isdigit(c[i+1])) goto done;
Packit 1422b7
	} else if(c[i] == '2') {
Packit 1422b7
		if(c[i+1] < '0' || c[i+1] > '3') goto done;
Packit 1422b7
	} else {
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	/* TODO: the code below is a duplicate of 24hr parser - create common function */
Packit 1422b7
	if(c[i+2] != ':') goto done;
Packit 1422b7
	if(c[i+3] < '0' || c[i+3] > '5') goto done;
Packit 1422b7
	if(!isdigit(c[i+4])) goto done;
Packit 1422b7
	if(c[i+5] != ':') goto done;
Packit 1422b7
	if(c[i+6] < '0' || c[i+6] > '5') goto done;
Packit 1422b7
	if(!isdigit(c[i+7])) goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = 8;
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a timestamp in 12hr format (exactly HH:MM:SS).
Packit 1422b7
 * Note: we do manual loop unrolling -- this is fast AND efficient.
Packit 1422b7
 * TODO: the code below is a duplicate of 24hr parser - create common function?
Packit 1422b7
 * rgerhards, 2011-01-14
Packit 1422b7
 */
Packit 1422b7
PARSER(Time12hr)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	if(*offs+8 > strLen)
Packit 1422b7
		goto done;	/* if it is not 8 chars, it can't be us */
Packit 1422b7
Packit 1422b7
	/* hour */
Packit 1422b7
	if(c[i] == '0') {
Packit 1422b7
		if(!isdigit(c[i+1])) goto done;
Packit 1422b7
	} else if(c[i] == '1') {
Packit 1422b7
		if(c[i+1] < '0' || c[i+1] > '2') goto done;
Packit 1422b7
	} else {
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	if(c[i+2] != ':') goto done;
Packit 1422b7
	if(c[i+3] < '0' || c[i+3] > '5') goto done;
Packit 1422b7
	if(!isdigit(c[i+4])) goto done;
Packit 1422b7
	if(c[i+5] != ':') goto done;
Packit 1422b7
	if(c[i+6] < '0' || c[i+6] > '5') goto done;
Packit 1422b7
	if(!isdigit(c[i+7])) goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = 8;
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* helper to IPv4 address parser, checks the next set of numbers.
Packit 1422b7
 * Syntax 1 to 3 digits, value together not larger than 255.
Packit 1422b7
 * @param[in] str parse buffer
Packit 1422b7
 * @param[in/out] offs offset into buffer, updated if successful
Packit 1422b7
 * @return 0 if OK, 1 otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
chkIPv4AddrByte(const char *str, size_t strLen, size_t *offs)
Packit 1422b7
{
Packit 1422b7
	int val = 0;
Packit 1422b7
	int r = 1;	/* default: done -- simplifies things */
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
Packit 1422b7
	c = str;
Packit 1422b7
	if(i == strLen || !isdigit(c[i]))
Packit 1422b7
		goto done;
Packit 1422b7
	val = c[i++] - '0';
Packit 1422b7
	if(i < strLen && isdigit(c[i])) {
Packit 1422b7
		val = val * 10 + c[i++] - '0';
Packit 1422b7
		if(i < strLen && isdigit(c[i]))
Packit 1422b7
			val = val * 10 + c[i++] - '0';
Packit 1422b7
	}
Packit 1422b7
	if(val > 255)	/* cannot be a valid IP address byte! */
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	*offs = i;
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parser for IPv4 addresses.
Packit 1422b7
 */
Packit 1422b7
PARSER(IPv4)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	i = *offs;
Packit 1422b7
	if(i + 7 > strLen) {
Packit 1422b7
		/* IPv4 addr requires at least 7 characters */
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	c = str;
Packit 1422b7
Packit 1422b7
	/* byte 1*/
Packit 1422b7
	if(chkIPv4AddrByte(str, strLen, &i) != 0) goto done;
Packit 1422b7
	if(i == strLen || c[i++] != '.') goto done;
Packit 1422b7
	/* byte 2*/
Packit 1422b7
	if(chkIPv4AddrByte(str, strLen, &i) != 0) goto done;
Packit 1422b7
	if(i == strLen || c[i++] != '.') goto done;
Packit 1422b7
	/* byte 3*/
Packit 1422b7
	if(chkIPv4AddrByte(str, strLen, &i) != 0) goto done;
Packit 1422b7
	if(i == strLen || c[i++] != '.') goto done;
Packit 1422b7
	/* byte 4 - we do NOT need any char behind it! */
Packit 1422b7
	if(chkIPv4AddrByte(str, strLen, &i) != 0) goto done;
Packit 1422b7
Packit 1422b7
	/* if we reach this point, we found a valid IP address */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* skip past the IPv6 address block, parse pointer is set to
Packit 1422b7
 * first char after the block. Returns an error if already at end
Packit 1422b7
 * of string.
Packit 1422b7
 * @param[in] str parse buffer
Packit 1422b7
 * @param[in/out] offs offset into buffer, updated if successful
Packit 1422b7
 * @return 0 if OK, 1 otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
skipIPv6AddrBlock(const char *const __restrict__ str,
Packit 1422b7
	const size_t strLen,
Packit 1422b7
	size_t *const __restrict__ offs)
Packit 1422b7
{
Packit 1422b7
	int j;
Packit 1422b7
	if(*offs == strLen)
Packit 1422b7
		return 1;
Packit 1422b7
Packit 1422b7
	for(j = 0 ; j < 4  && *offs+j < strLen && isxdigit(str[*offs+j]) ; ++j)
Packit 1422b7
		/*just skip*/ ;
Packit 1422b7
Packit 1422b7
	*offs += j;
Packit 1422b7
	return 0;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parser for IPv6 addresses.
Packit 1422b7
 * Bases on RFC4291 Section 2.2. The address must be followed
Packit 1422b7
 * by whitespace or end-of-string, else it is not considered
Packit 1422b7
 * a valid address. This prevents false positives.
Packit 1422b7
 */
Packit 1422b7
PARSER(IPv6)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
	size_t beginBlock; /* last block begin in case we need IPv4 parsing */
Packit 1422b7
	int hasIPv4 = 0;
Packit 1422b7
	int nBlocks = 0; /* how many blocks did we already have? */
Packit 1422b7
	int bHad0Abbrev = 0; /* :: already used? */
Packit 1422b7
Packit 1422b7
	assert(str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	i = *offs;
Packit 1422b7
	if(i + 2 > strLen) {
Packit 1422b7
		/* IPv6 addr requires at least 2 characters ("::") */
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	c = str;
Packit 1422b7
Packit 1422b7
	/* check that first block is non-empty */
Packit 1422b7
	if(! ( isxdigit(c[i]) || (c[i] == ':' && c[i+1] == ':') ) )
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* try for all potential blocks plus one more (so we see errors!) */
Packit 1422b7
	for(int j = 0 ; j < 9 ; ++j) {
Packit 1422b7
		beginBlock = i;
Packit 1422b7
		if(skipIPv6AddrBlock(str, strLen, &i) != 0) goto done;
Packit 1422b7
		nBlocks++;
Packit 1422b7
		if(i == strLen) goto chk_ok;
Packit 1422b7
		if(isspace(c[i])) goto chk_ok;
Packit 1422b7
		if(c[i] == '.'){ /* IPv4 processing! */
Packit 1422b7
			hasIPv4 = 1;
Packit 1422b7
			break;
Packit 1422b7
		}
Packit 1422b7
		if(c[i] != ':') goto done;
Packit 1422b7
		i++; /* "eat" ':' */
Packit 1422b7
		if(i == strLen) goto chk_ok;
Packit 1422b7
		/* check for :: */
Packit 1422b7
		if(bHad0Abbrev) {
Packit 1422b7
			if(c[i] == ':') goto done;
Packit 1422b7
		} else {
Packit 1422b7
			if(c[i] == ':') {
Packit 1422b7
				bHad0Abbrev = 1;
Packit 1422b7
				++i;
Packit 1422b7
				if(i == strLen) goto chk_ok;
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(hasIPv4) {
Packit 1422b7
		size_t ipv4_parsed;
Packit 1422b7
		--nBlocks;
Packit 1422b7
		/* prevent pure IPv4 address to be recognized */
Packit 1422b7
		if(beginBlock == *offs) goto done;
Packit 1422b7
		i = beginBlock;
Packit 1422b7
		if(ln_parseIPv4(str, strLen, &i, node, &ipv4_parsed, NULL) != 0)
Packit 1422b7
			goto done;
Packit 1422b7
		i += ipv4_parsed;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
chk_ok:	/* we are finished parsing, check if things are ok */
Packit 1422b7
	if(nBlocks > 8) goto done;
Packit 1422b7
	if(bHad0Abbrev && nBlocks >= 8) goto done;
Packit 1422b7
	/* now check if trailing block is missing. Note that i is already
Packit 1422b7
	 * on next character, so we need to go two back. Two are always
Packit 1422b7
	 * present, else we would not reach this code here.
Packit 1422b7
	 */
Packit 1422b7
	if(c[i-1] == ':' && c[i-2] != ':') goto done;
Packit 1422b7
Packit 1422b7
	/* if we reach this point, we found a valid IP address */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* check if a char is valid inside a name of the iptables motif.
Packit 1422b7
 * We try to keep the set as slim as possible, because the iptables
Packit 1422b7
 * parser may otherwise create a very broad match (especially the
Packit 1422b7
 * inclusion of simple words like "DF" cause grief here).
Packit 1422b7
 * Note: we have taken the permitted set from iptables log samples.
Packit 1422b7
 * Report bugs if we missed some additional rules.
Packit 1422b7
 */
Packit 1422b7
static inline int
Packit 1422b7
isValidIPTablesNameChar(const char c)
Packit 1422b7
{
Packit 1422b7
	/* right now, upper case only is valid */
Packit 1422b7
	return ('A' <= c && c <= 'Z') ? 1 : 0;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* helper to iptables parser, parses out a a single name=value pair
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
parseIPTablesNameValue(const char *const __restrict__ str,
Packit 1422b7
	const size_t strLen,
Packit 1422b7
	size_t *const __restrict__ offs,
Packit 1422b7
	struct json_object *const __restrict__ valroot)
Packit 1422b7
{
Packit 1422b7
	int r = LN_WRONGPARSER;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	char *name = NULL;
Packit 1422b7
Packit 1422b7
	const size_t iName = i;
Packit 1422b7
	while(i < strLen && isValidIPTablesNameChar(str[i]))
Packit 1422b7
		++i;
Packit 1422b7
	if(i == iName || (i < strLen && str[i] != '=' && str[i] != ' '))
Packit 1422b7
		goto done; /* no name at all! */
Packit 1422b7
Packit 1422b7
	const ssize_t lenName = i - iName;
Packit 1422b7
Packit 1422b7
	ssize_t iVal = -1;
Packit 1422b7
	size_t lenVal = i - iVal;
Packit 1422b7
	if(i < strLen && str[i] != ' ') {
Packit 1422b7
		/* we have a real value (not just a flag name like "DF") */
Packit 1422b7
		++i; /* skip '=' */
Packit 1422b7
		iVal = i;
Packit 1422b7
		while(i < strLen && !isspace(str[i]))
Packit 1422b7
			++i;
Packit 1422b7
		lenVal = i - iVal;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* parsing OK */
Packit 1422b7
	*offs = i;
Packit 1422b7
	r = 0;
Packit 1422b7
Packit 1422b7
	if(valroot == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	CHKN(name = malloc(lenName+1));
Packit 1422b7
	memcpy(name, str+iName, lenName);
Packit 1422b7
	name[lenName] = '\0';
Packit 1422b7
	json_object *json;
Packit 1422b7
	if(iVal == -1) {
Packit 1422b7
		json = NULL;
Packit 1422b7
	} else {
Packit 1422b7
		CHKN(json = json_object_new_string_len(str+iVal, lenVal));
Packit 1422b7
	}
Packit 1422b7
	json_object_object_add(valroot, name, json);
Packit 1422b7
done:
Packit 1422b7
	free(name);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parser for iptables logs (the structured part).
Packit 1422b7
 * This parser is named "v2-iptables" because of a traditional
Packit 1422b7
 * parser named "iptables", which we do not want to replace, at
Packit 1422b7
 * least right now (we may re-think this before the first release).
Packit 1422b7
 * For performance reasons, this works in two stages. In the first
Packit 1422b7
 * stage, we only detect if the motif is correct. The second stage is
Packit 1422b7
 * only called when we know it is. In it, we go once again over the
Packit 1422b7
 * message again and actually extract the data. This is done because
Packit 1422b7
 * data extraction is relatively expensive and in most cases we will
Packit 1422b7
 * have much more frequent mismatches than matches.
Packit 1422b7
 * Note that this motif must have at least one field, otherwise it
Packit 1422b7
 * could detect things that are not iptables to be it. Further limits
Packit 1422b7
 * may be imposed in the future as we see additional need.
Packit 1422b7
 * added 2015-04-30 rgerhards
Packit 1422b7
 */
Packit 1422b7
PARSER(v2IPTables)
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	int nfields = 0;
Packit 1422b7
Packit 1422b7
	/* stage one */
Packit 1422b7
	while(i < strLen) {
Packit 1422b7
		CHKR(parseIPTablesNameValue(str, strLen, &i, NULL));
Packit 1422b7
		++nfields;
Packit 1422b7
		/* exactly one SP is permitted between fields */
Packit 1422b7
		if(i < strLen && str[i] == ' ')
Packit 1422b7
			++i;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(nfields < 2) {
Packit 1422b7
		FAIL(LN_WRONGPARSER);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	r = 0;
Packit 1422b7
Packit 1422b7
	/* stage two */
Packit 1422b7
	if(value == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	i = *offs;
Packit 1422b7
	CHKN(*value = json_object_new_object());
Packit 1422b7
	while(i < strLen) {
Packit 1422b7
		CHKR(parseIPTablesNameValue(str, strLen, &i, *value));
Packit 1422b7
		while(i < strLen && isspace(str[i]))
Packit 1422b7
			++i;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	if(r != 0 && value != NULL && *value != NULL) {
Packit 1422b7
		json_object_put(*value);
Packit 1422b7
		*value = NULL;
Packit 1422b7
	}
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse JSON. This parser tries to find JSON data inside a message.
Packit 1422b7
 * If it finds valid JSON, it will extract it. Extra data after the
Packit 1422b7
 * JSON is permitted.
Packit 1422b7
 * Note: the json-c JSON parser treats whitespace after the actual
Packit 1422b7
 * json to be part of the json. So in essence, any whitespace is
Packit 1422b7
 * processed by this parser. We use the same semantics to keep things
Packit 1422b7
 * neatly in sync. If json-c changes for some reason or we switch to
Packit 1422b7
 * an alternate json lib, we probably need to be sure to keep that
Packit 1422b7
 * behaviour, and probably emulate it.
Packit 1422b7
 * added 2015-04-28 by rgerhards, v1.1.2
Packit 1422b7
 */
Packit 1422b7
PARSER(JSON)
Packit 1422b7
	const size_t i = *offs;
Packit 1422b7
	struct json_tokener *tokener = NULL;
Packit 1422b7
Packit 1422b7
	if(str[i] != '{' && str[i] != ']') {
Packit 1422b7
		/* this can't be json, see RFC4627, Sect. 2
Packit 1422b7
		 * see this bug in json-c:
Packit 1422b7
		 * https://github.com/json-c/json-c/issues/181
Packit 1422b7
		 * In any case, it's better to do this quick check,
Packit 1422b7
		 * even if json-c did not have the bug because this
Packit 1422b7
		 * check here is much faster than calling the parser.
Packit 1422b7
		 */
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if((tokener = json_tokener_new()) == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	struct json_object *const json
Packit 1422b7
		= json_tokener_parse_ex(tokener, str+i, (int) (strLen - i));
Packit 1422b7
Packit 1422b7
	if(json == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed =  (i + tokener->char_offset) - *offs;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
Packit 1422b7
	if(value == NULL) {
Packit 1422b7
		json_object_put(json);
Packit 1422b7
	} else {
Packit 1422b7
		*value = json;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	if(tokener != NULL)
Packit 1422b7
		json_tokener_free(tokener);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* check if a char is valid inside a name of a NameValue list
Packit 1422b7
 * The set of valid characters may be extended if there is good
Packit 1422b7
 * need to do so. We have selected the current set carefully, but
Packit 1422b7
 * may have overlooked some cases.
Packit 1422b7
 */
Packit 1422b7
static inline int
Packit 1422b7
isValidNameChar(const char c)
Packit 1422b7
{
Packit 1422b7
	return (isalnum(c)
Packit 1422b7
		|| c == '.'
Packit 1422b7
		|| c == '_'
Packit 1422b7
		|| c == '-'
Packit 1422b7
		) ? 1 : 0;
Packit 1422b7
}
Packit 1422b7
/* helper to NameValue parser, parses out a a single name=value pair
Packit 1422b7
 *
Packit 1422b7
 * name must be alphanumeric characters, value must be non-whitespace
Packit 1422b7
 * characters, if quoted than with symmetric quotes. Supported formats
Packit 1422b7
 * - name=value
Packit 1422b7
 * - name="value"
Packit 1422b7
 * - name='value'
Packit 1422b7
 * Note "name=" is valid and means a field with empty value.
Packit 1422b7
 * TODO: so far, quote characters are not permitted WITHIN quoted values.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
parseNameValue(const char *const __restrict__ str,
Packit 1422b7
	const size_t strLen,
Packit 1422b7
	size_t *const __restrict__ offs,
Packit 1422b7
	struct json_object *const __restrict__ valroot)
Packit 1422b7
{
Packit 1422b7
	int r = LN_WRONGPARSER;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	char *name = NULL;
Packit 1422b7
Packit 1422b7
	const size_t iName = i;
Packit 1422b7
	while(i < strLen && isValidNameChar(str[i]))
Packit 1422b7
		++i;
Packit 1422b7
	if(i == iName || str[i] != '=')
Packit 1422b7
		goto done; /* no name at all! */
Packit 1422b7
Packit 1422b7
	const size_t lenName = i - iName;
Packit 1422b7
	++i; /* skip '=' */
Packit 1422b7
Packit 1422b7
	const size_t iVal = i;
Packit 1422b7
	while(i < strLen && !isspace(str[i]))
Packit 1422b7
		++i;
Packit 1422b7
	const size_t lenVal = i - iVal;
Packit 1422b7
Packit 1422b7
	/* parsing OK */
Packit 1422b7
	*offs = i;
Packit 1422b7
	r = 0;
Packit 1422b7
Packit 1422b7
	if(valroot == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	CHKN(name = malloc(lenName+1));
Packit 1422b7
	memcpy(name, str+iName, lenName);
Packit 1422b7
	name[lenName] = '\0';
Packit 1422b7
	json_object *json;
Packit 1422b7
	CHKN(json = json_object_new_string_len(str+iVal, lenVal));
Packit 1422b7
	json_object_object_add(valroot, name, json);
Packit 1422b7
done:
Packit 1422b7
	free(name);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse CEE syslog.
Packit 1422b7
 * This essentially is a JSON parser, with additional restrictions:
Packit 1422b7
 * The message must start with "@cee:" and json must immediately follow (whitespace permitted).
Packit 1422b7
 * after the JSON, there must be no other non-whitespace characters.
Packit 1422b7
 * In other words: the message must consist of a single JSON object,
Packit 1422b7
 * only.
Packit 1422b7
 * added 2015-04-28 by rgerhards, v1.1.2
Packit 1422b7
 */
Packit 1422b7
PARSER(CEESyslog)
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	struct json_tokener *tokener = NULL;
Packit 1422b7
	struct json_object *json = NULL;
Packit 1422b7
Packit 1422b7
	if(strLen < i + 7  || /* "@cee:{}" is minimum text */
Packit 1422b7
	   str[i]   != '@' ||
Packit 1422b7
	   str[i+1] != 'c' ||
Packit 1422b7
	   str[i+2] != 'e' ||
Packit 1422b7
	   str[i+3] != 'e' ||
Packit 1422b7
	   str[i+4] != ':')
Packit 1422b7
	   	goto done;
Packit 1422b7
	
Packit 1422b7
	/* skip whitespace */
Packit 1422b7
	for(i += 5 ; i < strLen && isspace(str[i]) ; ++i)
Packit 1422b7
		/* just skip */;
Packit 1422b7
Packit 1422b7
	if(i == strLen || str[i] != '{')
Packit 1422b7
		goto done;
Packit 1422b7
		/* note: we do not permit arrays in CEE mode */
Packit 1422b7
Packit 1422b7
	if((tokener = json_tokener_new()) == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	json = json_tokener_parse_ex(tokener, str+i, (int) (strLen - i));
Packit 1422b7
Packit 1422b7
	if(json == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(i + tokener->char_offset != strLen)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed =  strLen;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json;
Packit 1422b7
		json = NULL; /* do NOT free below! */
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	if(tokener != NULL)
Packit 1422b7
		json_tokener_free(tokener);
Packit 1422b7
	if(json != NULL)
Packit 1422b7
		json_object_put(json);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parser for name/value pairs.
Packit 1422b7
 * On entry must point to alnum char. All following chars must be
Packit 1422b7
 * name/value pairs delimited by whitespace up until the end of string.
Packit 1422b7
 * For performance reasons, this works in two stages. In the first
Packit 1422b7
 * stage, we only detect if the motif is correct. The second stage is
Packit 1422b7
 * only called when we know it is. In it, we go once again over the
Packit 1422b7
 * message again and actually extract the data. This is done because
Packit 1422b7
 * data extraction is relatively expensive and in most cases we will
Packit 1422b7
 * have much more frequent mismatches than matches.
Packit 1422b7
 * added 2015-04-25 rgerhards
Packit 1422b7
 */
Packit 1422b7
PARSER(NameValue)
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
Packit 1422b7
	/* stage one */
Packit 1422b7
	while(i < strLen) {
Packit 1422b7
		CHKR(parseNameValue(str, strLen, &i, NULL));
Packit 1422b7
		while(i < strLen && isspace(str[i]))
Packit 1422b7
			++i;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
Packit 1422b7
	/* stage two */
Packit 1422b7
	if(value == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	i = *offs;
Packit 1422b7
	CHKN(*value = json_object_new_object());
Packit 1422b7
	while(i < strLen) {
Packit 1422b7
		CHKR(parseNameValue(str, strLen, &i, *value));
Packit 1422b7
		while(i < strLen && isspace(str[i]))
Packit 1422b7
			++i;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* TODO: fix mem leak if alloc json fails */
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a MAC layer address.
Packit 1422b7
 * The standard (IEEE 802) format for printing MAC-48 addresses in
Packit 1422b7
 * human-friendly form is six groups of two hexadecimal digits,
Packit 1422b7
 * separated by hyphens (-) or colons (:), in transmission order
Packit 1422b7
 * (e.g. 01-23-45-67-89-ab or 01:23:45:67:89:ab ).
Packit 1422b7
 * This form is also commonly used for EUI-64.
Packit 1422b7
 * from: http://en.wikipedia.org/wiki/MAC_address
Packit 1422b7
 *
Packit 1422b7
 * This parser must start on a hex digit.
Packit 1422b7
 * added 2015-05-04 by rgerhards, v1.1.2
Packit 1422b7
 */
Packit 1422b7
PARSER(MAC48)
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	char delim;
Packit 1422b7
Packit 1422b7
	if(strLen < i + 17 || /* this motif has exactly 17 characters */
Packit 1422b7
	   !isxdigit(str[i]) ||
Packit 1422b7
	   !isxdigit(str[i+1])
Packit 1422b7
	   )
Packit 1422b7
		FAIL(LN_WRONGPARSER);
Packit 1422b7
Packit 1422b7
	if(str[i+2] == ':')
Packit 1422b7
		delim = ':';
Packit 1422b7
	else if(str[i+2] == '-')
Packit 1422b7
		delim = '-';
Packit 1422b7
	else
Packit 1422b7
		FAIL(LN_WRONGPARSER);
Packit 1422b7
Packit 1422b7
	/* first byte ok */
Packit 1422b7
	if(!isxdigit(str[i+3])  ||
Packit 1422b7
	   !isxdigit(str[i+4])  ||
Packit 1422b7
	   str[i+5] != delim    || /* 2nd byte ok */
Packit 1422b7
	   !isxdigit(str[i+6])  ||
Packit 1422b7
	   !isxdigit(str[i+7])  ||
Packit 1422b7
	   str[i+8] != delim    || /* 3rd byte ok */
Packit 1422b7
	   !isxdigit(str[i+9])  ||
Packit 1422b7
	   !isxdigit(str[i+10]) ||
Packit 1422b7
	   str[i+11] != delim   || /* 4th byte ok */
Packit 1422b7
	   !isxdigit(str[i+12]) ||
Packit 1422b7
	   !isxdigit(str[i+13]) ||
Packit 1422b7
	   str[i+14] != delim   || /* 5th byte ok */
Packit 1422b7
	   !isxdigit(str[i+15]) ||
Packit 1422b7
	   !isxdigit(str[i+16])    /* 6th byte ok */
Packit 1422b7
	   )
Packit 1422b7
		FAIL(LN_WRONGPARSER);
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = 17;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		CHKN(*value = json_object_new_string_len(str+i, 17));
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* This parses the extension value and updates the index
Packit 1422b7
 * to point to the end of it.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
cefParseExtensionValue(const char *const __restrict__ str,
Packit 1422b7
	const size_t strLen,
Packit 1422b7
	size_t *__restrict__ iEndVal)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	size_t i = *iEndVal;
Packit 1422b7
	size_t iLastWordBegin;
Packit 1422b7
	/* first find next unquoted equal sign and record begin of
Packit 1422b7
	 * last word in front of it - this is the actual end of the
Packit 1422b7
	 * current name/value pair and the begin of the next one.
Packit 1422b7
	 */
Packit 1422b7
	int hadSP = 0;
Packit 1422b7
	int inEscape = 0;
Packit 1422b7
	for(iLastWordBegin = 0 ; i < strLen ; ++i) {
Packit 1422b7
		if(inEscape) {
Packit 1422b7
			if(str[i] != '=' &&
Packit 1422b7
			   str[i] != '\\' &&
Packit 1422b7
			   str[i] != 'r' &&
Packit 1422b7
			   str[i] != 'n')
Packit 1422b7
			FAIL(LN_WRONGPARSER);
Packit 1422b7
			inEscape = 0;
Packit 1422b7
		} else {
Packit 1422b7
			if(str[i] == '=') {
Packit 1422b7
				break;
Packit 1422b7
			} else if(str[i] == '\\') {
Packit 1422b7
				inEscape = 1;
Packit 1422b7
			} else if(str[i] == ' ') {
Packit 1422b7
				hadSP = 1;
Packit 1422b7
			} else {
Packit 1422b7
				if(hadSP) {
Packit 1422b7
					iLastWordBegin = i;
Packit 1422b7
					hadSP = 0;
Packit 1422b7
				}
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* Note: iLastWordBegin can never be at offset zero, because
Packit 1422b7
	 * the CEF header starts there!
Packit 1422b7
	 */
Packit 1422b7
	if(i < strLen) {
Packit 1422b7
		*iEndVal = (iLastWordBegin == 0) ? i : iLastWordBegin - 1;
Packit 1422b7
	} else {
Packit 1422b7
		*iEndVal = i;
Packit 1422b7
	}
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* must be positioned on first char of name, returns index
Packit 1422b7
 * of end of name.
Packit 1422b7
 * Note: ArcSight violates the CEF spec ifself: they generate
Packit 1422b7
 * leading underscores in their extension names, which are
Packit 1422b7
 * definetly not alphanumeric. We still accept them...
Packit 1422b7
 * They also seem to use dots.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
cefParseName(const char *const __restrict__ str,
Packit 1422b7
	const size_t strLen,
Packit 1422b7
	size_t *const __restrict__ i)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	while(*i < strLen && str[*i] != '=') {
Packit 1422b7
		if(!(isalnum(str[*i]) || str[*i] == '_' || str[*i] == '.'))
Packit 1422b7
			FAIL(LN_WRONGPARSER);
Packit 1422b7
		++(*i);
Packit 1422b7
	}
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* parse CEF extensions. They are basically name=value
Packit 1422b7
 * pairs with the ugly exception that values may contain
Packit 1422b7
 * spaces but need NOT to be quoted. Thankfully, at least
Packit 1422b7
 * names are specified as being alphanumeric without spaces
Packit 1422b7
 * in them. So we must add a lookahead parser to check if
Packit 1422b7
 * a word is a name (and thus the begin of a new pair) or
Packit 1422b7
 * not. This is done by subroutines.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
cefParseExtensions(const char *const __restrict__ str,
Packit 1422b7
	const size_t strLen,
Packit 1422b7
	size_t *const __restrict__ offs,
Packit 1422b7
	json_object *const __restrict__ jroot)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	size_t iName, lenName;
Packit 1422b7
	size_t iValue, lenValue;
Packit 1422b7
	char *name = NULL;
Packit 1422b7
	char *value = NULL;
Packit 1422b7
	
Packit 1422b7
	while(i < strLen) {
Packit 1422b7
		while(i < strLen && str[i] == ' ')
Packit 1422b7
			++i;
Packit 1422b7
		iName = i;
Packit 1422b7
		CHKR(cefParseName(str, strLen, &i);;
Packit 1422b7
		if(i+1 >= strLen || str[i] != '=')
Packit 1422b7
			FAIL(LN_WRONGPARSER);
Packit 1422b7
		lenName = i - iName;
Packit 1422b7
		++i; /* skip '=' */
Packit 1422b7
Packit 1422b7
		iValue = i;
Packit 1422b7
		CHKR(cefParseExtensionValue(str, strLen, &i);;
Packit 1422b7
		lenValue = i - iValue;
Packit 1422b7
Packit 1422b7
		++i; /* skip past value */
Packit 1422b7
Packit 1422b7
		if(jroot != NULL) {
Packit 1422b7
			CHKN(name = malloc(sizeof(char) * (lenName + 1)));
Packit 1422b7
			memcpy(name, str+iName, lenName);
Packit 1422b7
			name[lenName] = '\0';
Packit 1422b7
			CHKN(value = malloc(sizeof(char) * (lenValue + 1)));
Packit 1422b7
			/* copy value but escape it */
Packit 1422b7
			size_t iDst = 0;
Packit 1422b7
			for(size_t iSrc = 0 ; iSrc < lenValue ; ++iSrc) {
Packit 1422b7
				if(str[iValue+iSrc] == '\\') {
Packit 1422b7
					++iSrc; /* we know the next char must exist! */
Packit 1422b7
					switch(str[iValue+iSrc]) {
Packit 1422b7
					case '=':	value[iDst] = '=';
Packit 1422b7
							break;
Packit 1422b7
					case 'n':	value[iDst] = '\n';
Packit 1422b7
							break;
Packit 1422b7
					case 'r':	value[iDst] = '\r';
Packit 1422b7
							break;
Packit 1422b7
					case '\\':	value[iDst] = '\\';
Packit 1422b7
							break;
Packit 1422b7
					default:	break;
Packit 1422b7
					}
Packit 1422b7
				} else {
Packit 1422b7
					value[iDst] = str[iValue+iSrc];
Packit 1422b7
				}
Packit 1422b7
				++iDst;
Packit 1422b7
			}
Packit 1422b7
			value[iDst] = '\0';
Packit 1422b7
			json_object *json;
Packit 1422b7
			CHKN(json = json_object_new_string(value));
Packit 1422b7
			json_object_object_add(jroot, name, json);
Packit 1422b7
			free(name); name = NULL;
Packit 1422b7
			free(value); value = NULL;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	free(name);
Packit 1422b7
	free(value);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* gets a CEF header field. Must be positioned on the
Packit 1422b7
 * first char after the '|' in front of field.
Packit 1422b7
 * Note that '|' may be escaped as "\|", which also means
Packit 1422b7
 * we need to supprot "\\" (see CEF spec for details).
Packit 1422b7
 * We return the string in *val, if val is non-null. In
Packit 1422b7
 * that case we allocate memory that the caller must free.
Packit 1422b7
 * This is necessary because there are potentially escape
Packit 1422b7
 * sequences inside the string.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
cefGetHdrField(const char *const __restrict__ str,
Packit 1422b7
	const size_t strLen,
Packit 1422b7
	size_t *const __restrict__ offs,
Packit 1422b7
	char **val)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	assert(str[i] != '|');
Packit 1422b7
	while(i < strLen && str[i] != '|') {
Packit 1422b7
		if(str[i] == '\\') {
Packit 1422b7
			++i; /* skip esc char */
Packit 1422b7
			if(str[i] != '\\' && str[i] != '|')
Packit 1422b7
				FAIL(LN_WRONGPARSER);
Packit 1422b7
		}
Packit 1422b7
		++i; /* scan to next delimiter */
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(str[i] != '|')
Packit 1422b7
		FAIL(LN_WRONGPARSER);
Packit 1422b7
Packit 1422b7
	const size_t iBegin = *offs;
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*offs = i + 1;
Packit 1422b7
Packit 1422b7
	if(val == NULL) {
Packit 1422b7
		r = 0;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	
Packit 1422b7
	const size_t len = i - iBegin;
Packit 1422b7
	CHKN(*val = malloc(len + 1));
Packit 1422b7
	size_t iDst = 0;
Packit 1422b7
	for(size_t iSrc = 0 ; iSrc < len ; ++iSrc) {
Packit 1422b7
		if(str[iBegin+iSrc] == '\\')
Packit 1422b7
			++iSrc; /* we already checked above that this is OK! */
Packit 1422b7
		(*val)[iDst++] = str[iBegin+iSrc];
Packit 1422b7
	}
Packit 1422b7
	(*val)[iDst] = 0;
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parser for ArcSight Common Event Format (CEF) version 0.
Packit 1422b7
 * added 2015-05-05 by rgerhards, v1.1.2
Packit 1422b7
 */
Packit 1422b7
PARSER(CEF)
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	char *vendor = NULL;
Packit 1422b7
	char *product = NULL;
Packit 1422b7
	char *version = NULL;
Packit 1422b7
	char *sigID = NULL;
Packit 1422b7
	char *name = NULL;
Packit 1422b7
	char *severity = NULL;
Packit 1422b7
Packit 1422b7
	/* minumum header: "CEF:0|x|x|x|x|x|x|" -->  17 chars */
Packit 1422b7
	if(strLen < i + 17 ||
Packit 1422b7
	   str[i]   != 'C' ||
Packit 1422b7
	   str[i+1] != 'E' ||
Packit 1422b7
	   str[i+2] != 'F' ||
Packit 1422b7
	   str[i+3] != ':' ||
Packit 1422b7
	   str[i+4] != '0' ||
Packit 1422b7
	   str[i+5] != '|'
Packit 1422b7
	   )	FAIL(LN_WRONGPARSER);
Packit 1422b7
	
Packit 1422b7
	i += 6; /* position on '|' */
Packit 1422b7
Packit 1422b7
	CHKR(cefGetHdrField(str, strLen, &i, (value == NULL) ? NULL : &vendor));
Packit 1422b7
	CHKR(cefGetHdrField(str, strLen, &i, (value == NULL) ? NULL : &product));
Packit 1422b7
	CHKR(cefGetHdrField(str, strLen, &i, (value == NULL) ? NULL : &version));
Packit 1422b7
	CHKR(cefGetHdrField(str, strLen, &i, (value == NULL) ? NULL : &sigID));
Packit 1422b7
	CHKR(cefGetHdrField(str, strLen, &i, (value == NULL) ? NULL : &name));
Packit 1422b7
	CHKR(cefGetHdrField(str, strLen, &i, (value == NULL) ? NULL : &severity));
Packit 1422b7
	++i; /* skip over terminal '|' */
Packit 1422b7
Packit 1422b7
	/* OK, we now know we have a good header. Now, we need
Packit 1422b7
	 * to process extensions.
Packit 1422b7
	 * This time, we do NOT pre-process the extension, but rather
Packit 1422b7
	 * persist them directly to JSON. This is contrary to other
Packit 1422b7
	 * parsers, but as the CEF header is pretty unique, this time
Packit 1422b7
	 * it is exteremely unlike we will get a no-match during
Packit 1422b7
	 * extension processing. Even if so, nothing bad happens, as
Packit 1422b7
	 * the extracted data is discarded. But the regular case saves
Packit 1422b7
	 * us processing time and complexity. The only time when we
Packit 1422b7
	 * cannot directly process it is when the caller asks us not
Packit 1422b7
	 * to persist the data. So this must be handled differently.
Packit 1422b7
	 */
Packit 1422b7
	 size_t iBeginExtensions = i;
Packit 1422b7
	 CHKR(cefParseExtensions(str, strLen, &i, NULL));
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = *offs - i;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		CHKN(*value = json_object_new_object());
Packit 1422b7
		json_object *json;
Packit 1422b7
		CHKN(json = json_object_new_string(vendor));
Packit 1422b7
		json_object_object_add(*value, "DeviceVendor", json);
Packit 1422b7
		CHKN(json = json_object_new_string(product));
Packit 1422b7
		json_object_object_add(*value, "DeviceProduct", json);
Packit 1422b7
		CHKN(json = json_object_new_string(version));
Packit 1422b7
		json_object_object_add(*value, "DeviceVersion", json);
Packit 1422b7
		CHKN(json = json_object_new_string(sigID));
Packit 1422b7
		json_object_object_add(*value, "SignatureID", json);
Packit 1422b7
		CHKN(json = json_object_new_string(name));
Packit 1422b7
		json_object_object_add(*value, "Name", json);
Packit 1422b7
		CHKN(json = json_object_new_string(severity));
Packit 1422b7
		json_object_object_add(*value, "Severity", json);
Packit 1422b7
Packit 1422b7
		json_object *jext;
Packit 1422b7
		CHKN(jext = json_object_new_object());
Packit 1422b7
		json_object_object_add(*value, "Extensions", jext);
Packit 1422b7
Packit 1422b7
		i = iBeginExtensions;
Packit 1422b7
		cefParseExtensions(str, strLen, &i, jext);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	if(r != 0 && value != NULL && *value != NULL) {
Packit 1422b7
		json_object_put(*value);
Packit 1422b7
		value = NULL;
Packit 1422b7
	}
Packit 1422b7
	free(vendor);
Packit 1422b7
	free(product);
Packit 1422b7
	free(version);
Packit 1422b7
	free(sigID);
Packit 1422b7
	free(name);
Packit 1422b7
	free(severity);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parser for Checkpoint LEA on-disk format.
Packit 1422b7
 * added 2015-06-18 by rgerhards, v1.1.2
Packit 1422b7
 */
Packit 1422b7
PARSER(CheckpointLEA)
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	size_t iName, lenName;
Packit 1422b7
	size_t iValue, lenValue;
Packit 1422b7
	int foundFields = 0;
Packit 1422b7
	char *name = NULL;
Packit 1422b7
	char *val = NULL;
Packit 1422b7
Packit 1422b7
	while(i < strLen) {
Packit 1422b7
		while(i < strLen && str[i] == ' ') /* skip leading SP */
Packit 1422b7
			++i;
Packit 1422b7
		if(i == strLen) { /* OK if just trailing space */
Packit 1422b7
			if(foundFields == 0)
Packit 1422b7
				FAIL(LN_WRONGPARSER);
Packit 1422b7
			break; /* we are done with the loop, all processed */
Packit 1422b7
		} else {
Packit 1422b7
			++foundFields;
Packit 1422b7
		}
Packit 1422b7
		iName = i;
Packit 1422b7
		/* TODO: do a stricter check? ... but we don't have a spec */
Packit 1422b7
		while(i < strLen && str[i] != ':') {
Packit 1422b7
			++i;
Packit 1422b7
		}
Packit 1422b7
		if(i+1 >= strLen || str[i] != ':')
Packit 1422b7
			FAIL(LN_WRONGPARSER);
Packit 1422b7
		lenName = i - iName;
Packit 1422b7
		++i; /* skip ':' */
Packit 1422b7
Packit 1422b7
		while(i < strLen && str[i] == ' ') /* skip leading SP */
Packit 1422b7
			++i;
Packit 1422b7
		iValue = i;
Packit 1422b7
		while(i < strLen && str[i] != ';') {
Packit 1422b7
			++i;
Packit 1422b7
		}
Packit 1422b7
		if(i+1 > strLen || str[i] != ';')
Packit 1422b7
			FAIL(LN_WRONGPARSER);
Packit 1422b7
		lenValue = i - iValue;
Packit 1422b7
		++i; /* skip ';' */
Packit 1422b7
Packit 1422b7
		if(value != NULL) {
Packit 1422b7
			CHKN(name = malloc(sizeof(char) * (lenName + 1)));
Packit 1422b7
			memcpy(name, str+iName, lenName);
Packit 1422b7
			name[lenName] = '\0';
Packit 1422b7
			CHKN(val = malloc(sizeof(char) * (lenValue + 1)));
Packit 1422b7
			memcpy(val, str+iValue, lenValue);
Packit 1422b7
			val[lenValue] = '\0';
Packit 1422b7
			if(*value == NULL)
Packit 1422b7
				CHKN(*value = json_object_new_object());
Packit 1422b7
			json_object *json;
Packit 1422b7
			CHKN(json = json_object_new_string(val));
Packit 1422b7
			json_object_object_add(*value, name, json);
Packit 1422b7
			free(name); name = NULL;
Packit 1422b7
			free(val); val = NULL;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = *offs - i;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	free(name);
Packit 1422b7
	free(val);
Packit 1422b7
	if(r != 0 && value != NULL && *value != NULL) {
Packit 1422b7
		json_object_put(*value);
Packit 1422b7
		value = NULL;
Packit 1422b7
	}
Packit 1422b7
	return r;
Packit 1422b7
}