blob: 25cfb1c36e57de4c764332a00889af89b36df13f [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.
*/
/*****************************************************************************
*
* ControlBase.cc - Base class to process generic modifiers to
* ControlMatcher Directives
*
*
****************************************************************************/
#include "ink_platform.h"
#include "ink_port.h"
#include "ink_time.h"
#include "ink_unused.h" /* MAGIC_EDITING_TAG */
#include "Main.h"
#include "URL.h"
#include "Tokenizer.h"
#include "ControlBase.h"
#include "MatcherUtils.h"
#include "HTTP.h"
#include "ControlMatcher.h"
#include "HdrUtils.h"
// iPort added
static const char *ModTypeStrings[] = {
"Modifier Invalid",
"Port",
"Scheme",
"Prefix",
"Suffix",
"Method",
"TimeOfDay",
"SrcIP",
"iPort",
"Tag"
};
struct timeMod
{
time_t start_time;
time_t end_time;
};
struct ipMod
{
ip_addr_t start_addr;
ip_addr_t end_addr;
};
struct portMod
{
int start_port;
int end_port;
};
ControlBase::~ControlBase()
{
int num_el;
if (mod_elements != NULL) {
// Free all Prefix/Postfix strings and
// SrcIP, Port, Time Of Day Structures
num_el = (*mod_elements).length() + 1;
for (intptr_t i = 0; i < num_el; i++) {
modifier_el & cur_el = (*mod_elements)[i];
if (cur_el.type == MOD_PREFIX ||
cur_el.type == MOD_SUFFIX ||
cur_el.type == MOD_TIME || cur_el.type == MOD_PORT || cur_el.type == MOD_SRC_IP || cur_el.type == MOD_TAG) {
xfree(cur_el.opaque_data);
}
}
delete mod_elements;
}
}
static const modifier_el default_el = { MOD_INVALID, NULL };
void
ControlBase::Print()
{
int num_el;
struct in_addr a;
int port;
if (mod_elements == NULL) {
return;
}
num_el = (*mod_elements).length();
if (num_el <= 0) {
return;
}
printf("\t\t\t");
for (intptr_t i = 0; i < num_el; i++) {
modifier_el & cur_el = (*mod_elements)[i];
switch (cur_el.type) {
case MOD_INVALID:
printf("%s ", ModTypeStrings[MOD_INVALID]);
break;
case MOD_PORT:
port = ((portMod *) cur_el.opaque_data)->start_port;
printf("%s=%d-", ModTypeStrings[MOD_PORT], port);
port = ((portMod *) cur_el.opaque_data)->end_port;
printf("%d ", port);
break;
case MOD_IPORT:
printf("%s=%d ", ModTypeStrings[MOD_IPORT], (int) (long) cur_el.opaque_data);
break;
case MOD_SCHEME:
printf("%s=%s ", ModTypeStrings[MOD_SCHEME], (char *) cur_el.opaque_data);
break;
case MOD_PREFIX:
printf("%s=%s ", ModTypeStrings[MOD_PREFIX], (char *) cur_el.opaque_data);
break;
case MOD_SUFFIX:
printf("%s=%s ", ModTypeStrings[MOD_SUFFIX], (char *) cur_el.opaque_data);
break;
case MOD_METHOD:
printf("%s=%s ", ModTypeStrings[MOD_METHOD], (char *) cur_el.opaque_data);
break;
case MOD_TAG:
printf("%s=%s ", ModTypeStrings[MOD_TAG], (char *) cur_el.opaque_data);
break;
case MOD_SRC_IP:
a.s_addr = ((ipMod *) cur_el.opaque_data)->start_addr;
printf("%s=%s-", ModTypeStrings[MOD_SRC_IP], inet_ntoa(a));
a.s_addr = ((ipMod *) cur_el.opaque_data)->end_addr;
printf("%s ", inet_ntoa(a));
break;
case MOD_TIME:
printf("%s=%d-%d ", ModTypeStrings[MOD_TIME],
(int) ((timeMod *) cur_el.opaque_data)->start_time, (int) ((timeMod *) cur_el.opaque_data)->end_time);
break;
}
}
printf("\n");
}
bool
ControlBase::CheckModifiers(HttpRequestData * request_data)
{
if (!request_data->hdr) {
//we use the same request_data for Socks as well (only IpMatcher)
//we just return false here
return true;
}
const char *data;
const char *path;
const char *method;
ipMod *ipRange;
portMod *portRange;
timeMod *timeRange;
time_t timeOfDay;
URL *request_url = request_data->hdr->url_get();
int data_len, path_len, scheme_len, method_len;
char *tag, *request_tag;
ip_addr_t src_ip;
int port;
// If the incoming request has no tag but the entry does, or both
// have tags that do not match, then we do NOT have a match.
request_tag = request_data->tag;
if (!request_tag && mod_elements != NULL && getModElem(MOD_TAG) != NULL)
return false;
// If there are no modifiers, then of course we match them
if (mod_elements == NULL) {
return true;
}
for (intptr_t i = 0; i < mod_elements->length(); i++) {
modifier_el & cur_el = (*mod_elements)[i];
switch (cur_el.type) {
case MOD_PORT:
port = request_url->port_get();
portRange = (portMod *) cur_el.opaque_data;
if (port < portRange->start_port || port > portRange->end_port) {
return false;
}
break;
case MOD_IPORT:
if (request_data->incoming_port != (long) cur_el.opaque_data) {
return false;
}
break;
case MOD_SCHEME:
if (request_url->scheme_get(&scheme_len) != (char *) cur_el.opaque_data) {
return false;
}
break;
case MOD_PREFIX:
// INKqa07820
// The problem is that path_get() returns the URL's path
// without the leading '/'.
// E.g., If URL is http://inktomi/foo/bar,
// then path_get() returns "foo/bar" but not "/foo/bar".
// A simple solution is to skip the leading '/' in data.
data = (char *) cur_el.opaque_data;
if (*data == '/')
data++;
path = request_url->path_get(&path_len);
if (ptr_len_ncmp(path, path_len, data, strlen(data)) != 0) {
return false;
}
break;
case MOD_SUFFIX:
data = (char *) cur_el.opaque_data;
data_len = strlen(data);
ink_assert(data_len > 0);
path = request_url->path_get(&path_len);
// Suffix matching is case-insentive b/c it's
// mainly used for file type matching and
// jpeg, JPEG, Jpeg all mean the same thing
// (INKqa04363)
if (path_len < data_len || strncasecmp(path + (path_len - data_len), data, data_len) != 0) {
return false;
}
break;
case MOD_METHOD:
method = request_data->hdr->method_get(&method_len);
if (ptr_len_casecmp(method, method_len, (char *) cur_el.opaque_data) != 0) {
return false;
}
break;
case MOD_TIME:
timeRange = (timeMod *) cur_el.opaque_data;
// INKqa11534
// daylight saving time is not taken into consideration
// so use ink_localtime_r() instead.
// timeOfDay = (request_data->xact_start - ink_timezone()) % secondsInDay;
{
struct tm cur_tm;
timeOfDay = request_data->xact_start;
ink_localtime_r(&timeOfDay, &cur_tm);
timeOfDay = cur_tm.tm_hour * (60 * 60)
+ cur_tm.tm_min * 60 + cur_tm.tm_sec;
}
if (timeOfDay < timeRange->start_time || timeOfDay > timeRange->end_time) {
return false;
}
break;
case MOD_SRC_IP:
src_ip = htonl(request_data->src_ip);
ipRange = (ipMod *) cur_el.opaque_data;
if (src_ip < ipRange->start_addr || src_ip > ipRange->end_addr) {
return false;
}
break;
case MOD_TAG:
// Check for a tag match.
tag = (char *) cur_el.opaque_data;
ink_assert(tag);
if (request_tag && strcmp(request_tag, tag) != 0) {
return false;
}
break;
case MOD_INVALID:
// Fall Through
default:
// Should never get here
ink_assert(0);
break;
}
}
return true;
}
enum mod_errors
{ ME_UNKNOWN, ME_PARSE_FAILED,
ME_BAD_SCHEME, ME_BAD_METHOD, ME_BAD_MOD,
ME_CALLEE_GENERATED, ME_BAD_IPORT
};
static const char *errorFormats[] = {
"Unknown error parsing modifier",
"Unable to parse modifier",
"Unknown scheme",
"Unknown method",
"Unknown modifier",
"Callee Generated",
"Bad incoming port"
};
const void *
ControlBase::getModElem(ModifierTypes t)
{
for (int i = 0; i < mod_elements->length(); i++) {
modifier_el & cur_el = (*mod_elements) (i);
if (cur_el.type == t) {
return cur_el.opaque_data;
}
}
return NULL;
}
const char *
ControlBase::ProcessModifiers(matcher_line * line_info)
{
// Variables for error processing
const char *errBuf = NULL;
mod_errors err = ME_UNKNOWN;
int num = 0;
char *label;
char *val;
unsigned int tmp;
int num_mod_elements;
const char *tmp_scheme = NULL;
// Set up the array to handle the modifier
num_mod_elements = (line_info->num_el > 0) ? line_info->num_el : 1;
mod_elements = new DynArray<modifier_el> (&default_el, num_mod_elements);
for (int i = 0; i < MATCHER_MAX_TOKENS; i++) {
label = line_info->line[0][i];
val = line_info->line[1][i];
// Skip NULL tags
if (label == NULL) {
continue;
}
modifier_el & cur_el = (*mod_elements) (num);
num++;
// Make sure we have a value
if (val == NULL) {
err = ME_PARSE_FAILED;
goto error;
}
if (strcasecmp(label, "port") == 0) {
cur_el.type = MOD_PORT;
errBuf = ProcessPort(val, &cur_el.opaque_data);
if (errBuf != NULL) {
err = ME_CALLEE_GENERATED;
goto error;
}
} else if (strcasecmp(label, "iport") == 0) {
// coverity[secure_coding]
if (sscanf(val, "%u", &tmp) == 1) {
cur_el.type = MOD_IPORT;
cur_el.opaque_data = (void *)(uintptr_t)tmp;
} else {
err = ME_BAD_IPORT;
goto error;
}
} else if (strcasecmp(label, "scheme") == 0) {
tmp_scheme = hdrtoken_string_to_wks(val);
if (!tmp_scheme) {
err = ME_BAD_SCHEME;
goto error;
}
cur_el.type = MOD_SCHEME;
cur_el.opaque_data = (void *) tmp_scheme;
} else if (strcasecmp(label, "method") == 0) {
cur_el.type = MOD_METHOD;
cur_el.opaque_data = (void *) xstrdup(val);
} else if (strcasecmp(label, "prefix") == 0) {
cur_el.type = MOD_PREFIX;
cur_el.opaque_data = (void *) xstrdup(val);
} else if (strcasecmp(label, "suffix") == 0) {
cur_el.type = MOD_SUFFIX;
cur_el.opaque_data = (void *) xstrdup(val);
} else if (strcasecmp(label, "src_ip") == 0) {
cur_el.type = MOD_SRC_IP;
errBuf = ProcessSrcIp(val, &cur_el.opaque_data);
if (errBuf != NULL) {
err = ME_CALLEE_GENERATED;
goto error;
}
} else if (strcasecmp(label, "time") == 0) {
cur_el.type = MOD_TIME;
errBuf = ProcessTimeOfDay(val, &cur_el.opaque_data);
if (errBuf != NULL) {
err = ME_CALLEE_GENERATED;
goto error;
}
} else if (strcasecmp(label, "tag") == 0) {
cur_el.type = MOD_TAG;
cur_el.opaque_data = (void *) xstrdup(val);
} else {
err = ME_BAD_MOD;
goto error;
}
}
return NULL;
error:
delete mod_elements;
mod_elements = NULL;
if (err == ME_CALLEE_GENERATED) {
return errBuf;
} else {
return errorFormats[err];
}
}
// const char* ControlBase::ProcessSrcIp(char* val, void** opaque_ptr)
//
// Wrapper to Parse out the src ip range
//
// On success, sets *opaque_ptr to a malloc allocated ipMod
// structure and returns NULL
// On failure, returns a static error string
//
const char *
ControlBase::ProcessSrcIp(char *val, void **opaque_ptr)
{
ipMod *range = (ipMod *) xmalloc(sizeof(ipMod));
const char *errBuf = ExtractIpRange(val, &range->start_addr,
&range->end_addr);
if (errBuf == NULL) {
*opaque_ptr = range;
return NULL;
} else {
*opaque_ptr = NULL;
xfree(range);
return errBuf;
}
}
// const char* TODtoSeconds(const char* time_str, time_t* seconds) {
//
// Convents a TimeOfDay (TOD) to a second value
//
// On success, sets *seconds to number of seconds since midnight
// represented by time_str and returns NULL
//
// On failure, returns a static error string
//
const char *
TODtoSeconds(const char *time_str, time_t * seconds)
{
int hour = 0;
int min = 0;
int sec = 0;
time_t tmp = 0;
// coverity[secure_coding]
if (sscanf(time_str, "%d:%d:%d", &hour, &min, &sec) != 3) {
// coverity[secure_coding]
if (sscanf(time_str, "%d:%d", &hour, &min) != 2) {
return "Malformed time specified";
}
}
if (!(hour >= 0 && hour <= 23)) {
return "Illegal hour specification";
}
tmp = hour * 60;
if (!(min >= 0 && min <= 59)) {
return "Illegal minute specification";
}
tmp = (tmp + min) * 60;
if (!(sec >= 0 && sec <= 59)) {
return "Illegal second specification";
}
tmp += sec;
*seconds = tmp;
return NULL;
}
// const char* ControlBase::ProcessTimeOfDay(char* val, void** opaque_ptr)
//
// Parse out a time of day range.
//
// On success, sets *opaque_ptr to a malloc allocated timeMod
// structure and returns NULL
// On failure, returns a static error string
//
const char *
ControlBase::ProcessTimeOfDay(char *val, void **opaque_ptr)
{
Tokenizer rangeTok("-");
timeMod *t_mod = NULL;
int num_tok;
const char *errBuf;
*opaque_ptr = NULL;
num_tok = rangeTok.Initialize(val, SHARE_TOKS);
if (num_tok == 1) {
return "End time not specified";
} else if (num_tok > 2) {
return "Malformed Range";
}
t_mod = (timeMod *) xmalloc(sizeof(timeMod));
errBuf = TODtoSeconds(rangeTok[0], &t_mod->start_time);
if (errBuf != NULL) {
xfree(t_mod);
return errBuf;
}
errBuf = TODtoSeconds(rangeTok[1], &t_mod->end_time);
if (errBuf != NULL) {
xfree(t_mod);
return errBuf;
}
*opaque_ptr = t_mod;
return NULL;
}
// const char* ControlBase::ProcessPort(char* val, void** opaque_ptr)
//
// Parse out a port range.
//
// On success, sets *opaque_ptr to a malloc allocated portMod
// structure and returns NULL
// On failure, returns a static error string
//
const char *
ControlBase::ProcessPort(char *val, void **opaque_ptr)
{
Tokenizer rangeTok("-");
portMod *p_mod = NULL;
int num_tok;
*opaque_ptr = NULL;
num_tok = rangeTok.Initialize(val, SHARE_TOKS);
if (num_tok > 2) {
return "Malformed Range";
}
p_mod = (portMod *) xmalloc(sizeof(portMod));
// coverity[secure_coding]
if (sscanf(rangeTok[0], "%d", &p_mod->start_port) != 1) {
xfree(p_mod);
return "Invalid start port";
}
if (num_tok == 2) {
// coverity[secure_coding]
if (sscanf(rangeTok[1], "%d", &p_mod->end_port) != 1) {
xfree(p_mod);
return "Invalid end port";
}
if (p_mod->end_port < p_mod->start_port) {
xfree(p_mod);
return "Malformed Range: end port < start port";
}
} else {
p_mod->end_port = p_mod->start_port;
}
*opaque_ptr = p_mod;
return NULL;
}