blob: aada40a6bdabeabe3a93e7f27640b8bbf14115a8 [file] [log] [blame]
/** @file
A brief file description
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/***************************************************************************
LogFilter.cc
***************************************************************************/
#include "libts.h"
#include "Resource.h"
#include "Error.h"
#include "LogUtils.h"
#include "LogFilter.h"
#include "LogField.h"
#include "LogFormat.h"
#include "LogFile.h"
#include "LogBuffer.h"
#include "LogHost.h"
#include "LogObject.h"
#include "LogConfig.h"
#include "Log.h"
//#include "ink_ctype.h"
#include "SimpleTokenizer.h"
const char *LogFilter::OPERATOR_NAME[] = { "MATCH", "CASE_INSENSITIVE_MATCH","CONTAIN", "CASE_INSENSITIVE_CONTAIN" };
const char *LogFilter::ACTION_NAME[] = { "REJECT", "ACCEPT" };
/*-------------------------------------------------------------------------
LogFilter::LogFilter
note: it may be convenient to have the LogFilter constructor access the
global_field_list to get the log field, but this is an unnecessary dependency
between the classes and I think should be removed. ltavera
-------------------------------------------------------------------------*/
LogFilter::LogFilter(const char *name, LogField * field, LogFilter::Action action, LogFilter::Operator oper)
: m_name(ats_strdup(name)), m_field(NULL) , m_action(action), m_operator(oper), m_type(INT_FILTER), m_num_values(0)
{
m_field = NEW(new LogField(*field));
ink_assert(m_field);
}
/*-------------------------------------------------------------------------
LogFilter::~LogFilter
-------------------------------------------------------------------------*/
LogFilter::~LogFilter()
{
ats_free(m_name);
delete m_field;
}
/*-------------------------------------------------------------------------
LogFilterString::LogFilterString
-------------------------------------------------------------------------*/
void
LogFilterString::_setValues(size_t n, char **value)
{
m_type = STRING_FILTER;
m_num_values = n;
if (n) {
m_value = NEW(new char *[n]);
m_value_uppercase = NEW(new char *[n]);
m_length = NEW(new size_t[n]);
ink_assert(m_value && m_value_uppercase && m_length);
for (size_t i = 0; i < n; ++i) {
m_value[i] = ats_strdup(value[i]);
m_length[i] = strlen(value[i]);
m_value_uppercase[i] = (char *)ats_malloc((unsigned int) m_length[i] + 1);
size_t j;
for (j = 0; j < m_length[i]; ++j) {
m_value_uppercase[i][j] = ParseRules::ink_toupper(m_value[i][j]);
}
m_value_uppercase[i][j] = 0;
}
}
}
LogFilterString::LogFilterString(const char *name, LogField * field,
LogFilter::Action action, LogFilter::Operator oper, char *values)
: LogFilter(name, field, action, oper)
{
// parse the comma-separated list of values and construct array
//
char **val_array = 0;
size_t i = 0;
SimpleTokenizer tok(values, ',');
size_t n = tok.getNumTokensRemaining();
if (n) {
val_array = NEW(new char *[n]);
char *t;
while (t = tok.getNext(), t != NULL) {
val_array[i++] = t;
}
if (i < n) {
Warning("There were invalid values in the definition of filter %s"
"only %zu out of %zu values will be used", name, i, n);
}
}
_setValues(i, val_array);
delete[]val_array;
}
LogFilterString::LogFilterString(const char *name, LogField * field,
LogFilter::Action action, LogFilter::Operator oper, size_t num_values, char **value)
: LogFilter(name, field, action, oper)
{
_setValues(num_values, value);
}
LogFilterString::LogFilterString(const LogFilterString & rhs)
: LogFilter(rhs.m_name, rhs.m_field, rhs.m_action, rhs.m_operator)
{
_setValues(rhs.m_num_values, rhs.m_value);
}
/*-------------------------------------------------------------------------
LogFilterString::~LogFilterString
-------------------------------------------------------------------------*/
LogFilterString::~LogFilterString()
{
if (m_num_values > 0) {
for (size_t i = 0; i < m_num_values; ++i) {
ats_free(m_value[i]);
ats_free(m_value_uppercase[i]);
}
delete[]m_value;
delete[]m_value_uppercase;
delete[]m_length;
}
}
/*-------------------------------------------------------------------------
LogFilterString::operator==
This operator is not very intelligent and expects the objects being
compared to have the same values specified *in the same order*.
Filters with the same values specified in different order are considered
to be different.
-------------------------------------------------------------------------*/
bool
LogFilterString::operator==(LogFilterString & rhs)
{
if (m_type == rhs.m_type &&
*m_field == *rhs.m_field &&
m_action == rhs.m_action &&
m_operator == rhs.m_operator &&
m_num_values == rhs.m_num_values) {
for (size_t i = 0; i < m_num_values; i++) {
if (m_length[i] != rhs.m_length[i] ||
strncmp(m_value[i], rhs.m_value[i], m_length[i])) {
return false;
}
}
return true;
}
return false;
}
/*-------------------------------------------------------------------------
LogFilterString::toss_this_entry
For strings, we need to marshal the given string into a buffer so that we
can compare it with the filter value. Most strings are snall, so we'll
only allocate space dynamically if the marshal_len is very large (eg,
URL).
The m_substr field tells us whether we can match based on substrings, or
whether we should compare the entire string.
-------------------------------------------------------------------------*/
bool LogFilterString::toss_this_entry(LogAccess * lad)
{
if (m_num_values == 0 || m_field == NULL || lad == NULL) {
return false;
}
static const unsigned BUFSIZE = 1024;
char small_buf[BUFSIZE];
char small_buf_upper[BUFSIZE];
char *big_buf = NULL;
char *big_buf_upper = NULL;
char *buf = small_buf;
char *buf_upper = small_buf_upper;
size_t marsh_len = m_field->marshal_len(lad); // includes null termination
if (marsh_len > BUFSIZE) {
big_buf = (char *)ats_malloc((unsigned int) marsh_len);
buf = big_buf;
}
m_field->marshal(lad, buf);
bool
cond_satisfied = false;
switch (m_operator) {
case MATCH:
// marsh_len is an upper bound on the length of the marshalled string
// because marsh_len counts padding and the eos. So for a MATCH
// operator, we use the DATA_LENGTH_LARGER length condition rather
// than DATA_LENGTH_EQUAL, which we would use if we had the actual
// length of the string. It is probably not worth computing the
// actual length, so we just use the fact that a MATCH is not possible
// when marsh_len <= (length of the filter string)
//
cond_satisfied = _checkCondition(&strcmp, buf, marsh_len, m_value, DATA_LENGTH_LARGER);
break;
case CASE_INSENSITIVE_MATCH:
cond_satisfied = _checkCondition(&strcasecmp, buf, marsh_len, m_value, DATA_LENGTH_LARGER);
break;
case CONTAIN:
cond_satisfied = _checkCondition(&_isSubstring, buf, marsh_len, m_value, DATA_LENGTH_LARGER);
break;
case CASE_INSENSITIVE_CONTAIN:
{
if (big_buf) {
big_buf_upper = (char *)ats_malloc((unsigned int) marsh_len);
buf_upper = big_buf_upper;
}
for (size_t i = 0; i < marsh_len; i++) {
buf_upper[i] = ParseRules::ink_toupper(buf[i]);
}
cond_satisfied = _checkCondition(&_isSubstring, buf_upper, marsh_len, m_value_uppercase, DATA_LENGTH_LARGER);
break;
}
default:
ink_assert(!"INVALID FILTER OPERATOR");
}
ats_free(big_buf);
ats_free(big_buf_upper);
return ((m_action == REJECT && cond_satisfied) || (m_action == ACCEPT && !cond_satisfied));
}
/*-------------------------------------------------------------------------
LogFilterString::display
-------------------------------------------------------------------------*/
void
LogFilterString::display(FILE * fd)
{
ink_assert(fd != NULL);
if (m_num_values == 0) {
fprintf(fd, "Filter \"%s\" is inactive, no values specified\n", m_name);
} else {
fprintf(fd, "Filter \"%s\" %sS records if %s %s ", m_name,
ACTION_NAME[m_action], m_field->symbol(), OPERATOR_NAME[m_operator]);
fprintf(fd, "%s", m_value[0]);
for (size_t i = 1; i < m_num_values; ++i) {
fprintf(fd, ", %s", m_value[i]);
}
fprintf(fd, "\n");
}
}
void
LogFilterString::display_as_XML(FILE * fd)
{
ink_assert(fd != NULL);
fprintf(fd,
"<LogFilter>\n"
" <Name = \"%s\"/>\n"
" <Action = \"%s\"/>\n"
" <Condition = \"%s %s ", m_name, ACTION_NAME[m_action], m_field->symbol(), OPERATOR_NAME[m_operator]);
if (m_num_values == 0) {
fprintf(fd, "<no values>\"\n");
} else {
fprintf(fd, "%s", m_value[0]);
for (size_t i = 1; i < m_num_values; ++i) {
fprintf(fd, ", %s", m_value[i]);
}
fprintf(fd, "\"/>\n");
}
fprintf(fd, "</LogFilter>\n");
}
/*-------------------------------------------------------------------------
LogFilterInt::LogFilterInt
-------------------------------------------------------------------------*/
void
LogFilterInt::_setValues(size_t n, int64_t *value)
{
m_type = INT_FILTER;
m_num_values = n;
if (n) {
m_value = NEW(new int64_t[n]);
memcpy(m_value, value, n * sizeof(int64_t));
}
}
// TODO: ival should be int64_t
int
LogFilterInt::_convertStringToInt(char *value, int64_t *ival, LogFieldAliasMap * map)
{
size_t i, l = strlen(value);
for (i = 0; i < l && ParseRules::is_digit(value[i]); i++);
if (i < l) {
// not all characters of value are digits, assume that
// value is an alias and try to get the actual integer value
// from the log field alias map if field has one
//
if (map == NULL || map->asInt(value, ival) != LogFieldAliasMap::ALL_OK) {
return -1; // error
};
} else {
// all characters of value are digits, simply convert
// the string to int
//
*ival = ink_atoui(value);
}
return 0; // all OK
}
LogFilterInt::LogFilterInt(const char *name, LogField * field,
LogFilter::Action action, LogFilter::Operator oper, int64_t value)
: LogFilter(name, field, action, oper)
{
int64_t v[1];
v[0] = value;
_setValues(1, v);
}
LogFilterInt::LogFilterInt(const char *name, LogField * field,
LogFilter::Action action, LogFilter::Operator oper, size_t num_values, int64_t *value)
: LogFilter(name, field, action, oper)
{
_setValues(num_values, value);
}
LogFilterInt::LogFilterInt(const char *name, LogField * field,
LogFilter::Action action, LogFilter::Operator oper, char *values)
: LogFilter(name, field, action, oper)
{
// parse the comma-separated list of values and construct array
//
int64_t *val_array = 0;
size_t i = 0;
SimpleTokenizer tok(values, ',');
size_t n = tok.getNumTokensRemaining();
if (n) {
val_array = NEW(new int64_t[n]);
char *t;
while (t = tok.getNext(), t != NULL) {
int64_t ival;
if (!_convertStringToInt(t, &ival, field->map())) {
// conversion was successful, add entry to array
//
val_array[i++] = ival;
}
}
if (i < n) {
Warning("There were invalid values in the definition of filter %s"
" only %zu out of %zu values will be used.", name, i, n);
}
} else {
Warning("No values in the definition of filter %s.", name);
}
_setValues(i, val_array);
if (n) {
delete[]val_array;
}
}
LogFilterInt::LogFilterInt(const LogFilterInt & rhs)
:
LogFilter(rhs.m_name, rhs.m_field, rhs.m_action, rhs.m_operator)
{
_setValues(rhs.m_num_values, rhs.m_value);
}
/*-------------------------------------------------------------------------
LogFilterInt::~LogFilterInt
-------------------------------------------------------------------------*/
LogFilterInt::~LogFilterInt()
{
if (m_num_values > 0) {
delete[]m_value;
}
}
/*-------------------------------------------------------------------------
LogFilterInt::operator==
This operator is not very intelligent and expects the objects being
compared to have the same values specified *in the same order*.
Filters with the same values specified in different order are considered
to be different.
-------------------------------------------------------------------------*/
bool LogFilterInt::operator==(LogFilterInt & rhs)
{
if (m_type == rhs.m_type &&
* m_field == * rhs.m_field &&
m_action == rhs.m_action &&
m_operator == rhs.m_operator &&
m_num_values == rhs.m_num_values) {
for (size_t i = 0; i < m_num_values; i++) {
if (m_value[i] != rhs.m_value[i]) {
return false;
}
}
return true;
}
return false;
}
/*-------------------------------------------------------------------------
LogFilterInt::toss_this_entry
-------------------------------------------------------------------------*/
bool LogFilterInt::toss_this_entry(LogAccess * lad)
{
if (m_num_values == 0 || m_field == NULL || lad == NULL) {
return false;
}
bool cond_satisfied = false;
int64_t value;
m_field->marshal(lad, (char *) &value);
// This used to do an ntohl() on value, but that breaks various filters.
// Long term we should move IPs to their own log type.
// we don't use m_operator because we consider all operators to be
// equivalent to "MATCH" for an integer field
//
// most common case is single value, speed it up a little bit by unrolling
//
if (m_num_values == 1) {
cond_satisfied = (value == *m_value);
} else {
for (size_t i = 0; i < m_num_values; ++i) {
if (value == m_value[i]) {
cond_satisfied = true;
break;
}
}
}
return (m_action == REJECT && cond_satisfied) || (m_action == ACCEPT && !cond_satisfied);
}
/*-------------------------------------------------------------------------
LogFilterInt::display
-------------------------------------------------------------------------*/
void
LogFilterInt::display(FILE * fd)
{
ink_assert(fd != NULL);
if (m_num_values == 0) {
fprintf(fd, "Filter \"%s\" is inactive, no values specified\n", m_name);
} else {
fprintf(fd, "Filter \"%s\" %sS records if %s %s ", m_name,
ACTION_NAME[m_action], m_field->symbol(), OPERATOR_NAME[m_operator]);
fprintf(fd, "%" PRId64 "", m_value[0]);
for (size_t i = 1; i < m_num_values; ++i) {
fprintf(fd, ", %" PRId64 "", m_value[i]);
}
fprintf(fd, "\n");
}
}
void
LogFilterInt::display_as_XML(FILE * fd)
{
ink_assert(fd != NULL);
fprintf(fd,
"<LogFilter>\n"
" <Name = \"%s\"/>\n"
" <Action = \"%s\"/>\n"
" <Condition = \"%s %s ", m_name, ACTION_NAME[m_action], m_field->symbol(), OPERATOR_NAME[m_operator]);
if (m_num_values == 0) {
fprintf(fd, "<no values>\"\n");
} else {
fprintf(fd, "%" PRId64 "", m_value[0]);
for (size_t i = 1; i < m_num_values; ++i) {
fprintf(fd, ", %" PRId64 "", m_value[i]);
}
fprintf(fd, "\"/>\n");
}
fprintf(fd, "</LogFilter>\n");
}
bool
filters_are_equal(LogFilter * filt1, LogFilter * filt2)
{
bool ret = false;
// TODO: we should check name here
if (filt1->type() == filt2->type()) {
if (filt1->type() == LogFilter::INT_FILTER) {
Debug("log-filter-compare", "int compare");
ret = (*((LogFilterInt *) filt1) == *((LogFilterInt *) filt2));
} else if (filt1->type() == LogFilter::STRING_FILTER) {
ret = (*((LogFilterString *) filt1) == *((LogFilterString *) filt2));
} else {
ink_assert(!"invalid filter type");
}
} else {
Debug("log-filter-compare", "type diff");
}
return ret;
}
/*-------------------------------------------------------------------------
LogFilterList
It is ASSUMED that each element on this list has been allocated from the
heap with "new" and that each element is on at most ONE list. To enforce
this, we allow for copies to be made by the system, which is why the
add() function is overloaded for each sub-type of LogFilter.
-------------------------------------------------------------------------*/
LogFilterList::LogFilterList():m_does_conjunction(true)
{
}
/*-------------------------------------------------------------------------
-------------------------------------------------------------------------*/
LogFilterList::~LogFilterList()
{
clear();
}
/*-------------------------------------------------------------------------
-------------------------------------------------------------------------*/
bool LogFilterList::operator==(LogFilterList & rhs)
{
if (m_does_conjunction == rhs.does_conjunction()) {
LogFilter *f = first();
LogFilter *rhsf = rhs.first();
while (true) {
if (!(f || rhsf)) {
return true;
} else if (!f || !rhsf) {
return false;
} else if (!filters_are_equal(f, rhsf)) {
return false;
} else {
f = next(f);
rhsf = rhs.next(rhsf);
}
}
} else {
return false;
}
}
/*-------------------------------------------------------------------------
-------------------------------------------------------------------------*/
void
LogFilterList::clear()
{
LogFilter *f;
while ((f = m_filter_list.dequeue())) {
delete f; // safe given the semantics stated above
}
}
/*-------------------------------------------------------------------------
-------------------------------------------------------------------------*/
void
LogFilterList::add(LogFilter * filter, bool copy)
{
ink_assert(filter != NULL);
if (copy) {
if (filter->type() == LogFilter::INT_FILTER) {
LogFilterInt *f = NEW(new LogFilterInt(*((LogFilterInt *) filter)));
m_filter_list.enqueue(f);
} else {
LogFilterString *f = NEW(new LogFilterString(*((LogFilterString *) filter)));
m_filter_list.enqueue(f);
}
} else {
m_filter_list.enqueue(filter);
}
}
/*-------------------------------------------------------------------------
-------------------------------------------------------------------------*/
bool LogFilterList::toss_this_entry(LogAccess * lad)
{
if (m_does_conjunction) {
// toss if any filter rejects the entry (all filters should accept)
//
for (LogFilter * f = first(); f; f = next(f)) {
if (f->toss_this_entry(lad)) {
return true;
}
}
return false;
} else {
// toss if all filters reject the entry (any filter accepts)
//
for (LogFilter * f = first(); f; f = next(f)) {
if (!f->toss_this_entry(lad)) {
return false;
}
}
return true;
}
}
/*-------------------------------------------------------------------------
-------------------------------------------------------------------------*/
LogFilter *
LogFilterList::find_by_name(char *name)
{
for (LogFilter * f = first(); f; f = next(f)) {
if (strcmp(f->name(), name) == 0) {
return f;
}
}
return NULL;
}
/*-------------------------------------------------------------------------
-------------------------------------------------------------------------*/
unsigned
LogFilterList::count()
{
unsigned cnt = 0;
for (LogFilter * f = first(); f; f = next(f)) {
cnt++;
}
return cnt;
}
void
LogFilterList::display(FILE * fd)
{
for (LogFilter * f = first(); f; f = next(f)) {
f->display(fd);
}
}
void
LogFilterList::display_as_XML(FILE * fd)
{
for (LogFilter * f = first(); f; f = next(f)) {
f->display_as_XML(fd);
}
}