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