blob: 50928862cb5fdea3906f88cba11fe2a60f313442 [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.
*/
#include "RemapProcessor.h"
RemapProcessor remapProcessor;
extern ClassAllocator<RemapPlugins> pluginAllocator;
int
RemapProcessor::start(int num_threads, size_t stacksize)
{
if (_use_separate_remap_thread) {
ET_REMAP = eventProcessor.spawn_event_threads("ET_REMAP", num_threads, stacksize); // ET_REMAP is a class member
}
return 0;
}
/**
Most of this comes from UrlRewrite::Remap(). Generally, all this does
is set "map" to the appropriate entry from the HttpSM's leased m_remap
such that we will then have access to the correct url_mapping inside
perform_remap.
*/
bool
RemapProcessor::setup_for_remap(HttpTransact::State *s, UrlRewrite *table)
{
Debug("url_rewrite", "setting up for remap: %p", s);
URL *request_url = nullptr;
bool mapping_found = false;
HTTPHdr *request_header = &s->hdr_info.client_request;
char **redirect_url = &s->remap_redirect;
const char *request_host;
int request_host_len;
int request_port;
bool proxy_request = false;
s->reverse_proxy = table->reverse_proxy;
s->url_map.set(s->hdr_info.client_request.m_heap);
ink_assert(redirect_url != nullptr);
if (unlikely((table->num_rules_forward == 0) && (table->num_rules_forward_with_recv_port == 0))) {
ink_assert(table->forward_mappings.empty() && table->forward_mappings_with_recv_port.empty());
Debug("url_rewrite", "[lookup] No forward mappings found; Skipping...");
return false;
}
// Since we are called before request validity checking
// occurs, make sure that we have both a valid request
// header and a valid URL
if (unlikely(!request_header || (request_url = request_header->url_get()) == nullptr || !request_url->valid())) {
Error("NULL or invalid request data");
return false;
}
request_host = request_header->host_get(&request_host_len);
request_port = request_header->port_get();
proxy_request = request_header->is_target_in_url() || !s->reverse_proxy;
// Default to empty host.
if (!request_host) {
request_host = "";
request_host_len = 0;
}
Debug("url_rewrite", "[lookup] attempting %s lookup", proxy_request ? "proxy" : "normal");
if (table->num_rules_forward_with_recv_port) {
Debug("url_rewrite", "[lookup] forward mappings with recv port found; Using recv port %d", s->client_info.dst_addr.port());
if (table->forwardMappingWithRecvPortLookup(request_url, s->client_info.dst_addr.port(), request_host, request_host_len,
s->url_map)) {
Debug("url_rewrite", "Found forward mapping with recv port");
mapping_found = true;
} else if (table->num_rules_forward == 0) {
ink_assert(table->forward_mappings.empty());
Debug("url_rewrite", "No forward mappings left");
return false;
}
}
if (!mapping_found) {
mapping_found = table->forwardMappingLookup(request_url, request_port, request_host, request_host_len, s->url_map);
}
// If no rules match and we have a host, check empty host rules since
// they function as default rules for server requests.
// If there's no host, we've already done this.
if (!mapping_found && table->nohost_rules && request_host_len) {
Debug("url_rewrite", "[lookup] nothing matched");
mapping_found = table->forwardMappingLookup(request_url, 0, "", 0, s->url_map);
}
if (!proxy_request) { // do extra checks on a server request
// Save this information for later
// @amc: why is this done only for requests without a host in the URL?
s->hh_info.host_len = request_host_len;
s->hh_info.request_host = request_host;
s->hh_info.request_port = request_port;
if (mapping_found) {
// Downstream mapping logic (e.g., self::finish_remap())
// apparently assumes the presence of the target in the URL, so
// we need to copy it. Perhaps it's because it's simpler to just
// do the remap on the URL and then fix the field at the end.
request_header->set_url_target_from_host_field();
}
}
if (mapping_found) {
request_header->mark_target_dirty();
} else {
Debug("url_rewrite", "RemapProcessor::setup_for_remap did not find a mapping");
}
return mapping_found;
}
bool
RemapProcessor::finish_remap(HttpTransact::State *s, UrlRewrite *table)
{
url_mapping *map = nullptr;
HTTPHdr *request_header = &s->hdr_info.client_request;
URL *request_url = request_header->url_get();
char **redirect_url = &s->remap_redirect;
char tmp_referer_buf[4096], tmp_redirect_buf[4096], tmp_buf[2048];
const char *remapped_host;
int remapped_host_len, tmp;
int from_len;
referer_info *ri;
map = s->url_map.getMapping();
if (!map) {
return false;
}
// Do fast ACL filtering (it is safe to check map here)
table->PerformACLFiltering(s, map);
// Check referer filtering rules
if ((s->filter_mask & URL_REMAP_FILTER_REFERER) != 0 && (ri = map->referer_list) != nullptr) {
const char *referer_hdr = nullptr;
int referer_len = 0;
bool enabled_flag = map->optional_referer ? true : false;
if (request_header->presence(MIME_PRESENCE_REFERER) &&
(referer_hdr = request_header->value_get(MIME_FIELD_REFERER, MIME_LEN_REFERER, &referer_len)) != nullptr) {
if (referer_len >= static_cast<int>(sizeof(tmp_referer_buf))) {
referer_len = static_cast<int>(sizeof(tmp_referer_buf) - 1);
}
memcpy(tmp_referer_buf, referer_hdr, referer_len);
tmp_referer_buf[referer_len] = 0;
for (enabled_flag = false; ri; ri = ri->next) {
if (ri->any) {
enabled_flag = true;
if (!map->negative_referer) {
break;
}
} else if (ri->regx_valid && (pcre_exec(ri->regx, nullptr, tmp_referer_buf, referer_len, 0, 0, nullptr, 0) != -1)) {
enabled_flag = ri->negative ? false : true;
break;
}
}
}
if (!enabled_flag) {
if (!map->default_redirect_url) {
if ((s->filter_mask & URL_REMAP_FILTER_REDIRECT_FMT) != 0 && map->redir_chunk_list) {
redirect_tag_str *rc;
tmp_redirect_buf[(tmp = 0)] = 0;
for (rc = map->redir_chunk_list; rc; rc = rc->next) {
char *c = nullptr;
switch (rc->type) {
case 's':
c = rc->chunk_str;
break;
case 'r':
c = (referer_len && referer_hdr) ? &tmp_referer_buf[0] : nullptr;
break;
case 'f':
case 't':
remapped_host = (rc->type == 'f') ?
map->fromURL.string_get_buf(tmp_buf, static_cast<int>(sizeof(tmp_buf)), &from_len) :
((s->url_map).getToURL())->string_get_buf(tmp_buf, static_cast<int>(sizeof(tmp_buf)), &from_len);
if (remapped_host && from_len > 0) {
c = &tmp_buf[0];
}
break;
case 'o':
c = s->unmapped_url.string_get_ref(nullptr);
break;
};
if (c && tmp < static_cast<int>(sizeof(tmp_redirect_buf) - 1)) {
tmp += snprintf(&tmp_redirect_buf[tmp], sizeof(tmp_redirect_buf) - tmp, "%s", c);
}
}
tmp_redirect_buf[sizeof(tmp_redirect_buf) - 1] = 0;
*redirect_url = ats_strdup(tmp_redirect_buf);
}
} else {
*redirect_url = ats_strdup(table->http_default_redirect_url);
}
if (*redirect_url == nullptr) {
*redirect_url = ats_strdup(map->filter_redirect_url ? map->filter_redirect_url : table->http_default_redirect_url);
}
if (HTTP_STATUS_NONE == s->http_return_code) {
s->http_return_code = HTTP_STATUS_MOVED_TEMPORARILY;
}
return false;
}
}
// We also need to rewrite the "Host:" header if it exists and
// pristine host hdr is not enabled
int host_len;
const char *host_hdr = request_header->value_get(MIME_FIELD_HOST, MIME_LEN_HOST, &host_len);
if (request_url && host_hdr != nullptr && s->txn_conf->maintain_pristine_host_hdr == 0) {
if (is_debug_tag_set("url_rewrite")) {
int old_host_hdr_len;
char *old_host_hdr = const_cast<char *>(request_header->value_get(MIME_FIELD_HOST, MIME_LEN_HOST, &old_host_hdr_len));
if (old_host_hdr) {
Debug("url_rewrite", "Host: Header before rewrite %.*s", old_host_hdr_len, old_host_hdr);
}
}
//
// Create the new host header field being careful that our
// temporary buffer has adequate length
//
remapped_host = request_url->host_get(&remapped_host_len);
int remapped_port = request_url->port_get_raw();
char host_hdr_buf[TS_MAX_HOST_NAME_LEN];
if (TS_MAX_HOST_NAME_LEN > remapped_host_len) {
tmp = remapped_host_len;
memcpy(host_hdr_buf, remapped_host, remapped_host_len);
if (remapped_port) {
tmp += snprintf(host_hdr_buf + remapped_host_len, TS_MAX_HOST_NAME_LEN - remapped_host_len - 1, ":%d", remapped_port);
}
} else {
tmp = TS_MAX_HOST_NAME_LEN;
}
// It is possible that the hostname is too long. If it is punt,
// and remove the host header. If it is too long the HostDB
// won't be able to resolve it and the request will not go
// through
if (tmp >= TS_MAX_HOST_NAME_LEN) {
request_header->field_delete(MIME_FIELD_HOST, MIME_LEN_HOST);
Debug("url_rewrite", "Host: Header too long after rewrite");
} else {
Debug("url_rewrite", "Host: Header after rewrite %.*s", tmp, host_hdr_buf);
request_header->value_set(MIME_FIELD_HOST, MIME_LEN_HOST, host_hdr_buf, tmp);
}
}
request_header->mark_target_dirty();
return true;
}
Action *
RemapProcessor::perform_remap(Continuation *cont, HttpTransact::State *s)
{
Debug("url_rewrite", "Beginning RemapProcessor::perform_remap");
HTTPHdr *request_header = &s->hdr_info.client_request;
URL *request_url = request_header->url_get();
url_mapping *map = s->url_map.getMapping();
host_hdr_info *hh_info = &(s->hh_info);
if (!map) {
Error("Could not find corresponding url_mapping for this transaction %p", s);
Debug("url_rewrite", "Could not find corresponding url_mapping for this transaction");
ink_assert(!"this should never happen -- call setup_for_remap first");
cont->handleEvent(EVENT_REMAP_ERROR, nullptr);
return ACTION_RESULT_DONE;
}
if (_use_separate_remap_thread) {
RemapPlugins *plugins = pluginAllocator.alloc();
plugins->setState(s);
plugins->setRequestUrl(request_url);
plugins->setRequestHeader(request_header);
plugins->setHostHeaderInfo(hh_info);
// Execute "inline" if not using separate remap threads.
ink_assert(cont->mutex->thread_holding == this_ethread());
plugins->mutex = cont->mutex;
plugins->action = cont;
SET_CONTINUATION_HANDLER(plugins, &RemapPlugins::run_remap);
eventProcessor.schedule_imm(plugins, ET_REMAP);
return &plugins->action;
} else {
RemapPlugins plugins(s, request_url, request_header, hh_info);
while (!plugins.run_single_remap()) {
; // EMPTY
}
return ACTION_RESULT_DONE;
}
}