1 /*
2 * Copyright (c) 2002-2012 Balabit
3 * Copyright (c) 1998-2012 Balázs Scheidler
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * As an additional exemption you are allowed to compile & link against the
20 * OpenSSL libraries as published by the OpenSSL project. See the file
21 * COPYING for details.
22 *
23 */
24
25 #include "logmatcher.h"
26 #include "messages.h"
27 #include "cfg.h"
28 #include "str-utils.h"
29 #include "scratch-buffers.h"
30 #include "compat/string.h"
31 #include "compat/pcre.h"
32
33 static gboolean
_shall_set_values_indirectly(NVHandle value_handle)34 _shall_set_values_indirectly(NVHandle value_handle)
35 {
36 return value_handle != LM_V_NONE &&
37 !log_msg_is_handle_macro(value_handle) &&
38 !log_msg_is_handle_match(value_handle);
39 }
40
41 static void
log_matcher_store_pattern(LogMatcher * self,const gchar * pattern)42 log_matcher_store_pattern(LogMatcher *self, const gchar *pattern)
43 {
44 g_free(self->pattern);
45 self->pattern = g_strdup(pattern);
46 }
47
48 static void
log_matcher_free_method(LogMatcher * self)49 log_matcher_free_method(LogMatcher *self)
50 {
51 g_free(self->pattern);
52 }
53
54 static void
log_matcher_init(LogMatcher * self,const LogMatcherOptions * options)55 log_matcher_init(LogMatcher *self, const LogMatcherOptions *options)
56 {
57 self->ref_cnt = 1;
58 self->flags = options->flags;
59 self->free_fn = log_matcher_free_method;
60 }
61
62 typedef struct _LogMatcherString
63 {
64 LogMatcher super;
65 gint pattern_len;
66 } LogMatcherString;
67
68 static gboolean
log_matcher_string_compile(LogMatcher * s,const gchar * pattern,GError ** error)69 log_matcher_string_compile(LogMatcher *s, const gchar *pattern, GError **error)
70 {
71 LogMatcherString *self = (LogMatcherString *) s;
72
73 g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
74 log_matcher_store_pattern(s, pattern);
75
76 self->pattern_len = strlen(pattern);
77 return TRUE;
78 }
79
80 static const gchar *
log_matcher_string_match_string(LogMatcherString * self,const gchar * value,gsize value_len)81 log_matcher_string_match_string(LogMatcherString *self, const gchar *value, gsize value_len)
82 {
83 const gchar *result = NULL;
84 gboolean match = FALSE;
85 const gchar *pattern = self->super.pattern;
86
87 if (self->pattern_len > value_len)
88 return NULL;
89 if (G_LIKELY((self->super.flags & (LMF_SUBSTRING + LMF_PREFIX)) == 0))
90 {
91 if (self->super.flags & LMF_ICASE)
92 match = strncasecmp(value, pattern, value_len) == 0;
93 else
94 match = strncmp(value, pattern, value_len) == 0;
95 }
96 else if (self->super.flags & LMF_PREFIX)
97 {
98 if (self->super.flags & LMF_ICASE)
99 match = strncasecmp(value, pattern, MIN(value_len, self->pattern_len)) == 0;
100 else
101 match = strncmp(value, pattern, MIN(value_len, self->pattern_len)) == 0;
102 }
103 else if (self->super.flags & LMF_SUBSTRING)
104 {
105 if (self->super.flags & LMF_ICASE)
106 {
107 gchar *buf;
108 gchar *res;
109
110 APPEND_ZERO(buf, value, value_len);
111 res = strcasestr(buf, pattern);
112 if (res)
113 result = value + (res - buf);
114 }
115 else
116 {
117 result = g_strstr_len(value, value_len, pattern);
118 }
119 }
120
121 if (match && !result)
122 result = value;
123 return result;
124 }
125
126 static gboolean
log_matcher_string_match(LogMatcher * s,LogMessage * msg,gint value_handle,const gchar * value,gssize value_len)127 log_matcher_string_match(LogMatcher *s, LogMessage *msg, gint value_handle, const gchar *value, gssize value_len)
128 {
129 LogMatcherString *self = (LogMatcherString *) s;
130
131 return log_matcher_string_match_string(self, value, value_len) != NULL;
132 }
133
134 static gchar *
log_matcher_string_replace(LogMatcher * s,LogMessage * msg,gint value_handle,const gchar * value,gssize value_len,LogTemplate * replacement,gssize * new_length)135 log_matcher_string_replace(LogMatcher *s, LogMessage *msg, gint value_handle, const gchar *value, gssize value_len,
136 LogTemplate *replacement, gssize *new_length)
137 {
138 LogMatcherString *self = (LogMatcherString *) s;
139 GString *new_value = NULL;
140 gsize current_ofs = 0;
141 gboolean first_round = TRUE;
142
143 if (value_len < 0)
144 value_len = strlen(value);
145
146 const gchar *match;
147
148 do
149 {
150 if (current_ofs == value_len)
151 break;
152
153 match = log_matcher_string_match_string(self, value + current_ofs, value_len - current_ofs);
154
155 if (match != NULL)
156 {
157 /* start_ofs & end_ofs are relative to the original string */
158 gsize start_ofs = match - value;
159 gsize end_ofs = start_ofs + self->pattern_len;
160
161 if (start_ofs == end_ofs && !first_round)
162 {
163 start_ofs++;
164 end_ofs++;
165 }
166
167 if ((s->flags & LMF_STORE_MATCHES))
168 log_msg_clear_matches(msg);
169
170 if (!new_value)
171 new_value = g_string_sized_new(value_len);
172
173 g_string_append_len(new_value, value + current_ofs, start_ofs - current_ofs);
174 log_template_append_format(replacement, msg, &DEFAULT_TEMPLATE_EVAL_OPTIONS, new_value);
175 current_ofs = end_ofs;
176
177 if ((self->super.flags & LMF_GLOBAL) == 0)
178 {
179 g_string_append_len(new_value, value + current_ofs, value_len - current_ofs);
180 break;
181 }
182 }
183 else
184 {
185 if (new_value)
186 {
187 /* no more matches, append the end of the string */
188 g_string_append_len(new_value, value + current_ofs, value_len - current_ofs);
189 }
190 }
191 first_round = FALSE;
192 }
193 while (match && (self->super.flags & LMF_GLOBAL));
194
195 if (new_value)
196 {
197 if (new_length)
198 *new_length = new_value->len;
199 return g_string_free(new_value, FALSE);
200 }
201 return NULL;
202 }
203
204 LogMatcher *
log_matcher_string_new(const LogMatcherOptions * options)205 log_matcher_string_new(const LogMatcherOptions *options)
206 {
207 LogMatcherString *self = g_new0(LogMatcherString, 1);
208
209 log_matcher_init(&self->super, options);
210 self->super.compile = log_matcher_string_compile;
211 self->super.match = log_matcher_string_match;
212 self->super.replace = log_matcher_string_replace;
213
214 return &self->super;
215 }
216
217 typedef struct _LogMatcherGlob
218 {
219 LogMatcher super;
220 GPatternSpec *pattern;
221 } LogMatcherGlob;
222
223 static gboolean
log_matcher_glob_compile(LogMatcher * s,const gchar * pattern,GError ** error)224 log_matcher_glob_compile(LogMatcher *s, const gchar *pattern, GError **error)
225 {
226 LogMatcherGlob *self = (LogMatcherGlob *)s;
227
228 g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
229 log_matcher_store_pattern(s, pattern);
230
231 self->pattern = g_pattern_spec_new(pattern);
232 return TRUE;
233 }
234
235 /* GPattern only works with utf8 strings, if the input is not utf8, we risk
236 * a crash
237 */
238 static gboolean
log_matcher_glob_match(LogMatcher * s,LogMessage * msg,gint value_handle,const gchar * value,gssize value_len)239 log_matcher_glob_match(LogMatcher *s, LogMessage *msg, gint value_handle, const gchar *value, gssize value_len)
240 {
241 LogMatcherGlob *self = (LogMatcherGlob *) s;
242
243 if (G_LIKELY((msg->flags & LF_UTF8) || g_utf8_validate(value, value_len, NULL)))
244 {
245 static gboolean warned = FALSE;
246 gchar *buf;
247
248 if (G_UNLIKELY(!warned && (msg->flags & LF_UTF8) == 0))
249 {
250 msg_warning("Input is valid utf8, but the log message is not tagged as such, this performs worse than enabling validate-utf8 flag on input",
251 evt_tag_printf("value", "%.*s", (gint) value_len, value));
252 warned = TRUE;
253 }
254 APPEND_ZERO(buf, value, value_len);
255 return g_pattern_match(self->pattern, value_len, buf, NULL);
256 }
257 else
258 {
259 msg_warning("Input is not valid utf8, glob match requires utf8 input, thus it never matches in this case",
260 evt_tag_printf("value", "%.*s", (gint) value_len, value));
261 }
262 return FALSE;
263 }
264
265 static void
log_matcher_glob_free(LogMatcher * s)266 log_matcher_glob_free(LogMatcher *s)
267 {
268 LogMatcherGlob *self = (LogMatcherGlob *)s;
269 g_pattern_spec_free(self->pattern);
270 log_matcher_free_method(s);
271 }
272
273 LogMatcher *
log_matcher_glob_new(const LogMatcherOptions * options)274 log_matcher_glob_new(const LogMatcherOptions *options)
275 {
276 LogMatcherGlob *self = g_new0(LogMatcherGlob, 1);
277
278 log_matcher_init(&self->super, options);
279 self->super.compile = log_matcher_glob_compile;
280 self->super.match = log_matcher_glob_match;
281 self->super.replace = NULL;
282 self->super.free_fn = log_matcher_glob_free;
283
284 return &self->super;
285 }
286
287 /* libpcre support */
288
289 typedef struct _LogMatcherPcreRe
290 {
291 LogMatcher super;
292 pcre *pattern;
293 pcre_extra *extra;
294 gint match_options;
295 gchar *nv_prefix;
296 gint nv_prefix_len;
297 } LogMatcherPcreRe;
298
299 static gboolean
_compile_pcre_regexp(LogMatcherPcreRe * self,const gchar * re,GError ** error)300 _compile_pcre_regexp(LogMatcherPcreRe *self, const gchar *re, GError **error)
301 {
302 gint rc;
303 const gchar *errptr;
304 gint erroffset;
305 gint flags = 0;
306
307 g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
308
309 if (self->super.flags & LMF_ICASE)
310 flags |= PCRE_CASELESS;
311
312 if (self->super.flags & LMF_NEWLINE)
313 {
314 if (!PCRE_NEWLINE_ANYCRLF)
315 msg_warning("syslog-ng was compiled against an old PCRE which doesn't support the 'newline' flag");
316 flags |= PCRE_NEWLINE_ANYCRLF;
317 }
318 if (self->super.flags & LMF_UTF8)
319 {
320 gint support;
321 flags |= PCRE_UTF8 | PCRE_NO_UTF8_CHECK;
322 self->match_options |= PCRE_NO_UTF8_CHECK;
323
324 pcre_config(PCRE_CONFIG_UTF8, &support);
325 if (!support)
326 {
327 g_set_error(error, LOG_TEMPLATE_ERROR, 0, "PCRE library is compiled without UTF8 support and utf8 flag was present");
328 return FALSE;
329 }
330
331 pcre_config(PCRE_CONFIG_UNICODE_PROPERTIES, &support);
332 if (!support)
333 {
334 g_set_error(error, LOG_TEMPLATE_ERROR, 0,
335 "PCRE library is compiled without UTF8 properties support and utf8 flag was present");
336 return FALSE;
337 }
338 }
339 if (self->super.flags & LMF_DUPNAMES)
340 {
341 if (!PCRE_DUPNAMES)
342 msg_warning("syslog-ng was compiled against an old PCRE which doesn't support the 'dupnames' flag");
343 flags |= PCRE_DUPNAMES;
344 }
345
346 /* compile the regexp */
347 self->pattern = pcre_compile2(re, flags, &rc, &errptr, &erroffset, NULL);
348 if (!self->pattern)
349 {
350 g_set_error(error, LOG_TEMPLATE_ERROR, 0, "Failed to compile PCRE expression >>>%s<<< `%s' at character %d",
351 re, errptr, erroffset);
352 return FALSE;
353 }
354 return TRUE;
355 }
356
357 static gboolean
_study_pcre_regexp(LogMatcherPcreRe * self,const gchar * re,GError ** error)358 _study_pcre_regexp(LogMatcherPcreRe *self, const gchar *re, GError **error)
359 {
360 const gchar *errptr;
361 gint options = 0;
362
363 if ((self->super.flags & LMF_DISABLE_JIT) == 0)
364 options |= PCRE_STUDY_JIT_COMPILE;
365
366 /* optimize regexp */
367 self->extra = pcre_study(self->pattern, options, &errptr);
368 if (errptr != NULL)
369 {
370 g_set_error(error, LOG_TEMPLATE_ERROR, 0, "Failed to optimize regular expression >>>%s<<< `%s'",
371 re, errptr);
372 return FALSE;
373 }
374 return TRUE;
375 }
376
377 static gboolean
log_matcher_pcre_re_compile(LogMatcher * s,const gchar * re,GError ** error)378 log_matcher_pcre_re_compile(LogMatcher *s, const gchar *re, GError **error)
379 {
380 LogMatcherPcreRe *self = (LogMatcherPcreRe *) s;
381
382 g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
383 log_matcher_store_pattern(s, re);
384
385 if (!_compile_pcre_regexp(self, re, error))
386 return FALSE;
387
388 if (!_study_pcre_regexp(self, re, error))
389 return FALSE;
390
391 return TRUE;
392 }
393
394 static void
log_matcher_pcre_re_feed_backrefs(LogMatcher * s,LogMessage * msg,gint value_handle,int * matches,gint match_num,const gchar * value)395 log_matcher_pcre_re_feed_backrefs(LogMatcher *s, LogMessage *msg, gint value_handle, int *matches, gint match_num,
396 const gchar *value)
397 {
398 gint i;
399 gboolean indirect = _shall_set_values_indirectly(value_handle);
400
401 for (i = 0; i < (RE_MAX_MATCHES) && i < match_num; i++)
402 {
403 gint begin_index = matches[2 * i];
404 gint end_index = matches[2 * i + 1];
405
406 if (begin_index < 0 || end_index < 0)
407 continue;
408
409 if (indirect)
410 {
411 log_msg_set_match_indirect(msg, i, value_handle, 0, begin_index, end_index - begin_index);
412 }
413 else
414 {
415 log_msg_set_match(msg, i, &value[begin_index], end_index - begin_index);
416 }
417 }
418 }
419
420 static inline void
log_matcher_pcre_re_feed_value_by_name(LogMatcherPcreRe * s,LogMessage * msg,GString * formatted_name,gchar * tabptr,const gchar * value,gint begin_index,gint end_index)421 log_matcher_pcre_re_feed_value_by_name(LogMatcherPcreRe *s, LogMessage *msg, GString *formatted_name, gchar *tabptr,
422 const gchar *value, gint begin_index, gint end_index)
423 {
424 if(s->nv_prefix != NULL)
425 {
426 if (formatted_name->len > 0)
427 g_string_truncate(formatted_name, s->nv_prefix_len);
428 else
429 g_string_assign(formatted_name, s->nv_prefix);
430 g_string_append(formatted_name, tabptr + 2);
431
432 log_msg_set_value_by_name(msg, formatted_name->str, value + begin_index, end_index - begin_index);
433 }
434 else
435 {
436 log_msg_set_value_by_name(msg, tabptr + 2, value + begin_index, end_index - begin_index);
437 }
438 }
439
440 static void
log_matcher_pcre_re_feed_named_substrings(LogMatcher * s,LogMessage * msg,int * matches,const gchar * value)441 log_matcher_pcre_re_feed_named_substrings(LogMatcher *s, LogMessage *msg, int *matches, const gchar *value)
442 {
443 gchar *name_table = NULL;
444 gint i = 0;
445 gint namecount = 0;
446 gint name_entry_size = 0;
447 LogMatcherPcreRe *self = (LogMatcherPcreRe *) s;
448
449 pcre_fullinfo(self->pattern, self->extra, PCRE_INFO_NAMECOUNT, &namecount);
450 if (namecount > 0)
451 {
452 gchar *tabptr;
453 /* Before we can access the substrings, we must extract the table for
454 translating names to numbers, and the size of each entry in the table.
455 */
456 pcre_fullinfo(self->pattern, self->extra, PCRE_INFO_NAMETABLE, &name_table);
457 pcre_fullinfo(self->pattern, self->extra, PCRE_INFO_NAMEENTRYSIZE, &name_entry_size);
458 /* Now we can scan the table and, for each entry, print the number, the name,
459 and the substring itself.
460 */
461 GString *formatted_name = scratch_buffers_alloc();
462 tabptr = name_table;
463 for (i = 0; i < namecount; i++, tabptr += name_entry_size)
464 {
465 int n = (tabptr[0] << 8) | tabptr[1];
466 gint begin_index = matches[2 * n];
467 gint end_index = matches[2 * n + 1];
468
469 if (begin_index < 0 || end_index < 0)
470 continue;
471
472 log_matcher_pcre_re_feed_value_by_name(self, msg, formatted_name, tabptr, value, begin_index, end_index);
473 }
474 }
475 }
476
477 static gboolean
log_matcher_pcre_re_match(LogMatcher * s,LogMessage * msg,gint value_handle,const gchar * value,gssize value_len)478 log_matcher_pcre_re_match(LogMatcher *s, LogMessage *msg, gint value_handle, const gchar *value, gssize value_len)
479 {
480 LogMatcherPcreRe *self = (LogMatcherPcreRe *) s;
481 gint *matches;
482 gsize matches_size;
483 gint num_matches;
484 gint rc;
485
486 if (value_len == -1)
487 value_len = strlen(value);
488
489 if (pcre_fullinfo(self->pattern, self->extra, PCRE_INFO_CAPTURECOUNT, &num_matches) < 0)
490 g_assert_not_reached();
491 if (num_matches > RE_MAX_MATCHES)
492 num_matches = RE_MAX_MATCHES;
493
494 matches_size = 3 * (num_matches + 1);
495 matches = g_alloca(matches_size * sizeof(gint));
496
497 rc = pcre_exec(self->pattern, self->extra,
498 value, value_len, 0, self->match_options, matches, matches_size);
499 if (rc < 0)
500 {
501 switch (rc)
502 {
503 case PCRE_ERROR_NOMATCH:
504 break;
505
506 default:
507 /* Handle other special cases */
508 msg_error("Error while matching regexp",
509 evt_tag_int("error_code", rc));
510 break;
511 }
512 return FALSE;
513 }
514 if (rc == 0)
515 {
516 msg_error("Error while storing matching substrings");
517 }
518 else
519 {
520 if ((s->flags & LMF_STORE_MATCHES))
521 {
522 log_matcher_pcre_re_feed_backrefs(s, msg, value_handle, matches, rc, value);
523 log_matcher_pcre_re_feed_named_substrings(s, msg, matches, value);
524 }
525 }
526 return TRUE;
527 }
528
529 static gchar *
log_matcher_pcre_re_replace(LogMatcher * s,LogMessage * msg,gint value_handle,const gchar * value,gssize value_len,LogTemplate * replacement,gssize * new_length)530 log_matcher_pcre_re_replace(LogMatcher *s, LogMessage *msg, gint value_handle, const gchar *value, gssize value_len,
531 LogTemplate *replacement, gssize *new_length)
532 {
533 LogMatcherPcreRe *self = (LogMatcherPcreRe *) s;
534 GString *new_value = NULL;
535 gint *matches;
536 gsize matches_size;
537 gint num_matches;
538 gint rc;
539 gint start_offset, last_offset;
540 gint options;
541 gboolean last_match_was_empty;
542
543 if (pcre_fullinfo(self->pattern, self->extra, PCRE_INFO_CAPTURECOUNT, &num_matches) < 0)
544 g_assert_not_reached();
545 if (num_matches > RE_MAX_MATCHES)
546 num_matches = RE_MAX_MATCHES;
547
548 matches_size = 3 * (num_matches + 1);
549 matches = g_alloca(matches_size * sizeof(gint));
550
551 /* we need zero initialized offsets for the last match as the
552 * algorithm tries uses that as the base position */
553
554 matches[0] = matches[1] = matches[2] = 0;
555
556 if (value_len == -1)
557 value_len = strlen(value);
558
559 last_offset = start_offset = 0;
560 last_match_was_empty = FALSE;
561 do
562 {
563 /* loop over the string, replacing one occurrence at a time. */
564
565 /* NOTE: zero length matches need special care, as we could spin
566 * forever otherwise (since the current position wouldn't be
567 * advanced).
568 *
569 * A zero-length match can be as simple as "a*" which will be
570 * returned unless PCRE_NOTEMPTY is specified.
571 *
572 * By supporting zero-length matches, we basically make it
573 * possible to insert replacement between each incoming
574 * character.
575 *
576 * For example:
577 * pattern: a*
578 * replacement: #
579 * input: message
580 * result: #m#e#s#s#a#g#e#
581 *
582 * This mimics Perl behaviour.
583 */
584
585 if (last_match_was_empty)
586 {
587 /* Otherwise, arrange to run another match at the same point
588 * to see if a non-empty match can be found.
589 */
590
591 options = PCRE_NOTEMPTY | PCRE_ANCHORED;
592 }
593 else
594 {
595 options = 0;
596 }
597
598 rc = pcre_exec(self->pattern, self->extra,
599 value, value_len,
600 start_offset, (self->match_options | options), matches, matches_size);
601 if (rc < 0 && rc != PCRE_ERROR_NOMATCH)
602 {
603 msg_error("Error while matching regexp",
604 evt_tag_int("error_code", rc));
605 break;
606 }
607 else if (rc < 0)
608 {
609 if ((options & PCRE_NOTEMPTY) == 0)
610 {
611 /* we didn't match, even when we permitted to match the
612 * empty string. Nothing to find here, bail out */
613 break;
614 }
615
616 /* we didn't match, quite possibly because the empty match
617 * was not permitted. Skip one character in order to avoid
618 * infinite loop over the same zero-length match. */
619
620 start_offset = start_offset + 1;
621 /* FIXME: handle complex sequences like utf8 and newline characters */
622 last_match_was_empty = FALSE;
623 continue;
624 }
625 else
626 {
627 /* if the output array was too small, truncate the number of
628 captures to RE_MAX_MATCHES */
629
630 if (rc == 0)
631 rc = matches_size / 3;
632
633 log_matcher_pcre_re_feed_backrefs(s, msg, value_handle, matches, rc, value);
634 log_matcher_pcre_re_feed_named_substrings(s, msg, matches, value);
635
636 if (!new_value)
637 new_value = g_string_sized_new(value_len);
638 /* append non-matching portion */
639 g_string_append_len(new_value, &value[last_offset], matches[0] - last_offset);
640 /* replacement */
641 log_template_append_format(replacement, msg, &DEFAULT_TEMPLATE_EVAL_OPTIONS, new_value);
642
643 last_match_was_empty = (matches[0] == matches[1]);
644 start_offset = last_offset = matches[1];
645 }
646 }
647 while (self->super.flags & LMF_GLOBAL && start_offset < value_len);
648
649 if (new_value)
650 {
651 /* append the last literal */
652 g_string_append_len(new_value, &value[last_offset], value_len - last_offset);
653 if (new_length)
654 *new_length = new_value->len;
655 return g_string_free(new_value, FALSE);
656 }
657 return NULL;
658 }
659
660 static void
log_matcher_pcre_re_free(LogMatcher * s)661 log_matcher_pcre_re_free(LogMatcher *s)
662 {
663 LogMatcherPcreRe *self = (LogMatcherPcreRe *) s;
664 pcre_free_study(self->extra);
665 pcre_free(self->pattern);
666 log_matcher_free_method(s);
667 }
668
669 LogMatcher *
log_matcher_pcre_re_new(const LogMatcherOptions * options)670 log_matcher_pcre_re_new(const LogMatcherOptions *options)
671 {
672 LogMatcherPcreRe *self = g_new0(LogMatcherPcreRe, 1);
673 self->nv_prefix = NULL;
674 self->nv_prefix_len = 0;
675
676 log_matcher_init(&self->super, options);
677 self->super.compile = log_matcher_pcre_re_compile;
678 self->super.match = log_matcher_pcre_re_match;
679 self->super.replace = log_matcher_pcre_re_replace;
680 self->super.free_fn = log_matcher_pcre_re_free;
681
682 return &self->super;
683 }
684
685 void
log_matcher_pcre_set_nv_prefix(LogMatcher * s,const gchar * prefix)686 log_matcher_pcre_set_nv_prefix(LogMatcher *s, const gchar *prefix)
687 {
688 LogMatcherPcreRe *self = (LogMatcherPcreRe *) s;
689
690 g_free(self->nv_prefix);
691 if (prefix)
692 {
693 self->nv_prefix = g_strdup(prefix);
694 self->nv_prefix_len = strlen(prefix);
695 }
696 else
697 {
698 self->nv_prefix = NULL;
699 self->nv_prefix_len = 0;
700 }
701 }
702
703 typedef LogMatcher *(*LogMatcherConstructFunc)(const LogMatcherOptions *options);
704
705 struct
706 {
707 const gchar *name;
708 LogMatcherConstructFunc construct;
709 } matcher_types[] =
710 {
711 { "pcre", log_matcher_pcre_re_new },
712 { "string", log_matcher_string_new },
713 { "glob", log_matcher_glob_new },
714 { NULL, NULL },
715 };
716
717 static LogMatcherConstructFunc
log_matcher_lookup_construct(const gchar * type)718 log_matcher_lookup_construct(const gchar *type)
719 {
720 gint i;
721
722 for (i = 0; matcher_types[i].name; i++)
723 {
724 if (strcmp(matcher_types[i].name, type) == 0)
725 return matcher_types[i].construct;
726 }
727 return NULL;
728 }
729
730 LogMatcher *
log_matcher_new(const LogMatcherOptions * options)731 log_matcher_new(const LogMatcherOptions *options)
732 {
733 LogMatcherConstructFunc construct;
734
735 construct = log_matcher_lookup_construct(options->type);
736 return construct(options);
737 }
738
739 LogMatcher *
log_matcher_ref(LogMatcher * s)740 log_matcher_ref(LogMatcher *s)
741 {
742 s->ref_cnt++;
743 return s;
744 }
745
746 void
log_matcher_unref(LogMatcher * s)747 log_matcher_unref(LogMatcher *s)
748 {
749 if (--s->ref_cnt == 0)
750 {
751 if (s->free_fn)
752 s->free_fn(s);
753 g_free(s);
754 }
755 }
756
757 gboolean
log_matcher_options_set_type(LogMatcherOptions * options,const gchar * type)758 log_matcher_options_set_type(LogMatcherOptions *options, const gchar *type)
759 {
760 LogMatcherConstructFunc construct;
761
762 if (strcmp(type, "posix") == 0)
763 {
764 msg_warning_once("WARNING: syslog-ng dropped support for POSIX regexp implementations in " VERSION_3_14
765 " in favour of PCRE, which should be upward compatible. All 'posix' regexps are "
766 "automatically switched to 'pcre'. Please ensure that your regexps work with PCRE and "
767 "specify type('pcre') explicitly or increase @version to remove this warning");
768 type = "pcre";
769 }
770
771 construct = log_matcher_lookup_construct(type);
772 if (!construct)
773 return FALSE;
774
775 if (options->type)
776 g_free(options->type);
777 options->type = g_strdup(type);
778 return TRUE;
779 }
780
781 CfgFlagHandler log_matcher_flag_handlers[] =
782 {
783 /* NOTE: underscores are automatically converted to dashes */
784
785 { "global", CFH_SET, offsetof(LogMatcherOptions, flags), LMF_GLOBAL },
786 { "icase", CFH_SET, offsetof(LogMatcherOptions, flags), LMF_ICASE },
787 { "ignore-case", CFH_SET, offsetof(LogMatcherOptions, flags), LMF_ICASE },
788 { "newline", CFH_SET, offsetof(LogMatcherOptions, flags), LMF_NEWLINE },
789 { "unicode", CFH_SET, offsetof(LogMatcherOptions, flags), LMF_UTF8 },
790 { "utf8", CFH_SET, offsetof(LogMatcherOptions, flags), LMF_UTF8 },
791 { "store-matches", CFH_SET, offsetof(LogMatcherOptions, flags), LMF_STORE_MATCHES },
792 { "substring", CFH_SET, offsetof(LogMatcherOptions, flags), LMF_SUBSTRING },
793 { "prefix", CFH_SET, offsetof(LogMatcherOptions, flags), LMF_PREFIX },
794 { "disable-jit", CFH_SET, offsetof(LogMatcherOptions, flags), LMF_DISABLE_JIT },
795 { "dupnames", CFH_SET, offsetof(LogMatcherOptions, flags), LMF_DUPNAMES },
796
797 { NULL },
798 };
799
800 gboolean
log_matcher_options_process_flag(LogMatcherOptions * self,const gchar * flag)801 log_matcher_options_process_flag(LogMatcherOptions *self, const gchar *flag)
802 {
803 return cfg_process_flag(log_matcher_flag_handlers, self, flag);
804 }
805
806 void
log_matcher_options_defaults(LogMatcherOptions * options)807 log_matcher_options_defaults(LogMatcherOptions *options)
808 {
809 options->flags = 0;
810 options->type = NULL;
811 }
812
813 void
log_matcher_options_init(LogMatcherOptions * options)814 log_matcher_options_init(LogMatcherOptions *options)
815 {
816 if (!options->type)
817 {
818 const gchar *default_matcher = "pcre";
819
820 if (!log_matcher_options_set_type(options, default_matcher))
821 g_assert_not_reached();
822 }
823 }
824
825 void
log_matcher_options_destroy(LogMatcherOptions * options)826 log_matcher_options_destroy(LogMatcherOptions *options)
827 {
828 if (options->type)
829 g_free(options->type);
830 }
831
832 GQuark
log_matcher_error_quark(void)833 log_matcher_error_quark(void)
834 {
835 return g_quark_from_static_string("log-matcher-error-quark");
836 }
837