1 /* rainerscript.c - routines to support RainerScript config language
2  *
3  * Module begun 2011-07-01 by Rainer Gerhards
4  *
5  * Copyright 2011-2019 Rainer Gerhards and Others.
6  *
7  * This file is part of the rsyslog runtime library.
8  *
9  * The rsyslog runtime library is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * The rsyslog runtime library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with the rsyslog runtime library.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * A copy of the GPL can be found in the file "COPYING" in this distribution.
23  * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
24  */
25 #include "config.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <glob.h>
31 #include <errno.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #include <unistd.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <libestr.h>
38 #include <time.h>
39 
40 #include "rsyslog.h"
41 #include "rainerscript.h"
42 #include "conf.h"
43 #include "parserif.h"
44 #include "parse.h"
45 #include "rsconf.h"
46 #include "grammar.h"
47 #include "queue.h"
48 #include "srUtils.h"
49 #include "regexp.h"
50 #include "datetime.h"
51 #include "obj.h"
52 #include "modules.h"
53 #include "ruleset.h"
54 #include "msg.h"
55 #include "wti.h"
56 #include "unicode-helper.h"
57 #include "errmsg.h"
58 
59 PRAGMA_INGORE_Wswitch_enum
60 
61 DEFobjCurrIf(obj)
62 DEFobjCurrIf(regexp)
63 DEFobjCurrIf(datetime)
64 
65 struct cnfexpr* cnfexprOptimize(struct cnfexpr *expr);
66 static void cnfstmtOptimizePRIFilt(struct cnfstmt *stmt);
67 static void cnfarrayPrint(struct cnfarray *ar, int indent);
68 struct cnffunc * cnffuncNew_prifilt(int fac);
69 
70 static struct cnfparamdescr incpdescr[] = {
71 	{ "file", eCmdHdlrString, 0 },
72 	{ "text", eCmdHdlrString, 0 },
73 	{ "mode", eCmdHdlrGetWord, 0 }
74 };
75 static struct cnfparamblk incpblk =
76 	{ CNFPARAMBLK_VERSION,
77 	  sizeof(incpdescr)/sizeof(struct cnfparamdescr),
78 	  incpdescr
79 	};
80 
81 /* debug support: convert token to a human-readable string. Note that
82  * this function only supports a single thread due to a static buffer.
83  * This is deemed a solid solution, as it is intended to be used during
84  * startup, only.
85  * NOTE: This function MUST be updated if new tokens are defined in the
86  *       grammar.
87  */
88 static const char *
tokenToString(const int token)89 tokenToString(const int token)
90 {
91 	const char *tokstr;
92 	static char tokbuf[512];
93 
94 	switch(token) {
95 	case NAME: tokstr = "NAME"; break;
96 	case FUNC: tokstr = "FUNC"; break;
97 	case BEGINOBJ: tokstr ="BEGINOBJ"; break;
98 	case ENDOBJ: tokstr ="ENDOBJ"; break;
99 	case BEGIN_ACTION: tokstr ="BEGIN_ACTION"; break;
100 	case BEGIN_PROPERTY: tokstr ="BEGIN_PROPERTY"; break;
101 	case BEGIN_CONSTANT: tokstr ="BEGIN_CONSTANT"; break;
102 	case BEGIN_TPL: tokstr ="BEGIN_TPL"; break;
103 	case BEGIN_RULESET: tokstr ="BEGIN_RULESET"; break;
104 	case STOP: tokstr ="STOP"; break;
105 	case SET: tokstr ="SET"; break;
106 	case UNSET: tokstr ="UNSET"; break;
107 	case CONTINUE: tokstr ="CONTINUE"; break;
108 	case CALL: tokstr ="CALL"; break;
109 	case LEGACY_ACTION: tokstr ="LEGACY_ACTION"; break;
110 	case LEGACY_RULESET: tokstr ="LEGACY_RULESET"; break;
111 	case PRIFILT: tokstr ="PRIFILT"; break;
112 	case PROPFILT: tokstr ="PROPFILT"; break;
113 	case IF: tokstr ="IF"; break;
114 	case THEN: tokstr ="THEN"; break;
115 	case ELSE: tokstr ="ELSE"; break;
116 	case OR: tokstr ="OR"; break;
117 	case AND: tokstr ="AND"; break;
118 	case NOT: tokstr ="NOT"; break;
119 	case VAR: tokstr ="VAR"; break;
120 	case STRING: tokstr ="STRING"; break;
121 	case NUMBER: tokstr ="NUMBER"; break;
122 	case CMP_EQ: tokstr ="CMP_EQ"; break;
123 	case CMP_NE: tokstr ="CMP_NE"; break;
124 	case CMP_LE: tokstr ="CMP_LE"; break;
125 	case CMP_GE: tokstr ="CMP_GE"; break;
126 	case CMP_LT: tokstr ="CMP_LT"; break;
127 	case CMP_GT: tokstr ="CMP_GT"; break;
128 	case CMP_CONTAINS: tokstr ="CMP_CONTAINS"; break;
129 	case CMP_CONTAINSI: tokstr ="CMP_CONTAINSI"; break;
130 	case CMP_STARTSWITH: tokstr ="CMP_STARTSWITH"; break;
131 	case CMP_STARTSWITHI: tokstr ="CMP_STARTSWITHI"; break;
132 	case UMINUS: tokstr ="UMINUS"; break;
133 	case '&': tokstr ="&"; break;
134 	case '+': tokstr ="+"; break;
135 	case '-': tokstr ="-"; break;
136 	case '*': tokstr ="*"; break;
137 	case '/': tokstr ="/"; break;
138 	case '%': tokstr ="%"; break;
139 	case 'M': tokstr ="M"; break;
140 	case 'N': tokstr ="N"; break;
141 	case 'S': tokstr ="S"; break;
142 	case 'V': tokstr ="V"; break;
143 	case 'F': tokstr ="F"; break;
144 	case 'A': tokstr ="A"; break;
145 	case S_FUNC_EXISTS: tokstr ="exists()"; break;
146 	default: snprintf(tokbuf, sizeof(tokbuf), "%c[%d]", token, token);
147 		 tokstr = tokbuf; break;
148 	}
149 	return tokstr;
150 }
151 
152 
153 const char*
getFIOPName(const unsigned iFIOP)154 getFIOPName(const unsigned iFIOP)
155 {
156 	const char *pRet;
157 	switch(iFIOP) {
158 		case FIOP_CONTAINS:
159 			pRet = "contains";
160 			break;
161 		case FIOP_ISEQUAL:
162 			pRet = "isequal";
163 			break;
164 		case FIOP_STARTSWITH:
165 			pRet = "startswith";
166 			break;
167 		case FIOP_REGEX:
168 			pRet = "regex";
169 			break;
170 		case FIOP_EREREGEX:
171 			pRet = "ereregex";
172 			break;
173 		case FIOP_ISEMPTY:
174 			pRet = "isempty";
175 			break;
176 		default:
177 			pRet = "NOP";
178 			break;
179 	}
180 	return pRet;
181 }
182 
183 const char*
cnfFiltType2str(const enum cnfFiltType filttype)184 cnfFiltType2str(const enum cnfFiltType filttype)
185 {
186 	switch(filttype) {
187 	case CNFFILT_NONE:
188 		return("filter:none");
189 	case CNFFILT_PRI:
190 		return("filter:pri");
191 	case CNFFILT_PROP:
192 		return("filter:prop");
193 	case CNFFILT_SCRIPT:
194 		return("filter:script");
195 	default:
196 		return("error:invalid_filter_type");	/* should never be reached */
197 	}
198 }
199 
200 const char*
cnfobjType2str(const enum cnfobjType ot)201 cnfobjType2str(const enum cnfobjType ot)
202 {
203 	switch(ot) {
204 	case CNFOBJ_ACTION:
205 		return "action";
206 		break;
207 	case CNFOBJ_RULESET:
208 		return "ruleset";
209 		break;
210 	case CNFOBJ_GLOBAL:
211 		return "global";
212 		break;
213 	case CNFOBJ_INPUT:
214 		return "input";
215 		break;
216 	case CNFOBJ_MODULE:
217 		return "module";
218 		break;
219 	case CNFOBJ_TPL:
220 		return "template";
221 		break;
222 	case CNFOBJ_PROPERTY:
223 		return "property";
224 		break;
225 	case CNFOBJ_CONSTANT:
226 		return "constant";
227 		break;
228 	case CNFOBJ_MAINQ:
229 		return "main_queue";
230 	case CNFOBJ_LOOKUP_TABLE:
231 		return "lookup_table";
232 	case CNFOBJ_PARSER:
233 		return "parser";
234 		break;
235 	case CNFOBJ_TIMEZONE:
236 		return "timezone";
237 		break;
238 	case CNFOBJ_DYN_STATS:
239 		return "dyn_stats";
240 		break;
241 	case CNFOBJ_PERCTILE_STATS:
242 		return "perctile_stats";
243 		break;
244 	default:return "error: invalid cnfobjType";
245 	}
246 }
247 
248 /* This function takes the filter part of a property
249  * based filter and decodes it. It processes the line up to the beginning
250  * of the action part.
251  */
252 static rsRetVal
DecodePropFilter(uchar * pline,struct cnfstmt * stmt)253 DecodePropFilter(uchar *pline, struct cnfstmt *stmt)
254 {
255 	rsParsObj *pPars = NULL;
256 	cstr_t *pCSCompOp = NULL;
257 	cstr_t *pCSPropName = NULL;
258 	int iOffset; /* for compare operations */
259 	DEFiRet;
260 
261 	assert(pline != NULL);
262 
263 	DBGPRINTF("Decoding property-based filter '%s'\n", pline);
264 
265 	/* create parser object starting with line string without leading colon */
266 	if((iRet = rsParsConstructFromSz(&pPars, pline+1)) != RS_RET_OK) {
267 		parser_errmsg("error %d constructing parser object", iRet);
268 		FINALIZE;
269 	}
270 
271 	/* read property */
272 	iRet = parsDelimCStr(pPars, &pCSPropName, ',', 1, 1, 1);
273 	if(iRet != RS_RET_OK) {
274 		parser_errmsg("error %d parsing filter property", iRet);
275 		FINALIZE;
276 	}
277 	CHKiRet(msgPropDescrFill(&stmt->d.s_propfilt.prop, cstrGetSzStrNoNULL(pCSPropName),
278 		cstrLen(pCSPropName)));
279 
280 	/* read operation */
281 	iRet = parsDelimCStr(pPars, &pCSCompOp, ',', 1, 1, 1);
282 	if(iRet != RS_RET_OK) {
283 		parser_errmsg("error %d compare operation property - ignoring selector", iRet);
284 		FINALIZE;
285 	}
286 
287 	/* we now first check if the condition is to be negated. To do so, we first
288 	 * must make sure we have at least one char in the param and then check the
289 	 * first one.
290 	 * rgerhards, 2005-09-26
291 	 */
292 	if(rsCStrLen(pCSCompOp) > 0) {
293 		if(*rsCStrGetBufBeg(pCSCompOp) == '!') {
294 			stmt->d.s_propfilt.isNegated = 1;
295 			iOffset = 1; /* ignore '!' */
296 		} else {
297 			stmt->d.s_propfilt.isNegated = 0;
298 			iOffset = 0;
299 		}
300 	} else {
301 		stmt->d.s_propfilt.isNegated = 0;
302 		iOffset = 0;
303 	}
304 
305 	if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "contains", 8)) {
306 		stmt->d.s_propfilt.operation = FIOP_CONTAINS;
307 	} else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "isequal", 7)) {
308 		stmt->d.s_propfilt.operation = FIOP_ISEQUAL;
309 	} else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "isempty", 7)) {
310 		stmt->d.s_propfilt.operation = FIOP_ISEMPTY;
311 	} else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "startswith", 10)) {
312 		stmt->d.s_propfilt.operation = FIOP_STARTSWITH;
313 	} else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (unsigned char*) "regex", 5)) {
314 		stmt->d.s_propfilt.operation = FIOP_REGEX;
315 	} else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (unsigned char*) "ereregex", 8)) {
316 		stmt->d.s_propfilt.operation = FIOP_EREREGEX;
317 	} else {
318 		parser_errmsg("error: invalid compare operation '%s'",
319 		           (char*) rsCStrGetSzStrNoNULL(pCSCompOp));
320 		ABORT_FINALIZE(RS_RET_ERR);
321 	}
322 
323 	if(stmt->d.s_propfilt.operation != FIOP_ISEMPTY) {
324 		/* read compare value */
325 		iRet = parsQuotedCStr(pPars, &stmt->d.s_propfilt.pCSCompValue);
326 		if(iRet != RS_RET_OK) {
327 			parser_errmsg("error %d compare value property", iRet);
328 			FINALIZE;
329 		}
330 	}
331 
332 finalize_it:
333 	if(pPars != NULL)
334 		rsParsDestruct(pPars);
335 	if(pCSCompOp != NULL)
336 		rsCStrDestruct(&pCSCompOp);
337 	if(pCSPropName != NULL)
338 		cstrDestruct(&pCSPropName);
339 	RETiRet;
340 }
341 
342 static void
prifiltInvert(struct funcData_prifilt * __restrict__ const prifilt)343 prifiltInvert(struct funcData_prifilt *__restrict__ const prifilt)
344 {
345 	int i;
346 	for(i = 0 ; i < LOG_NFACILITIES+1 ; ++i) {
347 		prifilt->pmask[i] = ~prifilt->pmask[i];
348 	}
349 }
350 
351 /* set prifilt so that it matches for some severities, sev is its numerical
352  * value. Mode is one of the compop tokens CMP_EQ, CMP_LT, CMP_LE, CMP_GT,
353  * CMP_GE, CMP_NE.
354  */
355 static void
prifiltSetSeverity(struct funcData_prifilt * prifilt,int sev,int mode)356 prifiltSetSeverity(struct funcData_prifilt *prifilt, int sev, int mode)
357 {
358 	static int lessthanmasks[] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
359 	int i;
360 	for(i = 0 ; i < LOG_NFACILITIES+1 ; ++i) {
361 		if(mode == CMP_EQ || mode == CMP_NE)
362 			prifilt->pmask[i] = 1 << sev;
363 		else if(mode == CMP_LT)
364 			prifilt->pmask[i] = lessthanmasks[sev];
365 		else if(mode == CMP_LE)
366 			prifilt->pmask[i] = lessthanmasks[sev+1];
367 		else if(mode == CMP_GT)
368 			prifilt->pmask[i] = ~lessthanmasks[sev+1];
369 		else if(mode == CMP_GE)
370 			prifilt->pmask[i] = ~lessthanmasks[sev];
371 		else
372 			DBGPRINTF("prifiltSetSeverity: program error, invalid mode %s\n",
373 				  tokenToString(mode));
374 	}
375 	if(mode == CMP_NE)
376 		prifiltInvert(prifilt);
377 }
378 
379 /* set prifilt so that it matches for some facilities, fac is its numerical
380  * value. Mode is one of the compop tokens CMP_EQ, CMP_LT, CMP_LE, CMP_GT,
381  * CMP_GE, CMP_NE. For the given facilities, all severities are enabled.
382  * NOTE: fac MUST be in the range 0..24 (not multiplied by 8)!
383  */
384 static void
prifiltSetFacility(struct funcData_prifilt * __restrict__ const prifilt,const int fac,const int mode)385 prifiltSetFacility(struct funcData_prifilt *__restrict__ const prifilt, const int fac, const int mode)
386 {
387 	int i;
388 
389 	memset(prifilt->pmask, 0, sizeof(prifilt->pmask));
390 	switch(mode) {
391 	case CMP_EQ:
392 		prifilt->pmask[fac] = TABLE_ALLPRI;
393 		break;
394 	case CMP_NE:
395 		prifilt->pmask[fac] = TABLE_ALLPRI;
396 		prifiltInvert(prifilt);
397 		break;
398 	case CMP_LT:
399 		for(i = 0 ; i < fac ; ++i)
400 			prifilt->pmask[i] = TABLE_ALLPRI;
401 		break;
402 	case CMP_LE:
403 		for(i = 0 ; i < fac+1 ; ++i)
404 			prifilt->pmask[i] = TABLE_ALLPRI;
405 		break;
406 	case CMP_GE:
407 		for(i = fac ; i < LOG_NFACILITIES+1 ; ++i)
408 			prifilt->pmask[i] = TABLE_ALLPRI;
409 		break;
410 	case CMP_GT:
411 		for(i = fac+1 ; i < LOG_NFACILITIES+1 ; ++i)
412 			prifilt->pmask[i] = TABLE_ALLPRI;
413 		break;
414 	default:break;
415 	}
416 }
417 
418 /* combine a prifilt with AND/OR (the respective token values are
419  * used to keep things simple).
420  */
421 static void
prifiltCombine(struct funcData_prifilt * __restrict__ const prifilt,struct funcData_prifilt * __restrict__ const prifilt2,const int mode)422 prifiltCombine(struct funcData_prifilt *__restrict__ const prifilt,
423 	       struct funcData_prifilt *__restrict__ const prifilt2,
424 	       const int mode)
425 {
426 	int i;
427 	for(i = 0 ; i < LOG_NFACILITIES+1 ; ++i) {
428 		if(mode == AND)
429 			prifilt->pmask[i] = prifilt->pmask[i] & prifilt2->pmask[i];
430 		else
431 			prifilt->pmask[i] = prifilt->pmask[i] | prifilt2->pmask[i];
432 	}
433 }
434 
435 
436 void
readConfFile(FILE * const fp,es_str_t ** str)437 readConfFile(FILE * const fp, es_str_t **str)
438 {
439 	char ln[10240];
440 	char buf[512];
441 	int lenBuf;
442 	int bWriteLineno = 0;
443 	int len, i;
444 	int start;	/* start index of to be submitted text */
445 	int bContLine = 0;
446 	int lineno = 0;
447 
448 	*str = es_newStr(4096);
449 
450 	while(fgets(ln, sizeof(ln), fp) != NULL) {
451 		++lineno;
452 		if(bWriteLineno) {
453 			bWriteLineno = 0;
454 			lenBuf = sprintf(buf, "PreprocFileLineNumber(%d)\n", lineno);
455 			es_addBuf(str, buf, lenBuf);
456 		}
457 		len = strlen(ln);
458 		/* if we are continuation line, we need to drop leading WS */
459 		if(bContLine) {
460 			for(start = 0 ; start < len && isspace(ln[start]) ; ++start)
461 				/* JUST SCAN */;
462 		} else {
463 			start = 0;
464 		}
465 		for(i = len - 1 ; i >= start && isspace(ln[i]) ; --i)
466 			/* JUST SCAN */;
467 		if(i >= 0) {
468 			if(ln[i] == '\\') {
469 				--i;
470 				bContLine = 1;
471 			} else {
472 				if(bContLine) /* write line number if we had cont line */
473 					bWriteLineno = 1;
474 				bContLine = 0;
475 			}
476 			/* add relevant data to buffer */
477 			es_addBuf(str, ln+start, i+1 - start);
478 		}
479 		if(!bContLine)
480 			es_addChar(str, '\n');
481 	}
482 	/* indicate end of buffer to flex */
483 	es_addChar(str, '\0');
484 	es_addChar(str, '\0');
485 }
486 
487 /* comparison function for qsort() and bsearch() string array compare */
488 static int
qs_arrcmp(const void * s1,const void * s2)489 qs_arrcmp(const void *s1, const void *s2)
490 {
491 	return es_strcmp(*((es_str_t**)s1), *((es_str_t**)s2));
492 }
493 
494 
495 struct objlst*
objlstNew(struct cnfobj * o)496 objlstNew(struct cnfobj *o)
497 {
498 	struct objlst *lst;
499 
500 	if((lst = malloc(sizeof(struct objlst))) != NULL) {
501 		lst->next = NULL;
502 		lst->obj = o;
503 	}
504 cnfobjPrint(o);
505 
506 	return lst;
507 }
508 
509 /* add object to end of object list, always returns pointer to root object */
510 struct objlst*
objlstAdd(struct objlst * root,struct cnfobj * o)511 objlstAdd(struct objlst *root, struct cnfobj *o)
512 {
513 	struct objlst *l;
514 	struct objlst *newl;
515 
516 	newl = objlstNew(o);
517 	if(root == 0) {
518 		root = newl;
519 	} else { /* find last, linear search ok, as only during config phase */
520 		for(l = root ; l->next != NULL ; l = l->next)
521 			;
522 		l->next = newl;
523 	}
524 	return root;
525 }
526 
527 /* add stmt to current script, always return root stmt pointer */
528 struct cnfstmt*
scriptAddStmt(struct cnfstmt * root,struct cnfstmt * s)529 scriptAddStmt(struct cnfstmt *root, struct cnfstmt *s)
530 {
531 	struct cnfstmt *l;
532 
533 	if(root == NULL) {
534 		root = s;
535 	} else { /* find last, linear search ok, as only during config phase */
536 		for(l = root ; l->next != NULL ; l = l->next)
537 			;
538 		l->next = s;
539 	}
540 	return root;
541 }
542 
543 void
objlstDestruct(struct objlst * lst)544 objlstDestruct(struct objlst *lst)
545 {
546 	struct objlst *toDel;
547 
548 	while(lst != NULL) {
549 		toDel = lst;
550 		lst = lst->next;
551 		cnfobjDestruct(toDel->obj);
552 		free(toDel);
553 	}
554 }
555 
556 void
objlstPrint(struct objlst * lst)557 objlstPrint(struct objlst *lst)
558 {
559 	dbgprintf("objlst %p:\n", lst);
560 	while(lst != NULL) {
561 		cnfobjPrint(lst->obj);
562 		lst = lst->next;
563 	}
564 }
565 
566 struct nvlst* ATTR_NONNULL(1)
nvlstNewStr(es_str_t * const value)567 nvlstNewStr(es_str_t *const value)
568 {
569 	struct nvlst *lst;
570 
571 	if((lst = malloc(sizeof(struct nvlst))) != NULL) {
572 		lst->next = NULL;
573 		lst->val.datatype = 'S';
574 		lst->val.d.estr = value;
575 		lst->bUsed = 0;
576 	}
577 
578 	return lst;
579 }
580 
581 struct nvlst* ATTR_NONNULL(1)
nvlstNewStrBackticks(es_str_t * const value)582 nvlstNewStrBackticks(es_str_t *const value)
583 {
584 	es_str_t *val = NULL;
585 	const char *realval;
586 
587 	char *const param = es_str2cstr(value, NULL);
588 	if(param == NULL)
589 		goto done;
590 
591 	if(strncmp(param, "echo $", sizeof("echo $")-1) != 0) {
592 		parser_errmsg("invalid backtick parameter `%s` currently "
593 			"only `echo $<var>` is supported - replaced by "
594 			"empty strong (\"\")", param);
595 		realval = NULL;
596 	} else {
597 		size_t i;
598 		const size_t len = strlen(param);
599 		for(i = len - 1 ; isspace(param[i]) ; --i) {
600 			; /* just go down */
601 		}
602 		if(i > 6 && i < len - 1) {
603 			param[i+1] = '\0';
604 		}
605 		realval = getenv(param+6);
606 	}
607 
608 	free((void*)param);
609 	if(realval == NULL) {
610 		realval = "";
611 	}
612 	val = es_newStrFromCStr(realval, strlen(realval));
613 	es_deleteStr(value);
614 
615 done:
616 	return (val == NULL) ? NULL : nvlstNewStr(val);
617 }
618 
619 struct nvlst*
nvlstNewArray(struct cnfarray * ar)620 nvlstNewArray(struct cnfarray *ar)
621 {
622 	struct nvlst *lst;
623 
624 	if((lst = malloc(sizeof(struct nvlst))) != NULL) {
625 		lst->next = NULL;
626 		lst->val.datatype = 'A';
627 		lst->val.d.ar = ar;
628 		lst->bUsed = 0;
629 	}
630 
631 	return lst;
632 }
633 
634 struct nvlst*
nvlstSetName(struct nvlst * lst,es_str_t * name)635 nvlstSetName(struct nvlst *lst, es_str_t *name)
636 {
637 	lst->name = name;
638 	return lst;
639 }
640 
641 void
nvlstDestruct(struct nvlst * lst)642 nvlstDestruct(struct nvlst *lst)
643 {
644 	struct nvlst *toDel;
645 
646 	while(lst != NULL) {
647 		toDel = lst;
648 		lst = lst->next;
649 		es_deleteStr(toDel->name);
650 		varDelete(&toDel->val);
651 		free(toDel);
652 	}
653 }
654 
655 void
nvlstPrint(struct nvlst * lst)656 nvlstPrint(struct nvlst *lst)
657 {
658 	char *name, *value;
659 	dbgprintf("nvlst %p:\n", lst);
660 	while(lst != NULL) {
661 		name = es_str2cstr(lst->name, NULL);
662 		switch(lst->val.datatype) {
663 		case 'A':
664 			dbgprintf("\tname: '%s':\n", name);
665 			cnfarrayPrint(lst->val.d.ar, 5);
666 			break;
667 		case 'S':
668 			value = es_str2cstr(lst->val.d.estr, NULL);
669 			dbgprintf("\tname: '%s', value '%s'\n", name, value);
670 			free(value);
671 			break;
672 		default:dbgprintf("nvlstPrint: unknown type '%s'\n",
673 				tokenToString(lst->val.datatype));
674 			break;
675 		}
676 		free(name);
677 		lst = lst->next;
678 	}
679 }
680 
681 /* find a name starting at node lst. Returns node with this
682  * name or NULL, if none found.
683  */
684 struct nvlst*
nvlstFindName(struct nvlst * lst,es_str_t * name)685 nvlstFindName(struct nvlst *lst, es_str_t *name)
686 {
687 	while(lst != NULL && es_strcmp(lst->name, name))
688 		lst = lst->next;
689 	return lst;
690 }
691 
692 
693 /* find a name starting at node lst. Same as nvlstFindName, but
694  * for classical C strings. This is useful because the config system
695  * uses C string constants.
696  */
697 static struct nvlst*
nvlstFindNameCStr(struct nvlst * lst,const char * const __restrict__ name)698 nvlstFindNameCStr(struct nvlst *lst, const char *const __restrict__ name)
699 {
700 	es_size_t lenName = strlen(name);
701 	while(lst != NULL && es_strcasebufcmp(lst->name, (uchar*)name, lenName))
702 		lst = lst->next;
703 	return lst;
704 }
705 
706 /* check if the nvlst is disabled, and mark config.enabled directive
707  * as used if it is not. Returns 1 if block is disabled, 0 otherwise.
708  */
nvlstChkDisabled(struct nvlst * lst)709 int nvlstChkDisabled(struct nvlst *lst)
710 {
711 	struct nvlst *valnode;
712 
713 	if((valnode = nvlstFindNameCStr(lst, "config.enabled")) != NULL) {
714 		valnode->bUsed = 1;
715 		if(es_strbufcmp(valnode->val.d.estr, (unsigned char*) "on", 2)) {
716 			return 1;
717 		}
718 	}
719 	return 0;
720 }
721 
722 
723 /* check if there are duplicate names inside a nvlst and emit
724  * an error message, if so.
725  */
726 static void
nvlstChkDupes(struct nvlst * lst)727 nvlstChkDupes(struct nvlst *lst)
728 {
729 	char *cstr;
730 
731 	while(lst != NULL) {
732 		if(nvlstFindName(lst->next, lst->name) != NULL) {
733 			cstr = es_str2cstr(lst->name, NULL);
734 			parser_errmsg("duplicate parameter '%s' -- "
735 			  "interpretation is ambigious, one value "
736 			  "will be randomly selected. Fix this problem.",
737 			  cstr);
738 			free(cstr);
739 		}
740 		lst = lst->next;
741 	}
742 }
743 
744 
745 /* check for unused params and emit error message is found. This must
746  * be called after all config params have been pulled from the object
747  * (otherwise the flags are not correctly set).
748  */
749 void
nvlstChkUnused(struct nvlst * lst)750 nvlstChkUnused(struct nvlst *lst)
751 {
752 	char *cstr;
753 
754 	while(lst != NULL) {
755 		if(!lst->bUsed) {
756 			cstr = es_str2cstr(lst->name, NULL);
757 			parser_errmsg("parameter '%s' not known -- "
758 			  "typo in config file?",
759 			  cstr);
760 			free(cstr);
761 		}
762 		lst = lst->next;
763 	}
764 }
765 
766 
767 static int
doGetSize(struct nvlst * valnode,struct cnfparamdescr * param,struct cnfparamvals * val)768 doGetSize(struct nvlst *valnode, struct cnfparamdescr *param,
769 	  struct cnfparamvals *val)
770 {
771 	unsigned char *c;
772 	es_size_t i;
773 	long long n;
774 	int r;
775 	c = es_getBufAddr(valnode->val.d.estr);
776 	n = 0;
777 	i = 0;
778 	while(i < es_strlen(valnode->val.d.estr) && isdigit(*c)) {
779 		n = 10 * n + *c - '0';
780 		++i;
781 		++c;
782 	}
783 	if(i < es_strlen(valnode->val.d.estr)) {
784 		++i;
785 		switch(*c) {
786 		/* traditional binary-based definitions */
787 		case 'k': n *= 1024; break;
788 		case 'm': n *= 1024 * 1024; break;
789 		case 'g': n *= 1024 * 1024 * 1024; break;
790 		case 't': n *= (int64) 1024 * 1024 * 1024 * 1024; break; /* tera */
791 		case 'p': n *= (int64) 1024 * 1024 * 1024 * 1024 * 1024; break; /* peta */
792 		case 'e': n *= (int64) 1024 * 1024 * 1024 * 1024 * 1024 * 1024; break; /* exa */
793 		/* and now the "new" 1000-based definitions */
794 		case 'K': n *= 1000; break;
795 	        case 'M': n *= 1000000; break;
796 		case 'G': n *= 1000000000; break;
797 			  /* we need to use the multiplication below because otherwise
798 			   * the compiler gets an error during constant parsing */
799 		case 'T': n *= (int64) 1000       * 1000000000; break; /* tera */
800 		case 'P': n *= (int64) 1000000    * 1000000000; break; /* peta */
801 		case 'E': n *= (int64) 1000000000 * 1000000000; break; /* exa */
802 		default: --i; break; /* indicates error */
803 		}
804 	}
805 	if(i == es_strlen(valnode->val.d.estr)) {
806 		val->val.datatype = 'N';
807 		val->val.d.n = n;
808 		r = 1;
809 	} else {
810 		parser_errmsg("parameter '%s' does not contain a valid size",
811 			      param->name);
812 		r = 0;
813 	}
814 	return r;
815 }
816 
817 
818 static int
doGetBinary(struct nvlst * valnode,struct cnfparamdescr * param,struct cnfparamvals * val)819 doGetBinary(struct nvlst *valnode, struct cnfparamdescr *param,
820 	  struct cnfparamvals *val)
821 {
822 	int r = 1;
823 	val->val.datatype = 'N';
824 	if(!es_strbufcmp(valnode->val.d.estr, (unsigned char*) "on", 2)) {
825 		val->val.d.n = 1;
826 	} else if(!es_strbufcmp(valnode->val.d.estr, (unsigned char*) "off", 3)) {
827 		val->val.d.n = 0;
828 	} else {
829 		parser_errmsg("parameter '%s' must be \"on\" or \"off\" but "
830 		  "is neither. Results unpredictable.", param->name);
831 		val->val.d.n = 0;
832 		r = 0;
833 	}
834 	return r;
835 }
836 
837 static int
doGetQueueType(struct nvlst * valnode,struct cnfparamdescr * param,struct cnfparamvals * val)838 doGetQueueType(struct nvlst *valnode, struct cnfparamdescr *param,
839 	  struct cnfparamvals *val)
840 {
841 	char *cstr;
842 	int r = 1;
843 	if(!es_strcasebufcmp(valnode->val.d.estr, (uchar*)"fixedarray", 10)) {
844 		val->val.d.n = QUEUETYPE_FIXED_ARRAY;
845 	} else if(!es_strcasebufcmp(valnode->val.d.estr, (uchar*)"linkedlist", 10)) {
846 		val->val.d.n = QUEUETYPE_LINKEDLIST;
847 	} else if(!es_strcasebufcmp(valnode->val.d.estr, (uchar*)"disk", 4)) {
848 		val->val.d.n = QUEUETYPE_DISK;
849 	} else if(!es_strcasebufcmp(valnode->val.d.estr, (uchar*)"direct", 6)) {
850 		val->val.d.n = QUEUETYPE_DIRECT;
851 	} else {
852 		cstr = es_str2cstr(valnode->val.d.estr, NULL);
853 		parser_errmsg("param '%s': unknown queue type: '%s'",
854 			      param->name, cstr);
855 		free(cstr);
856 		r = 0;
857 	}
858 	val->val.datatype = 'N';
859 	return r;
860 }
861 
862 
863 /* A file create-mode must be a four-digit octal number
864  * starting with '0'.
865  */
866 static int
doGetFileCreateMode(struct nvlst * valnode,struct cnfparamdescr * param,struct cnfparamvals * val)867 doGetFileCreateMode(struct nvlst *valnode, struct cnfparamdescr *param,
868 	  struct cnfparamvals *val)
869 {
870 	int fmtOK = 0;
871 	char *cstr;
872 	uchar *c;
873 	const int len_val = es_strlen(valnode->val.d.estr);
874 
875 	if(len_val >= 4) {
876 		c = es_getBufAddr(valnode->val.d.estr);
877 		if(    (c[0] == '0')
878 		    && (c[1] >= '0' && c[1] <= '7')
879 		    && (c[2] >= '0' && c[2] <= '7')
880 		    && (c[3] >= '0' && c[3] <= '7')  )  {
881 			if(len_val == 5) {
882 				if(c[4] >= '0' && c[4] <= '7') {
883 					fmtOK = 1;
884 				}
885 			} else {
886 				fmtOK = 1;
887 			}
888 		}
889 	}
890 
891 	if(fmtOK) {
892 		val->val.datatype = 'N';
893 		val->val.d.n = (c[1]-'0') * 64 + (c[2]-'0') * 8 + (c[3]-'0');
894 		if(len_val == 5) {
895 			val->val.d.n  = val->val.d.n * 8 + (c[4]-'0');
896 		}
897 	} else {
898 		cstr = es_str2cstr(valnode->val.d.estr, NULL);
899 		parser_errmsg("file modes need to be specified as "
900 		  "4- or 5-digit octal numbers starting with '0' -"
901 		  "parameter '%s=\"%s\"' is not a file mode",
902 		param->name, cstr);
903 		free(cstr);
904 	}
905 	return fmtOK;
906 }
907 
908 static int
doGetGID(struct nvlst * valnode,struct cnfparamdescr * param,struct cnfparamvals * val)909 doGetGID(struct nvlst *valnode, struct cnfparamdescr *param,
910 	  struct cnfparamvals *val)
911 {
912 	char *cstr;
913 	int r;
914 	struct group *resultBuf = NULL;
915 	struct group wrkBuf;
916 	char *stringBuf = NULL;
917 	size_t bufSize = 1024;
918 	int e;
919 
920 	cstr = es_str2cstr(valnode->val.d.estr, NULL);
921 	do {
922 		char *p;
923 
924 		/* Increase bufsize and try again.*/
925 		bufSize *= 2;
926 		p = realloc(stringBuf, bufSize);
927 		if(!p) {
928 			e = ENOMEM;
929 			break;
930 		}
931 		stringBuf = p;
932 		e = getgrnam_r(cstr, &wrkBuf, stringBuf, bufSize, &resultBuf);
933 	} while(!resultBuf && (e == ERANGE));
934 
935 	if(resultBuf == NULL) {
936 		if(e != 0) {
937 			if(loadConf->globals.abortOnIDResolutionFail) {
938 				fprintf(stderr, "parameter '%s': error to "
939 				"obtaining group id for '%s'", param->name, cstr);
940 				exit(1); /* good exit */
941 			} else {
942 				LogError(e, RS_RET_ERR, "parameter '%s': error to "
943 					"obtaining group id for '%s'", param->name, cstr);
944 			}
945 		}
946 		parser_errmsg("parameter '%s': ID for group %s could not "
947 		  "be found", param->name, cstr);
948 		r = 0;
949 	} else {
950 		val->val.datatype = 'N';
951 		val->val.d.n = resultBuf->gr_gid;
952 		DBGPRINTF("param '%s': uid %d obtained for group '%s'\n",
953 		   param->name, (int) resultBuf->gr_gid, cstr);
954 		r = 1;
955 	}
956 	free(stringBuf);
957 	free(cstr);
958 	return r;
959 }
960 
961 static int
doGetUID(struct nvlst * valnode,struct cnfparamdescr * param,struct cnfparamvals * val)962 doGetUID(struct nvlst *valnode, struct cnfparamdescr *param,
963 	  struct cnfparamvals *val)
964 {
965 	char *cstr;
966 	int r;
967 	struct passwd *resultBuf;
968 	struct passwd wrkBuf;
969 	char stringBuf[2048]; /* 2048 has been proven to be large enough */
970 	char errStr[1024];
971 
972 	cstr = es_str2cstr(valnode->val.d.estr, NULL);
973 	const int err_no = getpwnam_r(cstr, &wrkBuf, stringBuf, sizeof(stringBuf), &resultBuf);
974 	if(resultBuf == NULL) {
975 		rs_strerror_r((err_no == 0) ? ENOENT : errno, errStr, sizeof(errStr));
976 		if(loadConf->globals.abortOnIDResolutionFail) {
977 			fprintf(stderr, "parameter '%s': ID for user '%s' could not "
978 			  "be found: %s", param->name, cstr, errStr);
979 			exit(1); /* good exit */
980 		} else {
981 			LogError(err_no, RS_RET_ERR, "parameter '%s': ID for user '%s' could not "
982 			  "be found: %s", param->name, cstr, errStr);
983 			parser_errmsg("parameter '%s': ID for user '%s' could not "
984 			  "be found: %s", param->name, cstr, errStr);
985 		}
986 
987 		r = 0;
988 	} else {
989 		val->val.datatype = 'N';
990 		val->val.d.n = resultBuf->pw_uid;
991 		DBGPRINTF("param '%s': uid %d obtained for user '%s'\n",
992 		   param->name, (int) resultBuf->pw_uid, cstr);
993 		r = 1;
994 	}
995 	free(cstr);
996 	return r;
997 }
998 
999 /* note: we support all integer formats that es_str2num support,
1000  * so hex and octal representations are also valid.
1001  */
1002 static int
doGetInt(struct nvlst * valnode,struct cnfparamdescr * param,struct cnfparamvals * val)1003 doGetInt(struct nvlst *valnode, struct cnfparamdescr *param,
1004 	  struct cnfparamvals *val)
1005 {
1006 	long long n;
1007 	int bSuccess;
1008 
1009 	n = es_str2num(valnode->val.d.estr, &bSuccess);
1010 	if(!bSuccess) {
1011 		parser_errmsg("parameter '%s' is not a proper number",
1012 		  param->name);
1013 	}
1014 	val->val.datatype = 'N';
1015 	val->val.d.n = n;
1016 	return bSuccess;
1017 }
1018 
1019 static int
doGetNonNegInt(struct nvlst * valnode,struct cnfparamdescr * param,struct cnfparamvals * val)1020 doGetNonNegInt(struct nvlst *valnode, struct cnfparamdescr *param,
1021 	  struct cnfparamvals *val)
1022 {
1023 	int bSuccess;
1024 
1025 	if((bSuccess = doGetInt(valnode, param, val))) {
1026 		if(val->val.d.n < 0) {
1027 			parser_errmsg("parameter '%s' cannot be less than zero (was %lld)",
1028 			  param->name, val->val.d.n);
1029 			bSuccess = 0;
1030 		}
1031 	}
1032 	return bSuccess;
1033 }
1034 
1035 static int
doGetPositiveInt(struct nvlst * valnode,struct cnfparamdescr * param,struct cnfparamvals * val)1036 doGetPositiveInt(struct nvlst *valnode, struct cnfparamdescr *param,
1037 	  struct cnfparamvals *val)
1038 {
1039 	int bSuccess;
1040 
1041 	if((bSuccess = doGetInt(valnode, param, val))) {
1042 		if(val->val.d.n < 1) {
1043 			parser_errmsg("parameter '%s' cannot be less than one (was %lld)",
1044 			  param->name, val->val.d.n);
1045 			bSuccess = 0;
1046 		}
1047 	}
1048 	return bSuccess;
1049 }
1050 
1051 static int
doGetWord(struct nvlst * valnode,struct cnfparamdescr * param,struct cnfparamvals * val)1052 doGetWord(struct nvlst *valnode, struct cnfparamdescr *param,
1053 	  struct cnfparamvals *val)
1054 {
1055 	es_size_t i;
1056 	int r = 1;
1057 	unsigned char *c;
1058 
1059 	val->val.datatype = 'S';
1060 	val->val.d.estr = es_newStr(32);
1061 	c = es_getBufAddr(valnode->val.d.estr);
1062 	for(i = 0 ; i < es_strlen(valnode->val.d.estr) && !isspace(c[i]) ; ++i) {
1063 		es_addChar(&val->val.d.estr, c[i]);
1064 	}
1065 	if(i != es_strlen(valnode->val.d.estr)) {
1066 		parser_errmsg("parameter '%s' contains whitespace, which is not "
1067 		  "permitted",
1068 		  param->name);
1069 		r = 0;
1070 	}
1071 	return r;
1072 }
1073 
1074 static int
doGetArray(struct nvlst * valnode,struct cnfparamdescr * param,struct cnfparamvals * val)1075 doGetArray(struct nvlst *valnode, struct cnfparamdescr *param,
1076 	  struct cnfparamvals *val)
1077 {
1078 	int r = 1;
1079 
1080 	switch(valnode->val.datatype) {
1081 	case 'S':
1082 		/* a constant string is assumed to be a single-element array */
1083 		val->val.datatype = 'A';
1084 		val->val.d.ar = cnfarrayNew(es_strdup(valnode->val.d.estr));
1085 		break;
1086 	case 'A':
1087 		val->val.datatype = 'A';
1088 		val->val.d.ar = cnfarrayDup(valnode->val.d.ar);
1089 		break;
1090 	default:parser_errmsg("parameter '%s' must be an array, but is a "
1091 			"different datatype", param->name);
1092 		r = 0;
1093 		break;
1094 	}
1095 	return r;
1096 }
1097 
1098 static int
doGetChar(struct nvlst * valnode,struct cnfparamdescr * param,struct cnfparamvals * val)1099 doGetChar(struct nvlst *valnode, struct cnfparamdescr *param,
1100 	  struct cnfparamvals *val)
1101 {
1102 	int r = 1;
1103 	if(es_strlen(valnode->val.d.estr) != 1) {
1104 		parser_errmsg("parameter '%s' must contain exactly one character "
1105 		  "but contains %d - cannot be processed",
1106 		  param->name, es_strlen(valnode->val.d.estr));
1107 		r = 0;
1108 	}
1109 	val->val.datatype = 'S';
1110 	val->val.d.estr = es_strdup(valnode->val.d.estr);
1111 	return r;
1112 }
1113 
1114 /* get a single parameter according to its definition. Helper to
1115  * nvlstGetParams. returns 1 if success, 0 otherwise
1116  */
1117 static int
nvlstGetParam(struct nvlst * valnode,struct cnfparamdescr * param,struct cnfparamvals * val)1118 nvlstGetParam(struct nvlst *valnode, struct cnfparamdescr *param,
1119 	       struct cnfparamvals *val)
1120 {
1121 	uchar *cstr;
1122 	int r;
1123 
1124 	DBGPRINTF("nvlstGetParam: name '%s', type %d, valnode->bUsed %d\n",
1125 		  param->name, (int) param->type, valnode->bUsed);
1126 	if(valnode->val.datatype != 'S' && param->type != eCmdHdlrArray) {
1127 		parser_errmsg("parameter '%s' is not a string, which is not "
1128 		  "permitted",
1129 		  param->name);
1130 		r = 0;
1131 		goto done;
1132 	}
1133 	valnode->bUsed = 1;
1134 	val->bUsed = 1;
1135 	switch(param->type) {
1136 	case eCmdHdlrQueueType:
1137 		r = doGetQueueType(valnode, param, val);
1138 		break;
1139 	case eCmdHdlrUID:
1140 		r = doGetUID(valnode, param, val);
1141 		break;
1142 	case eCmdHdlrGID:
1143 		r = doGetGID(valnode, param, val);
1144 		break;
1145 	case eCmdHdlrBinary:
1146 		r = doGetBinary(valnode, param, val);
1147 		break;
1148 	case eCmdHdlrFileCreateMode:
1149 		r = doGetFileCreateMode(valnode, param, val);
1150 		break;
1151 	case eCmdHdlrInt:
1152 		r = doGetInt(valnode, param, val);
1153 		break;
1154 	case eCmdHdlrNonNegInt:
1155 		r = doGetNonNegInt(valnode, param, val);
1156 		break;
1157 	case eCmdHdlrPositiveInt:
1158 		r = doGetPositiveInt(valnode, param, val);
1159 		break;
1160 	case eCmdHdlrSize:
1161 		r = doGetSize(valnode, param, val);
1162 		break;
1163 	case eCmdHdlrGetChar:
1164 		r = doGetChar(valnode, param, val);
1165 		break;
1166 	case eCmdHdlrFacility:
1167 		cstr = (uchar*) es_str2cstr(valnode->val.d.estr, NULL);
1168 		val->val.datatype = 'N';
1169 		val->val.d.n = decodeSyslogName(cstr, syslogFacNames);
1170 		free(cstr);
1171 		r = 1;
1172 		break;
1173 	case eCmdHdlrSeverity:
1174 		cstr = (uchar*) es_str2cstr(valnode->val.d.estr, NULL);
1175 		val->val.datatype = 'N';
1176 		val->val.d.n = decodeSyslogName(cstr, syslogPriNames);
1177 		free(cstr);
1178 		r = 1;
1179 		break;
1180 	case eCmdHdlrGetWord:
1181 		r = doGetWord(valnode, param, val);
1182 		break;
1183 	case eCmdHdlrString:
1184 		val->val.datatype = 'S';
1185 		val->val.d.estr = es_strdup(valnode->val.d.estr);
1186 		r = 1;
1187 		break;
1188 	case eCmdHdlrArray:
1189 		r = doGetArray(valnode, param, val);
1190 		break;
1191 	case eCmdHdlrGoneAway:
1192 		parser_errmsg("parameter '%s' is no longer supported",
1193 			      param->name);
1194 		r = 1; /* this *is* valid! */
1195 		break;
1196 	default:
1197 		DBGPRINTF("error: invalid param type\n");
1198 		r = 0;
1199 		break;
1200 	}
1201 done:	return r;
1202 }
1203 
1204 
1205 /* obtain conf params from an nvlst and emit error messages if
1206  * necessary. If an already-existing param value is passed, that is
1207  * used. If NULL is passed instead, a new one is allocated. In that case,
1208  * it is the caller's duty to free it when no longer needed.
1209  * NULL is returned on error, otherwise a pointer to the vals array.
1210  */
1211 struct cnfparamvals* ATTR_NONNULL(2)
nvlstGetParams(struct nvlst * lst,struct cnfparamblk * params,struct cnfparamvals * vals)1212 nvlstGetParams(struct nvlst *lst, struct cnfparamblk *params,
1213 	       struct cnfparamvals *vals)
1214 {
1215 #ifndef __clang_analyzer__ /* I give up on this one - let Coverity do the work */
1216 	int i;
1217 	int bValsWasNULL;
1218 	int bInError = 0;
1219 	struct nvlst *valnode;
1220 	struct cnfparamdescr *param;
1221 
1222 	if(params->version != CNFPARAMBLK_VERSION) {
1223 		DBGPRINTF("nvlstGetParams: invalid param block version "
1224 			  "%d, expected %d\n",
1225 			  params->version, CNFPARAMBLK_VERSION);
1226 		return NULL;
1227 	}
1228 
1229 	if(vals == NULL) {
1230 		bValsWasNULL = 1;
1231 		if((vals = calloc(params->nParams,
1232 				  sizeof(struct cnfparamvals))) == NULL)
1233 			return NULL;
1234 	} else {
1235 		bValsWasNULL = 0;
1236 	}
1237 
1238 	for(i = 0 ; i < params->nParams ; ++i) {
1239 		param = params->descr + i;
1240 		if((valnode = nvlstFindNameCStr(lst, param->name)) == NULL) {
1241 			if(param->flags & CNFPARAM_REQUIRED) {
1242 				parser_errmsg("parameter '%s' required but not specified - "
1243 				  "fix config", param->name);
1244 				bInError = 1;
1245 			}
1246 			continue;
1247 		}
1248 		if(param->flags & CNFPARAM_DEPRECATED) {
1249 			parser_errmsg("parameter '%s' deprecated but accepted, consider "
1250 			  "removing or replacing it", param->name);
1251 		}
1252 		if(vals[i].bUsed) {
1253 			parser_errmsg("parameter '%s' specified more than once - "
1254 			  "one instance is ignored. Fix config", param->name);
1255 			continue;
1256 		}
1257 		if(!nvlstGetParam(valnode, param, vals + i)) {
1258 			bInError = 1;
1259 		}
1260 	}
1261 
1262 	/* done parameter processing */
1263 	if(bInError) {
1264 		if(bValsWasNULL)
1265 			cnfparamvalsDestruct(vals, params);
1266 		vals = NULL;
1267 	}
1268 
1269 	return vals;
1270 #else
1271 	return NULL;
1272 #endif
1273 }
1274 
1275 
1276 /* check if at least one cnfparamval is actually set
1277  * returns 1 if so, 0 otherwise
1278  */
1279 int
cnfparamvalsIsSet(struct cnfparamblk * params,struct cnfparamvals * vals)1280 cnfparamvalsIsSet(struct cnfparamblk *params, struct cnfparamvals *vals)
1281 {
1282 	int i;
1283 
1284 	if(vals == NULL)
1285 		return 0;
1286 	if(params->version != CNFPARAMBLK_VERSION) {
1287 		DBGPRINTF("nvlstGetParams: invalid param block version "
1288 			  "%d, expected %d\n",
1289 			  params->version, CNFPARAMBLK_VERSION);
1290 		return 0;
1291 	}
1292 	for(i = 0 ; i < params->nParams ; ++i) {
1293 		if(vals[i].bUsed)
1294 			return 1;
1295 	}
1296 	return 0;
1297 }
1298 
1299 
1300 void
cnfparamsPrint(const struct cnfparamblk * params,const struct cnfparamvals * vals)1301 cnfparamsPrint(const struct cnfparamblk *params, const struct cnfparamvals *vals)
1302 {
1303 	int i;
1304 	char *cstr;
1305 
1306 	if(!Debug)
1307 		return;
1308 
1309 	for(i = 0 ; i < params->nParams ; ++i) {
1310 		dbgprintf("%s: ", params->descr[i].name);
1311 		if(vals[i].bUsed) {
1312 			// TODO: other types!
1313 			switch(vals[i].val.datatype) {
1314 			case 'S':
1315 				cstr = es_str2cstr(vals[i].val.d.estr, NULL);
1316 				dbgprintf(" '%s'", cstr);
1317 				free(cstr);
1318 				break;
1319 			case 'A':
1320 				cnfarrayPrint(vals[i].val.d.ar, 0);
1321 				break;
1322 			case 'N':
1323 				dbgprintf("%lld", vals[i].val.d.n);
1324 				break;
1325 			default:
1326 				dbgprintf("(unsupported datatype %c)",
1327 					  vals[i].val.datatype);
1328 			}
1329 		} else {
1330 			dbgprintf("(unset)");
1331 		}
1332 		dbgprintf("\n");
1333 	}
1334 }
1335 
1336 struct cnfobj*
cnfobjNew(enum cnfobjType objType,struct nvlst * lst)1337 cnfobjNew(enum cnfobjType objType, struct nvlst *lst)
1338 {
1339 	struct cnfobj *o;
1340 
1341 	if((o = malloc(sizeof(struct cnfobj))) != NULL) {
1342 		nvlstChkDupes(lst);
1343 		o->objType = objType;
1344 		o->nvlst = lst;
1345 		o->subobjs = NULL;
1346 		o->script = NULL;
1347 	}
1348 
1349 	return o;
1350 }
1351 
1352 void
cnfobjDestruct(struct cnfobj * o)1353 cnfobjDestruct(struct cnfobj *o)
1354 {
1355 	if(o != NULL) {
1356 		nvlstDestruct(o->nvlst);
1357 		objlstDestruct(o->subobjs);
1358 		free(o);
1359 	}
1360 }
1361 
1362 void
cnfobjPrint(struct cnfobj * o)1363 cnfobjPrint(struct cnfobj *o)
1364 {
1365 	dbgprintf("obj: '%s'\n", cnfobjType2str(o->objType));
1366 	nvlstPrint(o->nvlst);
1367 }
1368 
1369 
1370 struct cnfexpr*
cnfexprNew(unsigned nodetype,struct cnfexpr * l,struct cnfexpr * r)1371 cnfexprNew(unsigned nodetype, struct cnfexpr *l, struct cnfexpr *r)
1372 {
1373 	struct cnfexpr *expr;
1374 
1375 	/* optimize some constructs during parsing */
1376 	if(nodetype == 'M' && r->nodetype == 'N') {
1377 		((struct cnfnumval*)r)->val *= -1;
1378 		expr = r;
1379 		goto done;
1380 	}
1381 
1382 	if((expr = malloc(sizeof(struct cnfexpr))) != NULL) {
1383 		expr->nodetype = nodetype;
1384 		expr->l = l;
1385 		expr->r = r;
1386 	}
1387 done:
1388 	return expr;
1389 }
1390 
1391 
1392 static int64_t
str2num(es_str_t * s,int * bSuccess)1393 str2num(es_str_t *s, int *bSuccess)
1394 {
1395 	size_t i;
1396 	int neg;
1397 	int64_t num = 0;
1398 	const uchar *const c = es_getBufAddr(s);
1399 
1400 	if(s->lenStr == 0) {
1401 		DBGPRINTF("rainerscript: str2num: strlen == 0; invalid input (no string)\n");
1402 		if(bSuccess != NULL) {
1403 			*bSuccess = 1;
1404 		}
1405 		goto done;
1406 	}
1407 	if(c[0] == '-') {
1408 		neg = -1;
1409 		i = 1;
1410 	} else {
1411 		neg = 1;
1412 		i = 0;
1413 	}
1414 	while(i < s->lenStr && isdigit(c[i])) {
1415 		num = num * 10 + c[i] - '0';
1416 		++i;
1417 	}
1418 	num *= neg;
1419 	if(bSuccess != NULL)
1420 		*bSuccess = (i == s->lenStr) ? 1 : 0;
1421 done:
1422 	return num;
1423 }
1424 
1425 /* We support decimal integers. Unfortunately, previous versions
1426  * said they support oct and hex, but that wasn't really the case.
1427  * Everything based on JSON was just dec-converted. As this was/is
1428  * the norm, we fix that inconsistency. Luckly, oct and hex support
1429  * was never documented.
1430  * rgerhards, 2015-11-12
1431  */
1432 long long
var2Number(struct svar * r,int * bSuccess)1433 var2Number(struct svar *r, int *bSuccess)
1434 {
1435 	long long n = 0;
1436 	if(r->datatype == 'S') {
1437 		n = str2num(r->d.estr, bSuccess);
1438 	} else {
1439 		if(r->datatype == 'J') {
1440 			n = (r->d.json == NULL) ? 0 : json_object_get_int64(r->d.json);
1441 		} else {
1442 			n = r->d.n;
1443 		}
1444 		if(bSuccess != NULL)
1445 			*bSuccess = 1;
1446 	}
1447 	return n;
1448 }
1449 
1450 /* ensure that retval is a string
1451  */
1452 static es_str_t *
var2String(struct svar * __restrict__ const r,int * __restrict__ const bMustFree)1453 var2String(struct svar *__restrict__ const r, int *__restrict__ const bMustFree)
1454 {
1455 	es_str_t *estr;
1456 	const char *cstr;
1457 	rs_size_t lenstr;
1458 	if(r->datatype == 'N') {
1459 		*bMustFree = 1;
1460 		estr = es_newStrFromNumber(r->d.n);
1461 	} else if(r->datatype == 'J') {
1462 		*bMustFree = 1;
1463 		if(r->d.json == NULL) {
1464 			cstr = "",
1465 			lenstr = 0;
1466 		} else {
1467 			cstr = (char*)json_object_get_string(r->d.json);
1468 			lenstr = strlen(cstr);
1469 		}
1470 		estr = es_newStrFromCStr(cstr, lenstr);
1471 	} else {
1472 		*bMustFree = 0;
1473 		estr = r->d.estr;
1474 	}
1475 	return estr;
1476 }
1477 
1478 uchar*
var2CString(struct svar * __restrict__ const r,int * __restrict__ const bMustFree)1479 var2CString(struct svar *__restrict__ const r, int *__restrict__ const bMustFree)
1480 {
1481 	uchar *cstr;
1482 	es_str_t *estr;
1483 	estr = var2String(r, bMustFree);
1484 	cstr = (uchar*) es_str2cstr(estr, NULL);
1485 	if(*bMustFree)
1486 		es_deleteStr(estr);
1487 	*bMustFree = 1;
1488 	return cstr;
1489 }
1490 
1491 /* frees struct svar members, but not the struct itself. This is because
1492  * it usually is allocated on the stack. Callers why dynamically allocate
1493  * struct svar need to free the struct themselfes!
1494  */
1495 
1496 int SKIP_NOTHING = 0x0;
1497 int SKIP_STRING = 0x1;
1498 
1499 static void
varFreeMembersSelectively(const struct svar * r,const int skipMask)1500 varFreeMembersSelectively(const struct svar *r, const int skipMask)
1501 {
1502 	if(r->datatype == 'J') {
1503 		json_object_put(r->d.json);
1504 	} else if( !(skipMask & SKIP_STRING) && (r->datatype == 'S')) {
1505 		es_deleteStr(r->d.estr);
1506 	}
1507 }
1508 
1509 void
varFreeMembers(const struct svar * r)1510 varFreeMembers(const struct svar *r)
1511 {
1512 	varFreeMembersSelectively(r, SKIP_NOTHING);
1513 }
1514 
1515 
1516 static rsRetVal
doExtractFieldByChar(uchar * str,uchar delim,const int matchnbr,uchar ** resstr)1517 doExtractFieldByChar(uchar *str, uchar delim, const int matchnbr, uchar **resstr)
1518 {
1519 	int iCurrFld;
1520 	int allocLen;
1521 	int iLen;
1522 	uchar *pBuf;
1523 	uchar *pFld;
1524 	uchar *pFldEnd;
1525 	DEFiRet;
1526 
1527 	/* first, skip to the field in question */
1528 	iCurrFld = 1;
1529 	pFld = str;
1530 	while(*pFld && iCurrFld < matchnbr) {
1531 		/* skip fields until the requested field or end of string is found */
1532 		while(*pFld && (uchar) *pFld != delim)
1533 			++pFld; /* skip to field terminator */
1534 		if(*pFld == delim) {
1535 			++pFld; /* eat it */
1536 			++iCurrFld;
1537 		}
1538 	}
1539 	DBGPRINTF("field() field requested %d, field found %d\n", matchnbr, iCurrFld);
1540 
1541 	if(iCurrFld == matchnbr) {
1542 		/* field found, now extract it */
1543 		/* first of all, we need to find the end */
1544 		pFldEnd = pFld;
1545 		while(*pFldEnd && *pFldEnd != delim)
1546 			++pFldEnd;
1547 		--pFldEnd; /* we are already at the delimiter - so we need to
1548 			    * step back a little not to copy it as part of the field. */
1549 		/* we got our end pointer, now do the copy */
1550 		iLen = pFldEnd - pFld + 1; /* the +1 is for an actual char, NOT \0! */
1551 		allocLen = iLen + 1;
1552 #		ifdef VALGRIND
1553 		allocLen += (3 - (iLen % 4));
1554 		/*older versions of valgrind have a problem with strlen inspecting 4-bytes at a time*/
1555 #		endif
1556 		CHKmalloc(pBuf = malloc(allocLen));
1557 		/* now copy */
1558 		memcpy(pBuf, pFld, iLen);
1559 		pBuf[iLen] = '\0'; /* terminate it */
1560 		*resstr = pBuf;
1561 	} else {
1562 		ABORT_FINALIZE(RS_RET_FIELD_NOT_FOUND);
1563 	}
1564 finalize_it:
1565 	RETiRet;
1566 }
1567 
1568 
1569 static rsRetVal
doExtractFieldByStr(uchar * str,char * delim,const rs_size_t lenDelim,const int matchnbr,uchar ** resstr)1570 doExtractFieldByStr(uchar *str, char *delim, const rs_size_t lenDelim, const int matchnbr, uchar **resstr)
1571 {
1572 	int iCurrFld;
1573 	int iLen;
1574 	uchar *pBuf;
1575 	uchar *pFld;
1576 	uchar *pFldEnd;
1577 	DEFiRet;
1578 
1579 	if (str == NULL || delim == NULL)
1580 		ABORT_FINALIZE(RS_RET_FIELD_NOT_FOUND);
1581 
1582 	/* first, skip to the field in question */
1583 	iCurrFld = 1;
1584 	pFld = str;
1585 	while(pFld != NULL && iCurrFld < matchnbr) {
1586 		if((pFld = (uchar*) strstr((char*)pFld, delim)) != NULL) {
1587 			pFld += lenDelim;
1588 			++iCurrFld;
1589 		}
1590 	}
1591 	DBGPRINTF("field() field requested %d, field found %d\n", matchnbr, iCurrFld);
1592 
1593 	if(iCurrFld == matchnbr) {
1594 		/* field found, now extract it */
1595 		/* first of all, we need to find the end */
1596 		pFldEnd = (uchar*) strstr((char*)pFld, delim);
1597 		if(pFldEnd == NULL) {
1598 			iLen = strlen((char*) pFld);
1599 		} else { /* found delmiter!  Note that pFldEnd *is* already on
1600 			  * the first delmi char, we don't need that. */
1601 			iLen = pFldEnd - pFld;
1602 		}
1603 		/* we got our end pointer, now do the copy */
1604 		CHKmalloc(pBuf = malloc(iLen + 1));
1605 		/* now copy */
1606 		memcpy(pBuf, pFld, iLen);
1607 		pBuf[iLen] = '\0'; /* terminate it */
1608 		*resstr = pBuf;
1609 	} else {
1610 		ABORT_FINALIZE(RS_RET_FIELD_NOT_FOUND);
1611 	}
1612 finalize_it:
1613 	RETiRet;
1614 }
1615 
1616 static void
doFunc_re_extract(struct cnffunc * func,struct svar * ret,void * usrptr,wti_t * const pWti)1617 doFunc_re_extract(struct cnffunc *func, struct svar *ret, void* usrptr, wti_t *const pWti)
1618 {
1619 	size_t submatchnbr;
1620 	short matchnbr;
1621 	regmatch_t pmatch[50];
1622 	int bMustFree;
1623 	es_str_t *estr = NULL; /* init just to keep compiler happy */
1624 	char *str;
1625 	struct svar r[CNFFUNC_MAX_ARGS];
1626 	int iLenBuf;
1627 	unsigned iOffs;
1628 	short iTry = 0;
1629 	uchar bFound = 0;
1630 	iOffs = 0;
1631 	sbool bHadNoMatch = 0;
1632 
1633 	cnfexprEval(func->expr[0], &r[0], usrptr, pWti);
1634 	/* search string is already part of the compiled regex, so we don't
1635 	 * need it here!
1636 	 */
1637 	cnfexprEval(func->expr[2], &r[2], usrptr, pWti);
1638 	cnfexprEval(func->expr[3], &r[3], usrptr, pWti);
1639 	str = (char*) var2CString(&r[0], &bMustFree);
1640 	matchnbr = (short) var2Number(&r[2], NULL);
1641 	submatchnbr = (size_t) var2Number(&r[3], NULL);
1642 	if(submatchnbr >= sizeof(pmatch)/sizeof(regmatch_t)) {
1643 		DBGPRINTF("re_extract() submatch %zd is too large\n", submatchnbr);
1644 		bHadNoMatch = 1;
1645 		goto finalize_it;
1646 	}
1647 
1648 	/* first see if we find a match, iterating through the series of
1649 	 * potential matches over the string.
1650 	 */
1651 	while(!bFound) {
1652 		int iREstat;
1653 		iREstat = regexp.regexec(func->funcdata, (char*)(str + iOffs),
1654 					 submatchnbr+1, pmatch, 0);
1655 		DBGPRINTF("re_extract: regexec return is %d\n", iREstat);
1656 		if(iREstat == 0) {
1657 			if(pmatch[0].rm_so == -1) {
1658 				DBGPRINTF("oops ... start offset of successful regexec is -1\n");
1659 				break;
1660 			}
1661 			if(iTry == matchnbr) {
1662 				bFound = 1;
1663 			} else {
1664 				DBGPRINTF("re_extract: regex found at offset %d, new offset %d, tries %d\n",
1665 					  iOffs, (int) (iOffs + pmatch[0].rm_eo), iTry);
1666 				iOffs += pmatch[0].rm_eo;
1667 				++iTry;
1668 			}
1669 		} else {
1670 			break;
1671 		}
1672 	}
1673 	DBGPRINTF("re_extract: regex: end search, found %d\n", bFound);
1674 	if(!bFound) {
1675 		bHadNoMatch = 1;
1676 		goto finalize_it;
1677 	} else {
1678 		/* Match- but did it match the one we wanted? */
1679 		/* we got no match! */
1680 		if(pmatch[submatchnbr].rm_so == -1) {
1681 			bHadNoMatch = 1;
1682 			goto finalize_it;
1683 		}
1684 		/* OK, we have a usable match - we now need to malloc pB */
1685 		iLenBuf = pmatch[submatchnbr].rm_eo - pmatch[submatchnbr].rm_so;
1686 		estr = es_newStrFromBuf(str + iOffs + pmatch[submatchnbr].rm_so,
1687 					iLenBuf);
1688 	}
1689 
1690 finalize_it:
1691 	if(bMustFree) free(str);
1692 	varFreeMembers(&r[0]);
1693 	varFreeMembers(&r[2]);
1694 	varFreeMembers(&r[3]);
1695 
1696 	if(bHadNoMatch) {
1697 		cnfexprEval(func->expr[4], &r[4], usrptr, pWti);
1698 		estr = var2String(&r[4], &bMustFree);
1699 		varFreeMembersSelectively(&r[4], SKIP_STRING);
1700 		/* Note that we do NOT free the string that was returned/created
1701 		 * for r[4]. We pass it to the caller, which in turn frees it.
1702 		 * This saves us doing one unnecessary memory alloc & write.
1703 		 */
1704 	}
1705 	ret->datatype = 'S';
1706 	ret->d.estr = estr;
1707 	return;
1708 }
1709 
1710 
1711 /* note that we do not need to evaluate any parameters, as the template pointer
1712  * is set during initialization().
1713  * TODO: think if we can keep our buffer; but that may not be trival thinking about
1714  *       multiple threads.
1715  */
1716 static void
doFunc_exec_template(struct cnffunc * __restrict__ const func,struct svar * __restrict__ const ret,void * const usrptr,wti_t * const pWti)1717 doFunc_exec_template(struct cnffunc *__restrict__ const func,
1718 	struct svar *__restrict__ const ret,
1719 	void *const usrptr,
1720 	wti_t *const pWti __attribute__((unused)))
1721 {
1722 	smsg_t *const pMsg = (smsg_t*) usrptr;
1723 	rsRetVal localRet;
1724 	actWrkrIParams_t iparam;
1725 
1726 	wtiInitIParam(&iparam);
1727 	localRet = tplToString(func->funcdata, pMsg, &iparam, NULL);
1728 	if(localRet == RS_RET_OK) {
1729 		ret->d.estr = es_newStrFromCStr((char*)iparam.param, iparam.lenStr);
1730 	} else {
1731 		ret->d.estr = es_newStrFromCStr("", 0);
1732 	}
1733 	ret->datatype = 'S';
1734 	free(iparam.param);
1735 
1736 	return;
1737 }
1738 
1739 static es_str_t*
doFuncReplace(struct svar * __restrict__ const operandVal,struct svar * __restrict__ const findVal,struct svar * __restrict__ const replaceWithVal)1740 doFuncReplace(struct svar *__restrict__ const operandVal, struct svar *__restrict__ const findVal,
1741 		struct svar *__restrict__ const replaceWithVal) {
1742 	int freeOperand, freeFind, freeReplacement;
1743 	es_str_t *str = var2String(operandVal, &freeOperand);
1744 	es_str_t *findStr = var2String(findVal, &freeFind);
1745 	es_str_t *replaceWithStr = var2String(replaceWithVal, &freeReplacement);
1746 	uchar *find = es_getBufAddr(findStr);
1747 	uchar *replaceWith = es_getBufAddr(replaceWithStr);
1748 	uint lfind = es_strlen(findStr);
1749 	uint lReplaceWith = es_strlen(replaceWithStr);
1750 	uint lSrc = es_strlen(str);
1751 	uint lDst = 0;
1752 	uchar* src_buff = es_getBufAddr(str);
1753 	uint i, j;
1754 	for(i = j = 0; i <= lSrc; i++, lDst++) {
1755 		if (j == lfind) {
1756 			lDst = lDst - lfind + lReplaceWith;
1757 			j = 0;
1758 		}
1759 		if (i == lSrc) break;
1760 		if (src_buff[i] == find[j]) {
1761 			j++;
1762 		} else if (j > 0) {
1763 			i -= (j - 1);
1764 			lDst -= (j - 1);
1765 			j = 0;
1766 		}
1767 	}
1768 	es_str_t *res = es_newStr(lDst);
1769 	unsigned char* dest = es_getBufAddr(res);
1770 	uint k, s;
1771 	for(i = j = s = 0; i <= lSrc; i++, s++) {
1772 		if (j == lfind) {
1773 		s -= j;
1774 		for (k = 0; k < lReplaceWith; k++, s++) dest[s] = replaceWith[k];
1775 			j = 0;
1776 		}
1777 		if (i == lSrc) break;
1778 		if (src_buff[i] == find[j]) {
1779 			j++;
1780 		} else {
1781 			if (j > 0) {
1782 				i -= j;
1783 				s -= j;
1784 				j = 0;
1785 			}
1786 			dest[s] = src_buff[i];
1787 		}
1788 	}
1789 	if (j > 0) {
1790 		for (k = 1; k <= j; k++) dest[s - k] = src_buff[i - k];
1791 	}
1792 	res->lenStr = lDst;
1793 	if(freeOperand) es_deleteStr(str);
1794 	if(freeFind) es_deleteStr(findStr);
1795 	if(freeReplacement) es_deleteStr(replaceWithStr);
1796 	return res;
1797 }
1798 
1799 
ATTR_NONNULL()1800 static void ATTR_NONNULL()
1801 doFunc_parse_json(struct cnffunc *__restrict__ const func,
1802 	struct svar *__restrict__ const ret,
1803 	void *const usrptr,
1804 	wti_t *const pWti)
1805 {
1806 	struct svar srcVal[2];
1807 	int bMustFree;
1808 	int bMustFree2;
1809 	smsg_t *const pMsg = (smsg_t*)usrptr;
1810 	cnfexprEval(func->expr[0], &srcVal[0], usrptr, pWti);
1811 	cnfexprEval(func->expr[1], &srcVal[1], usrptr, pWti);
1812 	char *jsontext = (char*) var2CString(&srcVal[0], &bMustFree);
1813 	char *container = (char*) var2CString(&srcVal[1], &bMustFree2);
1814 	struct json_object *json;
1815 
1816 	int retVal;
1817 	assert(jsontext != NULL);
1818 	assert(container != NULL);
1819 	assert(pMsg != NULL);
1820 
1821 	struct json_tokener *const tokener = json_tokener_new();
1822 	if(tokener == NULL) {
1823 		retVal = 1;
1824 		goto finalize_it;
1825 	}
1826 	json = json_tokener_parse_ex(tokener, jsontext, strlen(jsontext));
1827 	if(json == NULL) {
1828 		retVal = RS_SCRIPT_EINVAL;
1829 	} else {
1830 		size_t off = (*container == '$') ? 1 : 0;
1831 		msgAddJSON(pMsg, (uchar*)container+off, json, 0, 0);
1832 		retVal = RS_SCRIPT_EOK;
1833 	}
1834 	wtiSetScriptErrno(pWti, retVal);
1835 	json_tokener_free(tokener);
1836 
1837 
1838 finalize_it:
1839 	ret->datatype = 'N';
1840 	ret->d.n = retVal;
1841 
1842 	if(bMustFree) {
1843 		free(jsontext);
1844 	}
1845 	if(bMustFree2) {
1846 		free(container);
1847 	}
1848 	varFreeMembers(&srcVal[0]);
1849 	varFreeMembers(&srcVal[1]);
1850 }
1851 
1852 
ATTR_NONNULL()1853 static void ATTR_NONNULL()
1854 doFunc_get_property(struct cnffunc *__restrict__ const func,
1855 	struct svar *__restrict__ const ret,
1856 	void *const usrptr,
1857 	wti_t *const pWti)
1858 {
1859 	int retVal = RS_SCRIPT_EOK;
1860 	int bMustFree = 0;
1861 	char *expr = NULL;
1862 	struct svar srcVal[2] = {{.d={0}, .datatype=0}};
1863 	struct json_object *json = NULL;
1864 
1865 	/* ignore string literals */
1866 	if (func->expr[0]->nodetype == 'S') {
1867 		retVal = RS_SCRIPT_EINVAL;
1868 		FINALIZE;
1869 	}
1870 
1871 	cnfexprEval(func->expr[0], &srcVal[0], usrptr, pWti);
1872 	cnfexprEval(func->expr[1], &srcVal[1], usrptr, pWti);
1873 	DBGPRINTF("srcval[0] datatype: %c\n", srcVal[0].datatype);
1874 	DBGPRINTF("srcval[1] datatype: %c\n", srcVal[1].datatype);
1875 
1876 	switch (srcVal[0].datatype) {
1877 		case 'J': {
1878 			json = srcVal[0].d.json;
1879 			break;
1880 		}
1881 		case 'S': {
1882 			ret->d.estr = es_strdup(srcVal[0].d.estr);
1883 			ret->datatype = 'S';
1884 			FINALIZE;
1885 			break;
1886 		}
1887 		default: {
1888 			ret->d.estr = es_newStrFromCStr("", 1);
1889 			ret->datatype = 'S';
1890 			FINALIZE;
1891 			break;
1892 		}
1893 	}
1894 
1895 	switch (json_object_get_type(json)) {
1896 		case json_type_object: {
1897 			expr = (char*) var2CString(&srcVal[1], &bMustFree);
1898 			if (expr && expr[0] == '\0') {
1899 				ret->d.json = json_object_get(json);
1900 				ret->datatype = 'J';
1901 				break;
1902 			}
1903 			if (expr && !json_object_object_get_ex(json, (char*)expr, &ret->d.json)) {
1904 					retVal = RS_SCRIPT_EINVAL;
1905 					FINALIZE;
1906 			}
1907 			if (ret->d.json) {
1908 				ret->d.json = json_object_get(ret->d.json);
1909 				ret->datatype = 'J';
1910 			} else {
1911 				ret->d.estr = es_newStrFromCStr("", 1);
1912 				ret->datatype = 'S';
1913 			}
1914 			break;
1915 		}
1916 		case json_type_array: {
1917 			int success = 0;
1918 			long long index = var2Number(&srcVal[1], &success);
1919 			if (!success || index < 0 || (size_t)index >= sizeof(size_t)) {
1920 				retVal = RS_SCRIPT_EINVAL;
1921 				FINALIZE;
1922 			}
1923 			ret->d.json = json_object_array_get_idx(json, index);
1924 			if (ret->d.json) {
1925 				ret->d.json = json_object_get(ret->d.json);
1926 				ret->datatype = 'J';
1927 			} else {
1928 				ret->d.estr = es_newStrFromCStr("", 1);
1929 				ret->datatype = 'S';
1930 			}
1931 			break;
1932 		}
1933 		case json_type_boolean:
1934 		case json_type_int: {
1935 			ret->d.n = json_object_get_int64(json);
1936 			ret->datatype = 'N';
1937 			break;
1938 		}
1939 		case json_type_double: {
1940 			ret->d.n = json_object_get_double(json);
1941 			ret->datatype = 'N';
1942 			break;
1943 		}
1944 		case json_type_string: {
1945 			ret->d.estr = es_newStrFromCStr(json_object_get_string(json), json_object_get_string_len(json));
1946 			ret->datatype = 'S';
1947 			break;
1948 		}
1949 		case json_type_null: {
1950 			ret->datatype = 'S';
1951 			ret->d.estr = es_newStrFromCStr("", 1);
1952 			break;
1953 		}
1954 		default:
1955 			LogError(0, RS_RET_INTERNAL_ERROR,
1956 				"Warning - unhandled json type(%d) !!!!\n", json_object_get_type(json));
1957 			retVal = RS_SCRIPT_EINVAL;
1958 			break;
1959 	}
1960 
1961 finalize_it:
1962 	wtiSetScriptErrno(pWti, retVal);
1963 
1964 	if (retVal != RS_SCRIPT_EOK) {
1965 		ret->datatype = 'S';
1966 		ret->d.estr = es_newStrFromCStr("", 1);
1967 	}
1968 	if (bMustFree) {
1969 		free(expr);
1970 	}
1971 	varFreeMembers(&srcVal[0]);
1972 	varFreeMembers(&srcVal[1]);
1973 }
1974 
ATTR_NONNULL()1975 static void ATTR_NONNULL()
1976 doFunct_RandomGen(struct cnffunc *__restrict__ const func,
1977 	struct svar *__restrict__ const ret,
1978 	void *__restrict__ const usrptr,
1979 	wti_t *__restrict__ const pWti)
1980 {
1981 	int success = 0;
1982 	struct svar srcVal;
1983 	long long retVal;
1984 	long int x;
1985 
1986 	cnfexprEval(func->expr[0], &srcVal, usrptr, pWti);
1987 	long long max = var2Number(&srcVal, &success);
1988 	if (! success) {
1989 		DBGPRINTF("rainerscript: random(max) didn't get a valid 'max' limit, defaulting random-number "
1990 			"value to 0");
1991 		retVal = 0;
1992 		goto done;
1993 	}
1994 	if(max == 0) {
1995 		DBGPRINTF("rainerscript: random(max) invalid, 'max' is zero, , defaulting random-number value to 0");
1996 		retVal = 0;
1997 		goto done;
1998 	}
1999 	x = labs(randomNumber());
2000 	if (max > MAX_RANDOM_NUMBER) {
2001 		DBGPRINTF("rainerscript: desired random-number range [0 - %lld] "
2002 			"is wider than supported limit of [0 - %d)\n",
2003 			max, MAX_RANDOM_NUMBER);
2004 	}
2005 
2006 	retVal = (x % max);
2007 done:
2008 	ret->d.n = retVal;
2009 	ret->datatype = 'N';
2010 	varFreeMembers(&srcVal);
2011 }
2012 
ATTR_NONNULL()2013 static void ATTR_NONNULL()
2014 doFunct_LTrim(struct cnffunc *__restrict__ const func,
2015 	struct svar *__restrict__ const ret,
2016 	void *__restrict__ const usrptr,
2017 	wti_t *__restrict__ const pWti)
2018 {
2019 	struct svar srcVal;
2020 	int bMustFree;
2021 	cnfexprEval(func->expr[0], &srcVal, usrptr, pWti);
2022 	char *str = (char*)var2CString(&srcVal, &bMustFree);
2023 
2024 	const int len = strlen(str);
2025 	int i;
2026 	es_str_t *estr = NULL;
2027 
2028 	for(i = 0; i < len; i++) {
2029 		if(str[i] != ' ') {
2030 			break;
2031 		}
2032 	}
2033 
2034 	estr = es_newStrFromCStr(str + i, len - i);
2035 
2036 	ret->d.estr = estr;
2037 	ret->datatype = 'S';
2038 	varFreeMembers(&srcVal);
2039 	if(bMustFree)
2040 		free(str);
2041 }
2042 
ATTR_NONNULL()2043 static void ATTR_NONNULL()
2044 doFunct_RTrim(struct cnffunc *__restrict__ const func,
2045 	struct svar *__restrict__ const ret,
2046 	void *__restrict__ const usrptr,
2047 	wti_t *__restrict__ const pWti)
2048 {
2049 	struct svar srcVal;
2050 	int bMustFree;
2051 	cnfexprEval(func->expr[0], &srcVal, usrptr, pWti);
2052 	char *str = (char*)var2CString(&srcVal, &bMustFree);
2053 
2054 	int len = strlen(str);
2055 	int i;
2056 	es_str_t *estr = NULL;
2057 
2058 	for(i = (len - 1); i > 0; i--) {
2059 		if(str[i] != ' ') {
2060 			break;
2061 		}
2062 	}
2063 
2064 	if(i > 0 || str[0] != ' ') {
2065 		estr = es_newStrFromCStr(str, (i + 1));
2066 	} else {
2067 		estr = es_newStr(1);
2068 	}
2069 
2070 	ret->d.estr = estr;
2071 	ret->datatype = 'S';
2072 	varFreeMembers(&srcVal);
2073 	if(bMustFree)
2074 		free(str);
2075 }
2076 
ATTR_NONNULL()2077 static void ATTR_NONNULL()
2078 doFunct_Getenv(struct cnffunc *__restrict__ const func,
2079 	struct svar *__restrict__ const ret,
2080 	void *__restrict__ const usrptr,
2081 	wti_t *__restrict__ const pWti)
2082 {
2083 	/* note: the optimizer shall have replaced calls to getenv()
2084 	 * with a constant argument to a single string (once obtained via
2085 	 * getenv()). So we do NOT need to check if there is just a
2086 	 * string following.
2087 	 */
2088 	struct svar srcVal;
2089 	char *envvar;
2090 	es_str_t *estr;
2091 	char *str;
2092 	int bMustFree;
2093 
2094 	cnfexprEval(func->expr[0], &srcVal, usrptr, pWti);
2095 	estr = var2String(&srcVal, &bMustFree);
2096 	str = (char*) es_str2cstr(estr, NULL);
2097 	envvar = getenv(str);
2098 	if(envvar == NULL) {
2099 		ret->d.estr = es_newStr(0);
2100 	} else {
2101 		ret->d.estr = es_newStrFromCStr(envvar, strlen(envvar));
2102 	}
2103 	ret->datatype = 'S';
2104 	if(bMustFree) {
2105 		es_deleteStr(estr);
2106 	}
2107 	varFreeMembers(&srcVal);
2108 	free(str);
2109 
2110 }
2111 
ATTR_NONNULL()2112 static void ATTR_NONNULL()
2113 doFunct_ToLower(struct cnffunc *__restrict__ const func,
2114 	struct svar *__restrict__ const ret,
2115 	void *__restrict__ const usrptr,
2116 	wti_t *__restrict__ const pWti)
2117 {
2118 	struct svar srcVal;
2119 	es_str_t *estr;
2120 	int bMustFree;
2121 
2122 	cnfexprEval(func->expr[0], &srcVal, usrptr, pWti);
2123 	estr = var2String(&srcVal, &bMustFree);
2124 	if(!bMustFree) {/* let caller handle that M) */
2125 		estr = es_strdup(estr);
2126 	}
2127 	es_tolower(estr);
2128 	ret->datatype = 'S';
2129 	ret->d.estr = estr;
2130 	varFreeMembers(&srcVal);
2131 }
2132 
ATTR_NONNULL()2133 static void ATTR_NONNULL()
2134 doFunct_CStr(struct cnffunc *__restrict__ const func,
2135 	struct svar *__restrict__ const ret,
2136 	void *__restrict__ const usrptr,
2137 	wti_t *__restrict__ const pWti)
2138 {
2139 	struct svar srcVal;
2140 	es_str_t *estr;
2141 	int bMustFree;
2142 
2143 	cnfexprEval(func->expr[0], &srcVal, usrptr, pWti);
2144 	estr = var2String(&srcVal, &bMustFree);
2145 	if(!bMustFree) /* let caller handle that M) */
2146 		estr = es_strdup(estr);
2147 	ret->datatype = 'S';
2148 	ret->d.estr = estr;
2149 	varFreeMembers(&srcVal);
2150 }
2151 
ATTR_NONNULL()2152 static void ATTR_NONNULL()
2153 doFunct_CNum(struct cnffunc *__restrict__ const func,
2154 	struct svar *__restrict__ const ret,
2155 	void *__restrict__ const usrptr,
2156 	wti_t *__restrict__ const pWti)
2157 {
2158 	struct svar srcVal;
2159 
2160 	if(func->expr[0]->nodetype == 'N') {
2161 		ret->d.n = ((struct cnfnumval*)func->expr[0])->val;
2162 	} else if(func->expr[0]->nodetype == 'S') {
2163 		ret->d.n = es_str2num(((struct cnfstringval*) func->expr[0])->estr,
2164 				      NULL);
2165 	} else {
2166 		cnfexprEval(func->expr[0], &srcVal, usrptr, pWti);
2167 		ret->d.n = var2Number(&srcVal, NULL);
2168 		varFreeMembers(&srcVal);
2169 	}
2170 	ret->datatype = 'N';
2171 	DBGPRINTF("JSONorString: cnum node type %c result %d\n", func->expr[0]->nodetype, (int) ret->d.n);
2172 }
2173 
ATTR_NONNULL()2174 static void ATTR_NONNULL()
2175 doFunct_ReMatch(struct cnffunc *__restrict__ const func,
2176 	struct svar *__restrict__ const ret,
2177 	void *__restrict__ const usrptr,
2178 	wti_t *__restrict__ const pWti)
2179 {
2180 	struct svar srcVal;
2181 	int bMustFree;
2182 	char *str;
2183 	int retval;
2184 
2185 	cnfexprEval(func->expr[0], &srcVal, usrptr, pWti);
2186 	str = (char*) var2CString(&srcVal, &bMustFree);
2187 	retval = regexp.regexec(func->funcdata, str, 0, NULL, 0);
2188 	if(retval == 0)
2189 		ret->d.n = 1;
2190 	else {
2191 		ret->d.n = 0;
2192 		if(retval != REG_NOMATCH) {
2193 			DBGPRINTF("re_match: regexec returned error %d\n", retval);
2194 		}
2195 	}
2196 	ret->datatype = 'N';
2197 	if(bMustFree) {
2198 		free(str);
2199 	}
2200 	varFreeMembers(&srcVal);
2201 }
2202 
ATTR_NONNULL()2203 static void ATTR_NONNULL()
2204 doFunct_Ipv42num(struct cnffunc *__restrict__ const func,
2205 	struct svar *__restrict__ const ret,
2206 	void *__restrict__ const usrptr,
2207 	wti_t *__restrict__ const pWti)
2208 {
2209 	struct svar srcVal;
2210 	int bMustFree;
2211 	char *str;
2212 
2213 	cnfexprEval(func->expr[0], &srcVal, usrptr, pWti);
2214 	str = (char*)var2CString(&srcVal, &bMustFree);
2215 
2216 
2217 	unsigned num[4] = {0, 0, 0, 0};
2218 	long long value = -1;
2219 	size_t len = strlen(str);
2220 	int cyc = 0;
2221 	int prevdot = 0;
2222 	int startblank = 0;
2223 	int endblank = 0;
2224 	DBGPRINTF("rainerscript: (ipv42num) arg: '%s'\n", str);
2225 	for(unsigned int i = 0 ; i < len ; i++) {
2226 		switch(str[i]){
2227 		case '0':
2228 		case '1':
2229 		case '2':
2230 		case '3':
2231 		case '4':
2232 		case '5':
2233 		case '6':
2234 		case '7':
2235 		case '8':
2236 		case '9':
2237 			if(endblank == 1){
2238 				DBGPRINTF("rainerscript: (ipv42num) error: wrong IP-Address format "
2239 					"(invalid space(1))\n");
2240 				goto done;
2241 			}
2242 			prevdot = 0;
2243 			startblank = 0;
2244 			DBGPRINTF("rainerscript: (ipv42num) cycle: %d\n", cyc);
2245 			num[cyc] = num[cyc]*10+(str[i]-'0');
2246 			break;
2247 		case ' ':
2248 			prevdot = 0;
2249 			if(i == 0 || startblank == 1){
2250 				startblank = 1;
2251 				break;
2252 			}
2253 			else{
2254 				endblank = 1;
2255 				break;
2256 			}
2257 		case '.':
2258 			if(endblank == 1){
2259 				DBGPRINTF("rainerscript: (ipv42num) error: wrong IP-Address format "
2260 					"(inalid space(2))\n");
2261 				goto done;
2262 			}
2263 			startblank = 0;
2264 			if(prevdot == 1){
2265 				DBGPRINTF("rainerscript: (ipv42num) error: wrong IP-Address format "
2266 					"(two dots after one another)\n");
2267 				goto done;
2268 			}
2269 			prevdot = 1;
2270 			cyc++;
2271 			if(cyc > 3){
2272 				DBGPRINTF("rainerscript: (ipv42num) error: wrong IP-Address format "
2273 					"(too many dots)\n");
2274 				goto done;
2275 			}
2276 			break;
2277 		default:
2278 			DBGPRINTF("rainerscript: (ipv42num) error: wrong IP-Address format (invalid charakter)\n");
2279 			goto done;
2280 		}
2281 	}
2282 	if(cyc != 3){
2283 		DBGPRINTF("rainerscript: (ipv42num) error: wrong IP-Address format (wrong number of dots)\n");
2284 		goto done;
2285 	}
2286 	value = num[0]*256*256*256+num[1]*256*256+num[2]*256+num[3];
2287 done:
2288 	DBGPRINTF("rainerscript: (ipv42num): return value:'%lld'\n",value);
2289 	ret->datatype = 'N';
2290 	ret->d.n = value;
2291 	varFreeMembers(&srcVal);
2292 	if(bMustFree)
2293 		free(str);
2294 }
2295 
ATTR_NONNULL()2296 static void ATTR_NONNULL()
2297 doFunct_Int2Hex(struct cnffunc *__restrict__ const func,
2298 	struct svar *__restrict__ const ret,
2299 	void *__restrict__ const usrptr,
2300 	wti_t *__restrict__ const pWti)
2301 {
2302 	struct svar srcVal;
2303 	int success = 0;
2304 	char str[18];
2305 	es_str_t* estr = NULL;
2306 	cnfexprEval(func->expr[0], &srcVal, usrptr, pWti);
2307 	long long num = var2Number(&srcVal, &success);
2308 
2309 	if (!success) {
2310 		DBGPRINTF("rainerscript: (int2hex) couldn't access number\n");
2311 		estr = es_newStrFromCStr("NAN", strlen("NAN"));
2312 		goto done;
2313 	}
2314 
2315 	snprintf(str, 18, "%llx", num);
2316 	estr = es_newStrFromCStr(str, strlen(str));
2317 
2318 done:
2319 	ret->d.estr = estr;
2320 	ret->datatype = 'S';
2321 	varFreeMembers(&srcVal);
2322 }
2323 
ATTR_NONNULL()2324 static void ATTR_NONNULL()
2325 doFunct_Replace(struct cnffunc *__restrict__ const func,
2326 	struct svar *__restrict__ const ret,
2327 	void *__restrict__ const usrptr,
2328 	wti_t *__restrict__ const pWti)
2329 {
2330 	struct svar srcVal[3];
2331 
2332 	cnfexprEval(func->expr[0], &srcVal[0], usrptr, pWti);
2333 	cnfexprEval(func->expr[1], &srcVal[1], usrptr, pWti);
2334 	cnfexprEval(func->expr[2], &srcVal[2], usrptr, pWti);
2335 	ret->d.estr = doFuncReplace(&srcVal[0], &srcVal[1], &srcVal[2]);
2336 	ret->datatype = 'S';
2337 	varFreeMembers(&srcVal[0]);
2338 	varFreeMembers(&srcVal[1]);
2339 	varFreeMembers(&srcVal[2]);
2340 }
2341 
ATTR_NONNULL()2342 static void ATTR_NONNULL()
2343 doFunct_Wrap(struct cnffunc *__restrict__ const func,
2344 	struct svar *__restrict__ const ret,
2345 	void *__restrict__ const usrptr,
2346 	wti_t *__restrict__ const pWti)
2347 {
2348 	struct svar sourceVal;
2349 	struct svar wrapperVal;
2350 	struct svar escaperVal;
2351 	int freeSource, freeWrapper;
2352 	es_str_t *sourceStr;
2353 
2354 	cnfexprEval(func->expr[0], &sourceVal, usrptr, pWti);
2355 	cnfexprEval(func->expr[1], &wrapperVal, usrptr, pWti);
2356 	if(func->nParams == 3) {
2357 		cnfexprEval(func->expr[2], &escaperVal, usrptr, pWti);
2358 		sourceStr = doFuncReplace(&sourceVal, &wrapperVal, &escaperVal);
2359 		freeSource = 1;
2360 
2361 	} else {
2362 		sourceStr = var2String(&sourceVal, &freeSource);
2363 	}
2364 	es_str_t *wrapperStr = var2String(&wrapperVal, &freeWrapper);
2365 	uchar *src = es_getBufAddr(sourceStr);
2366 	uchar *wrapper = es_getBufAddr(wrapperStr);
2367 	uint lWrapper = es_strlen(wrapperStr);
2368 	uint lSrc = es_strlen(sourceStr);
2369 	uint totalLen = lSrc + 2 * lWrapper;
2370 	es_str_t *res = es_newStr(totalLen);
2371 	uchar* resBuf = es_getBufAddr(res);
2372 	memcpy(resBuf, wrapper, lWrapper);
2373 	memcpy(resBuf + lWrapper, src, lSrc);
2374 	memcpy(resBuf + lSrc + lWrapper, wrapper, lWrapper);
2375 	res->lenStr = totalLen;
2376 	if (freeSource) {
2377 		es_deleteStr(sourceStr);
2378 	}
2379 	if (freeWrapper) {
2380 		es_deleteStr(wrapperStr);
2381 	}
2382 
2383 	ret->d.estr = res;
2384 	ret->datatype = 'S';
2385 	varFreeMembers(&sourceVal);
2386 	varFreeMembers(&wrapperVal);
2387 	if(func->nParams == 3) varFreeMembers(&escaperVal);
2388 }
2389 
ATTR_NONNULL()2390 static void ATTR_NONNULL()
2391 doFunct_StrLen(struct cnffunc *__restrict__ const func,
2392 	struct svar *__restrict__ const ret,
2393 	void *__restrict__ const usrptr,
2394 	wti_t *__restrict__ const pWti)
2395 {
2396 	struct svar srcVal;
2397 	int bMustFree;
2398 	es_str_t *estr;
2399 
2400 	if(func->expr[0]->nodetype == 'S') {
2401 		/* if we already have a string, we do not need to
2402 		 * do one more recursive call.
2403 		 */
2404 		ret->d.n = es_strlen(((struct cnfstringval*) func->expr[0])->estr);
2405 	} else {
2406 		cnfexprEval(func->expr[0], &srcVal, usrptr, pWti);
2407 		estr = var2String(&srcVal, &bMustFree);
2408 		ret->d.n = es_strlen(estr);
2409 		if(bMustFree) {
2410 			es_deleteStr(estr);
2411 		}
2412 		varFreeMembers(&srcVal);
2413 	}
2414 	ret->datatype = 'N';
2415 }
2416 
ATTR_NONNULL()2417 static void ATTR_NONNULL()
2418 doFunct_Substring(struct cnffunc *__restrict__ const func,
2419 	struct svar *__restrict__ const ret,
2420 	void *__restrict__ const usrptr,
2421 	wti_t *__restrict__ const pWti)
2422 {    //TODO: generalize parameter getter? jgerhards, 2018-02-26
2423 	int bMustFree;
2424 	struct svar srcVal[3];
2425 
2426 	cnfexprEval(func->expr[0], &srcVal[0], usrptr, pWti);
2427 	cnfexprEval(func->expr[1], &srcVal[1], usrptr, pWti);
2428 	cnfexprEval(func->expr[2], &srcVal[2], usrptr, pWti);
2429 	es_str_t *es = var2String(&srcVal[0], &bMustFree);
2430 	const int start = var2Number(&srcVal[1], NULL);
2431 	const int subStrLen = var2Number(&srcVal[2], NULL);
2432 
2433 	ret->datatype = 'S';
2434 	ret->d.estr = es_newStrFromSubStr(es, (es_size_t)start, (es_size_t)subStrLen);
2435 	if(bMustFree) es_deleteStr(es);
2436 	varFreeMembers(&srcVal[0]);
2437 	varFreeMembers(&srcVal[1]);
2438 	varFreeMembers(&srcVal[2]);
2439 }
2440 
ATTR_NONNULL()2441 static void ATTR_NONNULL()
2442 doFunct_Field(struct cnffunc *__restrict__ const func,
2443 	struct svar *__restrict__ const ret,
2444 	void *__restrict__ const usrptr,
2445 	wti_t *__restrict__ const pWti)
2446 {
2447 	struct svar srcVal[3];
2448 	int bMustFree;
2449 	char *str;
2450 	uchar *resStr;
2451 	int matchnbr;
2452 	int delim;
2453 	rsRetVal localRet;
2454 
2455 	cnfexprEval(func->expr[0], &srcVal[0], usrptr, pWti);
2456 	cnfexprEval(func->expr[1], &srcVal[1], usrptr, pWti);
2457 	cnfexprEval(func->expr[2], &srcVal[2], usrptr, pWti);
2458 	str = (char*) var2CString(&srcVal[0], &bMustFree);
2459 	matchnbr = var2Number(&srcVal[2], NULL);
2460 	if(srcVal[1].datatype == 'S') {
2461 		char *delimstr;
2462 		delimstr = (char*) es_str2cstr(srcVal[1].d.estr, NULL);
2463 		localRet = doExtractFieldByStr((uchar*)str, delimstr, es_strlen(srcVal[1].d.estr),
2464 						matchnbr, &resStr);
2465 		free(delimstr);
2466 	} else {
2467 		delim = var2Number(&srcVal[1], NULL);
2468 		localRet = doExtractFieldByChar((uchar*)str, (char) delim, matchnbr, &resStr);
2469 	}
2470 	if(localRet == RS_RET_OK) {
2471 		ret->d.estr = es_newStrFromCStr((char*)resStr, strlen((char*)resStr));
2472 		free(resStr);
2473 	} else if(localRet == RS_RET_FIELD_NOT_FOUND) {
2474 		ret->d.estr = es_newStrFromCStr("***FIELD NOT FOUND***",
2475 				sizeof("***FIELD NOT FOUND***")-1);
2476 	} else {
2477 		ret->d.estr = es_newStrFromCStr("***ERROR in field() FUNCTION***",
2478 				sizeof("***ERROR in field() FUNCTION***")-1);
2479 	}
2480 	ret->datatype = 'S';
2481 	if(bMustFree) free(str);
2482 	varFreeMembers(&srcVal[0]);
2483 	varFreeMembers(&srcVal[1]);
2484 	varFreeMembers(&srcVal[2]);
2485 }
2486 
ATTR_NONNULL()2487 static void ATTR_NONNULL()
2488 doFunct_Prifilt(struct cnffunc *__restrict__ const func,
2489 	struct svar *__restrict__ const ret,
2490 	void *__restrict__ const usrptr,
2491 	wti_t *const pWti __attribute__((unused)))
2492 {
2493 	struct funcData_prifilt *pPrifilt;
2494 
2495 	pPrifilt = (struct funcData_prifilt*) func->funcdata;
2496 	if( (pPrifilt->pmask[((smsg_t*)usrptr)->iFacility] == TABLE_NOPRI) ||
2497 	   ((pPrifilt->pmask[((smsg_t*)usrptr)->iFacility]
2498 		    & (1<<((smsg_t*)usrptr)->iSeverity)) == 0) )
2499 		ret->d.n = 0;
2500 	else
2501 		ret->d.n = 1;
2502 	ret->datatype = 'N';
2503 }
2504 
ATTR_NONNULL()2505 static void ATTR_NONNULL()
2506 doFunct_Lookup(struct cnffunc *__restrict__ const func,
2507 	struct svar *__restrict__ const ret,
2508 	void *__restrict__ const usrptr,
2509 	wti_t *__restrict__ const pWti)
2510 {
2511 	struct svar srcVal;
2512 	lookup_key_t key;
2513 	uint8_t lookup_key_type;
2514 	lookup_ref_t *lookup_table_ref;
2515 	lookup_t *lookup_table;
2516 	int bMustFree;
2517 
2518 	ret->datatype = 'S';
2519 	if(func->funcdata == NULL) {
2520 		ret->d.estr = es_newStrFromCStr("TABLE-NOT-FOUND", sizeof("TABLE-NOT-FOUND")-1);
2521 		return;
2522 	}
2523 	cnfexprEval(func->expr[1], &srcVal, usrptr, pWti);
2524 	lookup_table_ref = (lookup_ref_t*) func->funcdata;
2525 	pthread_rwlock_rdlock(&lookup_table_ref->rwlock);
2526 	lookup_table = lookup_table_ref->self;
2527 	if (lookup_table != NULL) {
2528 		lookup_key_type = lookup_table->key_type;
2529 		bMustFree = 0;
2530 		if (lookup_key_type == LOOKUP_KEY_TYPE_STRING) {
2531 			key.k_str = (uchar*) var2CString(&srcVal, &bMustFree);
2532 		} else if (lookup_key_type == LOOKUP_KEY_TYPE_UINT) {
2533 			key.k_uint = var2Number(&srcVal, NULL);
2534 		} else {
2535 			DBGPRINTF("program error in %s:%d: lookup_key_type unknown\n",
2536 				__FILE__, __LINE__);
2537 			key.k_uint = 0;
2538 		}
2539 		ret->d.estr = lookupKey((lookup_ref_t*)func->funcdata, key);
2540 		if(bMustFree) {
2541 			free(key.k_str);
2542 		}
2543 	} else {
2544 		ret->d.estr = es_newStrFromCStr("", 1);
2545 	}
2546 	pthread_rwlock_unlock(&lookup_table_ref->rwlock);
2547 	varFreeMembers(&srcVal);
2548 }
2549 
ATTR_NONNULL()2550 static void ATTR_NONNULL()
2551 doFunct_DynInc(struct cnffunc *__restrict__ const func,
2552 	struct svar *__restrict__ const ret,
2553 	void *__restrict__ const usrptr,
2554 	wti_t *__restrict__ const pWti)
2555 {
2556 	struct svar srcVal;
2557 	int bMustFree;
2558 	char *str;
2559 
2560 	ret->datatype = 'N';
2561 	if(func->funcdata == NULL) {
2562 		ret->d.n = -1;
2563 		return;
2564 	}
2565 	cnfexprEval(func->expr[1], &srcVal, usrptr, pWti);
2566 	str = (char*) var2CString(&srcVal, &bMustFree);
2567 	ret->d.n = dynstats_inc(func->funcdata, (uchar*)str);
2568 	if(bMustFree) free(str);
2569 	varFreeMembers(&srcVal);
2570 }
2571 
ATTR_NONNULL()2572 static void ATTR_NONNULL()
2573 doFunct_FormatTime(struct cnffunc *__restrict__ const func,
2574 	struct svar *__restrict__ const ret,
2575 	void *__restrict__ const usrptr,
2576 	wti_t *__restrict__ const pWti)
2577 {
2578 	struct svar srcVal[2];
2579 	int bMustFree;
2580 	char *str;
2581 	int retval;
2582 	long long unixtime;
2583 	const int resMax = 64;
2584 	char   result[resMax];
2585 	char  *formatstr = NULL;
2586 
2587 	cnfexprEval(func->expr[0], &srcVal[0], usrptr, pWti);
2588 	cnfexprEval(func->expr[1], &srcVal[1], usrptr, pWti);
2589 
2590 	unixtime = var2Number(&srcVal[0], &retval);
2591 
2592 	// Make sure that the timestamp we got can fit into
2593 	// time_t on older systems.
2594 	if (sizeof(time_t) == sizeof(int)) {
2595 		if (unixtime < INT_MIN || unixtime > INT_MAX) {
2596 			LogMsg(
2597 				0, RS_RET_VAL_OUT_OF_RANGE, LOG_WARNING,
2598 				"Timestamp value %lld is out of range for this system (time_t is "
2599 				"32bits)!\n", unixtime
2600 			);
2601 			retval = 0;
2602 		}
2603 	}
2604 
2605 	// We want the string form too so we can return it as the
2606 	// default if we run into problems parsing the number.
2607 	str = (char*) var2CString(&srcVal[0], &bMustFree);
2608 	formatstr = (char*) es_str2cstr(srcVal[1].d.estr, NULL);
2609 
2610 	ret->datatype = 'S';
2611 
2612 	if (objUse(datetime, CORE_COMPONENT) != RS_RET_OK) {
2613 		ret->d.estr = es_newStr(0);
2614 	} else {
2615 		if (!retval || datetime.formatUnixTimeFromTime_t(unixtime, formatstr, result, resMax) == -1) {
2616 			strncpy(result, str, resMax);
2617 			result[resMax - 1] = '\0';
2618 		}
2619 		ret->d.estr = es_newStrFromCStr(result, strlen(result));
2620 	}
2621 
2622 	if (bMustFree) {
2623 		free(str);
2624 	}
2625 	free(formatstr);
2626 
2627 	varFreeMembers(&srcVal[0]);
2628 	varFreeMembers(&srcVal[1]);
2629 
2630 }
2631 
2632 /*
2633  * Uses the given (current) year/month to decide which year
2634  * the incoming month likely belongs in.
2635  *
2636  * cy - Current Year (actual)
2637  * cm - Current Month (actual)
2638  * im - "Incoming" Month
2639  */
2640 static int
estimateYear(int cy,int cm,int im)2641 estimateYear(int cy, int cm, int im) {
2642 	im += 12;
2643 
2644 	if ((im - cm) == 1) {
2645 		if (cm == 12 && im == 13)
2646 			return cy + 1;
2647 	}
2648 
2649 	if ((im - cm) > 13)
2650 		return cy - 1;
2651 
2652 	return cy;
2653 }
2654 
ATTR_NONNULL()2655 static void ATTR_NONNULL()
2656 doFunct_ParseTime(struct cnffunc *__restrict__ const func,
2657 	struct svar *__restrict__ const ret,
2658 	void *__restrict__ const usrptr,
2659 	wti_t *__restrict__ const pWti)
2660 {
2661 	struct svar srcVal;
2662 	int bMustFree;
2663 	cnfexprEval(func->expr[0], &srcVal, usrptr, pWti);
2664 	char *str = (char*) var2CString(&srcVal, &bMustFree);
2665 	ret->datatype = 'N';
2666 	ret->d.n = 0;
2667 	wtiSetScriptErrno(pWti, RS_SCRIPT_EOK);
2668 
2669 	if (objUse(datetime, CORE_COMPONENT) == RS_RET_OK) {
2670 		struct syslogTime s;
2671 		int len = strlen(str);
2672 		uchar *pszTS = (uchar*) str;
2673 		memset(&s, 0, sizeof(struct syslogTime));
2674 		// Attempt to parse the date/time string
2675 		if (datetime.ParseTIMESTAMP3339(&s, (uchar**) &pszTS, &len) == RS_RET_OK) {
2676 			ret->d.n = datetime.syslogTime2time_t(&s);
2677 			DBGPRINTF("parse_time: RFC3339 format found\n");
2678 		} else if (datetime.ParseTIMESTAMP3164(&s, (uchar**) &pszTS, &len,
2679 			NO_PARSE3164_TZSTRING, NO_PERMIT_YEAR_AFTER_TIME) == RS_RET_OK) {
2680 			time_t t = time(NULL);
2681 			struct tm tm;
2682 			gmtime_r(&t, &tm); // Get the current UTC date
2683 			// Since properly formatted RFC 3164 timestamps do not have a YEAR
2684 			// specified, we have to assume one that seems reasonable - SW.
2685 			s.year = estimateYear(tm.tm_year + 1900, tm.tm_mon + 1, s.month);
2686 			ret->d.n = datetime.syslogTime2time_t(&s);
2687 			DBGPRINTF("parse_time: RFC3164 format found\n");
2688 		} else {
2689 			DBGPRINTF("parse_time: no valid format found\n");
2690 			wtiSetScriptErrno(pWti, RS_SCRIPT_EINVAL);
2691 		}
2692 	}
2693 
2694 	if(bMustFree) {
2695 		free(str);
2696 	}
2697 	varFreeMembers(&srcVal);
2698 
2699 }
2700 
2701 static int ATTR_NONNULL(1,3,4)
doFunc_is_time(const char * __restrict__ const str,const char * __restrict__ const fmt,struct svar * __restrict__ const r,wti_t * pWti)2702 doFunc_is_time(const char *__restrict__ const str,
2703 	const char *__restrict__ const fmt,
2704 	struct svar *__restrict__ const r,
2705 	wti_t *pWti) {
2706 
2707 	assert(str != NULL);
2708 	assert(r != NULL);
2709 	assert(pWti != NULL);
2710 
2711 	int ret = 0;
2712 
2713 	wtiSetScriptErrno(pWti, RS_SCRIPT_EOK);
2714 
2715 	if (objUse(datetime, CORE_COMPONENT) == RS_RET_OK) {
2716 		struct syslogTime s;
2717 		int len = strlen(str);
2718 		uchar *pszTS = (uchar*) str;
2719 
2720 		int numFormats  = 3;
2721 		dateTimeFormat_t formats[] = { DATE_RFC3164, DATE_RFC3339, DATE_UNIX };
2722 		dateTimeFormat_t pf[] = { DATE_INVALID };
2723 		dateTimeFormat_t *p  = formats;
2724 
2725 		// Check if a format specifier was explicitly provided
2726 		if (fmt != NULL) {
2727 			numFormats = 1;
2728 			*pf = getDateTimeFormatFromStr(fmt);
2729 			p = pf;
2730 		}
2731 
2732 		// Enumerate format specifier options, looking for the first match
2733 		for (int i = 0; i < numFormats; i++) {
2734 			dateTimeFormat_t f = p[i];
2735 
2736 			if (f == DATE_RFC3339) {
2737 				if (datetime.ParseTIMESTAMP3339(&s, (uchar**) &pszTS, &len) == RS_RET_OK) {
2738 					DBGPRINTF("is_time: RFC3339 format found.\n");
2739 					ret = 1;
2740 					break;
2741 				}
2742 			} else if (f == DATE_RFC3164) {
2743 				if (datetime.ParseTIMESTAMP3164(&s, (uchar**) &pszTS, &len,
2744 					NO_PARSE3164_TZSTRING, NO_PERMIT_YEAR_AFTER_TIME) == RS_RET_OK) {
2745 					DBGPRINTF("is_time: RFC3164 format found.\n");
2746 					ret = 1;
2747 					break;
2748 				}
2749 			} else if (f == DATE_UNIX) {
2750 				int result;
2751 				var2Number(r, &result);
2752 
2753 				if (result) {
2754 					DBGPRINTF("is_time: UNIX format found.\n");
2755 					ret = 1;
2756 					break;
2757 				}
2758 			} else {
2759 				DBGPRINTF("is_time: %s is not a valid date/time format specifier!\n", fmt);
2760 				break;
2761 			}
2762 		}
2763 	}
2764 
2765 	// If not a valid date/time string, set 'errno'
2766 	if (ret == 0) {
2767 		DBGPRINTF("is_time: Invalid date-time string: %s.\n", str);
2768 		wtiSetScriptErrno(pWti, RS_SCRIPT_EINVAL);
2769 	}
2770 
2771 	return ret;
2772 }
2773 
ATTR_NONNULL()2774 static void ATTR_NONNULL()
2775 doFunct_IsTime(struct cnffunc *__restrict__ const func,
2776 	struct svar *__restrict__ const ret,
2777 	void *__restrict__ const usrptr,
2778 	wti_t *__restrict__ const pWti)
2779 {
2780 	struct svar srcVal[2];
2781 	int bMustFree;
2782 	int bMustFree2;
2783 	char *fmt = NULL;
2784 
2785 	cnfexprEval(func->expr[0], &srcVal[0], usrptr, pWti);
2786 	char *str = (char*) var2CString(&srcVal[0], &bMustFree);
2787 
2788 	bMustFree2 = 0;
2789 
2790 	// Check if the optional 2nd parameter was provided
2791 	if(func->nParams == 2) {
2792 		cnfexprEval(func->expr[1], &srcVal[1], usrptr, pWti);
2793 		fmt = (char*) var2CString(&srcVal[1], &bMustFree2);
2794 	}
2795 
2796 	ret->datatype = 'N';
2797 	ret->d.n = doFunc_is_time(str, fmt, &srcVal[0], pWti);
2798 
2799 	if(bMustFree) {
2800 		free(str);
2801 	}
2802 	if(bMustFree2) {
2803 		free(fmt);
2804 	}
2805 	varFreeMembers(&srcVal[0]);
2806 	if(func->nParams == 2) {
2807 		varFreeMembers(&srcVal[1]);
2808 	}
2809 }
2810 
ATTR_NONNULL()2811 static void ATTR_NONNULL()
2812 doFunct_ScriptError(struct cnffunc *const func __attribute__((unused)),
2813 	struct svar *__restrict__ const ret,
2814 	void *const usrptr __attribute__((unused)),
2815 	wti_t *__restrict__ const pWti)
2816 {
2817 	ret->datatype = 'N';
2818 	ret->d.n = wtiGetScriptErrno(pWti);
2819 	DBGPRINTF("script_error() is %d\n", (int) ret->d.n);
2820 }
2821 
ATTR_NONNULL()2822 static void ATTR_NONNULL()
2823 doFunct_PreviousActionSuspended(struct cnffunc *const func __attribute__((unused)),
2824 	struct svar *__restrict__ const ret,
2825 	void *const usrptr __attribute__((unused)),
2826 	wti_t *__restrict__ const pWti)
2827 {
2828 	ret->datatype = 'N';
2829 	ret->d.n = wtiGetPrevWasSuspended(pWti);
2830 	DBGPRINTF("previous_action_suspended() is %d\n", (int) ret->d.n);
2831 }
2832 
ATTR_NONNULL()2833 static void ATTR_NONNULL()
2834 doFunct_num2ipv4(struct cnffunc *__restrict__ const func,
2835 	struct svar *__restrict__ const ret,
2836 	void *__restrict__ const usrptr,
2837 	wti_t *__restrict__ const pWti)
2838 {
2839 	struct svar srcVal;
2840 	cnfexprEval(func->expr[0], &srcVal, usrptr, pWti);
2841 	int success = 0;
2842 	long long num = var2Number(&srcVal, &success);
2843 	varFreeMembers(&srcVal);
2844 
2845 	int numip[4];
2846 	char str[16];
2847 	size_t len;
2848 	DBGPRINTF("rainrescript: (num2ipv4) var2Number output: '%lld\n'", num);
2849 	if (! success) {
2850 		DBGPRINTF("rainerscript: (num2ipv4) couldn't access number\n");
2851 		len = snprintf(str, 16, "-1");
2852 		goto done;
2853 	}
2854 	if(num < 0 || num > 4294967295) {
2855 		DBGPRINTF("rainerscript: (num2ipv4) invalid number(too big/negative); does "
2856 			"not represent IPv4 address\n");
2857 		len = snprintf(str, 16, "-1");
2858 		goto done;
2859 	}
2860 	for(int i = 0 ; i < 4 ; i++){
2861 		numip[i] = num % 256;
2862 		num = num / 256;
2863 	}
2864 	DBGPRINTF("rainerscript: (num2ipv4) Numbers: 1:'%d' 2:'%d' 3:'%d' 4:'%d'\n",
2865 		numip[0], numip[1], numip[2], numip[3]);
2866 	len = snprintf(str, 16, "%d.%d.%d.%d", numip[3], numip[2], numip[1], numip[0]);
2867 done:
2868 	DBGPRINTF("rainerscript: (num2ipv4) ipv4-Address: %s, lengh: %zu\n", str, len);
2869 	ret->d.estr = es_newStrFromCStr(str, len);
2870 	ret->datatype = 'S';
2871 }
2872 
2873 
2874 /* Perform a function call. This has been moved out of cnfExprEval in order
2875  * to keep the code small and easier to maintain.
2876  */
ATTR_NONNULL()2877 static void ATTR_NONNULL()
2878 doFuncCall(struct cnffunc *__restrict__ const func, struct svar *__restrict__ const ret,
2879 	void *__restrict__ const usrptr,
2880 	wti_t *__restrict__ const pWti)
2881 {
2882 
2883 	if(Debug) {
2884 		char *fname = es_str2cstr(func->fname, NULL);
2885 		DBGPRINTF("rainerscript: executing function id %s\n", fname);
2886 		free(fname);
2887 	}
2888 	if(func->fPtr == NULL) {
2889 		char *fname = es_str2cstr(func->fname, NULL);
2890 		LogError(0, RS_RET_INTERNAL_ERROR,
2891 			"rainerscript: internal error: NULL pointer for function named '%s'\n",
2892 			fname);
2893 		free(fname);
2894 		ret->datatype = 'N';
2895 		ret->d.n = 0;
2896 	} else {
2897 		func->fPtr(func, ret, usrptr, pWti);
2898 	}
2899 }
2900 
2901 
2902 /* Perform the special "exists()" function to check presence of a variable.
2903  */
ATTR_NONNULL()2904 static int ATTR_NONNULL()
2905 evalFuncExists(struct cnffuncexists *__restrict__ const fexists, void *__restrict__ const usrptr)
2906 {
2907 	int r = 0;
2908 	rsRetVal localRet;
2909 
2910 	if(fexists->prop.id == PROP_CEE        ||
2911 	   fexists->prop.id == PROP_LOCAL_VAR  ||
2912 	   fexists->prop.id == PROP_GLOBAL_VAR   ) {
2913 		localRet = msgCheckVarExists((smsg_t*)usrptr, &fexists->prop);
2914 		if(localRet == RS_RET_OK) {
2915 			r = 1;
2916 		}
2917 	}
2918 
2919 	return r;
2920 }
2921 
2922 static void
evalVar(struct cnfvar * __restrict__ const var,void * __restrict__ const usrptr,struct svar * __restrict__ const ret)2923 evalVar(struct cnfvar *__restrict__ const var, void *__restrict__ const usrptr,
2924 	struct svar *__restrict__ const ret)
2925 {
2926 	rs_size_t propLen;
2927 	uchar *pszProp = NULL;
2928 	unsigned short bMustBeFreed = 0;
2929 	rsRetVal localRet;
2930 	struct json_object *json;
2931 	uchar *cstr;
2932 
2933 	if(var->prop.id == PROP_CEE        ||
2934 	   var->prop.id == PROP_LOCAL_VAR  ||
2935 	   var->prop.id == PROP_GLOBAL_VAR   ) {
2936 		localRet = msgGetJSONPropJSONorString((smsg_t*)usrptr, &var->prop, &json, &cstr);
2937 		if(json != NULL) {
2938 			assert(cstr == NULL);
2939 			ret->datatype = 'J';
2940 			ret->d.json = (localRet == RS_RET_OK) ? json : NULL;
2941 			DBGPRINTF("rainerscript: (json) var %d:%s: '%s'\n",
2942 				var->prop.id, var->prop.name,
2943 			  (ret->d.json == NULL) ? "" : json_object_get_string(ret->d.json));
2944 		} else { /* we have a string */
2945 			DBGPRINTF("rainerscript: (json/string) var %d: '%s'\n", var->prop.id, cstr);
2946 			ret->datatype = 'S';
2947 			ret->d.estr = (localRet != RS_RET_OK || cstr == NULL) ?
2948 					  es_newStr(1)
2949 					: es_newStrFromCStr((char*) cstr, strlen((char*) cstr));
2950 			free(cstr);
2951 		}
2952 	} else {
2953 		ret->datatype = 'S';
2954 		pszProp = (uchar*) MsgGetProp((smsg_t*)usrptr, NULL, &var->prop, &propLen, &bMustBeFreed, NULL);
2955 		ret->d.estr = es_newStrFromCStr((char*)pszProp, propLen);
2956 		DBGPRINTF("rainerscript: (string) var %d: '%s'\n", var->prop.id, pszProp);
2957 		if(bMustBeFreed)
2958 			free(pszProp);
2959 	}
2960 
2961 }
2962 
2963 /* perform a string comparision operation against a while array. Semantic is
2964  * that one one comparison is true, the whole construct is true.
2965  * TODO: we can obviously optimize this process. One idea is to
2966  * compile a regex, which should work faster than serial comparison.
2967  * Note: compiling a regex does NOT work at all. I experimented with that
2968  * and it was generally 5 to 10 times SLOWER than what we do here...
2969  */
2970 static int
evalStrArrayCmp(es_str_t * const estr_l,const struct cnfarray * __restrict__ const ar,const int cmpop)2971 evalStrArrayCmp(es_str_t *const estr_l,
2972 		const struct cnfarray *__restrict__ const ar,
2973 		const int cmpop)
2974 {
2975 	int i;
2976 	int r = 0;
2977 	es_str_t **res;
2978 	if(cmpop == CMP_EQ) {
2979 		res = bsearch(&estr_l, ar->arr, ar->nmemb, sizeof(es_str_t*), qs_arrcmp);
2980 		r = res != NULL;
2981 	} else if(cmpop == CMP_NE) {
2982 		res = bsearch(&estr_l, ar->arr, ar->nmemb, sizeof(es_str_t*), qs_arrcmp);
2983 		r = res == NULL;
2984 	} else {
2985 		for(i = 0 ; (r == 0) && (i < ar->nmemb) ; ++i) {
2986 			switch(cmpop) {
2987 			case CMP_STARTSWITH:
2988 				r = es_strncmp(estr_l, ar->arr[i], es_strlen(ar->arr[i])) == 0;
2989 				break;
2990 			case CMP_STARTSWITHI:
2991 				r = es_strncasecmp(estr_l, ar->arr[i], es_strlen(ar->arr[i])) == 0;
2992 				break;
2993 			case CMP_CONTAINS:
2994 				r = es_strContains(estr_l, ar->arr[i]) != -1;
2995 				break;
2996 			case CMP_CONTAINSI:
2997 				r = es_strCaseContains(estr_l, ar->arr[i]) != -1;
2998 				break;
2999 			}
3000 		}
3001 	}
3002 	return r;
3003 }
3004 
3005 #define FREE_BOTH_RET \
3006 		varFreeMembers(&r); \
3007 		varFreeMembers(&l)
3008 
3009 #define COMP_NUM_BINOP(x) \
3010 	cnfexprEval(expr->l, &l, usrptr, pWti); \
3011 	cnfexprEval(expr->r, &r, usrptr, pWti); \
3012 	ret->datatype = 'N'; \
3013 	ret->d.n = var2Number(&l, &convok_l) x var2Number(&r, &convok_r); \
3014 	FREE_BOTH_RET
3015 
3016 #define COMP_NUM_BINOP_DIV(x) \
3017 	cnfexprEval(expr->l, &l, usrptr, pWti); \
3018 	cnfexprEval(expr->r, &r, usrptr, pWti); \
3019 	ret->datatype = 'N'; \
3020 	if((ret->d.n = var2Number(&r, &convok_r)) == 0) { \
3021 		/* division by zero */ \
3022 	} else { \
3023 		ret->d.n = var2Number(&l, &convok_l) x ret->d.n; \
3024 	} \
3025 	FREE_BOTH_RET
3026 
3027 /* NOTE: array as right-hand argument MUST be handled by user */
3028 #define PREP_TWO_STRINGS \
3029 		cnfexprEval(expr->l, &l, usrptr, pWti); \
3030 		estr_l = var2String(&l, &bMustFree2); \
3031 		if(expr->r->nodetype == 'S') { \
3032 			estr_r = ((struct cnfstringval*)expr->r)->estr;\
3033 			bMustFree = 0; \
3034 		} else if(expr->r->nodetype != 'A') { \
3035 			cnfexprEval(expr->r, &r, usrptr, pWti); \
3036 			estr_r = var2String(&r, &bMustFree); \
3037 		} else { \
3038 			/* Note: this is not really necessary, but if we do not */ \
3039 			/* do it, we get a very irritating compiler warning... */ \
3040 			estr_r = NULL; \
3041 		}
3042 
3043 #define FREE_TWO_STRINGS \
3044 		if(bMustFree) es_deleteStr(estr_r);  \
3045 		if(expr->r->nodetype != 'S' && expr->r->nodetype != 'A') varFreeMembers(&r); \
3046 		if(bMustFree2) es_deleteStr(estr_l);  \
3047 		varFreeMembers(&l)
3048 
3049 /* evaluate an expression.
3050  * Note that we try to avoid malloc whenever possible (because of
3051  * the large overhead it has, especially on highly threaded programs).
3052  * As such, the each caller level must provide buffer space for the
3053  * result on its stack during recursion. This permits the callee to store
3054  * the return value without malloc. As the value is a somewhat larger
3055  * struct, we could otherwise not return it without malloc.
3056  * Note that we implement boolean shortcut operations. For our needs, there
3057  * simply is no case where full evaluation would make any sense at all.
3058  */
ATTR_NONNULL()3059 void ATTR_NONNULL()
3060 cnfexprEval(const struct cnfexpr *__restrict__ const expr,
3061 	struct svar *__restrict__ const ret,
3062 	void *__restrict__ const usrptr,
3063 	wti_t *__restrict__ const pWti)
3064 {
3065 	struct svar r, l; /* memory for subexpression results */
3066 	es_str_t *__restrict__ estr_r, *__restrict__ estr_l;
3067 	int convok_r, convok_l;
3068 	int bMustFree, bMustFree2;
3069 	long long n_r, n_l;
3070 
3071 	DBGPRINTF("eval expr %p, type '%s'\n", expr, tokenToString(expr->nodetype));
3072 	switch(expr->nodetype) {
3073 	/* note: comparison operations are extremely similar. The code can be copyied, only
3074 	 * places flagged with "CMP" need to be changed.
3075 	 */
3076 	case CMP_EQ:
3077 		/* this is optimized in regard to right param as a PoC for all compOps
3078 		 * So this is a NOT yet the copy template!
3079 		 */
3080 		cnfexprEval(expr->l, &l, usrptr, pWti);
3081 		ret->datatype = 'N';
3082 		if(l.datatype == 'S') {
3083 			if(expr->r->nodetype == 'S') {
3084 				ret->d.n = !es_strcmp(l.d.estr, ((struct cnfstringval*)expr->r)->estr); /*CMP*/
3085 			} else if(expr->r->nodetype == 'A') {
3086 				ret->d.n = evalStrArrayCmp(l.d.estr,  (struct cnfarray*) expr->r, CMP_EQ);
3087 			} else {
3088 				cnfexprEval(expr->r, &r, usrptr, pWti);
3089 				if(r.datatype == 'S') {
3090 					ret->d.n = !es_strcmp(l.d.estr, r.d.estr); /*CMP*/
3091 				} else {
3092 					n_l = var2Number(&l, &convok_l);
3093 					if(convok_l) {
3094 						ret->d.n = (n_l == r.d.n); /*CMP*/
3095 					} else {
3096 						estr_r = var2String(&r, &bMustFree);
3097 						ret->d.n = !es_strcmp(l.d.estr, estr_r); /*CMP*/
3098 						if(bMustFree) es_deleteStr(estr_r);
3099 					}
3100 				}
3101 				varFreeMembers(&r);
3102 			}
3103 		} else if(l.datatype == 'J') {
3104 			estr_l = var2String(&l, &bMustFree);
3105 			if(expr->r->nodetype == 'S') {
3106 				ret->d.n = !es_strcmp(estr_l, ((struct cnfstringval*)expr->r)->estr); /*CMP*/
3107 			} else if(expr->r->nodetype == 'A') {
3108 				ret->d.n = evalStrArrayCmp(estr_l,  (struct cnfarray*) expr->r, CMP_EQ);
3109 			} else {
3110 				cnfexprEval(expr->r, &r, usrptr, pWti);
3111 				if(r.datatype == 'S') {
3112 					ret->d.n = !es_strcmp(estr_l, r.d.estr); /*CMP*/
3113 				} else {
3114 					n_l = var2Number(&l, &convok_l);
3115 					if(convok_l) {
3116 						ret->d.n = (n_l == r.d.n); /*CMP*/
3117 					} else {
3118 						estr_r = var2String(&r, &bMustFree2);
3119 						ret->d.n = !es_strcmp(estr_l, estr_r); /*CMP*/
3120 						if(bMustFree2) es_deleteStr(estr_r);
3121 					}
3122 				}
3123 				varFreeMembers(&r);
3124 			}
3125 			if(bMustFree) es_deleteStr(estr_l);
3126 		} else {
3127 			cnfexprEval(expr->r, &r, usrptr, pWti);
3128 			if(r.datatype == 'S') {
3129 				n_r = var2Number(&r, &convok_r);
3130 				if(convok_r) {
3131 					ret->d.n = (l.d.n == n_r); /*CMP*/
3132 				} else {
3133 					estr_l = var2String(&l, &bMustFree);
3134 					ret->d.n = !es_strcmp(r.d.estr, estr_l); /*CMP*/
3135 					if(bMustFree) es_deleteStr(estr_l);
3136 				}
3137 			} else {
3138 				ret->d.n = (l.d.n == r.d.n); /*CMP*/
3139 			}
3140 			varFreeMembers(&r);
3141 		}
3142 		varFreeMembers(&l);
3143 		break;
3144 	case CMP_NE:
3145 		cnfexprEval(expr->l, &l, usrptr, pWti);
3146 		cnfexprEval(expr->r, &r, usrptr, pWti);
3147 		ret->datatype = 'N';
3148 		if(l.datatype == 'S') {
3149 			if(expr->r->nodetype == 'S') {
3150 				ret->d.n = es_strcmp(l.d.estr, ((struct cnfstringval*)expr->r)->estr); /*CMP*/
3151 			} else if(expr->r->nodetype == 'A') {
3152 				ret->d.n = evalStrArrayCmp(l.d.estr,  (struct cnfarray*) expr->r, CMP_NE);
3153 			} else {
3154 				if(r.datatype == 'S') {
3155 					ret->d.n = es_strcmp(l.d.estr, r.d.estr); /*CMP*/
3156 				} else {
3157 					n_l = var2Number(&l, &convok_l);
3158 					if(convok_l) {
3159 						ret->d.n = (n_l != r.d.n); /*CMP*/
3160 					} else {
3161 						estr_r = var2String(&r, &bMustFree);
3162 						ret->d.n = es_strcmp(l.d.estr, estr_r); /*CMP*/
3163 						if(bMustFree) es_deleteStr(estr_r);
3164 					}
3165 				}
3166 			}
3167 		} else if(l.datatype == 'J') {
3168 			estr_l = var2String(&l, &bMustFree);
3169 			if(r.datatype == 'S') {
3170 				ret->d.n = es_strcmp(estr_l, r.d.estr); /*CMP*/
3171 			} else {
3172 				n_l = var2Number(&l, &convok_l);
3173 				if(convok_l) {
3174 					ret->d.n = (n_l != r.d.n); /*CMP*/
3175 				} else {
3176 					estr_r = var2String(&r, &bMustFree2);
3177 					ret->d.n = es_strcmp(estr_l, estr_r); /*CMP*/
3178 					if(bMustFree2) es_deleteStr(estr_r);
3179 				}
3180 			}
3181 			if(bMustFree) es_deleteStr(estr_l);
3182 		} else {
3183 			if(r.datatype == 'S') {
3184 				n_r = var2Number(&r, &convok_r);
3185 				if(convok_r) {
3186 					ret->d.n = (l.d.n != n_r); /*CMP*/
3187 				} else {
3188 					estr_l = var2String(&l, &bMustFree);
3189 					ret->d.n = es_strcmp(r.d.estr, estr_l); /*CMP*/
3190 					if(bMustFree) es_deleteStr(estr_l);
3191 				}
3192 			} else {
3193 				ret->d.n = (l.d.n != r.d.n); /*CMP*/
3194 			}
3195 		}
3196 		FREE_BOTH_RET;
3197 		break;
3198 	case CMP_LE:
3199 		cnfexprEval(expr->l, &l, usrptr, pWti);
3200 		cnfexprEval(expr->r, &r, usrptr, pWti);
3201 		ret->datatype = 'N';
3202 		if(l.datatype == 'S') {
3203 			if(r.datatype == 'S') {
3204 				ret->d.n = es_strcmp(l.d.estr, r.d.estr) <= 0; /*CMP*/
3205 			} else {
3206 				n_l = var2Number(&l, &convok_l);
3207 				if(convok_l) {
3208 					ret->d.n = (n_l <= r.d.n); /*CMP*/
3209 				} else {
3210 					estr_r = var2String(&r, &bMustFree);
3211 					ret->d.n = es_strcmp(l.d.estr, estr_r) <= 0; /*CMP*/
3212 					if(bMustFree) es_deleteStr(estr_r);
3213 				}
3214 			}
3215 		} else if(l.datatype == 'J') {
3216 			estr_l = var2String(&l, &bMustFree);
3217 			if(r.datatype == 'S') {
3218 				ret->d.n = es_strcmp(estr_l, r.d.estr) <= 0; /*CMP*/
3219 			} else {
3220 				n_l = var2Number(&l, &convok_l);
3221 				if(convok_l) {
3222 					ret->d.n = (n_l <= r.d.n); /*CMP*/
3223 				} else {
3224 					estr_r = var2String(&r, &bMustFree2);
3225 					ret->d.n = es_strcmp(estr_l, estr_r) <= 0; /*CMP*/
3226 					if(bMustFree2) es_deleteStr(estr_r);
3227 				}
3228 			}
3229 			if(bMustFree) es_deleteStr(estr_l);
3230 		} else {
3231 			if(r.datatype == 'S') {
3232 				n_r = var2Number(&r, &convok_r);
3233 				if(convok_r) {
3234 					ret->d.n = (l.d.n <= n_r); /*CMP*/
3235 				} else {
3236 					estr_l = var2String(&l, &bMustFree);
3237 					ret->d.n = es_strcmp(r.d.estr, estr_l) <= 0; /*CMP*/
3238 					if(bMustFree) es_deleteStr(estr_l);
3239 				}
3240 			} else {
3241 				ret->d.n = (l.d.n <= r.d.n); /*CMP*/
3242 			}
3243 		}
3244 		FREE_BOTH_RET;
3245 		break;
3246 	case CMP_GE:
3247 		cnfexprEval(expr->l, &l, usrptr, pWti);
3248 		cnfexprEval(expr->r, &r, usrptr, pWti);
3249 		ret->datatype = 'N';
3250 		if(l.datatype == 'S') {
3251 			if(r.datatype == 'S') {
3252 				ret->d.n = es_strcmp(l.d.estr, r.d.estr) >= 0; /*CMP*/
3253 			} else {
3254 				n_l = var2Number(&l, &convok_l);
3255 				if(convok_l) {
3256 					ret->d.n = (n_l >= r.d.n); /*CMP*/
3257 				} else {
3258 					estr_r = var2String(&r, &bMustFree);
3259 					ret->d.n = es_strcmp(l.d.estr, estr_r) >= 0; /*CMP*/
3260 					if(bMustFree) es_deleteStr(estr_r);
3261 				}
3262 			}
3263 		} else if(l.datatype == 'J') {
3264 			estr_l = var2String(&l, &bMustFree);
3265 			if(r.datatype == 'S') {
3266 				ret->d.n = es_strcmp(estr_l, r.d.estr) >= 0; /*CMP*/
3267 			} else {
3268 				n_l = var2Number(&l, &convok_l);
3269 				if(convok_l) {
3270 					ret->d.n = (n_l >= r.d.n); /*CMP*/
3271 				} else {
3272 					estr_r = var2String(&r, &bMustFree2);
3273 					ret->d.n = es_strcmp(estr_l, estr_r) >= 0; /*CMP*/
3274 					if(bMustFree2) es_deleteStr(estr_r);
3275 				}
3276 			}
3277 			if(bMustFree) es_deleteStr(estr_l);
3278 		} else {
3279 			if(r.datatype == 'S') {
3280 				n_r = var2Number(&r, &convok_r);
3281 				if(convok_r) {
3282 					ret->d.n = (l.d.n >= n_r); /*CMP*/
3283 				} else {
3284 					estr_l = var2String(&l, &bMustFree);
3285 					ret->d.n = es_strcmp(r.d.estr, estr_l) >= 0; /*CMP*/
3286 					if(bMustFree) es_deleteStr(estr_l);
3287 				}
3288 			} else {
3289 				ret->d.n = (l.d.n >= r.d.n); /*CMP*/
3290 			}
3291 		}
3292 		FREE_BOTH_RET;
3293 		break;
3294 	case CMP_LT:
3295 		cnfexprEval(expr->l, &l, usrptr, pWti);
3296 		cnfexprEval(expr->r, &r, usrptr, pWti);
3297 		ret->datatype = 'N';
3298 		if(l.datatype == 'S') {
3299 			if(r.datatype == 'S') {
3300 				ret->d.n = es_strcmp(l.d.estr, r.d.estr) < 0; /*CMP*/
3301 			} else {
3302 				n_l = var2Number(&l, &convok_l);
3303 				if(convok_l) {
3304 					ret->d.n = (n_l < r.d.n); /*CMP*/
3305 				} else {
3306 					estr_r = var2String(&r, &bMustFree);
3307 					ret->d.n = es_strcmp(l.d.estr, estr_r) < 0; /*CMP*/
3308 					if(bMustFree) es_deleteStr(estr_r);
3309 				}
3310 			}
3311 		} else if(l.datatype == 'J') {
3312 			estr_l = var2String(&l, &bMustFree);
3313 			if(r.datatype == 'S') {
3314 				ret->d.n = es_strcmp(estr_l, r.d.estr) < 0; /*CMP*/
3315 			} else {
3316 				n_l = var2Number(&l, &convok_l);
3317 				if(convok_l) {
3318 					ret->d.n = (n_l < r.d.n); /*CMP*/
3319 				} else {
3320 					estr_r = var2String(&r, &bMustFree2);
3321 					ret->d.n = es_strcmp(estr_l, estr_r) < 0; /*CMP*/
3322 					if(bMustFree2) es_deleteStr(estr_r);
3323 				}
3324 			}
3325 			if(bMustFree) es_deleteStr(estr_l);
3326 		} else {
3327 			if(r.datatype == 'S') {
3328 				n_r = var2Number(&r, &convok_r);
3329 				if(convok_r) {
3330 					ret->d.n = (l.d.n < n_r); /*CMP*/
3331 				} else {
3332 					estr_l = var2String(&l, &bMustFree);
3333 					ret->d.n = es_strcmp(r.d.estr, estr_l) < 0; /*CMP*/
3334 					if(bMustFree) es_deleteStr(estr_l);
3335 				}
3336 			} else {
3337 				ret->d.n = (l.d.n < r.d.n); /*CMP*/
3338 			}
3339 		}
3340 		FREE_BOTH_RET;
3341 		break;
3342 	case CMP_GT:
3343 		cnfexprEval(expr->l, &l, usrptr, pWti);
3344 		cnfexprEval(expr->r, &r, usrptr, pWti);
3345 		ret->datatype = 'N';
3346 		if(l.datatype == 'S') {
3347 			if(r.datatype == 'S') {
3348 				ret->d.n = es_strcmp(l.d.estr, r.d.estr) > 0; /*CMP*/
3349 			} else {
3350 				n_l = var2Number(&l, &convok_l);
3351 				if(convok_l) {
3352 					ret->d.n = (n_l > r.d.n); /*CMP*/
3353 				} else {
3354 					estr_r = var2String(&r, &bMustFree);
3355 					ret->d.n = es_strcmp(l.d.estr, estr_r) > 0; /*CMP*/
3356 					if(bMustFree) es_deleteStr(estr_r);
3357 				}
3358 			}
3359 		} else if(l.datatype == 'J') {
3360 			estr_l = var2String(&l, &bMustFree);
3361 			if(r.datatype == 'S') {
3362 				ret->d.n = es_strcmp(estr_l, r.d.estr) > 0; /*CMP*/
3363 			} else {
3364 				n_l = var2Number(&l, &convok_l);
3365 				if(convok_l) {
3366 					ret->d.n = (n_l > r.d.n); /*CMP*/
3367 				} else {
3368 					estr_r = var2String(&r, &bMustFree2);
3369 					ret->d.n = es_strcmp(estr_l, estr_r) > 0; /*CMP*/
3370 					if(bMustFree2) es_deleteStr(estr_r);
3371 				}
3372 			}
3373 			if(bMustFree) es_deleteStr(estr_l);
3374 		} else {
3375 			if(r.datatype == 'S') {
3376 				n_r = var2Number(&r, &convok_r);
3377 				if(convok_r) {
3378 					ret->d.n = (l.d.n > n_r); /*CMP*/
3379 				} else {
3380 					estr_l = var2String(&l, &bMustFree);
3381 					ret->d.n = es_strcmp(r.d.estr, estr_l) > 0; /*CMP*/
3382 					if(bMustFree) es_deleteStr(estr_l);
3383 				}
3384 			} else {
3385 				ret->d.n = (l.d.n > r.d.n); /*CMP*/
3386 			}
3387 		}
3388 		FREE_BOTH_RET;
3389 		break;
3390 	case CMP_STARTSWITH:
3391 		PREP_TWO_STRINGS;
3392 		ret->datatype = 'N';
3393 		if(expr->r->nodetype == 'A') {
3394 			ret->d.n = evalStrArrayCmp(estr_l,  (struct cnfarray*) expr->r, CMP_STARTSWITH);
3395 			bMustFree = 0;
3396 		} else {
3397 			ret->d.n = es_strncmp(estr_l, estr_r, estr_r->lenStr) == 0;
3398 		}
3399 		FREE_TWO_STRINGS;
3400 		break;
3401 	case CMP_STARTSWITHI:
3402 		PREP_TWO_STRINGS;
3403 		ret->datatype = 'N';
3404 		if(expr->r->nodetype == 'A') {
3405 			ret->d.n = evalStrArrayCmp(estr_l,  (struct cnfarray*) expr->r, CMP_STARTSWITHI);
3406 			bMustFree = 0;
3407 		} else {
3408 			ret->d.n = es_strncasecmp(estr_l, estr_r, estr_r->lenStr) == 0;
3409 		}
3410 		FREE_TWO_STRINGS;
3411 		break;
3412 	case CMP_CONTAINS:
3413 		PREP_TWO_STRINGS;
3414 		ret->datatype = 'N';
3415 		if(expr->r->nodetype == 'A') {
3416 			ret->d.n = evalStrArrayCmp(estr_l,  (struct cnfarray*) expr->r, CMP_CONTAINS);
3417 			bMustFree = 0;
3418 		} else {
3419 			ret->d.n = es_strContains(estr_l, estr_r) != -1;
3420 		}
3421 		FREE_TWO_STRINGS;
3422 		break;
3423 	case CMP_CONTAINSI:
3424 		PREP_TWO_STRINGS;
3425 		ret->datatype = 'N';
3426 		if(expr->r->nodetype == 'A') {
3427 			ret->d.n = evalStrArrayCmp(estr_l,  (struct cnfarray*) expr->r, CMP_CONTAINSI);
3428 			bMustFree = 0;
3429 		} else {
3430 			ret->d.n = es_strCaseContains(estr_l, estr_r) != -1;
3431 		}
3432 		FREE_TWO_STRINGS;
3433 		break;
3434 	case OR:
3435 		cnfexprEval(expr->l, &l, usrptr, pWti);
3436 		ret->datatype = 'N';
3437 		if(var2Number(&l, &convok_l)) {
3438 			ret->d.n = 1ll;
3439 		} else {
3440 			cnfexprEval(expr->r, &r, usrptr, pWti);
3441 			if(var2Number(&r, &convok_r))
3442 				ret->d.n = 1ll;
3443 			else
3444 				ret->d.n = 0ll;
3445 			varFreeMembers(&r);
3446 		}
3447 		varFreeMembers(&l);
3448 		break;
3449 	case AND:
3450 		cnfexprEval(expr->l, &l, usrptr, pWti);
3451 		ret->datatype = 'N';
3452 		if(var2Number(&l, &convok_l)) {
3453 			cnfexprEval(expr->r, &r, usrptr, pWti);
3454 			if(var2Number(&r, &convok_r))
3455 				ret->d.n = 1ll;
3456 			else
3457 				ret->d.n = 0ll;
3458 			varFreeMembers(&r);
3459 		} else {
3460 			ret->d.n = 0ll;
3461 		}
3462 		varFreeMembers(&l);
3463 		break;
3464 	case NOT:
3465 		cnfexprEval(expr->r, &r, usrptr, pWti);
3466 		ret->datatype = 'N';
3467 		ret->d.n = !var2Number(&r, &convok_r);
3468 		varFreeMembers(&r);
3469 		break;
3470 	case 'N':
3471 		ret->datatype = 'N';
3472 		ret->d.n = ((struct cnfnumval*)expr)->val;
3473 		break;
3474 	case 'S':
3475 		ret->datatype = 'S';
3476 		ret->d.estr = es_strdup(((struct cnfstringval*)expr)->estr);
3477 		break;
3478 	case 'A':
3479 		/* if an array is used with "normal" operations, it just evaluates
3480 		 * to its first element.
3481 		 */
3482 		ret->datatype = 'S';
3483 		ret->d.estr = es_strdup(((struct cnfarray*)expr)->arr[0]);
3484 		break;
3485 	case 'V':
3486 		evalVar((struct cnfvar*)expr, usrptr, ret);
3487 		break;
3488 	case '&':
3489 		/* TODO: think about optimization, should be possible ;) */
3490 		PREP_TWO_STRINGS;
3491 		if(expr->r->nodetype == 'A') {
3492 			estr_r = ((struct cnfarray*)expr->r)->arr[0];
3493 			bMustFree = 0;
3494 		}
3495 		ret->datatype = 'S';
3496 		ret->d.estr = es_strdup(estr_l);
3497 		es_addStr(&ret->d.estr, estr_r);
3498 		FREE_TWO_STRINGS;
3499 		break;
3500 	case '+':
3501 		COMP_NUM_BINOP(+);
3502 		break;
3503 	case '-':
3504 		COMP_NUM_BINOP(-);
3505 		break;
3506 	case '*':
3507 		COMP_NUM_BINOP(*);
3508 		break;
3509 	case '/':
3510 		COMP_NUM_BINOP_DIV(/);
3511 		break;
3512 	case '%':
3513 		COMP_NUM_BINOP_DIV(%);
3514 		break;
3515 	case 'M':
3516 		cnfexprEval(expr->r, &r, usrptr, pWti);
3517 		ret->datatype = 'N';
3518 		ret->d.n = -var2Number(&r, &convok_r);
3519 		varFreeMembers(&r);
3520 		break;
3521 	case 'F':
3522 		doFuncCall((struct cnffunc*) expr, ret, usrptr, pWti);
3523 		break;
3524 	case S_FUNC_EXISTS:
3525 		ret->datatype = 'N';
3526 		ret->d.n = evalFuncExists((struct cnffuncexists*) expr, usrptr);
3527 		break;
3528 	default:
3529 		ret->datatype = 'N';
3530 		ret->d.n = 0ll;
3531 		DBGPRINTF("eval error: unknown nodetype %u['%c']\n",
3532 			(unsigned) expr->nodetype, (char) expr->nodetype);
3533 		assert(0); /* abort on debug builds, this must not happen! */
3534 		break;
3535 	}
3536 	DBGPRINTF("eval expr %p, return datatype '%c':%d\n", expr, ret->datatype,
3537 		(ret->datatype == 'N') ? (int)ret->d.n: 0);
3538 }
3539 
3540 //---------------------------------------------------------
3541 
3542 void
cnfarrayContentDestruct(struct cnfarray * ar)3543 cnfarrayContentDestruct(struct cnfarray *ar)
3544 {
3545 	unsigned short i;
3546 	for(i = 0 ; i < ar->nmemb ; ++i) {
3547 		es_deleteStr(ar->arr[i]);
3548 	}
3549 	free(ar->arr);
3550 }
3551 
3552 static void
regex_destruct(struct cnffunc * func)3553 regex_destruct(struct cnffunc *func) {
3554 	if(func->funcdata != NULL) {
3555 		regexp.regfree(func->funcdata);
3556 	}
3557 }
3558 
3559 static rsRetVal
initFunc_dyn_stats(struct cnffunc * func)3560 initFunc_dyn_stats(struct cnffunc *func)
3561 {
3562 	uchar *cstr = NULL;
3563 	DEFiRet;
3564 
3565 	func->destructable_funcdata = 0;
3566 
3567 	if(func->nParams != 2) {
3568 		parser_errmsg("rsyslog logic error in line %d of file %s\n",
3569 					  __LINE__, __FILE__);
3570 		FINALIZE;
3571 	}
3572 
3573 	func->funcdata = NULL;
3574 	if(func->expr[0]->nodetype != 'S') {
3575 		parser_errmsg("dyn-stats bucket-name (param 1) of dyn-stats manipulating "
3576 		"functions like dyn_inc must be a constant string");
3577 		FINALIZE;
3578 	}
3579 
3580 	cstr = (uchar*)es_str2cstr(((struct cnfstringval*) func->expr[0])->estr, NULL);
3581 	if((func->funcdata = dynstats_findBucket(cstr)) == NULL) {
3582 		parser_errmsg("dyn-stats bucket '%s' not found", cstr);
3583 		FINALIZE;
3584 	}
3585 
3586 finalize_it:
3587 	free(cstr);
3588 	RETiRet;
3589 }
3590 
3591 static rsRetVal
initFunc_perctile_obs(struct cnffunc * func)3592 initFunc_perctile_obs(struct cnffunc *func)
3593 {
3594 	uchar *cstr = NULL;
3595 	DEFiRet;
3596 
3597 	func->destructable_funcdata = 0;
3598 	if (func->nParams != 3) {
3599 		parser_errmsg("rsyslog logic error in line %d of file %s\n",
3600 					  __LINE__, __FILE__);
3601 		FINALIZE;
3602 	}
3603 
3604 	func->funcdata = NULL;
3605 	if (func->expr[0]->nodetype != 'S') {
3606 		parser_errmsg("percentile-stats bucket-name (param 1) of perctile-stats manipulating "
3607 		"functions like percentile_observe must be a constant string");
3608 		FINALIZE;
3609 	}
3610 
3611 	cstr = (uchar*) es_str2cstr(((struct cnfstringval*) func->expr[0])->estr, NULL);
3612 	if ( (func->funcdata = perctile_findBucket(cstr)) == NULL) {
3613 		parser_errmsg("perctile-stats bucket '%s' not found", cstr);
3614 		FINALIZE;
3615 	}
3616 
3617 finalize_it:
3618 	free(cstr);
3619 	RETiRet;
3620 }
3621 
ATTR_NONNULL()3622 static void ATTR_NONNULL()
3623 doFunc_percentile_obs(struct cnffunc *__restrict__ const func,
3624 	struct svar *__restrict__ const ret,
3625 	void *__restrict__ const usrptr,
3626 	wti_t *__restrict__ const pWti)
3627 {
3628 	uchar *cstr = NULL;
3629 	struct svar srcVal;
3630 	int bMustFree;
3631 
3632 	ret->datatype = 'N';
3633 	if(func->funcdata == NULL) {
3634 		ret->d.n = -1;
3635 		return;
3636 	}
3637 
3638 	cnfexprEval(func->expr[1], &srcVal, usrptr, pWti);
3639 	cstr = (uchar*) var2CString(&srcVal, &bMustFree);
3640 
3641 	int success = 0;
3642 	struct svar srcVal2;
3643 	long long retVal;
3644 	cnfexprEval(func->expr[2], &srcVal2, usrptr, pWti);
3645 	long long val = var2Number(&srcVal2, &success);
3646 	if (!success) {
3647 		char *cstr2 = es_str2cstr(srcVal2.d.estr, NULL);
3648 		parser_errmsg("rainerscript: percentile_obs - didn't get a valid number: %s\n", cstr2);
3649 		free(cstr2);
3650 		retVal = 0;
3651 		FINALIZE;
3652 	}
3653 
3654 	retVal = perctile_obs(func->funcdata, cstr, val);
3655 
3656 finalize_it:
3657 	if (bMustFree) {
3658 		free(cstr);
3659 	}
3660 	varFreeMembers(&srcVal);
3661 	varFreeMembers(&srcVal2);
3662 	ret->d.n = retVal;
3663 	ret->datatype = 'N';
3664 }
3665 
3666 static rsRetVal
initFunc_re_match_generic(struct cnffunc * const func,const unsigned flags)3667 initFunc_re_match_generic(struct cnffunc *const func, const unsigned flags)
3668 {
3669 	rsRetVal localRet;
3670 	char *regex = NULL;
3671 	regex_t *re;
3672 	DEFiRet;
3673 
3674 	if(func->nParams < 2) {
3675 		parser_errmsg("rsyslog logic error in line %d of file %s\n",
3676 			__LINE__, __FILE__);
3677 		FINALIZE;
3678 	}
3679 
3680 	func->funcdata = NULL;
3681 	if(func->expr[1]->nodetype != 'S') {
3682 		parser_errmsg("param 2 of re_match/extract() must be a constant string");
3683 		FINALIZE;
3684 	}
3685 
3686 	CHKmalloc(re = malloc(sizeof(regex_t)));
3687 	func->funcdata = re;
3688 
3689 	regex = es_str2cstr(((struct cnfstringval*) func->expr[1])->estr, NULL);
3690 
3691 	if((localRet = objUse(regexp, LM_REGEXP_FILENAME)) == RS_RET_OK) {
3692 		int errcode;
3693 		if((errcode = regexp.regcomp(re, (char*) regex, REG_EXTENDED | flags)) != 0) {
3694 			char errbuff[512];
3695 			regexp.regerror(errcode, re, errbuff, sizeof(errbuff));
3696 			parser_errmsg("cannot compile regex '%s': %s", regex, errbuff);
3697 			ABORT_FINALIZE(RS_RET_ERR);
3698 		}
3699 	} else { /* regexp object could not be loaded */
3700 		parser_errmsg("could not load regex support - regex ignored");
3701 		ABORT_FINALIZE(localRet);
3702 	}
3703 
3704 finalize_it:
3705 	free(regex);
3706 	RETiRet;
3707 }
3708 
3709 static rsRetVal
initFunc_re_match(struct cnffunc * func)3710 initFunc_re_match(struct cnffunc *func)
3711 {
3712 	return initFunc_re_match_generic(func, 0);
3713 }
3714 
3715 static rsRetVal
initFunc_re_match_i(struct cnffunc * func)3716 initFunc_re_match_i(struct cnffunc *func)
3717 {
3718 	return initFunc_re_match_generic(func, REG_ICASE);
3719 }
3720 
3721 static rsRetVal
initFunc_exec_template(struct cnffunc * func)3722 initFunc_exec_template(struct cnffunc *func)
3723 {
3724 	char *tplName = NULL;
3725 	DEFiRet;
3726 
3727 	func->destructable_funcdata = 0;
3728 
3729 	if(func->nParams != 1) {
3730 		parser_errmsg("rsyslog logic error in line %d of file %s\n",
3731 			__LINE__, __FILE__);
3732 		FINALIZE;
3733 	}
3734 
3735 	if(func->expr[0]->nodetype != 'S') {
3736 		parser_errmsg("exec_template(): param 1 must be a constant string");
3737 		FINALIZE;
3738 	}
3739 
3740 	tplName = es_str2cstr(((struct cnfstringval*) func->expr[0])->estr, NULL);
3741 	func->funcdata = tplFind(ourConf, tplName, strlen(tplName));
3742 	if(func->funcdata == NULL) {
3743 		parser_errmsg("exec_template(): template '%s' could not be found", tplName);
3744 		FINALIZE;
3745 	}
3746 
3747 
3748 finalize_it:
3749 	free(tplName);
3750 	RETiRet;
3751 }
3752 
3753 static rsRetVal
initFunc_prifilt(struct cnffunc * func)3754 initFunc_prifilt(struct cnffunc *func)
3755 {
3756 	struct funcData_prifilt *pData;
3757 	uchar *cstr;
3758 	DEFiRet;
3759 
3760 	if(func->nParams != 1) {
3761 		parser_errmsg("rsyslog logic error in line %d of file %s\n",
3762 			__LINE__, __FILE__);
3763 		FINALIZE;
3764 	}
3765 
3766 	func->funcdata = NULL;
3767 	if(func->expr[0]->nodetype != 'S') {
3768 		parser_errmsg("param 1 of prifilt() must be a constant string");
3769 		FINALIZE;
3770 	}
3771 
3772 	CHKmalloc(pData = calloc(1, sizeof(struct funcData_prifilt)));
3773 	func->funcdata = pData;
3774 	cstr = (uchar*)es_str2cstr(((struct cnfstringval*) func->expr[0])->estr, NULL);
3775 	CHKiRet(DecodePRIFilter(cstr, pData->pmask));
3776 	free(cstr);
3777 finalize_it:
3778 	RETiRet;
3779 }
3780 
3781 static rsRetVal
resolveLookupTable(struct cnffunc * func)3782 resolveLookupTable(struct cnffunc *func)
3783 {
3784 	uchar *cstr = NULL;
3785 	char *fn_name = NULL;
3786 	DEFiRet;
3787 
3788 	func->destructable_funcdata = 0;
3789 
3790 	if(func->nParams == 0) {/*we assume first arg is lookup-table-name*/
3791 		parser_errmsg("rsyslog logic error in line %d of file %s\n",
3792 			__LINE__, __FILE__);
3793 		FINALIZE;
3794 	}
3795 
3796 	CHKmalloc(fn_name = es_str2cstr(func->fname, NULL));
3797 
3798 	func->funcdata = NULL;
3799 	if(func->expr[0]->nodetype != 'S') {
3800 		parser_errmsg("table name (param 1) of %s() must be a constant string", fn_name);
3801 		FINALIZE;
3802 	}
3803 
3804 	CHKmalloc(cstr = (uchar*)es_str2cstr(((struct cnfstringval*) func->expr[0])->estr, NULL));
3805 	if((func->funcdata = lookupFindTable(cstr)) == NULL) {
3806 		parser_errmsg("lookup table '%s' not found (used in function: %s)", cstr, fn_name);
3807 		FINALIZE;
3808 	}
3809 
3810 finalize_it:
3811 	free(cstr);
3812 	free(fn_name);
3813 	RETiRet;
3814 }
3815 
3816 struct modListNode {
3817 	int version;
3818 	struct scriptFunct *modFcts;
3819 	struct modListNode *next;
3820 };
3821 
3822 static struct modListNode *modListRoot = NULL;
3823 static struct modListNode *modListLast = NULL;
3824 
3825 static struct scriptFunct functions[] = {
3826 	{"strlen", 1, 1, doFunct_StrLen, NULL, NULL},
3827 	{"getenv", 1, 1, doFunct_Getenv, NULL, NULL},
3828 	{"num2ipv4", 1, 1, doFunct_num2ipv4, NULL, NULL},
3829 	{"int2hex", 1, 1, doFunct_Int2Hex, NULL, NULL},
3830 	{"substring", 3, 3, doFunct_Substring, NULL, NULL},
3831 	{"ltrim", 1, 1, doFunct_LTrim, NULL, NULL},
3832 	{"rtrim", 1, 1, doFunct_RTrim, NULL, NULL},
3833 	{"tolower", 1, 1, doFunct_ToLower, NULL, NULL},
3834 	{"cstr", 1, 1, doFunct_CStr, NULL, NULL},
3835 	{"cnum", 1, 1, doFunct_CNum, NULL, NULL},
3836 	{"ip42num", 1, 1, doFunct_Ipv42num, NULL, NULL},
3837 	{"ipv42num", 1, 1, doFunct_Ipv42num, NULL, NULL},
3838 	{"re_match", 2, 2, doFunct_ReMatch, initFunc_re_match, regex_destruct},
3839 	{"re_match_i", 2, 2, doFunct_ReMatch, initFunc_re_match_i, regex_destruct},
3840 	{"re_extract", 5, 5, doFunc_re_extract, initFunc_re_match, regex_destruct},
3841 	{"re_extract_i", 5, 5, doFunc_re_extract, initFunc_re_match_i, regex_destruct},
3842 	{"field", 3, 3, doFunct_Field, NULL, NULL},
3843 	{"exec_template", 1, 1, doFunc_exec_template, initFunc_exec_template, NULL},
3844 	{"prifilt", 1, 1, doFunct_Prifilt, initFunc_prifilt, NULL},
3845 	{"lookup", 2, 2, doFunct_Lookup, resolveLookupTable, NULL},
3846 	{"dyn_inc", 2, 2, doFunct_DynInc, initFunc_dyn_stats, NULL},
3847 	{"percentile_observe", 3, 3, doFunc_percentile_obs, initFunc_perctile_obs, NULL},
3848 	{"replace", 3, 3, doFunct_Replace, NULL, NULL},
3849 	{"wrap", 2, 3, doFunct_Wrap, NULL, NULL},
3850 	{"random", 1, 1, doFunct_RandomGen, NULL, NULL},
3851 	{"format_time", 2, 2, doFunct_FormatTime, NULL, NULL},
3852 	{"parse_time", 1, 1, doFunct_ParseTime, NULL, NULL},
3853 	{"is_time", 1, 2, doFunct_IsTime, NULL, NULL},
3854 	{"parse_json", 2, 2, doFunc_parse_json, NULL, NULL},
3855 	{"get_property", 2, 2, doFunc_get_property, NULL, NULL},
3856 	{"script_error", 0, 0, doFunct_ScriptError, NULL, NULL},
3857 	{"previous_action_suspended", 0, 0, doFunct_PreviousActionSuspended, NULL, NULL},
3858 	{NULL, 0, 0, NULL, NULL, NULL} //last element to check end of array
3859 };
3860 
ATTR_NONNULL()3861 static rscriptFuncPtr ATTR_NONNULL()
3862 extractFuncPtr(const struct scriptFunct *const funct, const unsigned int nParams)
3863 {
3864 	rscriptFuncPtr retPtr = NULL;
3865 
3866 	if(funct->minParams == funct->maxParams) {
3867 		if(nParams == funct->maxParams) {
3868 			retPtr = funct->fPtr;
3869 		} else {
3870 			parser_errmsg("number of parameters for %s() must be %hu but is %d.",
3871 				funct->fname, funct->maxParams, nParams);
3872 		}
3873 	} else {
3874 		if(nParams < funct->minParams) {
3875 			parser_errmsg("number of parameters for %s() must be at least %hu but is %d.",
3876 				funct->fname, funct->minParams, nParams);
3877 		} else if(nParams > funct->maxParams) {
3878 			parser_errmsg("number of parameters for %s() must be at most %hu but is %d.",
3879 				funct->fname, funct->maxParams, nParams);
3880 		} else {
3881 			retPtr = funct->fPtr;
3882 		}
3883 	}
3884 
3885 	return retPtr;
3886 }
3887 
ATTR_NONNULL()3888 static struct scriptFunct* ATTR_NONNULL()
3889 searchFunctArray(const char *const fname, struct scriptFunct *functArray)
3890 {
3891 	struct scriptFunct *retPtr = NULL;
3892 	int i = 0;
3893 	while(functArray[i].fname != NULL) {
3894 		if(!strcmp(fname, functArray[i].fname)){
3895 			retPtr = functArray + i;
3896 			goto done;
3897 		}
3898 		i++;
3899 	}
3900 done:
3901 	return retPtr;
3902 }
3903 
ATTR_NONNULL()3904 static struct scriptFunct* ATTR_NONNULL()
3905 searchModList(const char *const fname)
3906 {
3907 	struct modListNode *modListCurr = modListRoot;
3908 	struct scriptFunct *foundFunct;
3909 
3910 	do {
3911 		foundFunct = searchFunctArray(fname, modListCurr->modFcts);
3912 		if(foundFunct != NULL) {
3913 			return foundFunct;
3914 		}
3915 		modListCurr = modListCurr->next;
3916 	} while(modListCurr != NULL);
3917 	return NULL;
3918 }
3919 
3920 static void
cnffuncDestruct(struct cnffunc * func)3921 cnffuncDestruct(struct cnffunc *func)
3922 {
3923 	unsigned short i;
3924 
3925 	for(i = 0 ; i < func->nParams ; ++i) {
3926 		cnfexprDestruct(func->expr[i]);
3927 	}
3928 
3929 	/* some functions require special destruction */
3930 	char *cstr = es_str2cstr(func->fname, NULL);
3931 	struct scriptFunct *foundFunc = searchModList(cstr);
3932 	free(cstr);
3933 	if(foundFunc->destruct != NULL) {
3934 		foundFunc->destruct(func);
3935 	}
3936 
3937 	if(func->destructable_funcdata) {
3938 		free(func->funcdata);
3939 	}
3940 	free(func->fname);
3941 }
3942 
3943 /* Destruct an expression and all sub-expressions contained in it.
3944  */
3945 void
cnfexprDestruct(struct cnfexpr * __restrict__ const expr)3946 cnfexprDestruct(struct cnfexpr *__restrict__ const expr)
3947 {
3948 
3949 	if(expr == NULL) {
3950 		/* this is valid and can happen during optimizer run! */
3951 		DBGPRINTF("cnfexprDestruct got NULL ptr - valid, so doing nothing\n");
3952 		return;
3953 	}
3954 
3955 	DBGPRINTF("cnfexprDestruct expr %p, type '%s'\n", expr, tokenToString(expr->nodetype));
3956 	switch(expr->nodetype) {
3957 	case CMP_NE:
3958 	case CMP_EQ:
3959 	case CMP_LE:
3960 	case CMP_GE:
3961 	case CMP_LT:
3962 	case CMP_GT:
3963 	case CMP_STARTSWITH:
3964 	case CMP_STARTSWITHI:
3965 	case CMP_CONTAINS:
3966 	case CMP_CONTAINSI:
3967 	case OR:
3968 	case AND:
3969 	case '&':
3970 	case '+':
3971 	case '-':
3972 	case '*':
3973 	case '/':
3974 	case '%': /* binary */
3975 		cnfexprDestruct(expr->l);
3976 		cnfexprDestruct(expr->r);
3977 		break;
3978 	case NOT:
3979 	case 'M': /* unary */
3980 		cnfexprDestruct(expr->r);
3981 		break;
3982 	case 'N':
3983 		break;
3984 	case 'S':
3985 		es_deleteStr(((struct cnfstringval*)expr)->estr);
3986 		break;
3987 	case 'V':
3988 		free(((struct cnfvar*)expr)->name);
3989 		msgPropDescrDestruct(&(((struct cnfvar*)expr)->prop));
3990 		break;
3991 	case 'F':
3992 		cnffuncDestruct((struct cnffunc*)expr);
3993 		break;
3994 	case 'A':
3995 		cnfarrayContentDestruct((struct cnfarray*)expr);
3996 		break;
3997 	default:break;
3998 	}
3999 	free(expr);
4000 }
4001 
4002 //---- END
4003 
4004 
4005 /* Evaluate an expression as a bool. This is added because expressions are
4006  * mostly used inside filters, and so this function is quite common and
4007  * important.
4008  */
4009 int
cnfexprEvalBool(struct cnfexpr * __restrict__ const expr,void * __restrict__ const usrptr,wti_t * const pWti)4010 cnfexprEvalBool(struct cnfexpr *__restrict__ const expr, void *__restrict__ const usrptr, wti_t *const pWti)
4011 {
4012 	int convok;
4013 	struct svar ret;
4014 	cnfexprEval(expr, &ret, usrptr, pWti);
4015 	int retVal = var2Number(&ret, &convok);
4016 	varFreeMembers(&ret);
4017 	return retVal;
4018 }
4019 
4020 struct json_object*
cnfexprEvalCollection(struct cnfexpr * __restrict__ const expr,void * __restrict__ const usrptr,wti_t * const pWti)4021 cnfexprEvalCollection(struct cnfexpr *__restrict__ const expr, void *__restrict__ const usrptr, wti_t *const pWti)
4022 {
4023 	struct svar ret;
4024 	void *retptr;
4025 	cnfexprEval(expr, &ret, usrptr, pWti);
4026 	if(ret.datatype == 'J') {
4027 		retptr = ret.d.json; /*caller is supposed to free the returned json-object*/
4028 	} else {
4029 		retptr = NULL;
4030 		varFreeMembers(&ret); /* we must free the element */
4031 	}
4032 	return retptr;
4033 }
4034 
4035 static void
doIndent(int indent)4036 doIndent(int indent)
4037 {
4038 	int i;
4039 	for(i = 0 ; i < indent ; ++i)
4040 		dbgprintf("  ");
4041 }
4042 
4043 static void
pmaskPrint(uchar * pmask,int indent)4044 pmaskPrint(uchar *pmask, int indent)
4045 {
4046 	int i;
4047 	doIndent(indent);
4048 	dbgprintf("pmask: ");
4049 	for (i = 0; i <= LOG_NFACILITIES; i++)
4050 		if (pmask[i] == TABLE_NOPRI)
4051 			dbgprintf(" X ");
4052 		else
4053 			dbgprintf("%2X ", pmask[i]);
4054 	dbgprintf("\n");
4055 }
4056 
4057 static void
cnfarrayPrint(struct cnfarray * ar,int indent)4058 cnfarrayPrint(struct cnfarray *ar, int indent)
4059 {
4060 	int i;
4061 	doIndent(indent); dbgprintf("ARRAY:\n");
4062 	for(i = 0 ; i < ar->nmemb ; ++i) {
4063 		doIndent(indent+1);
4064 		cstrPrint("string '", ar->arr[i]);
4065 		dbgprintf("'\n");
4066 	}
4067 }
4068 
4069 void
cnfexprPrint(struct cnfexpr * expr,int indent)4070 cnfexprPrint(struct cnfexpr *expr, int indent)
4071 {
4072 	struct cnffunc *func;
4073 	char *fname;
4074 	int i;
4075 
4076 	switch(expr->nodetype) {
4077 	case CMP_EQ:
4078 		cnfexprPrint(expr->l, indent+1);
4079 		doIndent(indent);
4080 		dbgprintf("==\n");
4081 		cnfexprPrint(expr->r, indent+1);
4082 		break;
4083 	case CMP_NE:
4084 		cnfexprPrint(expr->l, indent+1);
4085 		doIndent(indent);
4086 		dbgprintf("!=\n");
4087 		cnfexprPrint(expr->r, indent+1);
4088 		break;
4089 	case CMP_LE:
4090 		cnfexprPrint(expr->l, indent+1);
4091 		doIndent(indent);
4092 		dbgprintf("<=\n");
4093 		cnfexprPrint(expr->r, indent+1);
4094 		break;
4095 	case CMP_GE:
4096 		cnfexprPrint(expr->l, indent+1);
4097 		doIndent(indent);
4098 		dbgprintf(">=\n");
4099 		cnfexprPrint(expr->r, indent+1);
4100 		break;
4101 	case CMP_LT:
4102 		cnfexprPrint(expr->l, indent+1);
4103 		doIndent(indent);
4104 		dbgprintf("<\n");
4105 		cnfexprPrint(expr->r, indent+1);
4106 		break;
4107 	case CMP_GT:
4108 		cnfexprPrint(expr->l, indent+1);
4109 		doIndent(indent);
4110 		dbgprintf(">\n");
4111 		cnfexprPrint(expr->r, indent+1);
4112 		break;
4113 	case CMP_CONTAINS:
4114 		cnfexprPrint(expr->l, indent+1);
4115 		doIndent(indent);
4116 		dbgprintf("CONTAINS\n");
4117 		cnfexprPrint(expr->r, indent+1);
4118 		break;
4119 	case CMP_CONTAINSI:
4120 		cnfexprPrint(expr->l, indent+1);
4121 		doIndent(indent);
4122 		dbgprintf("CONTAINS_I\n");
4123 		cnfexprPrint(expr->r, indent+1);
4124 		break;
4125 	case CMP_STARTSWITH:
4126 		cnfexprPrint(expr->l, indent+1);
4127 		doIndent(indent);
4128 		dbgprintf("STARTSWITH\n");
4129 		cnfexprPrint(expr->r, indent+1);
4130 		break;
4131 	case CMP_STARTSWITHI:
4132 		cnfexprPrint(expr->l, indent+1);
4133 		doIndent(indent);
4134 		dbgprintf("STARTSWITH_I\n");
4135 		cnfexprPrint(expr->r, indent+1);
4136 		break;
4137 	case OR:
4138 		cnfexprPrint(expr->l, indent+1);
4139 		doIndent(indent);
4140 		dbgprintf("OR\n");
4141 		cnfexprPrint(expr->r, indent+1);
4142 		break;
4143 	case AND:
4144 		cnfexprPrint(expr->l, indent+1);
4145 		doIndent(indent);
4146 		dbgprintf("AND\n");
4147 		cnfexprPrint(expr->r, indent+1);
4148 		break;
4149 	case NOT:
4150 		doIndent(indent);
4151 		dbgprintf("NOT\n");
4152 		cnfexprPrint(expr->r, indent+1);
4153 		break;
4154 	case S_FUNC_EXISTS:
4155 		doIndent(indent);
4156 		dbgprintf("exists(%s)\n", ((struct cnffuncexists*)expr)->varname);
4157 		break;
4158 	case 'S':
4159 		doIndent(indent);
4160 		cstrPrint("string '", ((struct cnfstringval*)expr)->estr);
4161 		dbgprintf("'\n");
4162 		break;
4163 	case 'A':
4164 		cnfarrayPrint((struct cnfarray*)expr, indent);
4165 		break;
4166 	case 'N':
4167 		doIndent(indent);
4168 		dbgprintf("%lld\n", ((struct cnfnumval*)expr)->val);
4169 		break;
4170 	case 'V':
4171 		doIndent(indent);
4172 		dbgprintf("var '%s'\n", ((struct cnfvar*)expr)->name);
4173 		break;
4174 	case 'F':
4175 		doIndent(indent);
4176 		func = (struct cnffunc*) expr;
4177 		cstrPrint("function '", func->fname);
4178 		fname = es_str2cstr(func->fname, NULL);
4179 		dbgprintf("' (name:%s, params:%hu)\n", fname, func->nParams);
4180 		free(fname);
4181 		if(func->fPtr == doFunct_Prifilt) {
4182 			struct funcData_prifilt *pD;
4183 			pD = (struct funcData_prifilt*) func->funcdata;
4184 			pmaskPrint(pD->pmask, indent+1);
4185 		}
4186 		for(i = 0 ; i < func->nParams ; ++i) {
4187 			cnfexprPrint(func->expr[i], indent+1);
4188 		}
4189 		break;
4190 	case '&':
4191 	case '+':
4192 	case '-':
4193 	case '*':
4194 	case '/':
4195 	case '%':
4196 	case 'M':
4197 		if(expr->l != NULL)
4198 			cnfexprPrint(expr->l, indent+1);
4199 		doIndent(indent);
4200 		dbgprintf("%c\n", (char) expr->nodetype);
4201 		cnfexprPrint(expr->r, indent+1);
4202 		break;
4203 	default:
4204 		dbgprintf("error: unknown nodetype %u['%c']\n",
4205 			(unsigned) expr->nodetype, (char) expr->nodetype);
4206 		assert(0); /* abort on debug builds, this must not happen! */
4207 		break;
4208 	}
4209 }
4210 
4211 /* print only the given stmt
4212  * if "subtree" equals 1, the full statement subtree is printed, else
4213  * really only the statement.
4214  */
4215 void
cnfstmtPrintOnly(struct cnfstmt * stmt,int indent,sbool subtree)4216 cnfstmtPrintOnly(struct cnfstmt *stmt, int indent, sbool subtree)
4217 {
4218 	char *cstr;
4219 	switch(stmt->nodetype) {
4220 	case S_NOP:
4221 		doIndent(indent); dbgprintf("NOP\n");
4222 		break;
4223 	case S_STOP:
4224 		doIndent(indent); dbgprintf("STOP\n");
4225 		break;
4226 	case S_CALL:
4227 		cstr = es_str2cstr(stmt->d.s_call.name, NULL);
4228 		doIndent(indent); dbgprintf("CALL [%s, queue:%d]\n", cstr,
4229 			stmt->d.s_call.ruleset == NULL ? 0 : 1);
4230 		free(cstr);
4231 		break;
4232 	case S_CALL_INDIRECT:
4233 		doIndent(indent); dbgprintf("CALL_INDIRECT\n");
4234 		cnfexprPrint(stmt->d.s_call_ind.expr, indent+1);
4235 		break;
4236 	case S_ACT:
4237 		doIndent(indent); dbgprintf("ACTION %d [%s:%s]\n", stmt->d.act->iActionNbr,
4238 			modGetName(stmt->d.act->pMod), stmt->printable);
4239 		break;
4240 	case S_IF:
4241 		doIndent(indent); dbgprintf("IF\n");
4242 		cnfexprPrint(stmt->d.s_if.expr, indent+1);
4243 		if(subtree) {
4244 			doIndent(indent); dbgprintf("THEN\n");
4245 			cnfstmtPrint(stmt->d.s_if.t_then, indent+1);
4246 			if(stmt->d.s_if.t_else != NULL) {
4247 				doIndent(indent); dbgprintf("ELSE\n");
4248 				cnfstmtPrint(stmt->d.s_if.t_else, indent+1);
4249 			}
4250 			doIndent(indent); dbgprintf("END IF\n");
4251 		}
4252 		break;
4253 	case S_FOREACH:
4254 		doIndent(indent); dbgprintf("FOREACH %s IN\n", stmt->d.s_foreach.iter->var);
4255 		cnfexprPrint(stmt->d.s_foreach.iter->collection, indent+1);
4256 		if(subtree) {
4257 			doIndent(indent); dbgprintf("DO\n");
4258 			cnfstmtPrint(stmt->d.s_foreach.body, indent+1);
4259 			doIndent(indent); dbgprintf("END FOREACH\n");
4260 		}
4261 		break;
4262 	case S_SET:
4263 		doIndent(indent); dbgprintf("SET %s =\n",
4264 				  stmt->d.s_set.varname);
4265 		cnfexprPrint(stmt->d.s_set.expr, indent+1);
4266 		doIndent(indent); dbgprintf("END SET\n");
4267 		break;
4268 	case S_UNSET:
4269 		doIndent(indent); dbgprintf("UNSET %s\n",
4270 				  stmt->d.s_unset.varname);
4271 		break;
4272 	case S_RELOAD_LOOKUP_TABLE:
4273 		doIndent(indent);
4274 		dbgprintf("RELOAD_LOOKUP_TABLE table(%s) (stub with '%s' on error)",
4275 			stmt->d.s_reload_lookup_table.table_name,
4276 			stmt->d.s_reload_lookup_table.stub_value);
4277 		break;
4278 	case S_PRIFILT:
4279 		doIndent(indent); dbgprintf("PRIFILT '%s'\n", stmt->printable);
4280 		pmaskPrint(stmt->d.s_prifilt.pmask, indent);
4281 		if(subtree) {
4282 			cnfstmtPrint(stmt->d.s_prifilt.t_then, indent+1);
4283 			if(stmt->d.s_prifilt.t_else != NULL) {
4284 				doIndent(indent); dbgprintf("ELSE\n");
4285 				cnfstmtPrint(stmt->d.s_prifilt.t_else, indent+1);
4286 			}
4287 			doIndent(indent); dbgprintf("END PRIFILT\n");
4288 		}
4289 		break;
4290 	case S_PROPFILT:
4291 		doIndent(indent); dbgprintf("PROPFILT\n");
4292 		doIndent(indent); dbgprintf("\tProperty.: '%s'\n",
4293 			propIDToName(stmt->d.s_propfilt.prop.id));
4294 		if(stmt->d.s_propfilt.prop.id == PROP_CEE ||
4295 		   stmt->d.s_propfilt.prop.id == PROP_LOCAL_VAR ||
4296 		   stmt->d.s_propfilt.prop.id == PROP_GLOBAL_VAR) {
4297 			doIndent(indent);
4298 			dbgprintf("\tCEE-Prop.: '%s'\n", stmt->d.s_propfilt.prop.name);
4299 		}
4300 		doIndent(indent); dbgprintf("\tOperation: ");
4301 		if(stmt->d.s_propfilt.isNegated)
4302 			dbgprintf("NOT ");
4303 		dbgprintf("'%s'\n", getFIOPName(stmt->d.s_propfilt.operation));
4304 		if(stmt->d.s_propfilt.pCSCompValue != NULL) {
4305 			doIndent(indent); dbgprintf("\tValue....: '%s'\n",
4306 			       rsCStrGetSzStrNoNULL(stmt->d.s_propfilt.pCSCompValue));
4307 		}
4308 		if(subtree) {
4309 			doIndent(indent); dbgprintf("THEN\n");
4310 			cnfstmtPrint(stmt->d.s_propfilt.t_then, indent+1);
4311 			doIndent(indent); dbgprintf("END PROPFILT\n");
4312 		}
4313 		break;
4314 	default:
4315 		dbgprintf("error: unknown stmt type %u\n",
4316 			(unsigned) stmt->nodetype);
4317 		break;
4318 	}
4319 }
4320 void
cnfstmtPrint(struct cnfstmt * root,int indent)4321 cnfstmtPrint(struct cnfstmt *root, int indent)
4322 {
4323 	struct cnfstmt *stmt;
4324 	for(stmt = root ; stmt != NULL ; stmt = stmt->next) {
4325 		cnfstmtPrintOnly(stmt, indent, 1);
4326 	}
4327 }
4328 
4329 struct cnfnumval*
cnfnumvalNew(const long long val)4330 cnfnumvalNew(const long long val)
4331 {
4332 	struct cnfnumval *numval;
4333 	if((numval = malloc(sizeof(struct cnfnumval))) != NULL) {
4334 		numval->nodetype = 'N';
4335 		numval->val = val;
4336 	}
4337 	return numval;
4338 }
4339 
4340 struct cnfstringval*
cnfstringvalNew(es_str_t * const estr)4341 cnfstringvalNew(es_str_t *const estr)
4342 {
4343 	struct cnfstringval *strval;
4344 	if((strval = malloc(sizeof(struct cnfstringval))) != NULL) {
4345 		strval->nodetype = 'S';
4346 		strval->estr = estr;
4347 	}
4348 	return strval;
4349 }
4350 
4351 /* creates array AND adds first element to it */
4352 struct cnfarray*
cnfarrayNew(es_str_t * val)4353 cnfarrayNew(es_str_t *val)
4354 {
4355 	struct cnfarray *ar;
4356 	if((ar = malloc(sizeof(struct cnfarray))) != NULL) {
4357 		ar->nodetype = 'A';
4358 		ar->nmemb = 1;
4359 		if((ar->arr = malloc(sizeof(es_str_t*))) == NULL) {
4360 			free(ar);
4361 			ar = NULL;
4362 			goto done;
4363 		}
4364 		ar->arr[0] = val;
4365 	}
4366 done:	return ar;
4367 }
4368 
4369 struct cnfarray*
cnfarrayAdd(struct cnfarray * __restrict__ const ar,es_str_t * __restrict__ val)4370 cnfarrayAdd(struct cnfarray *__restrict__ const ar, es_str_t *__restrict__ val)
4371 {
4372 	es_str_t **newptr;
4373 	if((newptr = realloc(ar->arr, (ar->nmemb+1)*sizeof(es_str_t*))) == NULL) {
4374 		DBGPRINTF("cnfarrayAdd: realloc failed, item ignored, ar->arr=%p\n", ar->arr);
4375 		goto done;
4376 	} else {
4377 		ar->arr = newptr;
4378 		ar->arr[ar->nmemb] = val;
4379 		ar->nmemb++;
4380 	}
4381 done:	return ar;
4382 }
4383 
4384 /* duplicate an array (deep copy) */
4385 struct cnfarray*
cnfarrayDup(struct cnfarray * old)4386 cnfarrayDup(struct cnfarray *old)
4387 {
4388 	int i;
4389 	struct cnfarray *ar;
4390 	ar = cnfarrayNew(es_strdup(old->arr[0]));
4391 	for(i = 1 ; i < old->nmemb ; ++i) {
4392 		cnfarrayAdd(ar, es_strdup(old->arr[i]));
4393 	}
4394 	return ar;
4395 }
4396 
4397 struct cnfvar*
cnfvarNew(char * name)4398 cnfvarNew(char *name)
4399 {
4400 	struct cnfvar *var;
4401 	if((var = malloc(sizeof(struct cnfvar))) != NULL) {
4402 		var->nodetype = 'V';
4403 		var->name = name;
4404 		msgPropDescrFill(&var->prop, (uchar*)var->name, strlen(var->name));
4405 	}
4406 	return var;
4407 }
4408 
4409 struct cnfstmt *
cnfstmtNew(unsigned s_type)4410 cnfstmtNew(unsigned s_type)
4411 {
4412 	struct cnfstmt* cnfstmt;
4413 	if((cnfstmt = malloc(sizeof(struct cnfstmt))) != NULL) {
4414 		cnfstmt->nodetype = s_type;
4415 		cnfstmt->printable = NULL;
4416 		cnfstmt->next = NULL;
4417 	}
4418 	return cnfstmt;
4419 }
4420 
4421 /* This function disables a cnfstmt by setting it to NOP. This is
4422  * useful when we detect errors late in the parsing processing, where
4423  * we need to return a valid cnfstmt. The optimizer later removes the
4424  * NOPs, so all is well.
4425  * NOTE: this call assumes that no dynamic data structures have been
4426  * allocated. If so, these MUST be freed before calling cnfstmtDisable().
4427  */
4428 static void
cnfstmtDisable(struct cnfstmt * cnfstmt)4429 cnfstmtDisable(struct cnfstmt *cnfstmt)
4430 {
4431 	cnfstmt->nodetype = S_NOP;
4432 }
4433 
4434 void cnfstmtDestructLst(struct cnfstmt *root);
4435 
4436 static void cnfIteratorDestruct(struct cnfitr *itr);
4437 
4438 /* delete a single stmt */
4439 static void
cnfstmtDestruct(struct cnfstmt * stmt)4440 cnfstmtDestruct(struct cnfstmt *stmt)
4441 {
4442 	switch(stmt->nodetype) {
4443 	case S_NOP:
4444 	case S_STOP:
4445 		break;
4446 	case S_CALL:
4447 		es_deleteStr(stmt->d.s_call.name);
4448 		break;
4449 	case S_CALL_INDIRECT:
4450 		cnfexprDestruct(stmt->d.s_call_ind.expr);
4451 		break;
4452 	case S_ACT:
4453 		actionDestruct(stmt->d.act);
4454 		break;
4455 	case S_IF:
4456 		cnfexprDestruct(stmt->d.s_if.expr);
4457 		if(stmt->d.s_if.t_then != NULL) {
4458 			cnfstmtDestructLst(stmt->d.s_if.t_then);
4459 		}
4460 		if(stmt->d.s_if.t_else != NULL) {
4461 			cnfstmtDestructLst(stmt->d.s_if.t_else);
4462 		}
4463 		break;
4464 	case S_FOREACH:
4465 		cnfIteratorDestruct(stmt->d.s_foreach.iter);
4466 		cnfstmtDestructLst(stmt->d.s_foreach.body);
4467 		break;
4468 	case S_SET:
4469 		free(stmt->d.s_set.varname);
4470 		cnfexprDestruct(stmt->d.s_set.expr);
4471 		break;
4472 	case S_UNSET:
4473 		free(stmt->d.s_set.varname);
4474 		break;
4475 	case S_PRIFILT:
4476 		cnfstmtDestructLst(stmt->d.s_prifilt.t_then);
4477 		cnfstmtDestructLst(stmt->d.s_prifilt.t_else);
4478 		break;
4479 	case S_PROPFILT:
4480 		msgPropDescrDestruct(&stmt->d.s_propfilt.prop);
4481 		if(stmt->d.s_propfilt.regex_cache != NULL)
4482 			rsCStrRegexDestruct(&stmt->d.s_propfilt.regex_cache);
4483 		if(stmt->d.s_propfilt.pCSCompValue != NULL)
4484 			cstrDestruct(&stmt->d.s_propfilt.pCSCompValue);
4485 		cnfstmtDestructLst(stmt->d.s_propfilt.t_then);
4486 		break;
4487 	case S_RELOAD_LOOKUP_TABLE:
4488 		if (stmt->d.s_reload_lookup_table.table_name != NULL) {
4489 				free(stmt->d.s_reload_lookup_table.table_name);
4490 		}
4491 		if (stmt->d.s_reload_lookup_table.stub_value != NULL) {
4492 				free(stmt->d.s_reload_lookup_table.stub_value);
4493 		}
4494 		break;
4495 	default:
4496 		DBGPRINTF("error: unknown stmt type during destruct %u\n",
4497 			(unsigned) stmt->nodetype);
4498 		break;
4499 	}
4500 	free(stmt->printable);
4501 	free(stmt);
4502 }
4503 
4504 /* delete a stmt and all others following it */
4505 void
cnfstmtDestructLst(struct cnfstmt * root)4506 cnfstmtDestructLst(struct cnfstmt *root)
4507 {
4508 	struct cnfstmt *stmt, *todel;
4509 	for(stmt = root ; stmt != NULL ; ) {
4510 		todel = stmt;
4511 		stmt = stmt->next;
4512 		cnfstmtDestruct(todel);
4513 	}
4514 }
4515 
4516 struct cnfitr *
cnfNewIterator(char * var,struct cnfexpr * collection)4517 cnfNewIterator(char *var, struct cnfexpr *collection)
4518 {
4519 	struct cnfitr* itr;
4520 	if ((itr = malloc(sizeof(struct cnfitr))) != NULL) {
4521 		itr->var = var;
4522 		itr->collection = collection;
4523 	}
4524 	return itr;
4525 }
4526 
4527 static void
cnfIteratorDestruct(struct cnfitr * itr)4528 cnfIteratorDestruct(struct cnfitr *itr)
4529 {
4530 	free(itr->var);
4531 	if(itr->collection != NULL)
4532 		cnfexprDestruct(itr->collection);
4533 	free(itr);
4534 }
4535 
4536 struct cnfstmt *
cnfstmtNewSet(char * var,struct cnfexpr * expr,int force_reset)4537 cnfstmtNewSet(char *var, struct cnfexpr *expr, int force_reset)
4538 {
4539 	propid_t propid;
4540 	struct cnfstmt* cnfstmt;
4541 	if((cnfstmt = cnfstmtNew(S_SET)) != NULL) {
4542 		if(propNameToID((uchar *)var, &propid) == RS_RET_OK
4543 		   && (   propid == PROP_CEE
4544 		       || propid == PROP_LOCAL_VAR
4545 		       || propid == PROP_GLOBAL_VAR)
4546 		   ) {
4547 			cnfstmt->d.s_set.varname = (uchar*) var;
4548 			cnfstmt->d.s_set.expr = expr;
4549 			cnfstmt->d.s_set.force_reset = force_reset;
4550 		} else {
4551 			parser_errmsg("invalid variable '%s' in set statement.", var);
4552 			free(var);
4553 			cnfstmtDisable(cnfstmt);
4554 		}
4555 	}
4556 	return cnfstmt;
4557 }
4558 
4559 struct cnfstmt *
cnfstmtNewCall(es_str_t * name)4560 cnfstmtNewCall(es_str_t *name)
4561 {
4562 	struct cnfstmt* cnfstmt;
4563 	if((cnfstmt = cnfstmtNew(S_CALL)) != NULL) {
4564 		cnfstmt->d.s_call.name = name;
4565 		cnfstmt->d.s_call.ruleset = NULL;
4566 	}
4567 	return cnfstmt;
4568 }
4569 
4570 struct cnfstmt *
cnfstmtNewReloadLookupTable(struct cnffparamlst * fparams)4571 cnfstmtNewReloadLookupTable(struct cnffparamlst *fparams)
4572 {
4573 	int nParams;
4574 	struct cnffparamlst *param, *nxt;
4575 	struct cnfstmt* cnfstmt;
4576 	uint8_t failed = 0;
4577 	if((cnfstmt = cnfstmtNew(S_RELOAD_LOOKUP_TABLE)) != NULL) {
4578 		nParams = 0;
4579 		for(param = fparams ; param != NULL ; param = param->next) {
4580 			++nParams;
4581 		}
4582 		cnfstmt->d.s_reload_lookup_table.table_name = cnfstmt->d.s_reload_lookup_table.stub_value = NULL;
4583 		switch(nParams) {
4584 		case 2:
4585 			param = fparams->next;
4586 			if (param->expr->nodetype != 'S') {
4587 				parser_errmsg("statement ignored: reload_lookup_table(table_name, "
4588 					"optional:stub_value_in_case_reload_fails) "
4589 					"expects a litteral string for second argument\n");
4590 				failed = 1;
4591 			}
4592 			if ((cnfstmt->d.s_reload_lookup_table.stub_value =
4593 			(uchar*) es_str2cstr(((struct cnfstringval*)param->expr)->estr, NULL)) == NULL) {
4594 				parser_errmsg("statement ignored: reload_lookup_table statement "
4595 				"failed to allocate memory for lookup-table stub-value\n");
4596 				failed = 1;
4597 			}
4598 			CASE_FALLTHROUGH
4599 		case 1:
4600 			param = fparams;
4601 			if (param->expr->nodetype != 'S') {
4602 				parser_errmsg("statement ignored: reload_lookup_table(table_name, "
4603 					"optional:stub_value_in_case_reload_fails) "
4604 				 	"expects a litteral string for first argument\n");
4605 				failed = 1;
4606 			}
4607 			if ((cnfstmt->d.s_reload_lookup_table.table_name =
4608 			(uchar*) es_str2cstr(((struct cnfstringval*)param->expr)->estr, NULL)) == NULL) {
4609 				parser_errmsg("statement ignored: reload_lookup_table statement "
4610 				"failed to allocate memory for lookup-table name\n");
4611 				failed = 1;
4612 			}
4613 			break;
4614 		default:
4615 			parser_errmsg("statement ignored: reload_lookup_table(table_name, optional:"
4616 				"stub_value_in_case_reload_fails) "
4617 				"expected 1 or 2 arguments, but found '%d'\n", nParams);
4618 			failed = 1;
4619 		}
4620 	}
4621 	param = fparams;
4622 	while(param != NULL) {
4623 		nxt = param->next;
4624 		if (param->expr != NULL) cnfexprDestruct(param->expr);
4625 		free(param);
4626 		param = nxt;
4627 	}
4628 	if (failed) {
4629 		cnfstmt->nodetype = S_NOP;
4630 		if (cnfstmt->d.s_reload_lookup_table.table_name != NULL) {
4631 			free(cnfstmt->d.s_reload_lookup_table.table_name);
4632 		}
4633 		if (cnfstmt->d.s_reload_lookup_table.stub_value != NULL) {
4634 			free(cnfstmt->d.s_reload_lookup_table.stub_value);
4635 		}
4636 	}
4637 	return cnfstmt;
4638 }
4639 
4640 struct cnfstmt *
cnfstmtNewUnset(char * var)4641 cnfstmtNewUnset(char *var)
4642 {
4643 	propid_t propid;
4644 	struct cnfstmt* cnfstmt;
4645 	if((cnfstmt = cnfstmtNew(S_UNSET)) != NULL) {
4646 		if(propNameToID((uchar *)var, &propid) == RS_RET_OK
4647 		   && (   propid == PROP_CEE
4648 		       || propid == PROP_LOCAL_VAR
4649 		       || propid == PROP_GLOBAL_VAR)
4650 		   ) {
4651 			cnfstmt->d.s_unset.varname = (uchar*) var;
4652 		} else {
4653 			parser_errmsg("invalid variable '%s' in unset statement.", var);
4654 			free(var);
4655 			cnfstmtDisable(cnfstmt);
4656 		}
4657 	}
4658 	return cnfstmt;
4659 }
4660 
4661 struct cnfstmt *
cnfstmtNewContinue(void)4662 cnfstmtNewContinue(void)
4663 {
4664 	return cnfstmtNew(S_NOP);
4665 }
4666 
4667 struct cnfstmt *
cnfstmtNewPRIFILT(char * prifilt,struct cnfstmt * t_then)4668 cnfstmtNewPRIFILT(char *prifilt, struct cnfstmt *t_then)
4669 {
4670 	struct cnfstmt* cnfstmt;
4671 	if((cnfstmt = cnfstmtNew(S_PRIFILT)) != NULL) {
4672 		cnfstmt->printable = (uchar*)prifilt;
4673 		cnfstmt->d.s_prifilt.t_then = t_then;
4674 		cnfstmt->d.s_prifilt.t_else = NULL;
4675 		DecodePRIFilter((uchar*)prifilt, cnfstmt->d.s_prifilt.pmask);
4676 	}
4677 	return cnfstmt;
4678 }
4679 
4680 struct cnfstmt *
cnfstmtNewPROPFILT(char * propfilt,struct cnfstmt * t_then)4681 cnfstmtNewPROPFILT(char *propfilt, struct cnfstmt *t_then)
4682 {
4683 	struct cnfstmt* cnfstmt;
4684 	if((cnfstmt = cnfstmtNew(S_PROPFILT)) != NULL) {
4685 		cnfstmt->printable = (uchar*)propfilt;
4686 		cnfstmt->d.s_propfilt.t_then = t_then;
4687 		cnfstmt->d.s_propfilt.regex_cache = NULL;
4688 		cnfstmt->d.s_propfilt.pCSCompValue = NULL;
4689 		if(DecodePropFilter((uchar*)propfilt, cnfstmt) != RS_RET_OK) {
4690 			cnfstmt->nodetype = S_NOP; /* disable action! */
4691 			cnfstmtDestructLst(t_then); /* we do no longer need this */
4692 		}
4693 	}
4694 	return cnfstmt;
4695 }
4696 
4697 struct cnfstmt *
cnfstmtNewAct(struct nvlst * lst)4698 cnfstmtNewAct(struct nvlst *lst)
4699 {
4700 	struct cnfstmt* cnfstmt;
4701 	char namebuf[256];
4702 	rsRetVal localRet;
4703 	if((cnfstmt = cnfstmtNew(S_ACT)) == NULL) {
4704 		goto done;
4705 	}
4706 	if (nvlstChkDisabled(lst)) {
4707 		dbgprintf("action disabled by configuration\n");
4708 		cnfstmt->nodetype = S_NOP;
4709 	}
4710 	localRet = actionNewInst(lst, &cnfstmt->d.act);
4711 	if(localRet == RS_RET_OK_WARN) {
4712 		parser_errmsg("warnings occured in file '%s' around line %d",
4713 			      cnfcurrfn, yylineno);
4714 	} else if(localRet != RS_RET_OK) {
4715 		parser_errmsg("errors occured in file '%s' around line %d",
4716 			      cnfcurrfn, yylineno);
4717 		cnfstmt->nodetype = S_NOP; /* disable action! */
4718 		goto done;
4719 	}
4720 	snprintf(namebuf, sizeof(namebuf)-1, "action(type=\"%s\" ...)",
4721 		 modGetName(cnfstmt->d.act->pMod));
4722 	namebuf[255] = '\0'; /* be on safe side */
4723 	cnfstmt->printable = (uchar*)strdup(namebuf);
4724 	nvlstChkUnused(lst);
4725 	nvlstDestruct(lst);
4726 done:	return cnfstmt;
4727 }
4728 
4729 struct cnfstmt *
cnfstmtNewLegaAct(char * actline)4730 cnfstmtNewLegaAct(char *actline)
4731 {
4732 	struct cnfstmt* cnfstmt;
4733 	rsRetVal localRet;
4734 	if((cnfstmt = cnfstmtNew(S_ACT)) == NULL)
4735 		goto done;
4736 	cnfstmt->printable = (uchar*)strdup((char*)actline);
4737 	localRet = cflineDoAction(loadConf, (uchar**)&actline, &cnfstmt->d.act);
4738 	if(localRet != RS_RET_OK) {
4739 		parser_errmsg("%s occured in file '%s' around line %d",
4740 			      (localRet == RS_RET_OK_WARN) ? "warnings" : "errors",
4741 			      cnfcurrfn, yylineno);
4742 		if(localRet != RS_RET_OK_WARN) {
4743 			cnfstmt->nodetype = S_NOP; /* disable action! */
4744 			goto done;
4745 		}
4746 	}
4747 done:	return cnfstmt;
4748 }
4749 
4750 
4751 /* returns 1 if the two expressions are constants, 0 otherwise
4752  * if both are constants, the expression subtrees are destructed
4753  * (this is an aid for constant folding optimizing)
4754  */
4755 static int
getConstNumber(struct cnfexpr * expr,long long * l,long long * r)4756 getConstNumber(struct cnfexpr *expr, long long *l, long long *r)
4757 {
4758 	int ret = 0;
4759 	cnfexprOptimize(expr->l);
4760 	cnfexprOptimize(expr->r);
4761 	if(expr->l->nodetype == 'N') {
4762 		if(expr->r->nodetype == 'N') {
4763 			ret = 1;
4764 			*l = ((struct cnfnumval*)expr->l)->val;
4765 			*r = ((struct cnfnumval*)expr->r)->val;
4766 			cnfexprDestruct(expr->l);
4767 			cnfexprDestruct(expr->r);
4768 		} else if(expr->r->nodetype == 'S') {
4769 			ret = 1;
4770 			*l = ((struct cnfnumval*)expr->l)->val;
4771 			*r = es_str2num(((struct cnfstringval*)expr->r)->estr, NULL);
4772 			cnfexprDestruct(expr->l);
4773 			cnfexprDestruct(expr->r);
4774 		}
4775 	} else if(expr->l->nodetype == 'S') {
4776 		if(expr->r->nodetype == 'N') {
4777 			ret = 1;
4778 			*l = es_str2num(((struct cnfstringval*)expr->l)->estr, NULL);
4779 			*r = ((struct cnfnumval*)expr->r)->val;
4780 			cnfexprDestruct(expr->l);
4781 			cnfexprDestruct(expr->r);
4782 		} else if(expr->r->nodetype == 'S') {
4783 			ret = 1;
4784 			*l = es_str2num(((struct cnfstringval*)expr->l)->estr, NULL);
4785 			*r = es_str2num(((struct cnfstringval*)expr->r)->estr, NULL);
4786 			cnfexprDestruct(expr->l);
4787 			cnfexprDestruct(expr->r);
4788 		}
4789 	}
4790 	return ret;
4791 }
4792 
4793 
4794 /* constant folding for string concatenation */
4795 static void
constFoldConcat(struct cnfexpr * expr)4796 constFoldConcat(struct cnfexpr *expr)
4797 {
4798 	es_str_t *estr;
4799 	cnfexprOptimize(expr->l);
4800 	cnfexprOptimize(expr->r);
4801 	if(expr->l->nodetype == 'S') {
4802 		if(expr->r->nodetype == 'S') {
4803 			estr = ((struct cnfstringval*)expr->l)->estr;
4804 			((struct cnfstringval*)expr->l)->estr = NULL;
4805 			es_addStr(&estr, ((struct cnfstringval*)expr->r)->estr);
4806 			cnfexprDestruct(expr->l);
4807 			cnfexprDestruct(expr->r);
4808 			expr->nodetype = 'S';
4809 			((struct cnfstringval*)expr)->estr = estr;
4810 		} else if(expr->r->nodetype == 'N') {
4811 			es_str_t *numstr;
4812 			estr = ((struct cnfstringval*)expr->l)->estr;
4813 			((struct cnfstringval*)expr->l)->estr = NULL;
4814 			numstr = es_newStrFromNumber(((struct cnfnumval*)expr->r)->val);
4815 			es_addStr(&estr, numstr);
4816 			es_deleteStr(numstr);
4817 			cnfexprDestruct(expr->l);
4818 			cnfexprDestruct(expr->r);
4819 			expr->nodetype = 'S';
4820 			((struct cnfstringval*)expr)->estr = estr;
4821 		}
4822 	} else if(expr->l->nodetype == 'N') {
4823 		if(expr->r->nodetype == 'S') {
4824 			estr = es_newStrFromNumber(((struct cnfnumval*)expr->l)->val);
4825 			es_addStr(&estr, ((struct cnfstringval*)expr->r)->estr);
4826 			cnfexprDestruct(expr->l);
4827 			cnfexprDestruct(expr->r);
4828 			expr->nodetype = 'S';
4829 			((struct cnfstringval*)expr)->estr = estr;
4830 		} else if(expr->r->nodetype == 'N') {
4831 			es_str_t *numstr;
4832 			estr = es_newStrFromNumber(((struct cnfnumval*)expr->l)->val);
4833 			numstr = es_newStrFromNumber(((struct cnfnumval*)expr->r)->val);
4834 			es_addStr(&estr, numstr);
4835 			es_deleteStr(numstr);
4836 			cnfexprDestruct(expr->l);
4837 			cnfexprDestruct(expr->r);
4838 			expr->nodetype = 'S';
4839 			((struct cnfstringval*)expr)->estr = estr;
4840 		}
4841 	}
4842 }
4843 
4844 
4845 /* optimize comparisons with syslog severity/facility. This is a special
4846  * handler as the numerical values also support GT, LT, etc ops.
4847  */
4848 static struct cnfexpr*
cnfexprOptimize_CMP_severity_facility(struct cnfexpr * expr)4849 cnfexprOptimize_CMP_severity_facility(struct cnfexpr *expr)
4850 {
4851 	struct cnffunc *func;
4852 
4853 	if(expr->l->nodetype != 'V')
4854 		FINALIZE;
4855 
4856 	if(!strcmp("syslogseverity", ((struct cnfvar*)expr->l)->name)) {
4857 		if(expr->r->nodetype == 'N') {
4858 			int sev = (int) ((struct cnfnumval*)expr->r)->val;
4859 			if(sev >= 0 && sev <= 7) {
4860 				DBGPRINTF("optimizer: change comparison OP to FUNC prifilt()\n");
4861 				func = cnffuncNew_prifilt(0); /* fac is irrelevant, set below... */
4862 				prifiltSetSeverity(func->funcdata, sev, expr->nodetype);
4863 				cnfexprDestruct(expr);
4864 				expr = (struct cnfexpr*) func;
4865 			} else {
4866 				parser_errmsg("invalid syslogseverity %d, expression will always "
4867 					      "evaluate to FALSE", sev);
4868 			}
4869 		}
4870 	} else if(!strcmp("syslogfacility", ((struct cnfvar*)expr->l)->name)) {
4871 		if(expr->r->nodetype == 'N') {
4872 			int fac = (int) ((struct cnfnumval*)expr->r)->val;
4873 			if(fac >= 0 && fac <= 24) {
4874 				DBGPRINTF("optimizer: change comparison OP to FUNC prifilt()\n");
4875 				func = cnffuncNew_prifilt(0); /* fac is irrelevant, set below... */
4876 				prifiltSetFacility(func->funcdata, fac, expr->nodetype);
4877 				cnfexprDestruct(expr);
4878 				expr = (struct cnfexpr*) func;
4879 			} else {
4880 				parser_errmsg("invalid syslogfacility %d, expression will always "
4881 					      "evaluate to FALSE", fac);
4882 			}
4883 		}
4884 	}
4885 finalize_it:
4886 	return expr;
4887 }
4888 
4889 /* optimize a comparison with a variable as left-hand operand
4890  * NOTE: Currently support CMP_EQ, CMP_NE only and code NEEDS
4891  *       TO BE CHANGED fgr other comparisons!
4892  */
4893 static struct cnfexpr*
cnfexprOptimize_CMP_var(struct cnfexpr * expr)4894 cnfexprOptimize_CMP_var(struct cnfexpr *expr)
4895 {
4896 	struct cnffunc *func;
4897 
4898 	if(!strcmp("syslogfacility-text", ((struct cnfvar*)expr->l)->name)) {
4899 		if(expr->r->nodetype == 'S') {
4900 			char *cstr = es_str2cstr(((struct cnfstringval*)expr->r)->estr, NULL);
4901 			int fac = decodeSyslogName((uchar*)cstr, syslogFacNames);
4902 			if(fac == -1) {
4903 				parser_errmsg("invalid facility '%s', expression will always "
4904 					      "evaluate to FALSE", cstr);
4905 			} else {
4906 				/* we can actually optimize! */
4907 				DBGPRINTF("optimizer: change comparison OP to FUNC prifilt()\n");
4908 				func = cnffuncNew_prifilt(fac);
4909 				if(expr->nodetype == CMP_NE)
4910 					prifiltInvert(func->funcdata);
4911 				cnfexprDestruct(expr);
4912 				expr = (struct cnfexpr*) func;
4913 			}
4914 			free(cstr);
4915 		}
4916 	} else if(!strcmp("syslogseverity-text", ((struct cnfvar*)expr->l)->name)) {
4917 		if(expr->r->nodetype == 'S') {
4918 			char *cstr = es_str2cstr(((struct cnfstringval*)expr->r)->estr, NULL);
4919 			int sev = decodeSyslogName((uchar*)cstr, syslogPriNames);
4920 			if(sev == -1) {
4921 				parser_errmsg("invalid syslogseverity '%s', expression will always "
4922 					      "evaluate to FALSE", cstr);
4923 			} else {
4924 				/* we can acutally optimize! */
4925 				DBGPRINTF("optimizer: change comparison OP to FUNC prifilt()\n");
4926 				func = cnffuncNew_prifilt(0);
4927 				prifiltSetSeverity(func->funcdata, sev, expr->nodetype);
4928 				cnfexprDestruct(expr);
4929 				expr = (struct cnfexpr*) func;
4930 			}
4931 			free(cstr);
4932 		}
4933 	} else {
4934 		expr = cnfexprOptimize_CMP_severity_facility(expr);
4935 	}
4936 	return expr;
4937 }
4938 
4939 static struct cnfexpr*
cnfexprOptimize_NOT(struct cnfexpr * expr)4940 cnfexprOptimize_NOT(struct cnfexpr *expr)
4941 {
4942 	struct cnffunc *func;
4943 
4944 	if(expr->r->nodetype == 'F') {
4945 		func = (struct cnffunc *)expr->r;
4946 		if(func->fPtr == doFunct_Prifilt) {
4947 			DBGPRINTF("optimize NOT prifilt() to inverted prifilt()\n");
4948 			expr->r = NULL;
4949 			cnfexprDestruct(expr);
4950 			prifiltInvert(func->funcdata);
4951 			expr = (struct cnfexpr*) func;
4952 		}
4953 	}
4954 	return expr;
4955 }
4956 
4957 static struct cnfexpr*
cnfexprOptimize_AND_OR(struct cnfexpr * expr)4958 cnfexprOptimize_AND_OR(struct cnfexpr *expr)
4959 {
4960 	struct cnffunc *funcl, *funcr;
4961 
4962 	if(expr->l->nodetype == 'F') {
4963 		if(expr->r->nodetype == 'F') {
4964 			funcl = (struct cnffunc *)expr->l;
4965 			funcr = (struct cnffunc *)expr->r;
4966 			if(funcl->fPtr == doFunct_Prifilt && funcr->fPtr == doFunct_Prifilt) {
4967 				DBGPRINTF("optimize combine AND/OR prifilt()\n");
4968 				expr->l = NULL;
4969 				prifiltCombine(funcl->funcdata, funcr->funcdata, expr->nodetype);
4970 				cnfexprDestruct(expr);
4971 				expr = (struct cnfexpr*) funcl;
4972 			}
4973 		}
4974 	}
4975 	return expr;
4976 }
4977 
4978 
4979 /* optimize array for EQ/NEQ comparisons. We sort the array in
4980  * this case so that we can apply binary search later on.
4981  */
4982 static inline void
cnfexprOptimize_CMPEQ_arr(struct cnfarray * arr)4983 cnfexprOptimize_CMPEQ_arr(struct cnfarray *arr)
4984 {
4985 	DBGPRINTF("optimizer: sorting array of %d members for CMP_EQ/NEQ comparison\n", arr->nmemb);
4986 	qsort(arr->arr, arr->nmemb, sizeof(es_str_t*), qs_arrcmp);
4987 }
4988 
4989 
4990 /* (recursively) optimize an expression */
4991 struct cnfexpr*
cnfexprOptimize(struct cnfexpr * expr)4992 cnfexprOptimize(struct cnfexpr *expr)
4993 {
4994 	long long ln, rn;
4995 	struct cnfexpr *exprswap;
4996 
4997 	DBGPRINTF("optimize expr %p, type '%s'\n", expr, tokenToString(expr->nodetype));
4998 	switch(expr->nodetype) {
4999 	case '&':
5000 		constFoldConcat(expr);
5001 		break;
5002 	case '+':
5003 		if(getConstNumber(expr, &ln, &rn))  {
5004 			expr->nodetype = 'N';
5005 			((struct cnfnumval*)expr)->val = ln + rn;
5006 		}
5007 		break;
5008 	case '-':
5009 		if(getConstNumber(expr, &ln, &rn))  {
5010 			expr->nodetype = 'N';
5011 			((struct cnfnumval*)expr)->val = ln - rn;
5012 		}
5013 		break;
5014 	case '*':
5015 		if(getConstNumber(expr, &ln, &rn))  {
5016 			expr->nodetype = 'N';
5017 			((struct cnfnumval*)expr)->val = ln * rn;
5018 		}
5019 		break;
5020 	case '/':
5021 		if(getConstNumber(expr, &ln, &rn))  {
5022 			expr->nodetype = 'N';
5023 			if(rn == 0) {
5024 				/* division by zero */
5025 				((struct cnfnumval*)expr)->val = 0;
5026 			} else {
5027 				((struct cnfnumval*)expr)->val = ln / rn;
5028 			}
5029 		}
5030 		break;
5031 	case '%':
5032 		if(getConstNumber(expr, &ln, &rn))  {
5033 			expr->nodetype = 'N';
5034 			if(rn == 0) {
5035 				/* division by zero */
5036 				((struct cnfnumval*)expr)->val = 0;
5037 			} else {
5038 				((struct cnfnumval*)expr)->val = ln % rn;
5039 			}
5040 		}
5041 		break;
5042 	case CMP_NE:
5043 	case CMP_EQ:
5044 		expr->l = cnfexprOptimize(expr->l);
5045 		expr->r = cnfexprOptimize(expr->r);
5046 		if(expr->l->nodetype == 'A') {
5047 			if(expr->r->nodetype == 'A') {
5048 				parser_errmsg("warning: '==' or '<>' "
5049 				  "comparison of two constant string "
5050 				  "arrays makes no sense");
5051 			} else { /* swap for simpler execution step */
5052 				exprswap = expr->l;
5053 				expr->l = expr->r;
5054 				expr->r = exprswap;
5055 			}
5056 		}
5057 		if(expr->r->nodetype == 'A') {
5058 			cnfexprOptimize_CMPEQ_arr((struct cnfarray *)expr->r);
5059 		}
5060 		/* This should be evaluated last because it may change expr
5061 		 * to a function.
5062 		 */
5063 		if(expr->l->nodetype == 'V') {
5064 			expr = cnfexprOptimize_CMP_var(expr);
5065 		}
5066 		break;
5067 	case CMP_LE:
5068 	case CMP_GE:
5069 	case CMP_LT:
5070 	case CMP_GT:
5071 		expr->l = cnfexprOptimize(expr->l);
5072 		expr->r = cnfexprOptimize(expr->r);
5073 		expr = cnfexprOptimize_CMP_severity_facility(expr);
5074 		break;
5075 	case CMP_CONTAINS:
5076 	case CMP_CONTAINSI:
5077 	case CMP_STARTSWITH:
5078 	case CMP_STARTSWITHI:
5079 		expr->l = cnfexprOptimize(expr->l);
5080 		expr->r = cnfexprOptimize(expr->r);
5081 		break;
5082 	case AND:
5083 	case OR:
5084 		expr->l = cnfexprOptimize(expr->l);
5085 		expr->r = cnfexprOptimize(expr->r);
5086 		expr = cnfexprOptimize_AND_OR(expr);
5087 		break;
5088 	case NOT:
5089 		expr->r = cnfexprOptimize(expr->r);
5090 		expr = cnfexprOptimize_NOT(expr);
5091 		break;
5092 	default:/* nodetypes we cannot optimize */
5093 		break;
5094 	}
5095 	return expr;
5096 }
5097 
5098 /* removes NOPs from a statement list and returns the
5099  * first non-NOP entry.
5100  */
5101 static struct cnfstmt *
removeNOPs(struct cnfstmt * const root)5102 removeNOPs(struct cnfstmt *const root)
5103 {
5104 	struct cnfstmt *stmt, *toDel, *prevstmt = NULL;
5105 	struct cnfstmt *newRoot = NULL;
5106 
5107 	if(root == NULL) goto done;
5108 	stmt = root;
5109 	while(stmt != NULL) {
5110 		if(stmt->nodetype == S_NOP) {
5111 			if(prevstmt != NULL)
5112 				/* end chain, is rebuild if more non-NOPs follow */
5113 				prevstmt->next = NULL;
5114 			toDel = stmt;
5115 			stmt = stmt->next;
5116 			cnfstmtDestruct(toDel);
5117 		} else {
5118 			if(newRoot == NULL)
5119 				newRoot = stmt;
5120 			if(prevstmt != NULL)
5121 				prevstmt->next = stmt;
5122 			prevstmt = stmt;
5123 			stmt = stmt->next;
5124 		}
5125 	}
5126 done:	return newRoot;
5127 }
5128 
5129 static void
cnfstmtOptimizeForeach(struct cnfstmt * stmt)5130 cnfstmtOptimizeForeach(struct cnfstmt *stmt)
5131 {
5132 	stmt->d.s_foreach.iter->collection = cnfexprOptimize(stmt->d.s_foreach.iter->collection);
5133 	stmt->d.s_foreach.body = cnfstmtOptimize(stmt->d.s_foreach.body);
5134 }
5135 
5136 
5137 static void
cnfstmtOptimizeIf(struct cnfstmt * stmt)5138 cnfstmtOptimizeIf(struct cnfstmt *stmt)
5139 {
5140 	struct cnfstmt *t_then, *t_else;
5141 	struct cnfexpr *expr;
5142 	struct cnffunc *func;
5143 	struct funcData_prifilt *prifilt;
5144 
5145 	assert(stmt->nodetype == S_IF);
5146 	expr = stmt->d.s_if.expr = cnfexprOptimize(stmt->d.s_if.expr);
5147 	stmt->d.s_if.t_then = cnfstmtOptimize(stmt->d.s_if.t_then);
5148 	stmt->d.s_if.t_else = cnfstmtOptimize(stmt->d.s_if.t_else);
5149 
5150 	if(stmt->d.s_if.t_then == NULL && stmt->d.s_if.t_else == NULL) {
5151 		/* pointless if, probably constructed by config mgmt system */
5152 		DBGPRINTF("optimizer: if with both empty then and else - remove\n");
5153 		cnfexprDestruct(stmt->d.s_if.expr);
5154 		/* set to NOP, this will be removed in later stage */
5155 		stmt->nodetype = S_NOP;
5156 		goto done;
5157 	}
5158 
5159 	assert(stmt->nodetype == S_IF);
5160 	if(stmt->d.s_if.expr->nodetype == 'F') {
5161 		func = (struct cnffunc*)expr;
5162 		   if(func->fPtr == doFunct_Prifilt) {
5163 			DBGPRINTF("optimizer: change IF to PRIFILT\n");
5164 			t_then = stmt->d.s_if.t_then;
5165 			t_else = stmt->d.s_if.t_else;
5166 			stmt->nodetype = S_PRIFILT;
5167 			prifilt = (struct funcData_prifilt*) func->funcdata;
5168 			memcpy(stmt->d.s_prifilt.pmask, prifilt->pmask,
5169 				sizeof(prifilt->pmask));
5170 			stmt->d.s_prifilt.t_then = t_then;
5171 			stmt->d.s_prifilt.t_else = t_else;
5172 			if(func->nParams == 0)
5173 				stmt->printable = (uchar*)strdup("[Optimizer Result]");
5174 			else
5175 				stmt->printable = (uchar*)
5176 					es_str2cstr(((struct cnfstringval*)func->expr[0])->estr, NULL);
5177 			cnfexprDestruct(expr);
5178 			cnfstmtOptimizePRIFilt(stmt);
5179 		}
5180 	}
5181 done:	return;
5182 }
5183 
5184 static void
cnfstmtOptimizeAct(struct cnfstmt * stmt)5185 cnfstmtOptimizeAct(struct cnfstmt *stmt)
5186 {
5187 	action_t *pAct;
5188 
5189 	pAct = stmt->d.act;
5190 	if(!strcmp((char*)modGetName(pAct->pMod), "builtin:omdiscard")) {
5191 		DBGPRINTF("optimizer: replacing omdiscard by STOP\n");
5192 		actionDestruct(stmt->d.act);
5193 		stmt->nodetype = S_STOP;
5194 	}
5195 }
5196 
5197 static void
cnfstmtOptimizePRIFilt(struct cnfstmt * stmt)5198 cnfstmtOptimizePRIFilt(struct cnfstmt *stmt)
5199 {
5200 	int i;
5201 	int isAlways = 1;
5202 	struct cnfstmt *subroot, *last;
5203 
5204 	stmt->d.s_prifilt.t_then = cnfstmtOptimize(stmt->d.s_prifilt.t_then);
5205 
5206 	for(i = 0; i <= LOG_NFACILITIES; i++)
5207 		if(stmt->d.s_prifilt.pmask[i] != 0xff) {
5208 			isAlways = 0;
5209 			break;
5210 		}
5211 	if(!isAlways)
5212 		goto done;
5213 
5214 	DBGPRINTF("optimizer: removing always-true PRIFILT %p\n", stmt);
5215 	if(stmt->d.s_prifilt.t_else != NULL) {
5216 		parser_errmsg("error: always-true PRI filter has else part!\n");
5217 		cnfstmtDestructLst(stmt->d.s_prifilt.t_else);
5218 	}
5219 	free(stmt->printable);
5220 	stmt->printable = NULL;
5221 	subroot = stmt->d.s_prifilt.t_then;
5222 	if(subroot == NULL) {
5223 		/* very strange, we set it to NOP, best we can do
5224 		 * This case is NOT expected in practice
5225 		 */
5226 		 stmt->nodetype = S_NOP;
5227 		 goto done;
5228 	}
5229 	for(last = subroot ; last->next != NULL ; last = last->next)
5230 		/* find last node in subtree */;
5231 	last->next = stmt->next;
5232 	memcpy(stmt, subroot, sizeof(struct cnfstmt));
5233 	free(subroot);
5234 
5235 done:	return;
5236 }
5237 
5238 static void
cnfstmtOptimizeReloadLookupTable(struct cnfstmt * stmt)5239 cnfstmtOptimizeReloadLookupTable(struct cnfstmt *stmt) {
5240 	if((stmt->d.s_reload_lookup_table.table = lookupFindTable(stmt->d.s_reload_lookup_table.table_name))
5241 	== NULL) {
5242 		parser_errmsg("lookup table '%s' not found\n", stmt->d.s_reload_lookup_table.table_name);
5243 	}
5244 }
5245 
5246 /* we abuse "optimize" a bit. Actually, we obtain a ruleset pointer, as
5247  * all rulesets are only known later in the process (now!).
5248  */
5249 static void
cnfstmtOptimizeCall(struct cnfstmt * stmt)5250 cnfstmtOptimizeCall(struct cnfstmt *stmt)
5251 {
5252 	ruleset_t *pRuleset;
5253 	rsRetVal localRet;
5254 	uchar *rsName;
5255 
5256 	rsName = (uchar*) es_str2cstr(stmt->d.s_call.name, NULL);
5257 	localRet = rulesetGetRuleset(loadConf, &pRuleset, rsName);
5258 	if(localRet != RS_RET_OK) {
5259 		/* in that case, we accept that a NOP will "survive" */
5260 		parser_errmsg("ruleset '%s' cannot be found\n", rsName);
5261 		es_deleteStr(stmt->d.s_call.name);
5262 		stmt->nodetype = S_NOP;
5263 		goto done;
5264 	}
5265 	DBGPRINTF("CALL obtained ruleset ptr %p for ruleset '%s' [hasQueue:%d]\n",
5266 		  pRuleset, rsName, rulesetHasQueue(pRuleset));
5267 	if(rulesetHasQueue(pRuleset)) {
5268 		stmt->d.s_call.ruleset = pRuleset;
5269 	} else {
5270 		stmt->d.s_call.ruleset = NULL;
5271 		stmt->d.s_call.stmt = pRuleset->root;
5272 	}
5273 done:
5274 	free(rsName);
5275 	return;
5276 }
5277 /* (recursively) optimize a statement */
5278 struct cnfstmt *
cnfstmtOptimize(struct cnfstmt * root)5279 cnfstmtOptimize(struct cnfstmt *root)
5280 {
5281 	struct cnfstmt *stmt;
5282 	if(root == NULL) goto done;
5283 	for(stmt = root ; stmt != NULL ; stmt = stmt->next) {
5284 		DBGPRINTF("optimizing cnfstmt type %d\n", (int) stmt->nodetype);
5285 		switch(stmt->nodetype) {
5286 		case S_IF:
5287 			cnfstmtOptimizeIf(stmt);
5288 			break;
5289 		case S_FOREACH:
5290 			cnfstmtOptimizeForeach(stmt);
5291 			break;
5292 		case S_PRIFILT:
5293 			cnfstmtOptimizePRIFilt(stmt);
5294 			break;
5295 		case S_PROPFILT:
5296 			stmt->d.s_propfilt.t_then = cnfstmtOptimize(stmt->d.s_propfilt.t_then);
5297 			break;
5298 		case S_SET:
5299 			stmt->d.s_set.expr = cnfexprOptimize(stmt->d.s_set.expr);
5300 			break;
5301 		case S_ACT:
5302 			cnfstmtOptimizeAct(stmt);
5303 			break;
5304 		case S_CALL:
5305 			cnfstmtOptimizeCall(stmt);
5306 			break;
5307 		case S_CALL_INDIRECT:
5308 			stmt->d.s_call_ind.expr = cnfexprOptimize(stmt->d.s_call_ind.expr);
5309 			break;
5310 		case S_STOP:
5311 			if(stmt->next != NULL)
5312 				parser_warnmsg("STOP is followed by unreachable statements!\n");
5313 			break;
5314 		case S_UNSET: /* nothing to do */
5315 			break;
5316 		case S_RELOAD_LOOKUP_TABLE:
5317 			cnfstmtOptimizeReloadLookupTable(stmt);
5318 			break;
5319 		case S_NOP:
5320 			// TODO: fix optimizer, re-enable. see:
5321 			// https://github.com/rsyslog/rsyslog/issues/2524
5322 			//LogError(0, RS_RET_INTERNAL_ERROR,
5323 			//	"optimizer error: we see a NOP, how come?");
5324 			dbgprintf("optimizer error: we see a NOP, how come?");
5325 			break;
5326 		default:
5327 			LogError(0, RS_RET_INTERNAL_ERROR,
5328 				"internal error: unknown stmt type %u during optimizer run\n",
5329 				(unsigned) stmt->nodetype);
5330 			break;
5331 		}
5332 	}
5333 	root = removeNOPs(root);
5334 done:	return root;
5335 }
5336 
5337 
5338 struct cnffparamlst *
cnffparamlstNew(struct cnfexpr * expr,struct cnffparamlst * next)5339 cnffparamlstNew(struct cnfexpr *expr, struct cnffparamlst *next)
5340 {
5341 	struct cnffparamlst* lst;
5342 	if((lst = malloc(sizeof(struct cnffparamlst))) != NULL) {
5343 		lst->nodetype = 'P';
5344 		lst->expr = expr;
5345 		lst->next = next;
5346 	}
5347 	return lst;
5348 }
5349 
5350 /* Obtain function id from name AND number of params. Issues the
5351  * relevant error messages if errors are detected.
5352  */
5353 static rscriptFuncPtr
funcName2Ptr(char * const fname,const unsigned short nParams)5354 funcName2Ptr(char *const fname, const unsigned short nParams)
5355 {
5356 	struct scriptFunct *foundFunc = searchModList(fname);
5357 	if(foundFunc == NULL) {
5358 		parser_errmsg("function '%s' not found", fname);
5359 		return NULL;
5360 	} else {
5361 		return extractFuncPtr(foundFunc, nParams);
5362 	}
5363 }
5364 
5365 rsRetVal
addMod2List(const int version,struct scriptFunct * functArray)5366 addMod2List(const int __attribute__((unused)) version, struct scriptFunct *functArray)
5367 /*version currently not used, might be needed later for versin check*/
5368 {
5369 	DEFiRet;
5370 	int i;
5371 	struct modListNode *newNode;
5372 	CHKmalloc(newNode = (struct modListNode*) malloc(sizeof(struct modListNode)));
5373 	newNode->version = 1;
5374 	newNode->next = NULL;
5375 
5376 	i = 0;
5377 	while(functArray[i].fname != NULL) {
5378 		if(searchModList(functArray[i].fname) != NULL) {
5379 			parser_errmsg("function %s defined multiple times, second time will be ignored",
5380 				functArray[i].fname);
5381 		}
5382 	i++;
5383 	}
5384 	newNode->modFcts = functArray;
5385 
5386 	modListLast->next = newNode;
5387 	modListLast = newNode;
5388 finalize_it:
5389 	RETiRet;
5390 }
5391 
5392 
5393 struct cnffunc *
cnffuncNew(es_str_t * fname,struct cnffparamlst * paramlst)5394 cnffuncNew(es_str_t *fname, struct cnffparamlst* paramlst)
5395 {
5396 	struct cnffunc* func;
5397 	struct cnffparamlst *param, *toDel;
5398 	unsigned short i;
5399 	unsigned short nParams;
5400 	char *cstr;
5401 
5402 	/* we first need to find out how many params we have */
5403 	nParams = 0;
5404 	for(param = paramlst ; param != NULL ; param = param->next)
5405 		++nParams;
5406 	if((func = malloc(sizeof(struct cnffunc) + (nParams * sizeof(struct cnfexp*))))
5407 	   != NULL) {
5408 		func->nodetype = 'F';
5409 		func->fname = fname;
5410 		func->nParams = nParams;
5411 		func->funcdata = NULL;
5412 		func->destructable_funcdata = 1;
5413 		cstr = es_str2cstr(fname, NULL);
5414 		func->fPtr = funcName2Ptr(cstr, nParams);
5415 
5416 		/* parse error if we have an unknown function */
5417 		if (func->fPtr == NULL) {
5418 			parser_errmsg("Invalid function %s", cstr);
5419 		}
5420 
5421 		/* shuffle params over to array (access speed!) */
5422 		param = paramlst;
5423 		for(i = 0 ; i < nParams ; ++i) {
5424 			func->expr[i] = param->expr;
5425 			toDel = param;
5426 			param = param->next;
5427 			free(toDel);
5428 		}
5429 		/* some functions require special initialization */
5430 		struct scriptFunct *foundFunc = searchModList(cstr);
5431 		if(foundFunc->initFunc != NULL) {
5432 			foundFunc->initFunc(func);
5433 		}
5434 		free(cstr);
5435 	}
5436 	return func;
5437 }
5438 
5439 
5440 /* A special function to create a prifilt() expression during optimization
5441  * phase.
5442  */
5443 struct cnffunc *
cnffuncNew_prifilt(int fac)5444 cnffuncNew_prifilt(int fac)
5445 {
5446 	struct cnffunc* func;
5447 
5448 	fac >>= 3;
5449 	if (fac >= LOG_NFACILITIES + 1 || fac < 0)
5450 		return NULL;
5451 
5452 	if((func = malloc(sizeof(struct cnffunc))) != NULL) {
5453 		if ((func->funcdata = calloc(1, sizeof(struct funcData_prifilt))) == NULL) {
5454 			free(func);
5455 			return NULL;
5456 		}
5457 		func->nodetype = 'F';
5458 		func->fname = es_newStrFromCStr("prifilt", sizeof("prifilt")-1);
5459 		func->nParams = 0;
5460 		func->fPtr = doFunct_Prifilt;
5461 		func->destructable_funcdata = 1;
5462 		((struct funcData_prifilt *)func->funcdata)->pmask[fac] = TABLE_ALLPRI;
5463 	}
5464 	return func;
5465 }
5466 
5467 
5468 /* The check-if-variable exists "exists($!var)" is a special beast and as such
5469  * also needs special code (we must not evaluate the var but need its name).
5470  */
ATTR_NONNULL()5471 struct cnffuncexists * ATTR_NONNULL()
5472 cnffuncexistsNew(const char *const varname)
5473 {
5474 	struct cnffuncexists* f_exists;
5475 
5476 	if((f_exists = malloc(sizeof(struct cnffuncexists))) != NULL) {
5477 		f_exists->nodetype = S_FUNC_EXISTS;
5478 		f_exists->varname = varname;
5479 		msgPropDescrFill(&f_exists->prop, (uchar*)varname, strlen(varname));
5480 	}
5481 	return f_exists;
5482 }
5483 
5484 
5485 /* returns 0 if everything is OK and config parsing shall continue,
5486  * and 1 if things are so wrong that config parsing shall be aborted.
5487  */
ATTR_NONNULL()5488 int ATTR_NONNULL()
5489 cnfDoInclude(const char *const name, const int optional)
5490 {
5491 	char *cfgFile;
5492 	const char *finalName;
5493 	int i;
5494 	int result;
5495 	glob_t cfgFiles;
5496 	int ret = 0;
5497 	struct stat fileInfo;
5498 	char errStr[1024];
5499 	char nameBuf[MAXFNAME+1];
5500 	char cwdBuf[MAXFNAME+1];
5501 
5502 	DBGPRINTF("cnfDoInclude: file: '%s', optional: %d\n", name, optional);
5503 	finalName = name;
5504 	if(stat(name, &fileInfo) == 0) {
5505 		/* stat usually fails if we have a wildcard - so this does NOT indicate error! */
5506 		if(S_ISDIR(fileInfo.st_mode)) {
5507 			/* if we have a directory, we need to add "*" to get its files */
5508 			snprintf(nameBuf, sizeof(nameBuf), "%s*", name);
5509 			finalName = nameBuf;
5510 		}
5511 	}
5512 
5513 	/* Use GLOB_MARK to append a trailing slash for directories. */
5514 	/* Use GLOB_NOMAGIC to detect wildcards that match nothing. */
5515 	#ifdef HAVE_GLOB_NOMAGIC
5516 		/* Silently ignore wildcards that match nothing */
5517 		result = glob(finalName, GLOB_MARK | GLOB_NOMAGIC, NULL, &cfgFiles);
5518 		if(result == GLOB_NOMATCH) {
5519 	#else
5520 		result = glob(finalName, GLOB_MARK, NULL, &cfgFiles);
5521 		if(result == GLOB_NOMATCH && containsGlobWildcard((char*)finalName)) {
5522 	#endif /* HAVE_GLOB_NOMAGIC */
5523 		goto done;
5524 	}
5525 
5526 	if(result == GLOB_NOSPACE || result == GLOB_ABORTED) {
5527 		if(optional == 0) {
5528 			rs_strerror_r(errno, errStr, sizeof(errStr));
5529 			if(getcwd(cwdBuf, sizeof(cwdBuf)) == NULL)
5530 				strcpy(cwdBuf, "??getcwd() failed??");
5531 			parser_errmsg("error accessing config file or directory '%s' "
5532 				"[cwd:%s]: %s", finalName, cwdBuf, errStr);
5533 			ret = 1;
5534 		}
5535 		goto done;
5536 	}
5537 
5538 	/* note: bison "stacks" the files, so we need to submit them
5539 	 * in reverse order to the *stack* in order to get the proper
5540 	 * parsing order. Also see
5541 	 * http://bugzilla.adiscon.com/show_bug.cgi?id=411
5542 	 */
5543 	for(i = cfgFiles.gl_pathc - 1; i >= 0 ; i--) {
5544 		cfgFile = cfgFiles.gl_pathv[i];
5545 		if(stat(cfgFile, &fileInfo) != 0) {
5546 			if(optional == 0) {
5547 				rs_strerror_r(errno, errStr, sizeof(errStr));
5548 				if(getcwd(cwdBuf, sizeof(cwdBuf)) == NULL)
5549 					strcpy(cwdBuf, "??getcwd() failed??");
5550 				parser_errmsg("error accessing config file or directory '%s' "
5551 					"[cwd: %s]: %s", cfgFile, cwdBuf, errStr);
5552 				ret = 1;
5553 			}
5554 			goto done;
5555 		}
5556 
5557 		if(S_ISREG(fileInfo.st_mode)) { /* config file */
5558 			DBGPRINTF("requested to include config file '%s'\n", cfgFile);
5559 			cnfSetLexFile(cfgFile);
5560 		} else if(S_ISDIR(fileInfo.st_mode)) { /* config directory */
5561 			DBGPRINTF("requested to include directory '%s'\n", cfgFile);
5562 			cnfDoInclude(cfgFile, optional);
5563 		} else {
5564 			DBGPRINTF("warning: unable to process IncludeConfig directive '%s'\n", cfgFile);
5565 		}
5566 	}
5567 
5568 done:
5569 	globfree(&cfgFiles);
5570 	return ret;
5571 }
5572 
5573 
5574 /* Process include() objects */
5575 void
5576 includeProcessCnf(struct nvlst *const lst)
5577 {
5578 	struct cnfparamvals *pvals = NULL;
5579 	const char *inc_file = NULL;
5580 	const char *text = NULL;
5581 	int optional = 0;
5582 	int abort_if_missing = 0;
5583 	int i;
5584 
5585 	if(lst == NULL) {
5586 		parser_errmsg("include() must have either 'file' or 'text' "
5587 			"parameter - ignored");
5588 		goto done;
5589 	}
5590 
5591 	if (nvlstChkDisabled(lst)) {
5592 		DBGPRINTF("include statement disabled\n");
5593 		goto done;
5594 	}
5595 
5596 	pvals = nvlstGetParams(lst, &incpblk, NULL);
5597 	if(pvals == NULL) {
5598 		goto done;
5599 	}
5600 	DBGPRINTF("include param blk after includeProcessCnf:\n");
5601 	cnfparamsPrint(&incpblk, pvals);
5602 	for(i = 0 ; i < incpblk.nParams ; ++i) {
5603 		if(!pvals[i].bUsed) {
5604 			continue;
5605 		}
5606 
5607 		if(!strcmp(incpblk.descr[i].name, "file")) {
5608 			inc_file = es_str2cstr(pvals[i].val.d.estr, NULL);
5609 		} else if(!strcmp(incpblk.descr[i].name, "text")) {
5610 			text = es_str2cstr(pvals[i].val.d.estr, NULL);
5611 		} else if(!strcmp(incpblk.descr[i].name, "mode")) {
5612 			char *const md = es_str2cstr(pvals[i].val.d.estr, NULL);
5613 			if(!strcmp(md, "abort-if-missing")) {
5614 				optional = 0;
5615 				abort_if_missing = 1;
5616 			} else if(!strcmp(md, "required")) {
5617 				optional = 0;
5618 			} else if(!strcmp(md, "optional")) {
5619 				optional = 1;
5620 			} else {
5621 				parser_errmsg("invalid 'mode' paramter: '%s' - ignored", md);
5622 			}
5623 			free((void*)md);
5624 		} else {
5625 			LogError(0, RS_RET_INTERNAL_ERROR,
5626 				"rainerscript/include: program error, non-handled inclpblk "
5627 				"param '%s' in includeProcessCnf()", incpblk.descr[i].name);
5628 		}
5629 	}
5630 
5631 	if(text != NULL && inc_file != NULL) {
5632 		parser_errmsg("include() must have either 'file' or 'text' "
5633 			"parameter, but both are set - ignored");
5634 		goto done;
5635 	}
5636 
5637 	if(inc_file != NULL) {
5638 		if(cnfDoInclude(inc_file, optional) != 0 && abort_if_missing) {
5639 			fprintf(stderr, "include file '%s' mode is set to abort-if-missing "
5640 				"and the file is indeed missing - thus aborting rsyslog\n",
5641 				inc_file);
5642 			exit(1); /* "good exit" - during config processing, requested by user */
5643 		}
5644 	} else if(text != NULL) {
5645 		es_str_t *estr = es_newStrFromCStr((char*)text, strlen(text));
5646 		/* lex needs 2 \0 bytes as terminator indication (wtf ;-)) */
5647 		es_addChar(&estr, '\0');
5648 		es_addChar(&estr, '\0');
5649 		cnfAddConfigBuffer(estr, "text");
5650 	} else {
5651 		parser_errmsg("include must have either 'file' or 'text' "
5652 			"parameter - ignored");
5653 		goto done;
5654 	}
5655 
5656 done:
5657 	free((void*)text);
5658 	free((void*)inc_file);
5659 	nvlstDestruct(lst);
5660 	if(pvals != NULL)
5661 		cnfparamvalsDestruct(pvals, &incpblk);
5662 	return;
5663 }
5664 
5665 
5666 void
5667 varDelete(const struct svar *v)
5668 {
5669 	switch(v->datatype) {
5670 	case 'S':
5671 	case 'J':
5672 		varFreeMembers(v);
5673 		break;
5674 	case 'A':
5675 		cnfarrayContentDestruct(v->d.ar);
5676 		free(v->d.ar);
5677 		break;
5678 	default:break;
5679 	}
5680 }
5681 
5682 void
5683 cnfparamvalsDestruct(const struct cnfparamvals *paramvals, const struct cnfparamblk *blk)
5684 {
5685 	int i;
5686 	if(paramvals == NULL)
5687 		return;
5688 	for(i = 0 ; i < blk->nParams ; ++i) {
5689 		if(paramvals[i].bUsed) {
5690 			varDelete(&paramvals[i].val);
5691 		}
5692 	}
5693 	free((void*)paramvals);
5694 }
5695 
5696 /* find the index (or -1!) for a config param by name. This is used to
5697  * address the parameter array. Of course, we could use with static
5698  * indices, but that would create some extra bug potential. So we
5699  * resort to names. As we do this only during the initial config parsing
5700  * stage the (considerable!) extra overhead is OK. -- rgerhards, 2011-07-19
5701  */
5702 int
5703 cnfparamGetIdx(struct cnfparamblk *params, const char *name)
5704 {
5705 	int i;
5706 	for(i = 0 ; i < params->nParams ; ++i)
5707 		if(!strcmp(params->descr[i].name, name))
5708 			break;
5709 	if(i == params->nParams)
5710 		i = -1; /* not found */
5711 	return i;
5712 }
5713 
5714 
5715 void
5716 cstrPrint(const char *text, es_str_t *estr)
5717 {
5718 	char *str;
5719 	str = es_str2cstr(estr, NULL);
5720 	dbgprintf("%s%s", text, str);
5721 	free(str);
5722 }
5723 
5724 char *
5725 rmLeadingSpace(char *s)
5726 {
5727 	char *p;
5728 	for(p = s ; *p && isspace(*p) ; ++p)
5729 		;
5730 	return(p);
5731 }
5732 
5733 /* init must be called once before any parsing of the script files start */
5734 rsRetVal
5735 initRainerscript(void)
5736 {
5737 	DEFiRet;
5738 	CHKmalloc(modListRoot = (struct modListNode*) malloc(sizeof(struct modListNode)));
5739 	modListRoot->version = 1;
5740 	modListRoot->modFcts = functions;
5741 	modListRoot->next = NULL;
5742 	modListLast = modListRoot;
5743 	iRet = objGetObjInterface(&obj);
5744 finalize_it:
5745 	RETiRet;
5746 }
5747 
5748 /* we need a function to check for octal digits */
5749 static inline int
5750 isodigit(uchar c)
5751 {
5752 	return(c >= '0' && c <= '7');
5753 }
5754 
5755 /**
5756  * Get numerical value of a hex digit. This is a helper function.
5757  * @param[in] c a character containing 0..9, A..Z, a..z anything else
5758  * is an (undetected) error.
5759  */
5760 static int
5761 hexDigitVal(char c)
5762 {
5763 	int r;
5764 	if(c < 'A')
5765 		r = c - '0';
5766 	else if(c < 'a')
5767 		r = c - 'A' + 10;
5768 	else
5769 		r = c - 'a' + 10;
5770 	return r;
5771 }
5772 
5773 /* Handle the actual unescaping.
5774  * a helper to unescapeStr(), to help make the function easier to read.
5775  */
5776 static void
5777 doUnescape(unsigned char *c, int len, int *iSrc, int iDst)
5778 {
5779 	if(c[*iSrc] == '\\') {
5780 		if(++(*iSrc) == len) {
5781 			/* error, incomplete escape, treat as single char */
5782 			c[iDst] = '\\';
5783 		}
5784 		/* regular case, unescape */
5785 		switch(c[*iSrc]) {
5786 		case 'a':
5787 			c[iDst] = '\007';
5788 			break;
5789 		case 'b':
5790 			c[iDst] = '\b';
5791 			break;
5792 		case 'f':
5793 			c[iDst] = '\014';
5794 			break;
5795 		case 'n':
5796 			c[iDst] = '\n';
5797 			break;
5798 		case 'r':
5799 			c[iDst] = '\r';
5800 			break;
5801 		case 't':
5802 			c[iDst] = '\t';
5803 			break;
5804 		case '\'':
5805 			c[iDst] = '\'';
5806 			break;
5807 		case '"':
5808 			c[iDst] = '"';
5809 			break;
5810 		case '?':
5811 			c[iDst] = '?';
5812 			break;
5813 		case '$':
5814 			c[iDst] = '$';
5815 			break;
5816 		case '\\':
5817 			c[iDst] = '\\';
5818 			break;
5819 		case 'x':
5820 			if(    (*iSrc)+2 >= len
5821 			   || !isxdigit(c[(*iSrc)+1])
5822 			   || !isxdigit(c[(*iSrc)+2])) {
5823 				/* error, incomplete escape, use as is */
5824 				c[iDst] = '\\';
5825 				--(*iSrc);
5826 			}
5827 			c[iDst] = (hexDigitVal(c[(*iSrc)+1]) << 4) +
5828 				  hexDigitVal(c[(*iSrc)+2]);
5829 			*iSrc += 2;
5830 			break;
5831 		case '0': /* octal escape */
5832 		case '1':
5833 		case '2':
5834 		case '3':
5835 		case '4':
5836 		case '5':
5837 		case '6':
5838 		case '7':
5839 			if(    (*iSrc)+2 >= len
5840 			   || !isodigit(c[(*iSrc)+1])
5841 			   || !isodigit(c[(*iSrc)+2])) {
5842 				/* error, incomplete escape, use as is */
5843 				c[iDst] = '\\';
5844 				--(*iSrc);
5845 			}
5846 			c[iDst] = ((c[(*iSrc)  ] - '0') << 6) +
5847 			          ((c[(*iSrc)+1] - '0') << 3) +
5848 			          ( c[(*iSrc)+2] - '0');
5849 			*iSrc += 2;
5850 			break;
5851 		default:
5852 			/* error, incomplete escape, indicate by '?' */
5853 			c[iDst] = '?';
5854 			break;
5855 		}
5856 	} else {
5857 		/* regular character */
5858 		c[iDst] = c[*iSrc];
5859 	}
5860 }
5861 
5862 void
5863 unescapeStr(uchar *s, int len)
5864 {
5865 	int iSrc, iDst;
5866 	assert(s != NULL);
5867 
5868 	/* scan for first escape sequence (if we are luky, there is none!) */
5869 	iSrc = 0;
5870 	while(iSrc < len && s[iSrc] != '\\')
5871 		++iSrc;
5872 	/* now we have a sequence or end of string. In any case, we process
5873 	 * all remaining characters (maybe 0!) and unescape.
5874 	 */
5875 	if(iSrc != len) {
5876 		iDst = iSrc;
5877 		while(iSrc < len) {
5878 			doUnescape(s, len, &iSrc, iDst);
5879 			++iSrc;
5880 			++iDst;
5881 		}
5882 		s[iDst] = '\0';
5883 	}
5884 }
5885 
5886 const char *
5887 tokenval2str(const int tok)
5888 {
5889 	if(tok < 256) return "";
5890 	switch(tok) {
5891 	case NAME: return "NAME";
5892 	case FUNC: return "FUNC";
5893 	case BEGINOBJ: return "BEGINOBJ";
5894 	case ENDOBJ: return "ENDOBJ";
5895 	case BEGIN_ACTION: return "BEGIN_ACTION";
5896 	case BEGIN_PROPERTY: return "BEGIN_PROPERTY";
5897 	case BEGIN_CONSTANT: return "BEGIN_CONSTANT";
5898 	case BEGIN_TPL: return "BEGIN_TPL";
5899 	case BEGIN_INCLUDE: return "BEGIN_INCLUDE";
5900 	case BEGIN_RULESET: return "BEGIN_RULESET";
5901 	case STOP: return "STOP";
5902 	case SET: return "SET";
5903 	case UNSET: return "UNSET";
5904 	case CONTINUE: return "CONTINUE";
5905 	case CALL: return "CALL";
5906 	case LEGACY_ACTION: return "LEGACY_ACTION";
5907 	case LEGACY_RULESET: return "LEGACY_RULESET";
5908 	case PRIFILT: return "PRIFILT";
5909 	case PROPFILT: return "PROPFILT";
5910 	case BSD_TAG_SELECTOR: return "BSD_TAG_SELECTOR";
5911 	case BSD_HOST_SELECTOR: return "BSD_HOST_SELECTOR";
5912 	case IF: return "IF";
5913 	case THEN: return "THEN";
5914 	case ELSE: return "ELSE";
5915 	case OR: return "OR";
5916 	case AND: return "AND";
5917 	case NOT: return "NOT";
5918 	case VAR: return "VAR";
5919 	case STRING: return "STRING";
5920 	case NUMBER: return "NUMBER";
5921 	case CMP_EQ: return "CMP_EQ";
5922 	case CMP_NE: return "CMP_NE";
5923 	case CMP_LE: return "CMP_LE";
5924 	case CMP_GE: return "CMP_GE";
5925 	case CMP_LT: return "CMP_LT";
5926 	case CMP_GT: return "CMP_GT";
5927 	case CMP_CONTAINS: return "CMP_CONTAINS";
5928 	case CMP_CONTAINSI: return "CMP_CONTAINSI";
5929 	case CMP_STARTSWITH: return "CMP_STARTSWITH";
5930 	case CMP_STARTSWITHI: return "CMP_STARTSWITHI";
5931 	case UMINUS: return "UMINUS";
5932 	default: return "UNKNOWN TOKEN";
5933 	}
5934 }
5935