static inline int ln_addTags_Syslog(struct json_object *taglist, es_str_t **str) { int r = 0; struct json_object *tagObj; int needComma = 0; const char *tagCstr; int i; assert(json_object_is_type(taglist, json_type_array)); CHKR(es_addBuf(str, " event.tags=\"", 13)); for (i = json_object_array_length(taglist) - 1; i >= 0; i--) { if(needComma) es_addChar(str, ','); else needComma = 1; CHKN(tagObj = json_object_array_get_idx(taglist, i)); CHKN(tagCstr = json_object_get_string(tagObj)); CHKR(es_addBuf(str, (char*)tagCstr, strlen(tagCstr))); } es_addChar(str, '"'); done: return r; }
/** * add unparsed string to event. */ static inline int addUnparsedField(const char *str, size_t strLen, int offs, struct json_object *json) { int r = 1; struct json_object *value; char *s = NULL; CHKN(s = strndup(str, strLen)); value = json_object_new_string(s); if (value == NULL) { goto done; } json_object_object_add(json, ORIGINAL_MSG_KEY, value); if (offs != 0) { value = json_object_new_string(s + offs); if (value == NULL) { goto done; } json_object_object_add(json, UNPARSED_DATA_KEY, value); } r = 0; done: free(s); return r; }
static int ln_addField_Syslog(char *name, struct json_object *field, es_str_t **str) { int r; const char *value; int needComma = 0; struct json_object *obj; int i; assert(field != NULL); assert(str != NULL); assert(*str != NULL); CHKR(es_addBuf(str, name, strlen(name))); CHKR(es_addBuf(str, "=\"", 2)); switch(json_object_get_type(field)) { case json_type_array: for (i = json_object_array_length(field) - 1; i >= 0; i--) { if(needComma) es_addChar(str, ','); else needComma = 1; CHKN(obj = json_object_array_get_idx(field, i)); CHKN(value = json_object_get_string(obj)); CHKR(ln_addValue_Syslog(value, str)); } break; case json_type_string: case json_type_int: CHKN(value = json_object_get_string(field)); CHKR(ln_addValue_Syslog(value, str)); break; case json_type_null: case json_type_boolean: case json_type_double: case json_type_object: CHKR(es_addBuf(str, "***unsupported type***", sizeof("***unsupported type***")-1)); break; default: CHKR(es_addBuf(str, "***OBJECT***", sizeof("***OBJECT***")-1)); } CHKR(es_addChar(str, '\"')); r = 0; done: return r; }
/** * Special parser for iptables-like name/value pairs. * The pull multiple fields. Note that once this parser has been selected, * it is very unlikely to be left, as it is *very* generic. This parser is * required because practice shows that already-structured data like iptables * can otherwise not be processed by liblognorm in a meaningful way. * * @param[in] tree current tree to process * @param[in] str string to be matched against (the to-be-normalized data) * @param[in] strLen length of str * @param[in/out] offs start position in input data, on exit first unparsed position * @param[in/out] event handle to event that is being created during normalization * * @return 0 if parser was successfully, something else on error */ static int ln_iptablesParser(struct ln_ptree *tree, const char *str, size_t strLen, size_t *offs, struct json_object *json) { int r; size_t o = *offs; es_str_t *fname; es_str_t *fval; const char *pstr; const char *end; struct json_object *value; ln_dbgprintf(tree->ctx, "%zu enter iptables parser, len %zu", *offs, strLen); if(o == strLen) { r = -1; /* can not be, we have no n/v pairs! */ goto done; } end = str + strLen; pstr = str + o; while(pstr < end) { while(pstr < end && isspace(*pstr)) ++pstr; CHKN(fname = es_newStr(16)); while(pstr < end && !isspace(*pstr) && *pstr != '=') { es_addChar(&fname, *pstr); ++pstr; } if(pstr < end && *pstr == '=') { CHKN(fval = es_newStr(16)); ++pstr; /* error on space */ while(pstr < end && !isspace(*pstr)) { es_addChar(&fval, *pstr); ++pstr; } } else { CHKN(fval = es_newStrFromCStr("[*PRESENT*]", sizeof("[*PRESENT*]")-1)); } char *cn, *cv; CHKN(cn = ln_es_str2cstr(&fname)); CHKN(cv = ln_es_str2cstr(&fval)); if (tree->ctx->debug) { ln_dbgprintf(tree->ctx, "iptables parser extracts %s=%s", cn, cv); } CHKN(value = json_object_new_string(cv)); json_object_object_add(json, cn, value); es_deleteStr(fval); es_deleteStr(fname); } r = 0; *offs = strLen; done: ln_dbgprintf(tree->ctx, "%zu iptables parser returns %d", *offs, r); return r; }
int ln_normalize(ln_ctx ctx, const char *str, size_t strLen, struct json_object **json_p) { int r; int left; struct ln_ptree *endNode = NULL; while (isspace(*str)) { ++str; --strLen; } if(*json_p == NULL) { CHKN(*json_p = json_object_new_object()); } left = ln_normalizeRec(ctx->ptree, str, strLen, 0, *json_p, &endNode); if(ctx->debug) { if(left == 0) { ln_dbgprintf(ctx, "final result for normalizer: left %d, endNode %p, " "isTerminal %d, tagbucket %p", left, endNode, endNode->flags.isTerminal, endNode->tags); } else { ln_dbgprintf(ctx, "final result for normalizer: left %d, endNode %p", left, endNode); } } if(left != 0 || !endNode->flags.isTerminal) { /* we could not successfully parse, some unparsed items left */ if(left < 0) { addUnparsedField(str, strLen, strLen, *json_p); } else { addUnparsedField(str, strLen, strLen - left, *json_p); } } else { /* success, finalize event */ if(endNode->tags != NULL) { /* add tags to an event */ json_object_get(endNode->tags); json_object_object_add(*json_p, "event.tags", endNode->tags); CHKR(ln_annotate(ctx, *json_p, endNode->tags)); } } r = 0; done: return r; }
/** * Recursive step of the normalizer. It walks the parse tree and calls itself * recursively when this is appropriate. It also implements backtracking in * those (hopefully rare) cases where it is required. * * @param[in] tree current tree to process * @param[in] string string to be matched against (the to-be-normalized data) * @param[in] offs start position in input data * @param[in/out] event handle to event that is being created during normalization * @param[out] endNode if a match was found, this is the matching node (undefined otherwise) * * @return number of characters left unparsed by following the subtree, negative if * the to-be-parsed message is shorter than the rule sample by this number of * characters. */ static int ln_normalizeRec(struct ln_ptree *tree, const char *str, size_t strLen, size_t offs, struct json_object *json, struct ln_ptree **endNode) { int r; int localR; size_t i; int left; ln_fieldList_t *node; char *cstr; const char *c; unsigned char *cpfix; unsigned ipfix; size_t parsed; char *namestr; struct json_object *value; if(offs >= strLen) { *endNode = tree; r = -tree->lenPrefix; goto done; } c = str; cpfix = prefixBase(tree); node = tree->froot; r = strLen - offs; /* first we need to check if the common prefix matches (and consume input data while we do) */ ipfix = 0; while(offs < strLen && ipfix < tree->lenPrefix) { ln_dbgprintf(tree->ctx, "%zu: prefix compare '%c', '%c'", offs, c[offs], cpfix[ipfix]); if(c[offs] != cpfix[ipfix]) { r -= ipfix; goto done; } ++offs, ++ipfix; } if(ipfix != tree->lenPrefix) { /* incomplete prefix match --> to-be-normalized string too short */ r = ipfix - tree->lenPrefix; goto done; } r -= ipfix; ln_dbgprintf(tree->ctx, "%zu: prefix compare succeeded, still valid", offs); /* now try the parsers */ while(node != NULL) { if(tree->ctx->debug) { cstr = es_str2cstr(node->name, NULL); ln_dbgprintf(tree->ctx, "%zu:trying parser for field '%s': %p", offs, cstr, node->parser); free(cstr); } i = offs; if(node->isIPTables) { localR = ln_iptablesParser(tree, str, strLen, &i, json); ln_dbgprintf(tree->ctx, "%zu iptables parser return, i=%zu", offs, i); if(localR == 0) { /* potential hit, need to verify */ ln_dbgprintf(tree->ctx, "potential hit, trying subtree"); left = ln_normalizeRec(node->subtree, str, strLen, i, json, endNode); if(left == 0 && (*endNode)->flags.isTerminal) { ln_dbgprintf(tree->ctx, "%zu: parser matches at %zu", offs, i); r = 0; goto done; } ln_dbgprintf(tree->ctx, "%zu nonmatch, backtracking required, left=%d", offs, left); if(left < r) r = left; } } else { value = NULL; localR = node->parser(str, strLen, &i, node, &parsed, &value); ln_dbgprintf(tree->ctx, "parser returns %d, parsed %zu", localR, parsed); if(localR == 0) { /* potential hit, need to verify */ ln_dbgprintf(tree->ctx, "potential hit, trying subtree"); left = ln_normalizeRec(node->subtree, str, strLen, i + parsed, json, endNode); if(left == 0 && (*endNode)->flags.isTerminal) { ln_dbgprintf(tree->ctx, "%zu: parser matches at %zu", offs, i); if(es_strbufcmp(node->name, (unsigned char*)"-", 1)) { /* Store the value here; create json if not already created */ if (value == NULL) { CHKN(cstr = strndup(str + i, parsed)); value = json_object_new_string(cstr); free(cstr); } if (value == NULL) { ln_dbgprintf(tree->ctx, "unable to create json"); goto done; } namestr = ln_es_str2cstr(&node->name); json_object_object_add(json, namestr, value); } else { if (value != NULL) { /* Free the unneeded value */ json_object_put(value); } } r = 0; goto done; } ln_dbgprintf(tree->ctx, "%zu nonmatch, backtracking required, left=%d", offs, left); if (value != NULL) { /* Free the value if it was created */ json_object_put(value); } if(left < r) r = left; } } node = node->next; } if(offs == strLen) { *endNode = tree; r = 0; goto done; } if(offs < strLen) { unsigned char cc = str[offs]; ln_dbgprintf(tree->ctx, "%zu no field, trying subtree char '%c': %p", offs, cc, tree->subtree[cc]); } else { ln_dbgprintf(tree->ctx, "%zu no field, offset already beyond end", offs); } /* now let's see if we have a literal */ if(tree->subtree[(int)str[offs]] != NULL) { left = ln_normalizeRec(tree->subtree[(int)str[offs]], str, strLen, offs + 1, json, endNode); if(left < r) r = left; } done: ln_dbgprintf(tree->ctx, "%zu returns %d", offs, r); return r; }