blob: 442ba0c3ee607a389c3fc26f3d6faff7e0377f87 [file]
/** @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.
*/
#include "UrlRewrite.h"
#include "Main.h"
#include "P_EventSystem.h"
#include "StatSystem.h"
#include "P_Cache.h"
#include "ProxyConfig.h"
#include "ReverseProxy.h"
#include "MatcherUtils.h"
#include "Tokenizer.h"
#include "api/ts/remap.h"
#include "UrlMappingPathIndex.h"
#include "luaConfig.h"
#include "ink_string.h"
#include "ink_cap.h"
unsigned long
check_remap_option(char *argv[], int argc, unsigned long findmode = 0, int *_ret_idx = NULL, char **argptr = NULL)
{
unsigned long ret_flags = 0;
int idx = 0;
if (argptr)
*argptr = NULL;
if (argv && argc > 0) {
for (int i = 0; i < argc; i++) {
if (!strcasecmp(argv[i], "map_with_referer")) {
if ((findmode & REMAP_OPTFLG_MAP_WITH_REFERER) != 0)
idx = i;
ret_flags |= REMAP_OPTFLG_MAP_WITH_REFERER;
} else if (!strncasecmp(argv[i], "plugin=", 7)) {
if ((findmode & REMAP_OPTFLG_PLUGIN) != 0)
idx = i;
if (argptr)
*argptr = &argv[i][7];
ret_flags |= REMAP_OPTFLG_PLUGIN;
} else if (!strncasecmp(argv[i], "pparam=", 7)) {
if ((findmode & REMAP_OPTFLG_PPARAM) != 0)
idx = i;
if (argptr)
*argptr = &argv[i][7];
ret_flags |= REMAP_OPTFLG_PPARAM;
} else if (!strncasecmp(argv[i], "method=", 7)) {
if ((findmode & REMAP_OPTFLG_METHOD) != 0)
idx = i;
if (argptr)
*argptr = &argv[i][7];
ret_flags |= REMAP_OPTFLG_METHOD;
} else if (!strncasecmp(argv[i], "src_ip=~", 8)) {
if ((findmode & REMAP_OPTFLG_SRC_IP) != 0)
idx = i;
if (argptr)
*argptr = &argv[i][8];
ret_flags |= (REMAP_OPTFLG_SRC_IP | REMAP_OPTFLG_INVERT);
} else if (!strncasecmp(argv[i], "src_ip=", 7)) {
if ((findmode & REMAP_OPTFLG_SRC_IP) != 0)
idx = i;
if (argptr)
*argptr = &argv[i][7];
ret_flags |= REMAP_OPTFLG_SRC_IP;
} else if (!strncasecmp(argv[i], "action=", 7)) {
if ((findmode & REMAP_OPTFLG_ACTION) != 0)
idx = i;
if (argptr)
*argptr = &argv[i][7];
ret_flags |= REMAP_OPTFLG_ACTION;
} else if (!strncasecmp(argv[i], "mapid=", 6)) {
if ((findmode & REMAP_OPTFLG_MAP_ID) != 0)
idx = i;
if (argptr)
*argptr = &argv[i][6];
ret_flags |= REMAP_OPTFLG_MAP_ID;
}
if ((findmode & ret_flags) && !argptr) {
if (_ret_idx)
*_ret_idx = idx;
return ret_flags;
}
}
}
if (_ret_idx)
*_ret_idx = idx;
return ret_flags;
}
/**
Determines where we are in a situation where a virtual path is
being mapped to a server home page. If it is, we set a special flag
instructing us to be on the lookout for the need to send a redirect
to if the request URL is a object, opposed to a directory. We need
the redirect for an object so that the browser is aware that it is
real accessing a directory (albeit a virtual one).
*/
static void
SetHomePageRedirectFlag(url_mapping *new_mapping, URL &new_to_url)
{
int fromLen, toLen;
const char *from_path = new_mapping->fromURL.path_get(&fromLen);
const char *to_path = new_to_url.path_get(&toLen);
new_mapping->homePageRedirect = (from_path && !to_path) ? true : false;
}
// ===============================================================================
static int
is_inkeylist(char *key, ...)
{
int i, idx, retcode = 0;
if (likely(key && key[0])) {
char tmpkey[512];
va_list ap;
va_start(ap, key);
for (i = 0; i < (int) (sizeof(tmpkey) - 2) && (tmpkey[i] = *key++) != 0;)
if (tmpkey[i] != '_' && tmpkey[i] != '.')
i++;
tmpkey[i] = 0;
if (tmpkey[0]) {
char *str = va_arg(ap, char *);
for (idx = 1; str; idx++) {
if (!strcasecmp(tmpkey, str)) {
retcode = idx;
break;
}
str = va_arg(ap, char *);
}
}
va_end(ap);
}
return retcode;
}
/**
Cleanup *char[] array - each item in array must be allocated via
ats_malloc or similar "x..." function.
*/
static void
clear_xstr_array(char *v[], int vsize)
{
if (v && vsize > 0) {
for (int i = 0; i < vsize; i++)
v[i] = (char *)ats_free_null(v[i]);
}
}
static const char *
validate_filter_args(acl_filter_rule ** rule_pp, char **argv, int argc, char *errStrBuf, int errStrBufSize)
{
acl_filter_rule *rule;
unsigned long ul;
char *argptr, tmpbuf[1024];
src_ip_info_t *ipi;
int i, j, m;
bool new_rule_flg = false;
if (!rule_pp) {
Debug("url_rewrite", "[validate_filter_args] Invalid argument(s)");
return (const char *) "Invalid argument(s)";
}
if (is_debug_tag_set("url_rewrite")) {
printf("validate_filter_args: ");
for (i = 0; i < argc; i++)
printf("\"%s\" ", argv[i]);
printf("\n");
}
if ((rule = *rule_pp) == NULL) {
rule = NEW(new acl_filter_rule());
if (unlikely((*rule_pp = rule) == NULL)) {
Debug("url_rewrite", "[validate_filter_args] Memory allocation error");
return (const char *) "Memory allocation Error";
}
new_rule_flg = true;
Debug("url_rewrite", "[validate_filter_args] new acl_filter_rule class was created during remap rule processing");
}
for (i = 0; i < argc; i++) {
if ((ul = check_remap_option(&argv[i], 1, 0, NULL, &argptr)) == 0) {
Debug("url_rewrite", "[validate_filter_args] Unknow remap option - %s", argv[i]);
snprintf(errStrBuf, errStrBufSize, "Unknown option - \"%s\"", argv[i]);
errStrBuf[errStrBufSize - 1] = 0;
if (new_rule_flg) {
delete rule;
*rule_pp = NULL;
}
return (const char *) errStrBuf;
}
if (!argptr || !argptr[0]) {
Debug("url_rewrite", "[validate_filter_args] Empty argument in %s", argv[i]);
snprintf(errStrBuf, errStrBufSize, "Empty argument in \"%s\"", argv[i]);
errStrBuf[errStrBufSize - 1] = 0;
if (new_rule_flg) {
delete rule;
*rule_pp = NULL;
}
return (const char *) errStrBuf;
}
if (ul & REMAP_OPTFLG_METHOD) { /* "method=" option */
if (rule->method_cnt >= ACL_FILTER_MAX_METHODS) {
Debug("url_rewrite", "[validate_filter_args] Too many \"method=\" filters");
snprintf(errStrBuf, errStrBufSize, "Defined more than %d \"method=\" filters!", ACL_FILTER_MAX_METHODS);
errStrBuf[errStrBufSize - 1] = 0;
if (new_rule_flg) {
delete rule;
*rule_pp = NULL;
}
return (const char *) errStrBuf;
}
// Please remember that the order of hash idx creation is very important and it is defined
// in HTTP.cc file
if (!strcasecmp(argptr, "CONNECT"))
m = HTTP_WKSIDX_CONNECT;
else if (!strcasecmp(argptr, "DELETE"))
m = HTTP_WKSIDX_DELETE;
else if (!strcasecmp(argptr, "GET"))
m = HTTP_WKSIDX_GET;
else if (!strcasecmp(argptr, "HEAD"))
m = HTTP_WKSIDX_HEAD;
else if (!strcasecmp(argptr, "ICP_QUERY"))
m = HTTP_WKSIDX_ICP_QUERY;
else if (!strcasecmp(argptr, "OPTIONS"))
m = HTTP_WKSIDX_OPTIONS;
else if (!strcasecmp(argptr, "POST"))
m = HTTP_WKSIDX_POST;
else if (!strcasecmp(argptr, "PURGE"))
m = HTTP_WKSIDX_PURGE;
else if (!strcasecmp(argptr, "PUT"))
m = HTTP_WKSIDX_PUT;
else if (!strcasecmp(argptr, "TRACE"))
m = HTTP_WKSIDX_TRACE;
else if (!strcasecmp(argptr, "PUSH"))
m = HTTP_WKSIDX_PUSH;
else {
Debug("url_rewrite", "[validate_filter_args] Unknown method value %s", argptr);
snprintf(errStrBuf, errStrBufSize, "Unknown method \"%s\"", argptr);
errStrBuf[errStrBufSize - 1] = 0;
if (new_rule_flg) {
delete rule;
*rule_pp = NULL;
}
return (const char *) errStrBuf;
}
for (j = 0; j < rule->method_cnt; j++) {
if (rule->method_array[j] == m) {
m = 0;
break; /* we already have it in the list */
}
}
if ((j = m) != 0) {
j = j - HTTP_WKSIDX_CONNECT; // get method index
if (j<0 || j>= ACL_FILTER_MAX_METHODS) {
Debug("url_rewrite", "[validate_filter_args] Incorrect method index! Method sequence in HTTP.cc is broken");
snprintf(errStrBuf, errStrBufSize, "Incorrect method index %d", j);
errStrBuf[errStrBufSize - 1] = 0;
if (new_rule_flg) {
delete rule;
*rule_pp = NULL;
}
return (const char *) errStrBuf;
}
rule->method_idx[j] = m;
rule->method_array[rule->method_cnt++] = m;
rule->method_valid = 1;
}
} else if (ul & REMAP_OPTFLG_SRC_IP) { /* "src_ip=" option */
if (rule->src_ip_cnt >= ACL_FILTER_MAX_SRC_IP) {
Debug("url_rewrite", "[validate_filter_args] Too many \"src_ip=\" filters");
snprintf(errStrBuf, errStrBufSize, "Defined more than %d \"src_ip=\" filters!", ACL_FILTER_MAX_SRC_IP);
errStrBuf[errStrBufSize - 1] = 0;
if (new_rule_flg) {
delete rule;
*rule_pp = NULL;
}
return (const char *) errStrBuf;
}
ipi = &rule->src_ip_array[rule->src_ip_cnt];
if (ul & REMAP_OPTFLG_INVERT)
ipi->invert = true;
ink_strlcpy(tmpbuf, argptr, sizeof(tmpbuf));
// important! use copy of argument
if (ExtractIpRange(tmpbuf, &ipi->start.sa, &ipi->end.sa) != NULL) {
Debug("url_rewrite", "[validate_filter_args] Unable to parse IP value in %s", argv[i]);
snprintf(errStrBuf, errStrBufSize, "Unable to parse IP value in %s", argv[i]);
errStrBuf[errStrBufSize - 1] = 0;
if (new_rule_flg) {
delete rule;
*rule_pp = NULL;
}
return (const char*) errStrBuf;
}
for (j = 0; j < rule->src_ip_cnt; j++) {
if (rule->src_ip_array[j].start == ipi->start && rule->src_ip_array[j].end == ipi->end) {
ipi->reset();
ipi = NULL;
break; /* we have the same src_ip in the list */
}
}
if (ipi) {
rule->src_ip_cnt++;
rule->src_ip_valid = 1;
}
} else if (ul & REMAP_OPTFLG_ACTION) { /* "action=" option */
if (is_inkeylist(argptr, "0", "off", "deny", "disable", NULL)) {
rule->allow_flag = 0;
} else if (is_inkeylist(argptr, "1", "on", "allow", "enable", NULL)) {
rule->allow_flag = 1;
} else {
Debug("url_rewrite", "[validate_filter_args] Unknown argument \"%s\"", argv[i]);
snprintf(errStrBuf, errStrBufSize, "Unknown argument \"%s\"", argv[i]);
errStrBuf[errStrBufSize - 1] = 0;
if (new_rule_flg) {
delete rule;
*rule_pp = NULL;
}
return (const char *) errStrBuf;
}
}
}
if (is_debug_tag_set("url_rewrite"))
rule->print();
return NULL; /* success */
}
static const char *
parse_directive(BUILD_TABLE_INFO *bti, char *errbuf, int errbufsize)
{
bool flg;
char *directive = NULL;
acl_filter_rule *rp, **rpp;
const char *cstr = NULL;
// Check arguments
if (unlikely(!bti || !errbuf || errbufsize <= 0 || !bti->paramc || (directive = bti->paramv[0]) == NULL)) {
Debug("url_rewrite", "[parse_directive] Invalid argument(s)");
return "Invalid argument(s)";
}
Debug("url_rewrite", "[parse_directive] Start processing \"%s\" directive", directive);
if (directive[0] != '.' || directive[1] == 0) {
snprintf(errbuf, errbufsize, "Invalid directive \"%s\"", directive);
Debug("url_rewrite", "[parse_directive] %s", errbuf);
return (const char *) errbuf;
}
if (is_inkeylist(&directive[1], "definefilter", "deffilter", "defflt", NULL)) {
if (bti->paramc < 2) {
snprintf(errbuf, errbufsize, "Directive \"%s\" must have name argument", directive);
Debug("url_rewrite", "[parse_directive] %s", errbuf);
return (const char *) errbuf;
}
if (bti->argc < 1) {
snprintf(errbuf, errbufsize, "Directive \"%s\" must have filter parameter(s)", directive);
Debug("url_rewrite", "[parse_directive] %s", errbuf);
return (const char *) errbuf;
}
flg = ((rp = acl_filter_rule::find_byname(bti->rules_list, (const char *) bti->paramv[1])) == NULL) ? true : false;
// coverity[alloc_arg]
if ((cstr = validate_filter_args(&rp, bti->argv, bti->argc, errbuf, errbufsize)) == NULL && rp) {
if (flg) { // new filter - add to list
Debug("url_rewrite", "[parse_directive] new rule \"%s\" was created", bti->paramv[1]);
for (rpp = &bti->rules_list; *rpp; rpp = &((*rpp)->next));
(*rpp = rp)->name(bti->paramv[1]);
}
Debug("url_rewrite", "[parse_directive] %d argument(s) were added to rule \"%s\"", bti->argc, bti->paramv[1]);
rp->add_argv(bti->argc, bti->argv); // store string arguments for future processing
}
} else if (is_inkeylist(&directive[1], "deletefilter", "delfilter", "delflt", NULL)) {
if (bti->paramc < 2) {
snprintf(errbuf, errbufsize, "Directive \"%s\" must have name argument", directive);
Debug("url_rewrite", "[parse_directive] %s", errbuf);
return (const char *) errbuf;
}
acl_filter_rule::delete_byname(&bti->rules_list, (const char *) bti->paramv[1]);
} else if (is_inkeylist(&directive[1], "usefilter", "activefilter", "activatefilter", "useflt", NULL)) {
if (bti->paramc < 2) {
snprintf(errbuf, errbufsize, "Directive \"%s\" must have name argument", directive);
Debug("url_rewrite", "[parse_directive] %s", errbuf);
return (const char *) errbuf;
}
if ((rp = acl_filter_rule::find_byname(bti->rules_list, (const char *) bti->paramv[1])) == NULL) {
snprintf(errbuf, errbufsize, "Undefined filter \"%s\" in directive \"%s\"", bti->paramv[1], directive);
Debug("url_rewrite", "[parse_directive] %s", errbuf);
return (const char *) errbuf;
}
acl_filter_rule::requeue_in_active_list(&bti->rules_list, rp);
} else
if (is_inkeylist(&directive[1], "unusefilter", "deactivatefilter", "unactivefilter", "deuseflt", "unuseflt", NULL))
{
if (bti->paramc < 2) {
snprintf(errbuf, errbufsize, "Directive \"%s\" must have name argument", directive);
Debug("url_rewrite", "[parse_directive] %s", errbuf);
return (const char *) errbuf;
}
if ((rp = acl_filter_rule::find_byname(bti->rules_list, (const char *) bti->paramv[1])) == NULL) {
snprintf(errbuf, errbufsize, "Undefined filter \"%s\" in directive \"%s\"", bti->paramv[1], directive);
Debug("url_rewrite", "[parse_directive] %s", errbuf);
return (const char *) errbuf;
}
acl_filter_rule::requeue_in_passive_list(&bti->rules_list, rp);
} else {
snprintf(errbuf, errbufsize, "Unknown directive \"%s\"", directive);
Debug("url_rewrite", "[parse_directive] %s", errbuf);
return (const char *) errbuf;
}
return cstr;
}
static const char *
process_filter_opt(url_mapping *mp, BUILD_TABLE_INFO *bti, char *errStrBuf, int errStrBufSize)
{
acl_filter_rule *rp, **rpp;
const char *errStr = NULL;
if (unlikely(!mp || !bti || !errStrBuf || errStrBufSize <= 0)) {
Debug("url_rewrite", "[process_filter_opt] Invalid argument(s)");
return (const char *) "[process_filter_opt] Invalid argument(s)";
}
for (rp = bti->rules_list; rp; rp = rp->next) {
if (rp->active_queue_flag) {
Debug("url_rewrite", "[process_filter_opt] Add active main filter \"%s\" (argc=%d)",
rp->filter_name ? rp->filter_name : "<NULL>", rp->argc);
for (rpp = &mp->filter; *rpp; rpp = &((*rpp)->next));
if ((errStr = validate_filter_args(rpp, rp->argv, rp->argc, errStrBuf, errStrBufSize)) != NULL)
break;
}
}
if (!errStr && (bti->remap_optflg & REMAP_OPTFLG_ALL_FILTERS) != 0) {
Debug("url_rewrite", "[process_filter_opt] Add per remap filter");
for (rpp = &mp->filter; *rpp; rpp = &((*rpp)->next));
errStr = validate_filter_args(rpp, bti->argv, bti->argc, errStrBuf, errStrBufSize);
}
return errStr;
}
//
// CTOR / DTOR for the UrlRewrite class.
//
UrlRewrite::UrlRewrite(const char *file_var_in)
: nohost_rules(0), reverse_proxy(0), backdoor_enabled(0),
mgmt_autoconf_port(0), default_to_pac(0), default_to_pac_port(0), file_var(NULL), ts_name(NULL),
http_default_redirect_url(NULL), num_rules_forward(0), num_rules_reverse(0), num_rules_redirect_permanent(0),
num_rules_redirect_temporary(0), num_rules_forward_with_recv_port(0), _valid(false)
{
forward_mappings.hash_lookup = reverse_mappings.hash_lookup =
permanent_redirects.hash_lookup = temporary_redirects.hash_lookup =
forward_mappings_with_recv_port.hash_lookup = NULL;
char *config_file = NULL;
ink_assert(file_var_in != NULL);
this->file_var = ats_strdup(file_var_in);
config_file_path[0] = '\0';
REC_ReadConfigStringAlloc(config_file, file_var_in);
if (config_file == NULL) {
pmgmt->signalManager(MGMT_SIGNAL_CONFIG_ERROR, "Unable to find proxy.config.url_remap.filename");
Warning("%s Unable to locate remap.config. No remappings in effect", modulePrefix);
return;
}
this->ts_name = NULL;
REC_ReadConfigStringAlloc(this->ts_name, "proxy.config.proxy_name");
if (this->ts_name == NULL) {
pmgmt->signalManager(MGMT_SIGNAL_CONFIG_ERROR, "Unable to read proxy.config.proxy_name");
Warning("%s Unable to determine proxy name. Incorrect redirects could be generated", modulePrefix);
this->ts_name = ats_strdup("");
}
this->http_default_redirect_url = NULL;
REC_ReadConfigStringAlloc(this->http_default_redirect_url, "proxy.config.http.referer_default_redirect");
if (this->http_default_redirect_url == NULL) {
pmgmt->signalManager(MGMT_SIGNAL_CONFIG_ERROR, "Unable to read proxy.config.http.referer_default_redirect");
Warning("%s Unable to determine default redirect url for \"referer\" filter.", modulePrefix);
this->http_default_redirect_url = ats_strdup("http://www.apache.org");
}
REC_ReadConfigInteger(reverse_proxy, "proxy.config.reverse_proxy.enabled");
REC_ReadConfigInteger(mgmt_autoconf_port, "proxy.config.admin.autoconf_port");
REC_ReadConfigInteger(default_to_pac, "proxy.config.url_remap.default_to_server_pac");
REC_ReadConfigInteger(default_to_pac_port, "proxy.config.url_remap.default_to_server_pac_port");
REC_ReadConfigInteger(url_remap_mode, "proxy.config.url_remap.url_remap_mode");
REC_ReadConfigInteger(backdoor_enabled, "proxy.config.url_remap.handle_backdoor_urls");
ink_strlcpy(config_file_path, system_config_directory, sizeof(config_file_path));
ink_strlcat(config_file_path, "/", sizeof(config_file_path));
ink_strlcat(config_file_path, config_file, sizeof(config_file_path));
ats_free(config_file);
if (0 == this->BuildTable()) {
_valid = true;
pcre_malloc = &ats_malloc;
pcre_free = &ats_free;
if (is_debug_tag_set("url_rewrite"))
Print();
} else {
Warning("something failed during BuildTable() -- check your remap plugins!");
}
}
UrlRewrite::~UrlRewrite()
{
ats_free(this->file_var);
ats_free(this->ts_name);
ats_free(this->http_default_redirect_url);
DestroyStore(forward_mappings);
DestroyStore(reverse_mappings);
DestroyStore(permanent_redirects);
DestroyStore(temporary_redirects);
DestroyStore(forward_mappings_with_recv_port);
_valid = false;
}
/** Sets the reverse proxy flag. */
void
UrlRewrite::SetReverseFlag(int flag)
{
reverse_proxy = flag;
if (is_debug_tag_set("url_rewrite"))
Print();
}
/**
Allocaites via new, and setups the default mapping to the PAC generator
port which is used to serve the PAC (proxy autoconfig) file.
*/
url_mapping *
UrlRewrite::SetupPacMapping()
{
const char *from_url = "http:///";
const char *local_url = "http://127.0.0.1/";
url_mapping *mapping;
int pac_generator_port;
mapping = new url_mapping;
mapping->fromURL.create(NULL);
mapping->fromURL.parse(from_url, strlen(from_url));
mapping->toUrl.create(NULL);
mapping->toUrl.parse(local_url, strlen(local_url));
pac_generator_port = (default_to_pac_port < 0) ? mgmt_autoconf_port : default_to_pac_port;
mapping->toUrl.port_set(pac_generator_port);
return mapping;
}
/**
Allocaites via new, and adds a mapping like this map /ink/rh
http://{backdoor}/ink/rh
These {backdoor} things are then rewritten in a request-hdr hook. (In the
future it might make sense to move the rewriting into HttpSM directly.)
*/
url_mapping *
UrlRewrite::SetupBackdoorMapping()
{
const char from_url[] = "/ink/rh";
const char to_url[] = "http://{backdoor}/ink/rh";
url_mapping *mapping = new url_mapping;
mapping->fromURL.create(NULL);
mapping->fromURL.parse(from_url, sizeof(from_url) - 1);
mapping->fromURL.scheme_set(URL_SCHEME_HTTP, URL_LEN_HTTP);
mapping->toUrl.create(NULL);
mapping->toUrl.parse(to_url, sizeof(to_url) - 1);
return mapping;
}
/** Deallocated a hash table and all the url_mappings in it. */
void
UrlRewrite::_destroyTable(InkHashTable *h_table)
{
InkHashTableEntry *ht_entry;
InkHashTableIteratorState ht_iter;
UrlMappingPathIndex *item;
if (h_table != NULL) { // Iterate over the hash tabel freeing up the all the url_mappings
// contained with in
for (ht_entry = ink_hash_table_iterator_first(h_table, &ht_iter); ht_entry != NULL;) {
item = (UrlMappingPathIndex *)ink_hash_table_entry_value(h_table, ht_entry);
delete item;
ht_entry = ink_hash_table_iterator_next(h_table, &ht_iter);
}
ink_hash_table_destroy(h_table);
}
}
/** Debugging Method. */
void
UrlRewrite::Print()
{
printf("URL Rewrite table with %d entries\n", num_rules_forward + num_rules_reverse +
num_rules_redirect_temporary + num_rules_redirect_permanent + num_rules_forward_with_recv_port);
printf(" Reverse Proxy is %s\n", (reverse_proxy == 0) ? "Off" : "On");
printf(" Forward Mapping Table with %d entries\n", num_rules_forward);
PrintStore(forward_mappings);
printf(" Reverse Mapping Table with %d entries\n", num_rules_reverse);
PrintStore(reverse_mappings);
printf(" Permanent Redirect Mapping Table with %d entries\n", num_rules_redirect_permanent);
PrintStore(permanent_redirects);
printf(" Temporary Redirect Mapping Table with %d entries\n", num_rules_redirect_temporary);
PrintStore(temporary_redirects);
printf(" Forward Mapping With Recv Port Table with %d entries\n", num_rules_forward_with_recv_port);
PrintStore(forward_mappings_with_recv_port);
if (http_default_redirect_url != NULL) {
printf(" Referer filter default redirect URL: \"%s\"\n", http_default_redirect_url);
}
}
/** Debugging method. */
void
UrlRewrite::PrintStore(MappingsStore &store)
{
if (store.hash_lookup != NULL) {
InkHashTableEntry *ht_entry;
InkHashTableIteratorState ht_iter;
UrlMappingPathIndex *value;
for (ht_entry = ink_hash_table_iterator_first(store.hash_lookup, &ht_iter); ht_entry != NULL;) {
value = (UrlMappingPathIndex *) ink_hash_table_entry_value(store.hash_lookup, ht_entry);
value->Print();
ht_entry = ink_hash_table_iterator_next(store.hash_lookup, &ht_iter);
}
}
if (!store.regex_list.empty()) {
printf(" Regex mappings:\n");
forl_LL(RegexMapping, list_iter, store.regex_list) {
list_iter->url_map->Print();
}
}
}
/**
If a remapping is found, returns a pointer to it otherwise NULL is
returned.
*/
url_mapping *
UrlRewrite::_tableLookup(InkHashTable *h_table, URL *request_url,
int request_port, char *request_host, int request_host_len)
{
UrlMappingPathIndex *ht_entry;
url_mapping *um = NULL;
int ht_result;
ht_result = ink_hash_table_lookup(h_table, request_host, (void **) &ht_entry);
if (likely(ht_result && ht_entry)) {
// for empty host don't do a normal search, get a mapping arbitrarily
um = ht_entry->Search(request_url, request_port, request_host_len ? true : false);
}
return um;
}
// This is only used for redirects and reverse rules, and the homepageredirect flag
// can never be set. The end result is that request_url is modified per remap container.
void
url_rewrite_remap_request(const UrlMappingContainer& mapping_container, URL *request_url)
{
const char *requestPath;
int requestPathLen;
int fromPathLen;
URL *map_to = mapping_container.getToURL();
URL *map_from = mapping_container.getFromURL();
const char *toHost;
const char *toPath;
const char *toScheme;
int toPathLen;
int toHostLen;
int toSchemeLen;
requestPath = request_url->path_get(&requestPathLen);
map_from->path_get(&fromPathLen);
toHost = map_to->host_get(&toHostLen);
toPath = map_to->path_get(&toPathLen);
toScheme = map_to->scheme_get(&toSchemeLen);
Debug("url_rewrite", "%s: Remapping rule id: %d matched", __func__, mapping_container.getMapping()->map_id);
request_url->host_set(toHost, toHostLen);
request_url->port_set(map_to->port_get_raw());
request_url->scheme_set(toScheme, toSchemeLen);
// Should be +3, little extra padding won't hurt. Use the stack allocation
// for better performance (bummer that arrays of variable length is not supported
// on Solaris CC.
char *newPath = static_cast<char*>(alloca(sizeof(char*)*((requestPathLen - fromPathLen) + toPathLen + 8)));
int newPathLen = 0;
*newPath = 0;
if (toPath) {
memcpy(newPath, toPath, toPathLen);
newPathLen += toPathLen;
}
// We might need to insert a trailing slash in the new portion of the path
// if more will be added and none is present and one will be needed.
if (!fromPathLen && requestPathLen && toPathLen && *(newPath + newPathLen - 1) != '/') {
*(newPath + newPathLen) = '/';
newPathLen++;
}
if (requestPath) {
//avoid adding another trailing slash if the requestPath already had one and so does the toPath
if (requestPathLen < fromPathLen) {
if (toPathLen && requestPath[requestPathLen - 1] == '/' && toPath[toPathLen - 1] == '/') {
fromPathLen++;
}
} else {
if (toPathLen && requestPath[fromPathLen] == '/' && toPath[toPathLen - 1] == '/') {
fromPathLen++;
}
}
// copy the end of the path past what has been mapped
if ((requestPathLen - fromPathLen) > 0) {
memcpy(newPath + newPathLen, requestPath + fromPathLen, requestPathLen - fromPathLen);
newPathLen += (requestPathLen - fromPathLen);
}
}
// Skip any leading / in the path when setting the new URL path
if (*newPath == '/') {
request_url->path_set(newPath + 1, newPathLen - 1);
} else {
request_url->path_set(newPath, newPathLen);
}
}
/** Used to do the backwards lookups. */
#define N_URL_HEADERS 4
bool
UrlRewrite::ReverseMap(HTTPHdr *response_header)
{
const char *location_hdr;
URL location_url;
int loc_length;
bool remap_found = false;
const char *host;
int host_len;
char *new_loc_hdr;
int new_loc_length;
int i;
const struct {
const char *const field;
const int len;
} url_headers[N_URL_HEADERS] = {
{ MIME_FIELD_LOCATION, MIME_LEN_LOCATION } ,
{ MIME_FIELD_CONTENT_LOCATION, MIME_LEN_CONTENT_LOCATION } ,
{ "URI", 3 } ,
{ "Destination", 11 }
};
if (unlikely(num_rules_reverse == 0)) {
ink_assert(reverse_mappings.empty());
return false;
}
for (i = 0; i < N_URL_HEADERS; ++i) {
location_hdr = response_header->value_get(url_headers[i].field, url_headers[i].len, &loc_length);
if (location_hdr == NULL) {
continue;
}
location_url.create(NULL);
location_url.parse(location_hdr, loc_length);
host = location_url.host_get(&host_len);
UrlMappingContainer reverse_mapping(response_header->m_heap);
if (reverseMappingLookup(&location_url, location_url.port_get(), host, host_len, reverse_mapping)) {
if (i == 0)
remap_found = true;
url_rewrite_remap_request(reverse_mapping, &location_url);
new_loc_hdr = location_url.string_get_ref(&new_loc_length);
response_header->value_set(url_headers[i].field, url_headers[i].len, new_loc_hdr, new_loc_length);
}
location_url.destroy();
}
return remap_found;
}
/** Perform fast ACL filtering. */
void
UrlRewrite::PerformACLFiltering(HttpTransact::State *s, url_mapping *map)
{
if (unlikely(!s || s->acl_filtering_performed || !s->client_connection_enabled))
return;
s->acl_filtering_performed = true; // small protection against reverse mapping
if (map->filter) {
int i, res, method;
i = (method = s->hdr_info.client_request.method_get_wksidx()) - HTTP_WKSIDX_CONNECT;
if (likely(i >= 0 && i < ACL_FILTER_MAX_METHODS)) {
bool client_enabled_flag = true;
ink_release_assert(ats_is_ip(&s->client_info.addr));
for (acl_filter_rule * rp = map->filter; rp; rp = rp->next) {
bool match = true;
if (rp->method_valid) {
if (rp->method_idx[i] != method)
match = false;
}
if (match && rp->src_ip_valid) {
match = false;
for (int j = 0; j < rp->src_ip_cnt && !match; j++) {
res = rp->src_ip_array[j].contains(s->client_info.addr) ? 1 : 0;
if (rp->src_ip_array[j].invert) {
if (res != 1)
match = true;
} else {
if (res == 1)
match = true;
}
}
}
if (match && client_enabled_flag) { //make sure that a previous filter did not DENY
Debug("url_rewrite", "matched ACL filter rule, %s request", rp->allow_flag ? "allowing" : "denying");
client_enabled_flag = rp->allow_flag ? true : false;
} else {
if (!client_enabled_flag) {
Debug("url_rewrite", "Previous ACL filter rule denied request, continuing to deny it");
} else {
Debug("url_rewrite", "did NOT match ACL filter rule, %s request", rp->allow_flag ? "denying" : "allowing");
client_enabled_flag = rp->allow_flag ? false : true;
}
}
} /* end of for(rp = map->filter;rp;rp = rp->next) */
s->client_connection_enabled = client_enabled_flag;
}
}
}
/**
Determines if a redirect is to occur and if so, figures out what the
redirect is. This was plaguiarized from UrlRewrite::Remap. redirect_url
ought to point to the new, mapped URL when the function exits.
*/
mapping_type
UrlRewrite::Remap_redirect(HTTPHdr *request_header, URL *redirect_url)
{
URL *request_url;
mapping_type mappingType;
const char *host = NULL;
int host_len = 0, request_port = 0;
bool prt, trt; // existence of permanent and temporary redirect tables, respectively
prt = (num_rules_redirect_permanent != 0);
trt = (num_rules_redirect_temporary != 0);
if (prt + trt == 0)
return NONE;
// Since are called before request validity checking
// occurs, make sure that we have both a valid request
// header and a valid URL
//
if (request_header == NULL) {
Debug("url_rewrite", "request_header was invalid. UrlRewrite::Remap_redirect bailing out.");
return NONE;
}
request_url = request_header->url_get();
if (!request_url->valid()) {
Debug("url_rewrite", "request_url was invalid. UrlRewrite::Remap_redirect bailing out.");
return NONE;
}
host = request_url->host_get(&host_len);
request_port = request_url->port_get();
if (host_len == 0 && reverse_proxy != 0) { // Server request. Use the host header to figure out where
// it goes. Host header parsing is same as in ::Remap
int host_hdr_len;
const char *host_hdr = request_header->value_get(MIME_FIELD_HOST, MIME_LEN_HOST, &host_hdr_len);
if (!host_hdr) {
host_hdr = "";
host_hdr_len = 0;
}
const char *tmp = (const char *) memchr(host_hdr, ':', host_hdr_len);
if (tmp == NULL) {
host_len = host_hdr_len;
} else {
host_len = tmp - host_hdr;
request_port = ink_atoi(tmp + 1, host_hdr_len - host_len);
// If atoi fails, try the default for the
// protocol
if (request_port == 0) {
request_port = request_url->port_get();
}
}
host = host_hdr;
}
// Temporary Redirects have precedence over Permanent Redirects
// the rationale behind this is that network administrators might
// want quick redirects and not want to worry about all the existing
// permanent rules
mappingType = NONE;
UrlMappingContainer redirect_mapping(request_header->m_heap);
if (trt) {
if (temporaryRedirectLookup(request_url, request_port, host, host_len, redirect_mapping)) {
mappingType = TEMPORARY_REDIRECT;
}
}
if ((mappingType == NONE) && prt) {
if (permanentRedirectLookup(request_url, request_port, host, host_len, redirect_mapping)) {
mappingType = PERMANENT_REDIRECT;
}
}
if (mappingType != NONE) {
ink_assert((mappingType == PERMANENT_REDIRECT) || (mappingType == TEMPORARY_REDIRECT));
// Make a copy of the request url so that we can munge it
// for the redirect
redirect_url->create(NULL);
redirect_url->copy(request_url);
// Perform the actual URL rewrite
url_rewrite_remap_request(redirect_mapping, redirect_url);
return mappingType;
}
ink_assert(mappingType == NONE);
return NONE;
}
/**
Returns the length of the URL.
Will replace the terminator with a '/' if this is a full URL and
there are no '/' in it after the the host. This ensures that class
URL parses the URL correctly.
*/
int
UrlRewrite::UrlWhack(char *toWhack, int *origLength)
{
int length = strlen(toWhack);
char *tmp;
*origLength = length;
// Check to see if this a full URL
tmp = strstr(toWhack, "://");
if (tmp != NULL) {
if (strchr(tmp + 3, '/') == NULL) {
toWhack[length] = '/';
length++;
}
}
return length;
}
inline bool
UrlRewrite::_addToStore(MappingsStore &store, url_mapping *new_mapping, RegexMapping *reg_map,
char *src_host, bool is_cur_mapping_regex, int &count)
{
bool retval;
if (is_cur_mapping_regex) {
store.regex_list.enqueue(reg_map);
retval = true;
} else {
retval = TableInsert(store.hash_lookup, new_mapping, src_host);
}
if (retval) {
++count;
}
return retval;
}
struct luabuilder_crutch {
int line; // fake line number for ordering
UrlRewrite *urlrewrite;
BUILD_TABLE_INFO *bti;
};
static int
UrlRewrite_lua_filterstuff(lua_State *L) {
int startN = 0;
bool flg;
const char *func;
const char *filtername;
struct luabuilder_crutch *lbc;
acl_filter_rule *rp, **rpp;
lbc = (struct luabuilder_crutch *)lua_touserdata(L, lua_upvalueindex(1));
func = lua_tostring(L, lua_upvalueindex(2));
// we were called as a method
if(lua_gettop(L) > 0 && lua_isuserdata(L,1)) startN++;
if(lua_gettop(L) < (startN+1) || !lua_isstring(L,(startN+1)))
luaL_error(L, "filter function require a filter name");
filtername = lua_tostring(L,(startN+1));
if(!strcmp(func, "deletefilter")) {
acl_filter_rule::delete_byname(&lbc->bti->rules_list, filtername);
return 0;
}
else if(!strcmp(func, "activatefilter")) {
if ((rp = acl_filter_rule::find_byname(lbc->bti->rules_list, filtername)) == NULL)
luaL_error(L, "Undefined filter \"%s\" in directive \"%s\"", filtername, func);
acl_filter_rule::requeue_in_active_list(&lbc->bti->rules_list, rp);
return 0;
}
else if(!strcmp(func, "deactivatefilter")) {
if ((rp = acl_filter_rule::find_byname(lbc->bti->rules_list, filtername)) == NULL)
luaL_error(L, "Undefined filter \"%s\" in directive \"%s\"", filtername, func);
acl_filter_rule::requeue_in_passive_list(&lbc->bti->rules_list, rp);
return 0;
}
else if(!strcmp(func, "definefilter")) {
const char *cstr = NULL;
int argc = 0;
char *argv[256];
char errStrBuf[1024];
if(lua_gettop(L) != (startN+2) || !lua_istable(L,(startN+2)))
luaL_error(L, "Invalid arguments to UrlRewrite:definefilter");
lua_pushnil(L); /* first key */
while (lua_next(L, (startN+2)) != 0) {
char pbuf[512];
if(lua_istable(L,-1)) {
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
snprintf(pbuf, sizeof(pbuf), "%s=%s", lua_tostring(L,-4), lua_tostring(L,-1));
argv[argc++] = ats_strdup(pbuf);
lua_pop(L,1);
}
}
else {
snprintf(pbuf, sizeof(pbuf), "%s=%s", lua_tostring(L,-2), lua_tostring(L,-1));
argv[argc++] = ats_strdup(pbuf);
}
if(argc >= 256) luaL_error(L, "too many arguments to definefilter");
lua_pop(L, 1);
}
if(argc == 0) luaL_error(L, "filter require arguments");
flg = ((rp = acl_filter_rule::find_byname(lbc->bti->rules_list, filtername)) == NULL) ? true : false;
// coverity[alloc_arg]
if ((cstr = validate_filter_args(&rp, argv, argc, errStrBuf, sizeof(errStrBuf))) == NULL && rp) {
if (flg) { // new filter - add to list
Debug("url_rewrite", "[parse_directive] new rule \"%s\" was created", filtername);
for (rpp = &lbc->bti->rules_list; *rpp; rpp = &((*rpp)->next));
(*rpp = rp)->name(filtername);
}
Debug("url_rewrite", "[parse_directive] %d argument(s) were added to rule \"%s\"", argc, filtername);
rp->add_argv(argc, argv); // store string arguments for future processing
}
for(;argc>0;argc--) ats_free(argv[argc-1]);
if(cstr) luaL_error(L, "filter issue: %s", cstr);
return 0;
}
luaL_error(L, "filter helper encountered impossible upvalue: %s\n", func);
return 0;
}
int
UrlRewrite::lua_mapstuff(lua_State *L) {
int startN = 0, i = 0, argc = 0, nargs;
char *argv[256];
const char *errStr;
char *tag = NULL;
struct luabuilder_crutch *lbc;
char errStrBuf[1024];
bool alarm_already = false;
// Vars to build the mapping
const char *fromScheme, *toScheme;
int fromSchemeLen, toSchemeLen;
const char *fromHost, *toHost;
int fromHostLen, toHostLen;
char *map_from = NULL, *map_from_start;
char *map_to = NULL, *map_to_start;
const char *tmp; // Appease the DEC compiler
char *fromHost_lower = NULL;
char *fromHost_lower_ptr = NULL;
char fromHost_lower_buf[1024];
url_mapping *new_mapping = NULL;
mapping_type maptype = FORWARD_MAP;
referer_info *ri;
int origLength;
int length;
int rparse;
RegexMapping* reg_map;
bool is_cur_mapping_regex;
bool add_result;
lbc = (struct luabuilder_crutch *)lua_touserdata(L, lua_upvalueindex(1));
maptype = (mapping_type)lua_tointeger(L, lua_upvalueindex(2));
is_cur_mapping_regex = (bool)lua_toboolean(L, lua_upvalueindex(3));
// we were called as a method
nargs = lua_gettop(L);
if(nargs > 0 && lua_isuserdata(L,1)) startN++;
if(nargs < (startN+2)) luaL_error(L, "wrong number of argument to mapping");
if(!lua_isstring(L, (startN+1)) || !lua_isstring(L, (startN+2)))
luaL_error(L, "mapping requires to and from");
// bump our fake line number...
lbc->line++;
// process parameters
// loop through and create paramstrings
for(i=startN+2;i<=nargs;i++) {
char pbuf[512];
if(lua_istable(L,i)) {
lua_pushnil(L); /* first key */
while (lua_next(L, i) != 0) {
if(lua_istable(L,-1)) {
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
// internal tables use that outside table's key
if(lua_isboolean(L,-1) && lua_toboolean(L,-1))
snprintf(pbuf, sizeof(pbuf), "%s", lua_tostring(L,-4));
else
snprintf(pbuf, sizeof(pbuf), "%s=%s", lua_tostring(L,-4), lua_tostring(L,-1));
argv[argc++] = ats_strdup(pbuf);
lua_pop(L,1);
if(argc >= 256) {
errStr = "too many arguments to mapping";
goto LUA_MAP_ERROR;
}
}
}
else {
if(lua_isboolean(L,-1) && lua_toboolean(L,-1))
snprintf(pbuf, sizeof(pbuf), "%s", lua_tostring(L,-2));
else
snprintf(pbuf, sizeof(pbuf), "%s=%s", lua_tostring(L,-2), lua_tostring(L,-1));
argv[argc++] = ats_strdup(pbuf);
}
lua_pop(L, 1);
if(argc >= 256) {
errStr = "too many arguments to mapping";
goto LUA_MAP_ERROR;
}
}
}
}
lbc->bti->remap_optflg = check_remap_option(argv, argc);
// if FORWARD_MAP and @map_with_referer is set, we need tup update
if(maptype == FORWARD_MAP &&
(lbc->bti->remap_optflg & REMAP_OPTFLG_MAP_WITH_REFERER))
maptype = FORWARD_MAP_REFERER;
new_mapping = NEW(new url_mapping(lbc->line)); // use line # for rank for now
// apply filter rules if we have to
if ((errStr = process_filter_opt(new_mapping, lbc->bti, errStrBuf, sizeof(errStrBuf))) != NULL)
goto LUA_MAP_ERROR;
new_mapping->map_id = 0;
if ((lbc->bti->remap_optflg & REMAP_OPTFLG_MAP_ID) != 0) {
int idx = 0;
char *c;
int ret = check_remap_option(argv, argc, REMAP_OPTFLG_MAP_ID, &idx);
if (ret & REMAP_OPTFLG_MAP_ID) {
c = strchr(argv[idx], (int) '=');
new_mapping->map_id = (unsigned int) atoi(++c);
}
}
map_from = ats_strdup(lua_tostring(L,(startN+1)));
length = UrlWhack(map_from, &origLength);
if ((tmp = (map_from_start = map_from)) != NULL && length > 2 && tmp[length - 1] == '/' && tmp[length - 2] == '/') {
new_mapping->unique = true;
length -= 2;
}
new_mapping->fromURL.create(NULL);
rparse = new_mapping->fromURL.parse_no_path_component_breakdown(tmp, length);
map_from_start[origLength] = '\0'; // Unwhack
if (rparse != PARSE_DONE) {
errStr = "Malformed From URL";
goto LUA_MAP_ERROR;
}
map_to = ats_strdup(lua_tostring(L,(startN+2)));
length = UrlWhack(map_to, &origLength);
map_to_start = map_to;
tmp = map_to;
new_mapping->toUrl.create(NULL);
rparse = new_mapping->toUrl.parse_no_path_component_breakdown(tmp, length);
map_to_start[origLength] = '\0'; // Unwhack
if (rparse != PARSE_DONE) {
errStr = "Malformed From URL";
goto LUA_MAP_ERROR;
}
fromScheme = new_mapping->fromURL.scheme_get(&fromSchemeLen);
if (fromScheme == NULL || fromSchemeLen == 0) {
new_mapping->fromURL.scheme_set(URL_SCHEME_HTTP, URL_LEN_HTTP);
fromScheme = new_mapping->fromURL.scheme_get(&fromSchemeLen);
new_mapping->wildcard_from_scheme = true;
}
toScheme = new_mapping->toUrl.scheme_get(&toSchemeLen);
if ((fromScheme != URL_SCHEME_HTTP && fromScheme != URL_SCHEME_HTTPS &&
fromScheme != URL_SCHEME_FILE &&
fromScheme != URL_SCHEME_TUNNEL) ||
(toScheme != URL_SCHEME_HTTP && toScheme != URL_SCHEME_HTTPS &&
toScheme != URL_SCHEME_TUNNEL)) {
errStr = "Only http, https, and tunnel remappings are supported";
goto LUA_MAP_ERROR;
}
if(lua_isstring(L,(startN+3))) {
tag = ats_strdup(lua_tostring(L,(startN+3)));
if (maptype == FORWARD_MAP_REFERER) {
int j;
new_mapping->filter_redirect_url = ats_strdup(tag);
if (!strcasecmp(tag, "<default>") || !strcasecmp(tag, "default") ||
!strcasecmp(tag, "<default_redirect_url>") || !strcasecmp(tag, "default_redirect_url"))
new_mapping->default_redirect_url = true;
new_mapping->redir_chunk_list = redirect_tag_str::parse_format_redirect_url(tag);
j = startN+4;
while(lua_isstring(L,j) && j<=nargs) j++;
for (; j > (startN + 3); j--) {
char refinfo_error_buf[1024];
bool refinfo_error = false;
ri = NEW(new referer_info((char *) lua_tostring(L,j), &refinfo_error, refinfo_error_buf,
sizeof(refinfo_error_buf)));
if (refinfo_error) {
snprintf(errStrBuf, sizeof(errStrBuf), "%s Incorrect Referer regular expression \"%s\" - %s",
modulePrefix, lua_tostring(L,j), refinfo_error_buf);
SignalError(errStrBuf, alarm_already);
delete ri;
ri = 0;
}
if (ri && ri->negative) {
if (ri->any) {
new_mapping->optional_referer = true; /* referer header is optional */
delete ri;
ri = 0;
} else {
new_mapping->negative_referer = true; /* we have negative referer in list */
}
}
if (ri) {
ri->next = new_mapping->referer_list;
new_mapping->referer_list = ri;
}
}
} else {
new_mapping->tag = ats_strdup(lua_tostring(L,(startN+3)));
}
}
fromHost = new_mapping->fromURL.host_get(&fromHostLen);
if (fromHost == NULL || fromHostLen <= 0) {
if (maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT) {
if (*map_from_start != '/') {
errStr = "Relative remappings must begin with a /";
goto LUA_MAP_ERROR;
} else {
fromHost = "";
fromHostLen = 0;
}
} else {
errStr = "Remap source in reverse mappings requires a hostname";
goto LUA_MAP_ERROR;
}
}
toHost = new_mapping->toUrl.host_get(&toHostLen);
if (toHost == NULL || toHostLen <= 0) {
errStr = "The remap destinations require a hostname";
goto LUA_MAP_ERROR;
}
if (unlikely(fromHostLen >= (int) sizeof(fromHost_lower_buf))) {
fromHost_lower = (fromHost_lower_ptr = (char *)ats_malloc(fromHostLen + 1));
} else {
fromHost_lower = &fromHost_lower_buf[0];
}
memcpy(fromHost_lower, fromHost, fromHostLen);
fromHost_lower[fromHostLen] = 0;
LowerCaseStr(fromHost_lower);
new_mapping->fromURL.host_set(fromHost_lower, fromHostLen);
reg_map = NULL;
if (is_cur_mapping_regex) {
reg_map = NEW(new RegexMapping);
if (!_processRegexMappingConfig(fromHost_lower, new_mapping, reg_map)) {
errStr = "Could not process regex mapping config line";
goto LUA_MAP_ERROR;
}
Debug("url_rewrite_regex", "Configured regex rule for host [%s]", fromHost_lower);
}
if ((maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT) &&
fromScheme == URL_SCHEME_TUNNEL && (fromHost_lower[0]<'0' || fromHost_lower[0]> '9')) {
addrinfo* ai_records; // returned records.
ip_text_buffer ipb; // buffer for address string conversion.
if (0 == getaddrinfo(fromHost_lower, 0, 0, &ai_records)) {
for ( addrinfo* ai_spot = ai_records ; ai_spot ; ai_spot = ai_spot->ai_next) {
if (ats_is_ip(ai_spot->ai_addr) &&
!ats_is_ip_any(ai_spot->ai_addr)) {
url_mapping *u_mapping;
ats_ip_ntop(ai_spot->ai_addr, ipb, sizeof ipb);
u_mapping = NEW(new url_mapping);
u_mapping->fromURL.create(NULL);
u_mapping->fromURL.copy(&new_mapping->fromURL);
u_mapping->fromURL.host_set(ipb, strlen(ipb));
u_mapping->toUrl.create(NULL);
u_mapping->toUrl.copy(&new_mapping->toUrl);
if (lua_isstring(L,(startN+3)))
u_mapping->tag = ats_strdup(lua_tostring(L,(startN+3)));
bool insert_result = (maptype != FORWARD_MAP_WITH_RECV_PORT) ?
TableInsert(forward_mappings.hash_lookup, u_mapping, ipb) :
TableInsert(forward_mappings_with_recv_port.hash_lookup, u_mapping, ipb);
if (!insert_result) {
errStr = "Unable to add mapping rule to lookup table";
goto LUA_MAP_ERROR;
}
(maptype != FORWARD_MAP_WITH_RECV_PORT) ? ++num_rules_forward : ++num_rules_forward_with_recv_port;
SetHomePageRedirectFlag(u_mapping, u_mapping->toUrl);
}
}
freeaddrinfo(ai_records);
}
}
if ((lbc->bti->remap_optflg & REMAP_OPTFLG_PLUGIN) != 0 && (maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER ||
maptype == FORWARD_MAP_WITH_RECV_PORT)) {
int tok_count;
if ((check_remap_option(argv, argc, REMAP_OPTFLG_PLUGIN, &tok_count) & REMAP_OPTFLG_PLUGIN) != 0) {
int plugin_found_at = 0;
int jump_to_argc = 0;
// this loads the first plugin
if (load_remap_plugin(argv, argc, new_mapping, errStrBuf, sizeof(errStrBuf), 0, &plugin_found_at)) {
Debug("remap_plugin", "Remap plugin load error - %s", errStrBuf[0] ? errStrBuf : "Unknown error");
errStr = errStrBuf;
goto LUA_MAP_ERROR;
}
//this loads any subsequent plugins (if present)
while (plugin_found_at) {
jump_to_argc += plugin_found_at;
if (load_remap_plugin(argv, argc, new_mapping, errStrBuf, sizeof(errStrBuf), jump_to_argc, &plugin_found_at)) {
Debug("remap_plugin", "Remap plugin load error - %s", errStrBuf[0] ? errStrBuf : "Unknown error");
errStr = errStrBuf;
goto LUA_MAP_ERROR;
}
}
}
}
// Now add the mapping to appropriate container
add_result = false;
switch (maptype) {
case FORWARD_MAP:
case FORWARD_MAP_REFERER:
if ((add_result = _addToStore(forward_mappings, new_mapping, reg_map, fromHost_lower,
is_cur_mapping_regex, num_rules_forward)) == true) {
// @todo: is this applicable to regex mapping too?
SetHomePageRedirectFlag(new_mapping, new_mapping->toUrl);
}
break;
case REVERSE_MAP:
add_result = _addToStore(reverse_mappings, new_mapping, reg_map, fromHost_lower,
is_cur_mapping_regex, num_rules_reverse);
new_mapping->homePageRedirect = false;
break;
case PERMANENT_REDIRECT:
add_result = _addToStore(permanent_redirects, new_mapping, reg_map, fromHost_lower,
is_cur_mapping_regex, num_rules_redirect_permanent);
break;
case TEMPORARY_REDIRECT:
add_result = _addToStore(temporary_redirects, new_mapping, reg_map, fromHost_lower,
is_cur_mapping_regex, num_rules_redirect_temporary);
break;
case FORWARD_MAP_WITH_RECV_PORT:
add_result = _addToStore(forward_mappings_with_recv_port, new_mapping, reg_map, fromHost_lower,
is_cur_mapping_regex, num_rules_forward_with_recv_port);
break;
default:
// 'default' required to avoid compiler warning; unsupported map
// type would have been dealt with much before this
break;
}
if (!add_result) {
errStr = "Unable to add mapping rule to lookup table";
goto LUA_MAP_ERROR;
}
fromHost_lower_ptr = (char *)ats_free_null(fromHost_lower_ptr);
errStr = NULL;
LUA_MAP_ERROR:
if(map_from) ats_free(map_from);
if(map_to) ats_free(map_to);
if(tag) ats_free(tag);
for(;argc>0;argc--) ats_free(argv[argc-1]);
if(errStr) luaL_error(L, errStr);
return 0;
}
static int
UrlRewrite_lua_mapstuff(lua_State *L) {
struct luabuilder_crutch *lbc;
lbc = (struct luabuilder_crutch *)lua_touserdata(L, lua_upvalueindex(1));
return lbc->urlrewrite->lua_mapstuff(L);
}
static int
UrlRewrite_lua_index_func(lua_State *L) {
bool is_cur_mapping_regex;
bool should_domap;
mapping_type maptype = FORWARD_MAP;
const char *func;
const char *mapfunc;
struct luabuilder_crutch *lbc;
lbc = (struct luabuilder_crutch *)lua_touserdata(L,1);
func = lua_tostring(L,2);
if(!strcmp(func,"definefilter") ||
!strcmp(func,"deletefilter") ||
!strcmp(func,"activatefilter") ||
!strcmp(func,"deactivatefilter")) {
lua_pushlightuserdata(L, lbc);
lua_pushstring(L, func);
lua_pushcclosure(L, UrlRewrite_lua_filterstuff, 2);
return 1;
}
mapfunc = func;
should_domap = true;
is_cur_mapping_regex = (strncasecmp("regex_", mapfunc, 6) == 0);
if(is_cur_mapping_regex) mapfunc += 6;
if (!strcasecmp("reverse_map", mapfunc)) maptype = REVERSE_MAP;
else if (!strcasecmp("map", mapfunc)) maptype = FORWARD_MAP; // update late for optflg
else if (!strcasecmp("redirect", mapfunc)) maptype = PERMANENT_REDIRECT;
else if (!strcasecmp("redirect_temporary", mapfunc)) maptype = TEMPORARY_REDIRECT;
else if (!strcasecmp("map_with_referer", mapfunc)) maptype = FORWARD_MAP_REFERER;
else if (!strcasecmp("map_with_recv_port", mapfunc)) maptype = FORWARD_MAP_WITH_RECV_PORT;
else if (is_cur_mapping_regex) luaL_error(L, "ats.UrlRewite doen't have a method '%s'", mapfunc);
else should_domap = false;
if(should_domap) {
lua_pushlightuserdata(L, lbc);
lua_pushinteger(L, maptype);
lua_pushboolean(L, is_cur_mapping_regex);
lua_pushcclosure(L, UrlRewrite_lua_mapstuff, 3);
return 1;
}
luaL_error(L, "ats.UrlRewite doen't have a method '%s'", func);
return 0;
}
void UrlRewrite::luaopen(lua_State *L) {
luaL_newmetatable(L, "ats.UrlRewrite");
lua_pushcclosure(L, UrlRewrite_lua_index_func, 0);
lua_setfield(L, -2, "__index");
}
/**
Runs lua to finiah building
@return zero on success and non-zero on failure.
*/
int
UrlRewrite::loadLua(BUILD_TABLE_INFO *bti)
{
lua_State *L = globalLuaConfig.getL();
struct luabuilder_crutch lbc;
lbc.urlrewrite = this;
lbc.bti = bti;
lua_pushlightuserdata(L, (void *)&lbc);
luaL_getmetatable(L, "ats.UrlRewrite");
lua_setmetatable(L, -2);
return globalLuaConfig.call(L, "config_remap", 1);
}
/**
Reads the configuration file and creates a new hash table.
@return zero on success and non-zero on failure.
*/
int
UrlRewrite::BuildTable()
{
BUILD_TABLE_INFO bti;
char *file_buf, errBuf[1024], errStrBuf[1024];
Tokenizer whiteTok(" \t");
bool alarm_already = false;
const char *errStr;
// Vars to parse line in file
char *tok_state, *cur_line, *cur_line_tmp;
int rparse, cur_line_size, cln = 0; // Our current line number
// Vars to build the mapping
const char *fromScheme, *toScheme;
int fromSchemeLen, toSchemeLen;
const char *fromHost, *toHost;
int fromHostLen, toHostLen;
char *map_from, *map_from_start;
char *map_to, *map_to_start;
const char *tmp; // Appease the DEC compiler
char *fromHost_lower = NULL;
char *fromHost_lower_ptr = NULL;
char fromHost_lower_buf[1024];
url_mapping *new_mapping = NULL;
mapping_type maptype;
referer_info *ri;
int origLength;
int length;
int tok_count;
RegexMapping* reg_map;
bool is_cur_mapping_regex;
const char *type_id_str;
bool add_result;
ink_assert(forward_mappings.empty());
ink_assert(reverse_mappings.empty());
ink_assert(permanent_redirects.empty());
ink_assert(temporary_redirects.empty());
ink_assert(forward_mappings_with_recv_port.empty());
ink_assert(num_rules_forward == 0);
ink_assert(num_rules_reverse == 0);
ink_assert(num_rules_redirect_permanent == 0);
ink_assert(num_rules_redirect_temporary == 0);
ink_assert(num_rules_forward_with_recv_port == 0);
memset(&bti, 0, sizeof(bti));
if ((file_buf = readIntoBuffer(config_file_path, modulePrefix, NULL)) == NULL) {
Warning("Can't load remapping configuration file - %s", config_file_path);
return 1;
}
forward_mappings.hash_lookup = ink_hash_table_create(InkHashTableKeyType_String);
reverse_mappings.hash_lookup = ink_hash_table_create(InkHashTableKeyType_String);
permanent_redirects.hash_lookup = ink_hash_table_create(InkHashTableKeyType_String);
temporary_redirects.hash_lookup = ink_hash_table_create(InkHashTableKeyType_String);
forward_mappings_with_recv_port.hash_lookup = ink_hash_table_create(InkHashTableKeyType_String);
bti.paramc = (bti.argc = 0);
memset(bti.paramv, 0, sizeof(bti.paramv));
memset(bti.argv, 0, sizeof(bti.argv));
Debug("url_rewrite", "[BuildTable] UrlRewrite::BuildTable()");
for (cur_line = tokLine(file_buf, &tok_state, '\\'); cur_line != NULL;) {
errStrBuf[0] = 0;
clear_xstr_array(bti.paramv, sizeof(bti.paramv) / sizeof(char *));
clear_xstr_array(bti.argv, sizeof(bti.argv) / sizeof(char *));
bti.paramc = (bti.argc = 0);
// Strip leading whitespace
while (*cur_line && isascii(*cur_line) && isspace(*cur_line))
++cur_line;
if ((cur_line_size = strlen((char *) cur_line)) <= 0) {
cur_line = tokLine(NULL, &tok_state, '\\');
++cln;
continue;
}
// Strip trailing whitespace
cur_line_tmp = cur_line + cur_line_size - 1;
while (cur_line_tmp != cur_line && isascii(*cur_line_tmp) && isspace(*cur_line_tmp)) {
*cur_line_tmp = '\0';
--cur_line_tmp;
}
if ((cur_line_size = strlen((char *) cur_line)) <= 0 || *cur_line == '#' || *cur_line == '\0') {
cur_line = tokLine(NULL, &tok_state, '\\');
++cln;
continue;
}
Debug("url_rewrite", "[BuildTable] Parsing: \"%s\"", cur_line);
tok_count = whiteTok.Initialize(cur_line, SHARE_TOKS);
for (int j = 0; j < tok_count; j++) {
if (((char *) whiteTok[j])[0] == '@') {
if (((char *) whiteTok[j])[1])
bti.argv[bti.argc++] = ats_strdup(&(((char *) whiteTok[j])[1]));
} else {
bti.paramv[bti.paramc++] = ats_strdup((char *) whiteTok[j]);
}
}
// Initial verification for number of arguments
if (bti.paramc<1 || (bti.paramc < 3 && bti.paramv[0][0] != '.') || bti.paramc> BUILD_TABLE_MAX_ARGS) {
snprintf(errBuf, sizeof(errBuf), "%s Malformed line %d in file %s", modulePrefix, cln + 1, config_file_path);
errStr = errStrBuf;
goto MAP_ERROR;
}
// just check all major flags/optional arguments
bti.remap_optflg = check_remap_option(bti.argv, bti.argc);
// Check directive keywords (starting from '.')
if (bti.paramv[0][0] == '.') {
if ((errStr = parse_directive(&bti, errStrBuf, sizeof(errStrBuf))) != NULL) {
snprintf(errBuf, sizeof(errBuf) - 1, "%s Error on line %d - %s", modulePrefix, cln + 1, errStr);
errStr = errStrBuf;
goto MAP_ERROR;
}
// We skip the rest of the parsing here.
cur_line = tokLine(NULL, &tok_state, '\\');
++cln;
continue;
}
is_cur_mapping_regex = (strncasecmp("regex_", bti.paramv[0], 6) == 0);
type_id_str = is_cur_mapping_regex ? (bti.paramv[0] + 6) : bti.paramv[0];
// Check to see whether is a reverse or forward mapping
if (!strcasecmp("reverse_map", type_id_str)) {
Debug("url_rewrite", "[BuildTable] - REVERSE_MAP");
maptype = REVERSE_MAP;
} else if (!strcasecmp("map", type_id_str)) {
Debug("url_rewrite", "[BuildTable] - %s",
((bti.remap_optflg & REMAP_OPTFLG_MAP_WITH_REFERER) == 0) ? "FORWARD_MAP" : "FORWARD_MAP_REFERER");
maptype = ((bti.remap_optflg & REMAP_OPTFLG_MAP_WITH_REFERER) == 0) ? FORWARD_MAP : FORWARD_MAP_REFERER;
} else if (!strcasecmp("redirect", type_id_str)) {
Debug("url_rewrite", "[BuildTable] - PERMANENT_REDIRECT");
maptype = PERMANENT_REDIRECT;
} else if (!strcasecmp("redirect_temporary", type_id_str)) {
Debug("url_rewrite", "[BuildTable] - TEMPORARY_REDIRECT");
maptype = TEMPORARY_REDIRECT;
} else if (!strcasecmp("map_with_referer", type_id_str)) {
Debug("url_rewrite", "[BuildTable] - FORWARD_MAP_REFERER");
maptype = FORWARD_MAP_REFERER;
} else if (!strcasecmp("map_with_recv_port", type_id_str)) {
Debug("url_rewrite", "[BuildTable] - FORWARD_MAP_WITH_RECV_PORT");
maptype = FORWARD_MAP_WITH_RECV_PORT;
} else {
snprintf(errBuf, sizeof(errBuf) - 1, "%s Unknown mapping type at line %d", modulePrefix, cln + 1);
errStr = errStrBuf;
goto MAP_ERROR;
}
new_mapping = NEW(new url_mapping(cln)); // use line # for rank for now
// apply filter rules if we have to
if ((errStr = process_filter_opt(new_mapping, &bti, errStrBuf, sizeof(errStrBuf))) != NULL) {
goto MAP_ERROR;
}
new_mapping->map_id = 0;
if ((bti.remap_optflg & REMAP_OPTFLG_MAP_ID) != 0) {
int idx = 0;
char *c;
int ret = check_remap_option(bti.argv, bti.argc, REMAP_OPTFLG_MAP_ID, &idx);
if (ret & REMAP_OPTFLG_MAP_ID) {
c = strchr(bti.argv[idx], (int) '=');
new_mapping->map_id = (unsigned int) atoi(++c);
}
}
map_from = bti.paramv[1];
length = UrlWhack(map_from, &origLength);
// FIX --- what does this comment mean?
//
// URL::create modified map_from so keep a point to
// the beginning of the string
if ((tmp = (map_from_start = map_from)) != NULL && length > 2 && tmp[length - 1] == '/' && tmp[length - 2] == '/') {
new_mapping->unique = true;
length -= 2;
}
new_mapping->fromURL.create(NULL);
rparse = new_mapping->fromURL.parse_no_path_component_breakdown(tmp, length);
map_from_start[origLength] = '\0'; // Unwhack
if (rparse != PARSE_DONE) {
errStr = "Malformed From URL";
goto MAP_ERROR;
}
map_to = bti.paramv[2];
length = UrlWhack(map_to, &origLength);
map_to_start = map_to;
tmp = map_to;
new_mapping->toUrl.create(NULL);
rparse = new_mapping->toUrl.parse_no_path_component_breakdown(tmp, length);
map_to_start[origLength] = '\0'; // Unwhack
if (rparse != PARSE_DONE) {
errStr = "Malformed To URL";
goto MAP_ERROR;
}
fromScheme = new_mapping->fromURL.scheme_get(&fromSchemeLen);
// If the rule is "/" or just some other relative path
// we need to default the scheme to http
if (fromScheme == NULL || fromSchemeLen == 0) {
new_mapping->fromURL.scheme_set(URL_SCHEME_HTTP, URL_LEN_HTTP);
fromScheme = new_mapping->fromURL.scheme_get(&fromSchemeLen);
new_mapping->wildcard_from_scheme = true;
}
toScheme = new_mapping->toUrl.scheme_get(&toSchemeLen);
// Include support for HTTPS scheme
// includes support for FILE scheme
if ((fromScheme != URL_SCHEME_HTTP && fromScheme != URL_SCHEME_HTTPS &&
fromScheme != URL_SCHEME_FILE &&
fromScheme != URL_SCHEME_TUNNEL) ||
(toScheme != URL_SCHEME_HTTP && toScheme != URL_SCHEME_HTTPS &&
toScheme != URL_SCHEME_TUNNEL)) {
errStr = "Only http, https, and tunnel remappings are supported";
goto MAP_ERROR;
}
// Check if a tag is specified.
if (bti.paramv[3] != NULL) {
if (maptype == FORWARD_MAP_REFERER) {
new_mapping->filter_redirect_url = ats_strdup(bti.paramv[3]);
if (!strcasecmp(bti.paramv[3], "<default>") || !strcasecmp(bti.paramv[3], "default") ||
!strcasecmp(bti.paramv[3], "<default_redirect_url>") || !strcasecmp(bti.paramv[3], "default_redirect_url"))
new_mapping->default_redirect_url = true;
new_mapping->redir_chunk_list = redirect_tag_str::parse_format_redirect_url(bti.paramv[3]);
for (int j = bti.paramc; j > 4; j--) {
if (bti.paramv[j - 1] != NULL) {
char refinfo_error_buf[1024];
bool refinfo_error = false;
ri = NEW(new referer_info((char *) bti.paramv[j - 1], &refinfo_error, refinfo_error_buf,
sizeof(refinfo_error_buf)));
if (refinfo_error) {
snprintf(errBuf, sizeof(errBuf), "%s Incorrect Referer regular expression \"%s\" at line %d - %s",
modulePrefix, bti.paramv[j - 1], cln + 1, refinfo_error_buf);
SignalError(errBuf, alarm_already);
delete ri;
ri = 0;
}
if (ri && ri->negative) {
if (ri->any) {
new_mapping->optional_referer = true; /* referer header is optional */
delete ri;
ri = 0;
} else {
new_mapping->negative_referer = true; /* we have negative referer in list */
}
}
if (ri) {
ri->next = new_mapping->referer_list;
new_mapping->referer_list = ri;
}
}
}
} else {
new_mapping->tag = ats_strdup(&(bti.paramv[3][0]));
}
}
// Check to see the fromHost remapping is a relative one
fromHost = new_mapping->fromURL.host_get(&fromHostLen);
if (fromHost == NULL || fromHostLen <= 0) {
if (maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT) {
if (*map_from_start != '/') {
errStr = "Relative remappings must begin with a /";
goto MAP_ERROR;
} else {
fromHost = "";
fromHostLen = 0;
}
} else {
errStr = "Remap source in reverse mappings requires a hostname";
goto MAP_ERROR;
}
}
toHost = new_mapping->toUrl.host_get(&toHostLen);
if (toHost == NULL || toHostLen <= 0) {
errStr = "The remap destinations require a hostname";
goto MAP_ERROR;
}
// Get rid of trailing slashes since they interfere
// with our ability to send redirects
// You might be tempted to remove these lines but the new
// optimized header system will introduce problems. You
// might get two slashes occasionally instead of one because
// the rest of the system assumes that trailing slashes have
// been removed.
if (unlikely(fromHostLen >= (int) sizeof(fromHost_lower_buf))) {
fromHost_lower = (fromHost_lower_ptr = (char *)ats_malloc(fromHostLen + 1));
} else {
fromHost_lower = &fromHost_lower_buf[0];
}
// Canonicalize the hostname by making it lower case
memcpy(fromHost_lower, fromHost, fromHostLen);
fromHost_lower[fromHostLen] = 0;
LowerCaseStr(fromHost_lower);
// set the normalized string so nobody else has to normalize this
new_mapping->fromURL.host_set(fromHost_lower, fromHostLen);
reg_map = NULL;
if (is_cur_mapping_regex) {
reg_map = NEW(new RegexMapping);
if (!_processRegexMappingConfig(fromHost_lower, new_mapping, reg_map)) {
errStr = "Could not process regex mapping config line";
goto MAP_ERROR;
}
Debug("url_rewrite_regex", "Configured regex rule for host [%s]", fromHost_lower);
}
// If a TS receives a request on a port which is set to tunnel mode
// (ie, blind forwarding) and a client connects directly to the TS,
// then the TS will use its IPv4 address and remap rules given
// to send the request to its proper destination.
// See HttpTransact::HandleBlindTunnel().
// Therefore, for a remap rule like "map tunnel://hostname..."
// in remap.config, we also needs to convert hostname to its IPv4 addr
// and gives a new remap rule with the IPv4 addr.
if ((maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT) &&
fromScheme == URL_SCHEME_TUNNEL && (fromHost_lower[0]<'0' || fromHost_lower[0]> '9')) {
addrinfo* ai_records; // returned records.
ip_text_buffer ipb; // buffer for address string conversion.
if (0 == getaddrinfo(fromHost_lower, 0, 0, &ai_records)) {
for ( addrinfo* ai_spot = ai_records ; ai_spot ; ai_spot = ai_spot->ai_next) {
if (ats_is_ip(ai_spot->ai_addr) &&
!ats_is_ip_any(ai_spot->ai_addr)) {
url_mapping *u_mapping;
ats_ip_ntop(ai_spot->ai_addr, ipb, sizeof ipb);
u_mapping = NEW(new url_mapping);
u_mapping->fromURL.create(NULL);
u_mapping->fromURL.copy(&new_mapping->fromURL);
u_mapping->fromURL.host_set(ipb, strlen(ipb));
u_mapping->toUrl.create(NULL);
u_mapping->toUrl.copy(&new_mapping->toUrl);
if (bti.paramv[3] != NULL)
u_mapping->tag = ats_strdup(&(bti.paramv[3][0]));
bool insert_result = (maptype != FORWARD_MAP_WITH_RECV_PORT) ?
TableInsert(forward_mappings.hash_lookup, u_mapping, ipb) :
TableInsert(forward_mappings_with_recv_port.hash_lookup, u_mapping, ipb);
if (!insert_result) {
errStr = "Unable to add mapping rule to lookup table";
goto MAP_ERROR;
}
(maptype != FORWARD_MAP_WITH_RECV_PORT) ? ++num_rules_forward : ++num_rules_forward_with_recv_port;
SetHomePageRedirectFlag(u_mapping, u_mapping->toUrl);
}
}
freeaddrinfo(ai_records);
}
}
// Check "remap" plugin options and load .so object
if ((bti.remap_optflg & REMAP_OPTFLG_PLUGIN) != 0 && (maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER ||
maptype == FORWARD_MAP_WITH_RECV_PORT)) {
if ((check_remap_option(bti.argv, bti.argc, REMAP_OPTFLG_PLUGIN, &tok_count) & REMAP_OPTFLG_PLUGIN) != 0) {
int plugin_found_at = 0;
int jump_to_argc = 0;
// this loads the first plugin
if (load_remap_plugin(bti.argv, bti.argc, new_mapping, errStrBuf, sizeof(errStrBuf), 0, &plugin_found_at)) {
Debug("remap_plugin", "Remap plugin load error - %s", errStrBuf[0] ? errStrBuf : "Unknown error");
errStr = errStrBuf;
goto MAP_ERROR;
}
//this loads any subsequent plugins (if present)
while (plugin_found_at) {
jump_to_argc += plugin_found_at;
if (load_remap_plugin(bti.argv, bti.argc, new_mapping, errStrBuf, sizeof(errStrBuf), jump_to_argc, &plugin_found_at)) {
Debug("remap_plugin", "Remap plugin load error - %s", errStrBuf[0] ? errStrBuf : "Unknown error");
errStr = errStrBuf;
goto MAP_ERROR;
}
}
}
}
// Now add the mapping to appropriate container
add_result = false;
switch (maptype) {
case FORWARD_MAP:
case FORWARD_MAP_REFERER:
if ((add_result = _addToStore(forward_mappings, new_mapping, reg_map, fromHost_lower,
is_cur_mapping_regex, num_rules_forward)) == true) {
// @todo: is this applicable to regex mapping too?
SetHomePageRedirectFlag(new_mapping, new_mapping->toUrl);
}
break;
case REVERSE_MAP:
add_result = _addToStore(reverse_mappings, new_mapping, reg_map, fromHost_lower,
is_cur_mapping_regex, num_rules_reverse);
new_mapping->homePageRedirect = false;
break;
case PERMANENT_REDIRECT:
add_result = _addToStore(permanent_redirects, new_mapping, reg_map, fromHost_lower,
is_cur_mapping_regex, num_rules_redirect_permanent);
break;
case TEMPORARY_REDIRECT:
add_result = _addToStore(temporary_redirects, new_mapping, reg_map, fromHost_lower,
is_cur_mapping_regex, num_rules_redirect_temporary);
break;
case FORWARD_MAP_WITH_RECV_PORT:
add_result = _addToStore(forward_mappings_with_recv_port, new_mapping, reg_map, fromHost_lower,
is_cur_mapping_regex, num_rules_forward_with_recv_port);
break;
default:
// 'default' required to avoid compiler warning; unsupported map
// type would have been dealt with much before this
break;
}
if (!add_result) {
errStr = "Unable to add mapping rule to lookup table";
goto MAP_ERROR;
}
fromHost_lower_ptr = (char *)ats_free_null(fromHost_lower_ptr);
cur_line = tokLine(NULL, &tok_state, '\\');
++cln;
continue;
// Deal with error / warning scenarios
MAP_ERROR:
Warning("Could not add rule at line #%d; Aborting!", cln + 1);
snprintf(errBuf, sizeof(errBuf), "%s %s at line %d", modulePrefix, errStr, cln + 1);
SignalError(errBuf, alarm_already);
return 2;
} /* end of while(cur_line != NULL) */
clear_xstr_array(bti.paramv, sizeof(bti.paramv) / sizeof(char *));
clear_xstr_array(bti.argv, sizeof(bti.argv) / sizeof(char *));
bti.paramc = (bti.argc = 0);
if(loadLua(&bti)) {
Warning("Could not add rules via lua");
SignalError("Could not add rules via lua", alarm_already);
return 2;
}
// Add the mapping for backdoor urls if enabled.
// This needs to be before the default PAC mapping for ""
// since this is more specific
if (unlikely(backdoor_enabled)) {
new_mapping = SetupBackdoorMapping();
if (TableInsert(forward_mappings.hash_lookup, new_mapping, "")) {
num_rules_forward++;
} else {
Warning("Could not insert backdoor mapping into store");
delete new_mapping;
return 3;
}
}
// Add the default mapping to the manager PAC file
// if we need it
if (default_to_pac) {
new_mapping = SetupPacMapping();
if (TableInsert(forward_mappings.hash_lookup, new_mapping, "")) {
num_rules_forward++;
} else {
Warning("Could not insert pac mapping into store");
delete new_mapping;
return 3;
}
}
// Destroy unused tables
if (num_rules_forward == 0) {
forward_mappings.hash_lookup = ink_hash_table_destroy(forward_mappings.hash_lookup);
} else {
if (ink_hash_table_isbound(forward_mappings.hash_lookup, "")) {
nohost_rules = 1;
}
}
if (num_rules_reverse == 0) {
reverse_mappings.hash_lookup = ink_hash_table_destroy(reverse_mappings.hash_lookup);
}
if (num_rules_redirect_permanent == 0) {
permanent_redirects.hash_lookup = ink_hash_table_destroy(permanent_redirects.hash_lookup);
}
if (num_rules_redirect_temporary == 0) {
temporary_redirects.hash_lookup = ink_hash_table_destroy(temporary_redirects.hash_lookup);
}
if (num_rules_forward_with_recv_port == 0) {
forward_mappings_with_recv_port.hash_lookup = ink_hash_table_destroy(
forward_mappings_with_recv_port.hash_lookup);
}
ats_free(file_buf);
return 0;
}
/**
Inserts arg mapping in h_table with key src_host chaining the mapping
of existing entries bound to src_host if necessary.
*/
bool
UrlRewrite::TableInsert(InkHashTable *h_table, url_mapping *mapping, const char *src_host)
{
char src_host_tmp_buf[1];
UrlMappingPathIndex *ht_contents;
if (!src_host) {
src_host = &src_host_tmp_buf[0];
src_host_tmp_buf[0] = 0;
}
// Insert the new_mapping into hash table
if (ink_hash_table_lookup(h_table, src_host, (void**) &ht_contents)) {
// There is already a path index for this host
if (ht_contents == NULL) {
// why should this happen?
Warning("Found entry cannot be null!");
return false;
}
} else {
ht_contents = new UrlMappingPathIndex();
ink_hash_table_insert(h_table, src_host, ht_contents);
}
if (!ht_contents->Insert(mapping)) {
Warning("Could not insert new mapping");
return false;
}
return true;
}
int
UrlRewrite::load_remap_plugin(char *argv[], int argc, url_mapping *mp, char *errbuf, int errbufsize, int jump_to_argc,
int *plugin_found_at)
{
TSRemapInterface ri;
struct stat stat_buf;
remap_plugin_info *pi;
char *c, *err, tmpbuf[2048], *parv[1024], default_path[PATH_NAME_MAX];
char *new_argv[1024];
int idx = 0, retcode = 0;
int parc = 0;
*plugin_found_at = 0;
memset(parv, 0, sizeof(parv));
memset(new_argv, 0, sizeof(new_argv));
tmpbuf[0] = 0;
if (jump_to_argc != 0) {
argc -= jump_to_argc;
int i = 0;
while (argv[i + jump_to_argc]) {
new_argv[i] = argv[i + jump_to_argc];
i++;
}
argv = new_argv;
if (!check_remap_option(argv, argc, REMAP_OPTFLG_PLUGIN, &idx)) {
return -1;
}
} else {
if (unlikely(!mp || (check_remap_option(argv, argc, REMAP_OPTFLG_PLUGIN, &idx) & REMAP_OPTFLG_PLUGIN) == 0)) {
snprintf(errbuf, errbufsize, "Can't find remap plugin keyword or \"url_mapping\" is NULL");
return -1; /* incorrect input data - almost impossible case */
}
}
if (unlikely((c = strchr(argv[idx], (int) '=')) == NULL || !(*(++c)))) {
snprintf(errbuf, errbufsize, "Can't find remap plugin file name in \"@%s\"", argv[idx]);
return -2; /* incorrect input data */
}
if (stat(c, &stat_buf) != 0) {
const char *plugin_default_path = TSPluginDirGet();
// Try with the plugin path instead
if (strlen(c) + strlen(plugin_default_path) > (PATH_NAME_MAX - 1)) {
Debug("remap_plugin", "way too large a path specified for remap plugin");
return -3;
}
snprintf(default_path, PATH_NAME_MAX, "%s/%s", plugin_default_path, c);
Debug("remap_plugin", "attempting to stat default plugin path: %s", default_path);
if (stat(default_path, &stat_buf) == 0) {
Debug("remap_plugin", "stat successful on %s using that", default_path);
c = &default_path[0];
} else {
snprintf(errbuf, errbufsize, "Can't find remap plugin file \"%s\"", c);
return -3;
}
}
Debug("remap_plugin", "using path %s for plugin", c);
if (!remap_pi_list || (pi = remap_pi_list->find_by_path(c)) == 0) {
pi = NEW(new remap_plugin_info(c));
if (!remap_pi_list) {
remap_pi_list = pi;
} else {
remap_pi_list->add_to_list(pi);
}
Debug("remap_plugin", "New remap plugin info created for \"%s\"", c);
if ((pi->dlh = dlopen(c, RTLD_NOW)) == NULL) {
#if defined(freebsd) || defined(openbsd)
err = (char *)dlerror();
#else
err = dlerror();
#endif
snprintf(errbuf, errbufsize, "Can't load plugin \"%s\" - %s", c, err ? err : "Unknown dlopen() error");
return -4;
}
pi->fp_tsremap_init = (remap_plugin_info::_tsremap_init *) dlsym(pi->dlh, TSREMAP_FUNCNAME_INIT);
pi->fp_tsremap_done = (remap_plugin_info::_tsremap_done *) dlsym(pi->dlh, TSREMAP_FUNCNAME_DONE);
pi->fp_tsremap_new_instance = (remap_plugin_info::_tsremap_new_instance *) dlsym(pi->dlh, TSREMAP_FUNCNAME_NEW_INSTANCE);
pi->fp_tsremap_delete_instance = (remap_plugin_info::_tsremap_delete_instance *) dlsym(pi->dlh, TSREMAP_FUNCNAME_DELETE_INSTANCE);
pi->fp_tsremap_do_remap = (remap_plugin_info::_tsremap_do_remap *) dlsym(pi->dlh, TSREMAP_FUNCNAME_DO_REMAP);
pi->fp_tsremap_os_response = (remap_plugin_info::_tsremap_os_response *) dlsym(pi->dlh, TSREMAP_FUNCNAME_OS_RESPONSE);
if (!pi->fp_tsremap_init) {
snprintf(errbuf, errbufsize, "Can't find \"%s\" function in remap plugin \"%s\"", TSREMAP_FUNCNAME_INIT, c);
retcode = -10;
} else if (!pi->fp_tsremap_new_instance) {
snprintf(errbuf, errbufsize, "Can't find \"%s\" function in remap plugin \"%s\"",
TSREMAP_FUNCNAME_NEW_INSTANCE, c);
retcode = -11;
} else if (!pi->fp_tsremap_do_remap) {
snprintf(errbuf, errbufsize, "Can't find \"%s\" function in remap plugin \"%s\"", TSREMAP_FUNCNAME_DO_REMAP, c);
retcode = -12;
}
if (retcode) {
if (errbuf && errbufsize > 0)
Debug("remap_plugin", "%s", errbuf);
dlclose(pi->dlh);
pi->dlh = NULL;
return retcode;
}
memset(&ri, 0, sizeof(ri));
ri.size = sizeof(ri);
ri.tsremap_version = TSREMAP_VERSION;
// elevate the access to read files as root if compiled with capabilities, if not
// change the effective user to root
{
uint32_t elevate_access = 0;
REC_ReadConfigInteger(elevate_access, "proxy.config.plugin.load_elevated");
ElevateAccess access(elevate_access != 0);
if (pi->fp_tsremap_init(&ri, tmpbuf, sizeof(tmpbuf) - 1) != TS_SUCCESS) {
Warning("Failed to initialize plugin %s (non-zero retval) ... bailing out", pi->path);
return -5;
}
} // done elevating access
Debug("remap_plugin", "Remap plugin \"%s\" - initialization completed", c);
}
if (!pi->dlh) {
snprintf(errbuf, errbufsize, "Can't load plugin \"%s\"", c);
return -6;
}
if ((err = mp->fromURL.string_get(NULL)) == NULL) {
snprintf(errbuf, errbufsize, "Can't load fromURL from URL class");
return -7;
}
parv[parc++] = ats_strdup(err);
ats_free(err);
if ((err = mp->toUrl.string_get(NULL)) == NULL) {
snprintf(errbuf, errbufsize, "Can't load toURL from URL class");
return -7;
}
parv[parc++] = ats_strdup(err);
ats_free(err);
bool plugin_encountered = false;
// how many plugin parameters we have for this remapping
for (idx = 0; idx < argc && parc < (int) ((sizeof(parv) / sizeof(char *)) - 1); idx++) {
if (plugin_encountered && !strncasecmp("plugin=", argv[idx], 7) && argv[idx][7]) {
*plugin_found_at = idx;
break; //if there is another plugin, lets deal with that later
}
if (!strncasecmp("plugin=", argv[idx], 7)) {
plugin_encountered = true;
}
if (!strncasecmp("pparam=", argv[idx], 7) && argv[idx][7]) {
parv[parc++] = &(argv[idx][7]);
}
}
Debug("url_rewrite", "Viewing all parameters for config line");
for (int k = 0; k < argc; k++) {
Debug("url_rewrite", "Argument %d: %s", k, argv[k]);
}
Debug("url_rewrite", "Viewing parsed plugin parameters for %s: [%d]", pi->path, *plugin_found_at);
for (int k = 0; k < parc; k++) {
Debug("url_rewrite", "Argument %d: %s", k, parv[k]);
}
void* ih;
Debug("remap_plugin", "creating new plugin instance");
TSReturnCode res = TS_ERROR;
// elevate the access to read files as root if compiled with capabilities, if not
// change the effective user to root
{
uint32_t elevate_access = 0;
REC_ReadConfigInteger(elevate_access, "proxy.config.plugin.load_elevated");
ElevateAccess access(elevate_access != 0);
res = pi->fp_tsremap_new_instance(parc, parv, &ih, tmpbuf, sizeof(tmpbuf) - 1);
} // done elevating access
Debug("remap_plugin", "done creating new plugin instance");
ats_free(parv[0]); // fromURL
ats_free(parv[1]); // toURL
if (res != TS_SUCCESS) {
snprintf(errbuf, errbufsize, "Can't create new remap instance for plugin \"%s\" - %s", c,
tmpbuf[0] ? tmpbuf : "Unknown plugin error");
Warning("Failed to create new instance for plugin %s (not a TS_SUCCESS return)", pi->path);
return -8;
}
mp->add_plugin(pi, ih);
return 0;
}
/** First looks up the hash table for "simple" mappings and then the
regex mappings. Only higher-ranked regex mappings are examined if
a hash mapping is found; or else all regex mappings are examined
Returns highest-ranked mapping on success, NULL on failure
*/
bool
UrlRewrite::_mappingLookup(MappingsStore &mappings, URL *request_url,
int request_port, const char *request_host, int request_host_len,
UrlMappingContainer &mapping_container)
{
char request_host_lower[TS_MAX_HOST_NAME_LEN];
if (!request_host || !request_url ||
(request_host_len < 0) || (request_host_len >= TS_MAX_HOST_NAME_LEN)) {
Debug("url_rewrite", "Invalid arguments!");
return false;
}
// lowercase
for (int i = 0; i < request_host_len; ++i) {
request_host_lower[i] = tolower(request_host[i]);
}
request_host_lower[request_host_len] = 0;
bool retval = false;
int rank_ceiling = -1;
url_mapping *mapping = _tableLookup(mappings.hash_lookup, request_url, request_port, request_host_lower,
request_host_len);
if (mapping != NULL) {
rank_ceiling = mapping->getRank();
Debug("url_rewrite", "Found 'simple' mapping with rank %d", rank_ceiling);
mapping_container.set(mapping);
retval = true;
}
if (_regexMappingLookup(mappings.regex_list, request_url, request_port, request_host_lower, request_host_len,
rank_ceiling, mapping_container)) {
Debug("url_rewrite", "Using regex mapping with rank %d", (mapping_container.getMapping())->getRank());
retval = true;
}
return retval;
}
// does not null terminate return string
int
UrlRewrite::_expandSubstitutions(int *matches_info, const RegexMapping *reg_map,
const char *matched_string,
char *dest_buf, int dest_buf_size)
{
int cur_buf_size = 0;
int token_start = 0;
int n_bytes_needed;
int match_index;
for (int i = 0; i < reg_map->n_substitutions; ++i) {
// first copy preceding bytes
n_bytes_needed = reg_map->substitution_markers[i] - token_start;
if ((cur_buf_size + n_bytes_needed) > dest_buf_size) {
goto lOverFlow;
}
memcpy(dest_buf + cur_buf_size, reg_map->to_url_host_template + token_start, n_bytes_needed);
cur_buf_size += n_bytes_needed;
// then copy the sub pattern match
match_index = reg_map->substitution_ids[i] * 2;
n_bytes_needed = matches_info[match_index + 1] - matches_info[match_index];
if ((cur_buf_size + n_bytes_needed) > dest_buf_size) {
goto lOverFlow;
}
memcpy(dest_buf + cur_buf_size, matched_string + matches_info[match_index], n_bytes_needed);
cur_buf_size += n_bytes_needed;
token_start = reg_map->substitution_markers[i] + 2; // skip the place holder
}
// copy last few bytes (if any)
if (token_start < reg_map->to_url_host_template_len) {
n_bytes_needed = reg_map->to_url_host_template_len - token_start;
if ((cur_buf_size + n_bytes_needed) > dest_buf_size) {
goto lOverFlow;
}
memcpy(dest_buf + cur_buf_size, reg_map->to_url_host_template + token_start, n_bytes_needed);
cur_buf_size += n_bytes_needed;
}
Debug("url_rewrite_regex", "Expanded substitutions and returning string [%.*s] with length %d",
cur_buf_size, dest_buf, cur_buf_size);
return cur_buf_size;
lOverFlow:
Warning("Overflow while expanding substitutions");
return 0;
}
bool
UrlRewrite::_regexMappingLookup(RegexMappingList &regex_mappings, URL *request_url, int request_port,
const char *request_host, int request_host_len, int rank_ceiling,
UrlMappingContainer &mapping_container)
{
bool retval = false;
if (rank_ceiling == -1) { // we will now look at all regex mappings
rank_ceiling = INT_MAX;
Debug("url_rewrite_regex", "Going to match all regexes");
}
else {
Debug("url_rewrite_regex", "Going to match regexes with rank <= %d", rank_ceiling);
}
int request_scheme_len, reg_map_scheme_len;
const char *request_scheme = request_url->scheme_get(&request_scheme_len), *reg_map_scheme;
int request_path_len, reg_map_path_len;
const char *request_path = request_url->path_get(&request_path_len), *reg_map_path;
// Loop over the entire linked list, or until we're satisfied
forl_LL(RegexMapping, list_iter, regex_mappings) {
int reg_map_rank = list_iter->url_map->getRank();
if (reg_map_rank > rank_ceiling) {
break;
}
reg_map_scheme = list_iter->url_map->fromURL.scheme_get(&reg_map_scheme_len);
if ((request_scheme_len != reg_map_scheme_len) ||
strncmp(request_scheme, reg_map_scheme, request_scheme_len)) {
Debug("url_rewrite_regex", "Skipping regex with rank %d as scheme does not match request scheme",
reg_map_rank);
continue;
}
if (list_iter->url_map->fromURL.port_get() != request_port) {
Debug("url_rewrite_regex", "Skipping regex with rank %d as regex map port does not match request port. "
"regex map port: %d, request port %d",
reg_map_rank, list_iter->url_map->fromURL.port_get(), request_port);
continue;
}
reg_map_path = list_iter->url_map->fromURL.path_get(&reg_map_path_len);
if ((request_path_len < reg_map_path_len) ||
strncmp(reg_map_path, request_path, reg_map_path_len)) { // use the shorter path length here
Debug("url_rewrite_regex", "Skipping regex with rank %d as path does not cover request path",
reg_map_rank);
continue;
}
int matches_info[MAX_REGEX_SUBS * 3];
int match_result = pcre_exec(list_iter->re, list_iter->re_extra, request_host, request_host_len,
0, 0, matches_info, (sizeof(matches_info) / sizeof(int)));
if (match_result > 0) {
Debug("url_rewrite_regex", "Request URL host [%.*s] matched regex in mapping of rank %d "
"with %d possible substitutions", request_host_len, request_host, reg_map_rank, match_result);
mapping_container.set(list_iter->url_map);
char buf[4096];
int buf_len;
// Expand substitutions in the host field from the stored template
buf_len = _expandSubstitutions(matches_info, list_iter, request_host, buf, sizeof(buf));
URL *expanded_url = mapping_container.createNewToURL();
expanded_url->copy(&((list_iter->url_map)->toUrl));
expanded_url->host_set(buf, buf_len);
Debug("url_rewrite_regex", "Expanded toURL to [%.*s]",
expanded_url->length_get(), expanded_url->string_get_ref());
retval = true;
break;
} else if (match_result == PCRE_ERROR_NOMATCH) {
Debug("url_rewrite_regex", "Request URL host [%.*s] did NOT match regex in mapping of rank %d",
request_host_len, request_host, reg_map_rank);
} else {
Warning("pcre_exec() failed with error code %d", match_result);
break;
}
}
return retval;
}
void
UrlRewrite::_destroyList(RegexMappingList &mappings)
{
forl_LL(RegexMapping, list_iter, mappings) {
delete list_iter->url_map;
if (list_iter->re) {
pcre_free(list_iter->re);
}
if (list_iter->re_extra) {
pcre_free(list_iter->re_extra);
}
if (list_iter->to_url_host_template) {
ats_free(list_iter->to_url_host_template);
}
delete list_iter;
}
mappings.clear();
}
/** will process the regex mapping configuration and create objects in
output argument reg_map. It assumes existing data in reg_map is
inconsequential and will be perfunctorily null-ed;
*/
bool
UrlRewrite::_processRegexMappingConfig(const char *from_host_lower, url_mapping *new_mapping,
RegexMapping *reg_map)
{
const char *str;
int str_index;
const char *to_host;
int to_host_len;
int substitution_id;
int substitution_count = 0;
reg_map->re = NULL;
reg_map->re_extra = NULL;
reg_map->to_url_host_template = NULL;
reg_map->to_url_host_template_len = 0;
reg_map->n_substitutions = 0;
reg_map->url_map = new_mapping;
// using from_host_lower (and not new_mapping->fromURL.host_get())
// as this one will be NULL-terminated (required by pcre_compile)
reg_map->re = pcre_compile(from_host_lower, 0, &str, &str_index, NULL);
if (reg_map->re == NULL) {
Warning("pcre_compile failed! Regex has error starting at %s", from_host_lower + str_index);
goto lFail;
}
reg_map->re_extra = pcre_study(reg_map->re, 0, &str);
if ((reg_map->re_extra == NULL) && (str != NULL)) {
Warning("pcre_study failed with message [%s]", str);
goto lFail;
}
int n_captures;
if (pcre_fullinfo(reg_map->re, reg_map->re_extra, PCRE_INFO_CAPTURECOUNT, &n_captures) != 0) {
Warning("pcre_fullinfo failed!");
goto lFail;
}
if (n_captures >= MAX_REGEX_SUBS) { // off by one for $0 (implicit capture)
Warning("Regex has %d capturing subpatterns (including entire regex); Max allowed: %d",
n_captures + 1, MAX_REGEX_SUBS);
goto lFail;
}
to_host = new_mapping->toUrl.host_get(&to_host_len);
for (int i = 0; i < (to_host_len - 1); ++i) {
if (to_host[i] == '$') {
if (substitution_count > MAX_REGEX_SUBS) {
Warning("Cannot have more than %d substitutions in mapping with host [%s]",
MAX_REGEX_SUBS, from_host_lower);
goto lFail;
}
substitution_id = to_host[i + 1] - '0';
if ((substitution_id < 0) || (substitution_id > n_captures)) {
Warning("Substitution id [%c] has no corresponding capture pattern in regex [%s]",
to_host[i + 1], from_host_lower);
goto lFail;
}
reg_map->substitution_markers[reg_map->n_substitutions] = i;
reg_map->substitution_ids[reg_map->n_substitutions] = substitution_id;
++reg_map->n_substitutions;
}
}
// so the regex itself is stored in fromURL.host; string to match
// will be in the request; string to use for substitutions will be
// in this buffer
str = new_mapping->toUrl.host_get(&str_index); // reusing str and str_index
reg_map->to_url_host_template_len = str_index;
reg_map->to_url_host_template = static_cast<char *>(ats_malloc(str_index));
memcpy(reg_map->to_url_host_template, str, str_index);
return true;
lFail:
if (reg_map->re) {
pcre_free(reg_map->re);
reg_map->re = NULL;
}
if (reg_map->re_extra) {
pcre_free(reg_map->re_extra);
reg_map->re_extra = NULL;
}
if (reg_map->to_url_host_template) {
ats_free(reg_map->to_url_host_template);
reg_map->to_url_host_template = NULL;
reg_map->to_url_host_template_len = 0;
}
return false;
}