1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 /***************************************************************************
25 LogFilter.cc
26
27
28 ***************************************************************************/
29 #include "tscore/ink_platform.h"
30
31 #include "LogUtils.h"
32 #include "LogFilter.h"
33 #include "LogField.h"
34 #include "LogFormat.h"
35 #include "LogFile.h"
36 #include "LogBuffer.h"
37 #include "LogObject.h"
38 #include "LogConfig.h"
39 #include "Log.h"
40 #include "tscore/SimpleTokenizer.h"
41
42 const char *LogFilter::OPERATOR_NAME[] = {"MATCH", "CASE_INSENSITIVE_MATCH", "CONTAIN", "CASE_INSENSITIVE_CONTAIN"};
43 const char *LogFilter::ACTION_NAME[] = {"REJECT", "ACCEPT", "WIPE_FIELD_VALUE"};
44
45 /*-------------------------------------------------------------------------
46 LogFilter::LogFilter
47
48 note: it may be convenient to have the LogFilter constructor access the
49 global_field_list to get the log field, but this is an unnecessary dependency
50 between the classes and I think should be removed. ltavera
51 -------------------------------------------------------------------------*/
LogFilter(const char * name,LogField * field,LogFilter::Action action,LogFilter::Operator oper)52 LogFilter::LogFilter(const char *name, LogField *field, LogFilter::Action action, LogFilter::Operator oper)
53 : m_name(ats_strdup(name)), m_field(nullptr), m_action(action), m_operator(oper), m_type(INT_FILTER), m_num_values(0)
54 {
55 m_field = new LogField(*field);
56 ink_assert(m_field);
57 }
58
59 /*-------------------------------------------------------------------------
60 LogFilter::~LogFilter
61 -------------------------------------------------------------------------*/
~LogFilter()62 LogFilter::~LogFilter()
63 {
64 ats_free(m_name);
65 delete m_field;
66 }
67
68 LogFilter *
parse(const char * name,Action action,const char * condition)69 LogFilter::parse(const char *name, Action action, const char *condition)
70 {
71 SimpleTokenizer tok(condition);
72 ats_scoped_obj<LogField> logfield;
73
74 ink_release_assert(action != N_ACTIONS);
75
76 if (tok.getNumTokensRemaining() < 3) {
77 Error("Invalid condition syntax '%s'; cannot create filter '%s'", condition, name);
78 return nullptr;
79 }
80
81 char *field_str = tok.getNext();
82 char *oper_str = tok.getNext();
83 char *val_str = tok.getRest();
84
85 // validate field symbol
86 if (strlen(field_str) > 2 && field_str[0] == '%' && field_str[1] == '<') {
87 Debug("log", "Field symbol has <> form: %s", field_str);
88 char *end = field_str;
89 while (*end && *end != '>') {
90 end++;
91 }
92 *end = '\0';
93 field_str += 2;
94 Debug("log", "... now field symbol is %s", field_str);
95 }
96
97 if (LogField *f = Log::global_field_list.find_by_symbol(field_str)) {
98 logfield = new LogField(*f);
99 }
100
101 if (!logfield) {
102 // check for container fields
103 if (*field_str == '{') {
104 Debug("log", "%s appears to be a container field", field_str);
105
106 char *fname;
107 char *cname;
108 char *fname_end;
109
110 fname_end = strchr(field_str, '}');
111 if (nullptr == fname_end) {
112 Error("Invalid container field specification: no trailing '}' in '%s' cannot create filter '%s'", field_str, name);
113 return nullptr;
114 }
115
116 fname = field_str + 1;
117 *fname_end = 0; // changes '}' to '\0'
118
119 // start of container symbol
120 cname = fname_end + 1;
121
122 Debug("log", "found container field: Name = %s, symbol = %s", fname, cname);
123
124 LogField::Container container = LogField::valid_container_name(cname);
125 if (container == LogField::NO_CONTAINER) {
126 Error("'%s' is not a valid container; cannot create filter '%s'", cname, name);
127 return nullptr;
128 }
129
130 logfield = new LogField(fname, container);
131 ink_assert(logfield != nullptr);
132 }
133 }
134
135 if (!logfield) {
136 Error("'%s' is not a valid field; cannot create filter '%s'", field_str, name);
137 return nullptr;
138 }
139
140 // convert the operator string to an enum value and validate it
141 LogFilter::Operator oper = LogFilter::N_OPERATORS;
142 for (unsigned i = 0; i < LogFilter::N_OPERATORS; ++i) {
143 if (strcasecmp(oper_str, LogFilter::OPERATOR_NAME[i]) == 0) {
144 oper = static_cast<LogFilter::Operator>(i);
145 break;
146 }
147 }
148
149 if (oper == LogFilter::N_OPERATORS) {
150 Error("'%s' is not a valid operator; cannot create filter '%s'", oper_str, name);
151 return nullptr;
152 }
153
154 // now create the correct LogFilter
155 LogField::Type field_type = logfield->type();
156 LogFilter *filter;
157
158 switch (field_type) {
159 case LogField::sINT:
160 filter = new LogFilterInt(name, logfield, action, oper, val_str);
161 break;
162
163 case LogField::dINT:
164 Error("Invalid field type (double int); cannot create filter '%s'", name);
165 return nullptr;
166
167 case LogField::STRING:
168 filter = new LogFilterString(name, logfield, action, oper, val_str);
169 break;
170
171 case LogField::IP:
172 filter = new LogFilterIP(name, logfield, action, oper, val_str);
173 break;
174
175 default:
176 Error("Unknown logging field type %d; cannot create filter '%s'", field_type, name);
177 return nullptr;
178 }
179
180 if (filter->get_num_values() == 0) {
181 Error("'%s' does not specify any valid values; cannot create filter '%s'", val_str, name);
182 delete filter;
183 return nullptr;
184 }
185
186 return filter;
187 }
188
189 /*-------------------------------------------------------------------------
190 LogFilterString::LogFilterString
191 -------------------------------------------------------------------------*/
192 void
_setValues(size_t n,char ** value)193 LogFilterString::_setValues(size_t n, char **value)
194 {
195 m_type = STRING_FILTER;
196 m_num_values = n;
197 if (n) {
198 m_value = new char *[n];
199 m_value_uppercase = new char *[n];
200 m_length = new size_t[n];
201 ink_assert(m_value && m_value_uppercase && m_length);
202 for (size_t i = 0; i < n; ++i) {
203 m_value[i] = ats_strdup(value[i]);
204 m_length[i] = strlen(value[i]);
205 m_value_uppercase[i] = static_cast<char *>(ats_malloc(static_cast<unsigned int>(m_length[i]) + 1));
206 size_t j;
207 for (j = 0; j < m_length[i]; ++j) {
208 m_value_uppercase[i][j] = ParseRules::ink_toupper(m_value[i][j]);
209 }
210 m_value_uppercase[i][j] = 0;
211 }
212 }
213 }
214
LogFilterString(const char * name,LogField * field,LogFilter::Action action,LogFilter::Operator oper,char * values)215 LogFilterString::LogFilterString(const char *name, LogField *field, LogFilter::Action action, LogFilter::Operator oper,
216 char *values)
217 : LogFilter(name, field, action, oper)
218 {
219 // parse the comma-separated list of values and construct array
220 //
221 char **val_array = nullptr;
222 size_t i = 0;
223 SimpleTokenizer tok(values, ',');
224 size_t n = tok.getNumTokensRemaining();
225 if (n) {
226 val_array = new char *[n];
227 char *t;
228 while (t = tok.getNext(), t != nullptr) {
229 val_array[i++] = t;
230 }
231 if (i < n) {
232 Warning("There were invalid values in the definition of filter %s"
233 "only %zu out of %zu values will be used",
234 name, i, n);
235 }
236 }
237 _setValues(i, val_array);
238 delete[] val_array;
239 }
240
LogFilterString(const char * name,LogField * field,LogFilter::Action action,LogFilter::Operator oper,size_t num_values,char ** value)241 LogFilterString::LogFilterString(const char *name, LogField *field, LogFilter::Action action, LogFilter::Operator oper,
242 size_t num_values, char **value)
243 : LogFilter(name, field, action, oper)
244 {
245 _setValues(num_values, value);
246 }
247
LogFilterString(const LogFilterString & rhs)248 LogFilterString::LogFilterString(const LogFilterString &rhs) : LogFilter(rhs.m_name, rhs.m_field, rhs.m_action, rhs.m_operator)
249 {
250 _setValues(rhs.m_num_values, rhs.m_value);
251 }
252
253 /*-------------------------------------------------------------------------
254 LogFilterString::~LogFilterString
255 -------------------------------------------------------------------------*/
256
~LogFilterString()257 LogFilterString::~LogFilterString()
258 {
259 if (m_num_values > 0) {
260 for (size_t i = 0; i < m_num_values; ++i) {
261 ats_free(m_value[i]);
262 ats_free(m_value_uppercase[i]);
263 }
264 delete[] m_value;
265 delete[] m_value_uppercase;
266 delete[] m_length;
267 }
268 }
269
270 /*-------------------------------------------------------------------------
271 LogFilterString::operator==
272
273 This operator is not very intelligent and expects the objects being
274 compared to have the same values specified *in the same order*.
275 Filters with the same values specified in different order are considered
276 to be different.
277
278 -------------------------------------------------------------------------*/
279
280 bool
operator ==(LogFilterString & rhs)281 LogFilterString::operator==(LogFilterString &rhs)
282 {
283 if (m_type == rhs.m_type && *m_field == *rhs.m_field && m_action == rhs.m_action && m_operator == rhs.m_operator &&
284 m_num_values == rhs.m_num_values) {
285 for (size_t i = 0; i < m_num_values; i++) {
286 if (m_length[i] != rhs.m_length[i] || strncmp(m_value[i], rhs.m_value[i], m_length[i])) {
287 return false;
288 }
289 }
290 return true;
291 }
292 return false;
293 }
294
295 /*-------------------------------------------------------------------------
296 LogFilterString::wipe_this_entry
297
298 For strings, we need to marshal the given string into a buffer so that we
299 can compare it with the filter value. Most strings are snall, so we'll
300 only allocate space dynamically if the marshal_len is very large (eg,
301 URL).
302
303 The m_substr field tells us whether we can match based on substrings, or
304 whether we should compare the entire string.
305 -------------------------------------------------------------------------*/
306
307 bool
wipe_this_entry(LogAccess * lad)308 LogFilterString::wipe_this_entry(LogAccess *lad)
309 {
310 if (m_num_values == 0 || m_field == nullptr || lad == nullptr || m_action != WIPE_FIELD_VALUE) {
311 return false;
312 }
313
314 static const unsigned BUFSIZE = 1024;
315 char small_buf[BUFSIZE];
316 char small_buf_upper[BUFSIZE];
317 char *big_buf = nullptr;
318 char *big_buf_upper = nullptr;
319 char *buf = small_buf;
320 char *buf_upper = small_buf_upper;
321 size_t marsh_len = m_field->marshal_len(lad); // includes null termination
322
323 if (marsh_len > BUFSIZE) {
324 big_buf = static_cast<char *>(ats_malloc(marsh_len));
325 ink_assert(big_buf != nullptr);
326 buf = big_buf;
327 }
328
329 ink_assert(buf != nullptr);
330 m_field->marshal(lad, buf);
331
332 ink_assert(buf != nullptr);
333
334 bool cond_satisfied = false;
335 switch (m_operator) {
336 case MATCH:
337 // marsh_len is an upper bound on the length of the marshalled string
338 // because marsh_len counts padding and the eos. So for a MATCH
339 // operator, we use the DATA_LENGTH_LARGER length condition rather
340 // than DATA_LENGTH_EQUAL, which we would use if we had the actual
341 // length of the string. It is probably not worth computing the
342 // actual length, so we just use the fact that a MATCH is not possible
343 // when marsh_len <= (length of the filter string)
344 //
345 cond_satisfied = _checkConditionAndWipe(&strcmp, &buf, marsh_len, m_value, nullptr, DATA_LENGTH_LARGER);
346 break;
347 case CASE_INSENSITIVE_MATCH:
348 cond_satisfied = _checkConditionAndWipe(&strcasecmp, &buf, marsh_len, m_value, nullptr, DATA_LENGTH_LARGER);
349 break;
350 case CONTAIN:
351 cond_satisfied = _checkConditionAndWipe(&_isSubstring, &buf, marsh_len, m_value, nullptr, DATA_LENGTH_LARGER);
352 break;
353 case CASE_INSENSITIVE_CONTAIN:
354 if (big_buf) {
355 big_buf_upper = static_cast<char *>(ats_malloc(static_cast<unsigned int>(marsh_len)));
356 buf_upper = big_buf_upper;
357 } else {
358 buf = small_buf; // make clang happy
359 }
360 for (size_t i = 0; i < marsh_len; i++) {
361 buf_upper[i] = ParseRules::ink_toupper(buf[i]);
362 }
363 cond_satisfied = _checkConditionAndWipe(&_isSubstring, &buf, marsh_len, m_value_uppercase, buf_upper, DATA_LENGTH_LARGER);
364 break;
365 default:
366 ink_assert(!"INVALID FILTER OPERATOR");
367 }
368
369 if (cond_satisfied) {
370 m_field->updateField(lad, buf, strlen(buf));
371 }
372
373 ats_free(big_buf);
374 ats_free(big_buf_upper);
375 return cond_satisfied;
376 }
377
378 /*-------------------------------------------------------------------------
379 LogFilterString::toss_this_entry
380
381 For strings, we need to marshal the given string into a buffer so that we
382 can compare it with the filter value. Most strings are snall, so we'll
383 only allocate space dynamically if the marshal_len is very large (eg,
384 URL).
385
386 The m_substr field tells us whether we can match based on substrings, or
387 whether we should compare the entire string.
388 -------------------------------------------------------------------------*/
389
390 bool
toss_this_entry(LogAccess * lad)391 LogFilterString::toss_this_entry(LogAccess *lad)
392 {
393 if (m_num_values == 0 || m_field == nullptr || lad == nullptr) {
394 return false;
395 }
396
397 static const unsigned BUFSIZE = 1024;
398 char small_buf[BUFSIZE];
399 char small_buf_upper[BUFSIZE];
400 char *big_buf = nullptr;
401 char *big_buf_upper = nullptr;
402 char *buf = small_buf;
403 char *buf_upper = small_buf_upper;
404 size_t marsh_len = m_field->marshal_len(lad); // includes null termination
405
406 if (marsh_len > BUFSIZE) {
407 big_buf = static_cast<char *>(ats_malloc(static_cast<unsigned int>(marsh_len)));
408 ink_assert(big_buf != nullptr);
409 buf = big_buf;
410 }
411
412 m_field->marshal(lad, buf);
413
414 bool cond_satisfied = false;
415 switch (m_operator) {
416 case MATCH:
417 // marsh_len is an upper bound on the length of the marshalled string
418 // because marsh_len counts padding and the eos. So for a MATCH
419 // operator, we use the DATA_LENGTH_LARGER length condition rather
420 // than DATA_LENGTH_EQUAL, which we would use if we had the actual
421 // length of the string. It is probably not worth computing the
422 // actual length, so we just use the fact that a MATCH is not possible
423 // when marsh_len <= (length of the filter string)
424 //
425 cond_satisfied = _checkCondition(&strcmp, buf, marsh_len, m_value, DATA_LENGTH_LARGER);
426 break;
427 case CASE_INSENSITIVE_MATCH:
428 cond_satisfied = _checkCondition(&strcasecmp, buf, marsh_len, m_value, DATA_LENGTH_LARGER);
429 break;
430 case CONTAIN:
431 cond_satisfied = _checkCondition(&_isSubstring, buf, marsh_len, m_value, DATA_LENGTH_LARGER);
432 break;
433 case CASE_INSENSITIVE_CONTAIN: {
434 if (big_buf) {
435 big_buf_upper = static_cast<char *>(ats_malloc(static_cast<unsigned int>(marsh_len)));
436 buf_upper = big_buf_upper;
437 } else {
438 buf = small_buf; // make clang happy
439 }
440 for (size_t i = 0; i < marsh_len; i++) {
441 buf_upper[i] = ParseRules::ink_toupper(buf[i]);
442 }
443 cond_satisfied = _checkCondition(&_isSubstring, buf_upper, marsh_len, m_value_uppercase, DATA_LENGTH_LARGER);
444 break;
445 }
446 default:
447 ink_assert(!"INVALID FILTER OPERATOR");
448 }
449
450 ats_free(big_buf);
451 ats_free(big_buf_upper);
452
453 return ((m_action == REJECT && cond_satisfied) || (m_action == ACCEPT && !cond_satisfied));
454 }
455
456 /*-------------------------------------------------------------------------
457 LogFilterString::display
458 -------------------------------------------------------------------------*/
459
460 void
display(FILE * fd)461 LogFilterString::display(FILE *fd)
462 {
463 ink_assert(fd != nullptr);
464 if (m_num_values == 0) {
465 fprintf(fd, "Filter \"%s\" is inactive, no values specified\n", m_name);
466 } else {
467 fprintf(fd, "Filter \"%s\" %sS records if %s %s ", m_name, ACTION_NAME[m_action], m_field->symbol(), OPERATOR_NAME[m_operator]);
468 fprintf(fd, "%s", m_value[0]);
469 for (size_t i = 1; i < m_num_values; ++i) {
470 fprintf(fd, ", %s", m_value[i]);
471 }
472 fprintf(fd, "\n");
473 }
474 }
475
476 /*-------------------------------------------------------------------------
477 LogFilterInt::LogFilterInt
478 -------------------------------------------------------------------------*/
479
480 void
_setValues(size_t n,int64_t * value)481 LogFilterInt::_setValues(size_t n, int64_t *value)
482 {
483 m_type = INT_FILTER;
484 m_num_values = n;
485 if (n) {
486 m_value = new int64_t[n];
487 memcpy(m_value, value, n * sizeof(int64_t));
488 }
489 }
490
491 // TODO: ival should be int64_t
492 int
_convertStringToInt(char * value,int64_t * ival,LogFieldAliasMap * map)493 LogFilterInt::_convertStringToInt(char *value, int64_t *ival, LogFieldAliasMap *map)
494 {
495 size_t i, l = strlen(value);
496 for (i = 0; i < l && ParseRules::is_digit(value[i]); i++) {
497 ;
498 }
499
500 if (i < l) {
501 // not all characters of value are digits, assume that
502 // value is an alias and try to get the actual integer value
503 // from the log field alias map if field has one
504 //
505 if (map == nullptr || map->asInt(value, ival) != LogFieldAliasMap::ALL_OK) {
506 return -1; // error
507 };
508 } else {
509 // all characters of value are digits, simply convert
510 // the string to int
511 //
512 *ival = ink_atoui(value);
513 }
514 return 0; // all OK
515 }
516
LogFilterInt(const char * name,LogField * field,LogFilter::Action action,LogFilter::Operator oper,int64_t value)517 LogFilterInt::LogFilterInt(const char *name, LogField *field, LogFilter::Action action, LogFilter::Operator oper, int64_t value)
518 : LogFilter(name, field, action, oper)
519 {
520 int64_t v[1];
521 v[0] = value;
522 _setValues(1, v);
523 }
524
LogFilterInt(const char * name,LogField * field,LogFilter::Action action,LogFilter::Operator oper,size_t num_values,int64_t * value)525 LogFilterInt::LogFilterInt(const char *name, LogField *field, LogFilter::Action action, LogFilter::Operator oper, size_t num_values,
526 int64_t *value)
527 : LogFilter(name, field, action, oper)
528 {
529 _setValues(num_values, value);
530 }
531
LogFilterInt(const char * name,LogField * field,LogFilter::Action action,LogFilter::Operator oper,char * values)532 LogFilterInt::LogFilterInt(const char *name, LogField *field, LogFilter::Action action, LogFilter::Operator oper, char *values)
533 : LogFilter(name, field, action, oper)
534 {
535 // parse the comma-separated list of values and construct array
536 //
537 int64_t *val_array = nullptr;
538 size_t i = 0;
539 SimpleTokenizer tok(values, ',');
540 size_t n = tok.getNumTokensRemaining();
541 auto field_map{field->map()}; // because clang-analyzer freaks out if this is inlined.
542 // It doesn't realize the value is held in place by the smart
543 // pointer in @c field.
544
545 if (n) {
546 val_array = new int64_t[n];
547 char *t;
548 while (t = tok.getNext(), t != nullptr) {
549 int64_t ival;
550 if (!_convertStringToInt(t, &ival, field_map.get())) {
551 // conversion was successful, add entry to array
552 //
553 val_array[i++] = ival;
554 }
555 }
556 if (i < n) {
557 Warning("There were invalid values in the definition of filter %s"
558 " only %zu out of %zu values will be used.",
559 name, i, n);
560 }
561 } else {
562 Warning("No values in the definition of filter %s.", name);
563 }
564
565 _setValues(i, val_array);
566
567 if (n) {
568 delete[] val_array;
569 }
570 }
571
LogFilterInt(const LogFilterInt & rhs)572 LogFilterInt::LogFilterInt(const LogFilterInt &rhs) : LogFilter(rhs.m_name, rhs.m_field, rhs.m_action, rhs.m_operator)
573 {
574 _setValues(rhs.m_num_values, rhs.m_value);
575 }
576
577 /*-------------------------------------------------------------------------
578 LogFilterInt::~LogFilterInt
579 -------------------------------------------------------------------------*/
580
~LogFilterInt()581 LogFilterInt::~LogFilterInt()
582 {
583 if (m_num_values > 0) {
584 delete[] m_value;
585 }
586 }
587
588 /*-------------------------------------------------------------------------
589 LogFilterInt::operator==
590
591 This operator is not very intelligent and expects the objects being
592 compared to have the same values specified *in the same order*.
593 Filters with the same values specified in different order are considered
594 to be different.
595
596 -------------------------------------------------------------------------*/
597
598 bool
operator ==(LogFilterInt & rhs)599 LogFilterInt::operator==(LogFilterInt &rhs)
600 {
601 if (m_type == rhs.m_type && *m_field == *rhs.m_field && m_action == rhs.m_action && m_operator == rhs.m_operator &&
602 m_num_values == rhs.m_num_values) {
603 for (size_t i = 0; i < m_num_values; i++) {
604 if (m_value[i] != rhs.m_value[i]) {
605 return false;
606 }
607 }
608 return true;
609 }
610 return false;
611 }
612
613 /*-------------------------------------------------------------------------
614 LogFilterInt::wipe_this_entry
615 -------------------------------------------------------------------------*/
616
617 bool
wipe_this_entry(LogAccess * lad)618 LogFilterInt::wipe_this_entry(LogAccess *lad)
619 {
620 if (m_num_values == 0 || m_field == nullptr || lad == nullptr || m_action != WIPE_FIELD_VALUE) {
621 return false;
622 }
623
624 bool cond_satisfied = false;
625 int64_t value;
626
627 m_field->marshal(lad, reinterpret_cast<char *>(&value));
628 // This used to do an ntohl() on value, but that breaks various filters.
629 // Long term we should move IPs to their own log type.
630
631 // we don't use m_operator because we consider all operators to be
632 // equivalent to "MATCH" for an integer field
633 //
634
635 // most common case is single value, speed it up a little bit by unrolling
636 //
637 if (m_num_values == 1) {
638 cond_satisfied = (value == *m_value);
639 } else {
640 for (size_t i = 0; i < m_num_values; ++i) {
641 if (value == m_value[i]) {
642 cond_satisfied = true;
643 break;
644 }
645 }
646 }
647
648 return cond_satisfied;
649 }
650
651 /*-------------------------------------------------------------------------
652 LogFilterInt::toss_this_entry
653 -------------------------------------------------------------------------*/
654
655 bool
toss_this_entry(LogAccess * lad)656 LogFilterInt::toss_this_entry(LogAccess *lad)
657 {
658 if (m_num_values == 0 || m_field == nullptr || lad == nullptr) {
659 return false;
660 }
661
662 bool cond_satisfied = false;
663 int64_t value;
664
665 m_field->marshal(lad, reinterpret_cast<char *>(&value));
666 // This used to do an ntohl() on value, but that breaks various filters.
667 // Long term we should move IPs to their own log type.
668
669 // we don't use m_operator because we consider all operators to be
670 // equivalent to "MATCH" for an integer field
671 //
672
673 // most common case is single value, speed it up a little bit by unrolling
674 //
675 if (m_num_values == 1) {
676 cond_satisfied = (value == *m_value);
677 } else {
678 for (size_t i = 0; i < m_num_values; ++i) {
679 if (value == m_value[i]) {
680 cond_satisfied = true;
681 break;
682 }
683 }
684 }
685
686 return (m_action == REJECT && cond_satisfied) || (m_action == ACCEPT && !cond_satisfied);
687 }
688
689 /*-------------------------------------------------------------------------
690 LogFilterInt::display
691 -------------------------------------------------------------------------*/
692
693 void
display(FILE * fd)694 LogFilterInt::display(FILE *fd)
695 {
696 ink_assert(fd != nullptr);
697 if (m_num_values == 0) {
698 fprintf(fd, "Filter \"%s\" is inactive, no values specified\n", m_name);
699 } else {
700 fprintf(fd, "Filter \"%s\" %sS records if %s %s ", m_name, ACTION_NAME[m_action], m_field->symbol(), OPERATOR_NAME[m_operator]);
701 fprintf(fd, "%" PRId64 "", m_value[0]);
702 for (size_t i = 1; i < m_num_values; ++i) {
703 fprintf(fd, ", %" PRId64 "", m_value[i]);
704 }
705 fprintf(fd, "\n");
706 }
707 }
708
709 /*-------------------------------------------------------------------------
710 LogFilterIP::LogFilterIP
711 -------------------------------------------------------------------------*/
LogFilterIP(const char * name,LogField * field,LogFilter::Action action,LogFilter::Operator oper,IpAddr value)712 LogFilterIP::LogFilterIP(const char *name, LogField *field, LogFilter::Action action, LogFilter::Operator oper, IpAddr value)
713 : LogFilter(name, field, action, oper)
714 {
715 m_map.mark(value, value);
716 this->init();
717 }
718
LogFilterIP(const char * name,LogField * field,LogFilter::Action action,LogFilter::Operator oper,size_t num_values,IpAddr * value)719 LogFilterIP::LogFilterIP(const char *name, LogField *field, LogFilter::Action action, LogFilter::Operator oper, size_t num_values,
720 IpAddr *value)
721 : LogFilter(name, field, action, oper)
722 {
723 for (IpAddr *limit = value + num_values; value != limit; ++value) {
724 m_map.mark(*value, *value);
725 }
726 this->init();
727 }
728
LogFilterIP(const char * name,LogField * field,LogFilter::Action action,LogFilter::Operator oper,char * values)729 LogFilterIP::LogFilterIP(const char *name, LogField *field, LogFilter::Action action, LogFilter::Operator oper, char *values)
730 : LogFilter(name, field, action, oper)
731 {
732 // parse the comma-separated list of values and construct array
733 //
734 size_t i = 0;
735 SimpleTokenizer tok(values, ',');
736 size_t n = tok.getNumTokensRemaining();
737 char *t; // temp token pointer.
738
739 if (n) {
740 while (t = tok.getNext(), t != nullptr) {
741 IpAddr min, max;
742 char *x = strchr(t, '-');
743 if (x) {
744 *x++ = 0;
745 }
746 if (0 == min.load(t)) {
747 if (x) {
748 if (0 != max.load(x)) {
749 Warning("LogFilterIP Configuration: '%s-%s' looks like a range but the second address was ill formed", t, x);
750 continue;
751 }
752 } else {
753 max = min;
754 }
755 m_map.mark(min, max);
756 ++i;
757 } else {
758 Warning("LogFilterIP Configuration: '%s' is ill formed", t);
759 }
760 }
761 if (i < n) {
762 Warning("There were invalid IP values in the definition of filter %s"
763 " only %zu out of %zu values will be used.",
764 name, i, n);
765 }
766 } else {
767 Warning("No values in the definition of filter %s.", name);
768 }
769 this->init();
770 }
771
LogFilterIP(const LogFilterIP & rhs)772 LogFilterIP::LogFilterIP(const LogFilterIP &rhs) : LogFilter(rhs.m_name, rhs.m_field, rhs.m_action, rhs.m_operator)
773 {
774 for (auto &spot : rhs.m_map) {
775 m_map.mark(spot.min(), spot.max(), spot.data());
776 }
777 this->init();
778 }
779
780 void
init()781 LogFilterIP::init()
782 {
783 m_type = IP_FILTER;
784 m_num_values = m_map.count();
785 }
786
787 /*-------------------------------------------------------------------------
788 LogFilterIP::~LogFilterIP
789 -------------------------------------------------------------------------*/
790
791 LogFilterIP::~LogFilterIP() = default;
792
793 /*-------------------------------------------------------------------------
794 LogFilterIP::operator==
795
796 This operator is not very intelligent and expects the objects being
797 compared to have the same values specified *in the same order*.
798 Filters with the same values specified in different order are considered
799 to be different.
800
801 -------------------------------------------------------------------------*/
802
803 bool
operator ==(LogFilterIP & rhs)804 LogFilterIP::operator==(LogFilterIP &rhs)
805 {
806 if (m_type == rhs.m_type && *m_field == *rhs.m_field && m_action == rhs.m_action && m_operator == rhs.m_operator &&
807 m_num_values == rhs.m_num_values) {
808 IpMap::iterator left_spot(m_map.begin());
809 IpMap::iterator left_limit(m_map.end());
810 IpMap::iterator right_spot(rhs.m_map.begin());
811 IpMap::iterator right_limit(rhs.m_map.end());
812
813 while (left_spot != left_limit && right_spot != right_limit) {
814 if (!ats_ip_addr_eq(left_spot->min(), right_spot->min()) || !ats_ip_addr_eq(left_spot->max(), right_spot->max())) {
815 break;
816 }
817 ++left_spot;
818 ++right_spot;
819 }
820
821 return left_spot == left_limit && right_spot == right_limit;
822 }
823 return false;
824 }
825
826 /*-------------------------------------------------------------------------
827 LogFilterIP::toss_this_entry
828 -------------------------------------------------------------------------*/
829
830 bool
is_match(LogAccess * lad)831 LogFilterIP::is_match(LogAccess *lad)
832 {
833 bool zret = false;
834
835 if (m_field && lad) {
836 LogFieldIpStorage value;
837 m_field->marshal(lad, reinterpret_cast<char *>(&value));
838 // This is bad, we abuse the fact that the initial layout of LogFieldIpStorage and IpAddr
839 // are identical. We should look at converting the log stuff to use IpAddr directly.
840 zret = m_map.contains(reinterpret_cast<IpAddr &>(value));
841 }
842
843 return zret;
844 }
845
846 bool
toss_this_entry(LogAccess * lad)847 LogFilterIP::toss_this_entry(LogAccess *lad)
848 {
849 bool cond_satisfied = this->is_match(lad);
850 return (m_action == REJECT && cond_satisfied) || (m_action == ACCEPT && !cond_satisfied);
851 }
852
853 bool
wipe_this_entry(LogAccess *)854 LogFilterIP::wipe_this_entry(LogAccess *)
855 {
856 return false;
857 }
858
859 /*-------------------------------------------------------------------------
860 LogFilterIP::display
861 -------------------------------------------------------------------------*/
862
863 void
displayRange(FILE * fd,IpMap::iterator const & iter)864 LogFilterIP::displayRange(FILE *fd, IpMap::iterator const &iter)
865 {
866 ip_text_buffer ipb;
867
868 fprintf(fd, "%s", ats_ip_ntop(iter->min(), ipb, sizeof(ipb)));
869
870 if (!ats_ip_addr_eq(iter->min(), iter->max())) {
871 fprintf(fd, "-%s", ats_ip_ntop(iter->max(), ipb, sizeof(ipb)));
872 }
873 }
874
875 void
displayRanges(FILE * fd)876 LogFilterIP::displayRanges(FILE *fd)
877 {
878 IpMap::iterator spot(m_map.begin()), limit(m_map.end());
879 ink_assert(spot != limit);
880
881 this->displayRange(fd, spot);
882 for (++spot; spot != limit; ++spot) {
883 for (size_t i = 1; i < m_num_values; ++i) {
884 fprintf(fd, ",");
885 this->displayRange(fd, spot);
886 }
887 }
888 }
889
890 void
display(FILE * fd)891 LogFilterIP::display(FILE *fd)
892 {
893 ink_assert(fd != nullptr);
894
895 if (0 == m_map.count()) {
896 fprintf(fd, "Filter \"%s\" is inactive, no values specified\n", m_name);
897 } else {
898 fprintf(fd, "Filter \"%s\" %sS records if %s %s ", m_name, ACTION_NAME[m_action], m_field->symbol(), OPERATOR_NAME[m_operator]);
899 this->displayRanges(fd);
900 fprintf(fd, "\n");
901 }
902 }
903
904 bool
filters_are_equal(LogFilter * filt1,LogFilter * filt2)905 filters_are_equal(LogFilter *filt1, LogFilter *filt2)
906 {
907 bool ret = false;
908
909 // TODO: we should check name here
910 if (filt1->type() == filt2->type()) {
911 if (filt1->type() == LogFilter::INT_FILTER) {
912 Debug("log-filter-compare", "int compare");
913 ret = (*((LogFilterInt *)filt1) == *((LogFilterInt *)filt2));
914 } else if (filt1->type() == LogFilter::IP_FILTER) {
915 ret = (*((LogFilterIP *)filt1) == *((LogFilterIP *)filt2));
916 } else if (filt1->type() == LogFilter::STRING_FILTER) {
917 ret = (*((LogFilterString *)filt1) == *((LogFilterString *)filt2));
918 } else {
919 ink_assert(!"invalid filter type");
920 }
921 } else {
922 Debug("log-filter-compare", "type diff");
923 }
924 return ret;
925 }
926
927 /*-------------------------------------------------------------------------
928 LogFilterList
929
930 It is ASSUMED that each element on this list has been allocated from the
931 heap with "new" and that each element is on at most ONE list. To enforce
932 this, we allow for copies to be made by the system, which is why the
933 add() function is overloaded for each sub-type of LogFilter.
934 -------------------------------------------------------------------------*/
935
936 LogFilterList::LogFilterList() = default;
937
938 /*-------------------------------------------------------------------------
939 -------------------------------------------------------------------------*/
940
~LogFilterList()941 LogFilterList::~LogFilterList()
942 {
943 clear();
944 }
945
946 /*-------------------------------------------------------------------------
947 -------------------------------------------------------------------------*/
948
949 bool
operator ==(LogFilterList & rhs)950 LogFilterList::operator==(LogFilterList &rhs)
951 {
952 if (m_does_conjunction == rhs.does_conjunction()) {
953 LogFilter *f = first();
954 LogFilter *rhsf = rhs.first();
955
956 while (true) {
957 if (!(f || rhsf)) {
958 return true;
959 } else if (!f || !rhsf) {
960 return false;
961 } else if (!filters_are_equal(f, rhsf)) {
962 return false;
963 } else {
964 f = next(f);
965 rhsf = rhs.next(rhsf);
966 }
967 }
968 } else {
969 return false;
970 }
971 }
972
973 /*-------------------------------------------------------------------------
974 -------------------------------------------------------------------------*/
975
976 void
clear()977 LogFilterList::clear()
978 {
979 LogFilter *f;
980 while ((f = m_filter_list.dequeue())) {
981 delete f; // safe given the semantics stated above
982 }
983 }
984
985 /*-------------------------------------------------------------------------
986 -------------------------------------------------------------------------*/
987
988 void
add(LogFilter * filter,bool copy)989 LogFilterList::add(LogFilter *filter, bool copy)
990 {
991 ink_assert(filter != nullptr);
992 if (copy) {
993 if (filter->type() == LogFilter::INT_FILTER) {
994 LogFilterInt *f = new LogFilterInt(*((LogFilterInt *)filter));
995 m_filter_list.enqueue(f);
996 } else if (filter->type() == LogFilter::IP_FILTER) {
997 LogFilterIP *f = new LogFilterIP(*((LogFilterIP *)filter));
998 m_filter_list.enqueue(f);
999 } else {
1000 LogFilterString *f = new LogFilterString(*((LogFilterString *)filter));
1001 m_filter_list.enqueue(f);
1002 }
1003 } else {
1004 m_filter_list.enqueue(filter);
1005 }
1006 }
1007
1008 /*-------------------------------------------------------------------------
1009 -------------------------------------------------------------------------*/
1010
1011 bool
wipe_this_entry(LogAccess * lad)1012 LogFilterList::wipe_this_entry(LogAccess *lad)
1013 {
1014 bool wipeFlag = false;
1015 for (LogFilter *f = first(); f; f = next(f)) {
1016 if (f->wipe_this_entry(lad)) {
1017 wipeFlag = true;
1018 }
1019 }
1020 return wipeFlag;
1021 }
1022
1023 /*-------------------------------------------------------------------------
1024 -------------------------------------------------------------------------*/
1025
1026 bool
toss_this_entry(LogAccess * lad)1027 LogFilterList::toss_this_entry(LogAccess *lad)
1028 {
1029 if (m_does_conjunction) {
1030 // toss if any filter rejects the entry (all filters should accept)
1031 //
1032 for (LogFilter *f = first(); f; f = next(f)) {
1033 if (f->toss_this_entry(lad)) {
1034 return true;
1035 }
1036 }
1037 return false;
1038 } else {
1039 // toss if all filters reject the entry (any filter accepts)
1040 //
1041 for (LogFilter *f = first(); f; f = next(f)) {
1042 if (!f->toss_this_entry(lad)) {
1043 return false;
1044 }
1045 }
1046 return true;
1047 }
1048 }
1049
1050 /*-------------------------------------------------------------------------
1051 -------------------------------------------------------------------------*/
1052
1053 LogFilter *
find_by_name(const char * name)1054 LogFilterList::find_by_name(const char *name)
1055 {
1056 for (LogFilter *f = first(); f; f = next(f)) {
1057 if (strcmp(f->name(), name) == 0) {
1058 return f;
1059 }
1060 }
1061 return nullptr;
1062 }
1063
1064 /*-------------------------------------------------------------------------
1065 -------------------------------------------------------------------------*/
1066
1067 unsigned
count() const1068 LogFilterList::count() const
1069 {
1070 unsigned cnt = 0;
1071
1072 for (LogFilter *f = first(); f; f = next(f)) {
1073 cnt++;
1074 }
1075 return cnt;
1076 }
1077
1078 void
display(FILE * fd)1079 LogFilterList::display(FILE *fd)
1080 {
1081 for (LogFilter *f = first(); f; f = next(f)) {
1082 f->display(fd);
1083 }
1084 }
1085
1086 #if TS_HAS_TESTS
1087 #include "tscore/TestBox.h"
1088
REGRESSION_TEST(Log_FilterParse)1089 REGRESSION_TEST(Log_FilterParse)(RegressionTest *t, int /* atype */, int *pstatus)
1090 {
1091 TestBox box(t, pstatus);
1092
1093 #define CHECK_FORMAT_PARSE(fmt) \
1094 do { \
1095 LogFilter *f = LogFilter::parse(fmt, LogFilter::ACCEPT, fmt); \
1096 box.check(f != NULL, "failed to parse filter '%s'", fmt); \
1097 delete f; \
1098 } while (0)
1099
1100 *pstatus = REGRESSION_TEST_PASSED;
1101 LogFilter *retfilter;
1102
1103 retfilter = LogFilter::parse("t1", LogFilter::ACCEPT, "tok1 tok2");
1104 box.check(retfilter == nullptr, "At least 3 tokens are required");
1105 delete retfilter;
1106 retfilter = LogFilter::parse("t2", LogFilter::ACCEPT, "%<sym operator value");
1107 box.check(retfilter == nullptr, "Unclosed symbol token");
1108 delete retfilter;
1109 retfilter = LogFilter::parse("t3", LogFilter::ACCEPT, "%<{Age ssh> operator value");
1110 box.check(retfilter == nullptr, "Unclosed container field");
1111 delete retfilter;
1112 retfilter = LogFilter::parse("t4", LogFilter::ACCEPT, "%<james> operator value");
1113 box.check(retfilter == nullptr, "Invalid log field");
1114 delete retfilter;
1115 retfilter = LogFilter::parse("t5", LogFilter::ACCEPT, "%<chi> invalid value");
1116 box.check(retfilter == nullptr, "Invalid operator name");
1117 delete retfilter;
1118
1119 CHECK_FORMAT_PARSE("pssc MATCH 200");
1120 CHECK_FORMAT_PARSE("shn CASE_INSENSITIVE_CONTAIN unwanted.com");
1121
1122 #undef CHECK_FORMAT_PARSE
1123 }
1124
1125 #endif
1126