1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 #include "RemapProcessor.h"
25
26 RemapProcessor remapProcessor;
27 extern ClassAllocator<RemapPlugins> pluginAllocator;
28
29 /**
30 Most of this comes from UrlRewrite::Remap(). Generally, all this does
31 is set "map" to the appropriate entry from the HttpSM's leased m_remap
32 such that we will then have access to the correct url_mapping inside
33 perform_remap.
34
35 */
36 bool
setup_for_remap(HttpTransact::State * s,UrlRewrite * table)37 RemapProcessor::setup_for_remap(HttpTransact::State *s, UrlRewrite *table)
38 {
39 Debug("url_rewrite", "setting up for remap: %p", s);
40 URL *request_url = nullptr;
41 bool mapping_found = false;
42 HTTPHdr *request_header = &s->hdr_info.client_request;
43 char **redirect_url = &s->remap_redirect;
44 const char *request_host;
45 int request_host_len;
46 int request_port;
47 bool proxy_request = false;
48
49 s->reverse_proxy = table->reverse_proxy;
50 s->url_map.set(s->hdr_info.client_request.m_heap);
51
52 ink_assert(redirect_url != nullptr);
53
54 if (unlikely((table->num_rules_forward == 0) && (table->num_rules_forward_with_recv_port == 0))) {
55 ink_assert(table->forward_mappings.empty() && table->forward_mappings_with_recv_port.empty());
56 Debug("url_rewrite", "[lookup] No forward mappings found; Skipping...");
57 return false;
58 }
59
60 // Since we are called before request validity checking
61 // occurs, make sure that we have both a valid request
62 // header and a valid URL
63 if (unlikely(!request_header || (request_url = request_header->url_get()) == nullptr || !request_url->valid())) {
64 Error("NULL or invalid request data");
65 return false;
66 }
67
68 request_host = request_header->host_get(&request_host_len);
69 request_port = request_header->port_get();
70 proxy_request = request_header->is_target_in_url() || !s->reverse_proxy;
71 // Default to empty host.
72 if (!request_host) {
73 request_host = "";
74 request_host_len = 0;
75 }
76
77 Debug("url_rewrite", "[lookup] attempting %s lookup", proxy_request ? "proxy" : "normal");
78
79 if (table->num_rules_forward_with_recv_port) {
80 Debug("url_rewrite", "[lookup] forward mappings with recv port found; Using recv port %d", s->client_info.dst_addr.port());
81 if (table->forwardMappingWithRecvPortLookup(request_url, s->client_info.dst_addr.port(), request_host, request_host_len,
82 s->url_map)) {
83 Debug("url_rewrite", "Found forward mapping with recv port");
84 mapping_found = true;
85 } else if (table->num_rules_forward == 0) {
86 ink_assert(table->forward_mappings.empty());
87 Debug("url_rewrite", "No forward mappings left");
88 return false;
89 }
90 }
91
92 if (!mapping_found) {
93 mapping_found = table->forwardMappingLookup(request_url, request_port, request_host, request_host_len, s->url_map);
94 }
95
96 // If no rules match and we have a host, check empty host rules since
97 // they function as default rules for server requests.
98 // If there's no host, we've already done this.
99 if (!mapping_found && table->nohost_rules && request_host_len) {
100 Debug("url_rewrite", "[lookup] nothing matched");
101 mapping_found = table->forwardMappingLookup(request_url, 0, "", 0, s->url_map);
102 }
103
104 if (!proxy_request) { // do extra checks on a server request
105
106 // Save this information for later
107 // @amc: why is this done only for requests without a host in the URL?
108 s->hh_info.host_len = request_host_len;
109 s->hh_info.request_host = request_host;
110 s->hh_info.request_port = request_port;
111
112 if (mapping_found) {
113 // Downstream mapping logic (e.g., self::finish_remap())
114 // apparently assumes the presence of the target in the URL, so
115 // we need to copy it. Perhaps it's because it's simpler to just
116 // do the remap on the URL and then fix the field at the end.
117 request_header->set_url_target_from_host_field();
118 }
119 }
120
121 if (mapping_found) {
122 request_header->mark_target_dirty();
123 } else {
124 Debug("url_rewrite", "RemapProcessor::setup_for_remap did not find a mapping");
125 }
126
127 return mapping_found;
128 }
129
130 bool
finish_remap(HttpTransact::State * s,UrlRewrite * table)131 RemapProcessor::finish_remap(HttpTransact::State *s, UrlRewrite *table)
132 {
133 url_mapping *map = nullptr;
134 HTTPHdr *request_header = &s->hdr_info.client_request;
135 URL *request_url = request_header->url_get();
136 char **redirect_url = &s->remap_redirect;
137 char tmp_referer_buf[4096], tmp_redirect_buf[4096], tmp_buf[2048];
138 const char *remapped_host;
139 int remapped_host_len, tmp;
140 int from_len;
141 referer_info *ri;
142
143 map = s->url_map.getMapping();
144 if (!map) {
145 return false;
146 }
147
148 // if there is a configured next hop strategy, make it available in the state.
149 if (map->strategy) {
150 s->next_hop_strategy = map->strategy;
151 }
152 // Do fast ACL filtering (it is safe to check map here)
153 table->PerformACLFiltering(s, map);
154
155 // Check referer filtering rules
156 if ((s->filter_mask & URL_REMAP_FILTER_REFERER) != 0 && (ri = map->referer_list) != nullptr) {
157 const char *referer_hdr = nullptr;
158 int referer_len = 0;
159 bool enabled_flag = map->optional_referer ? true : false;
160
161 if (request_header->presence(MIME_PRESENCE_REFERER) &&
162 (referer_hdr = request_header->value_get(MIME_FIELD_REFERER, MIME_LEN_REFERER, &referer_len)) != nullptr) {
163 if (referer_len >= static_cast<int>(sizeof(tmp_referer_buf))) {
164 referer_len = static_cast<int>(sizeof(tmp_referer_buf) - 1);
165 }
166 memcpy(tmp_referer_buf, referer_hdr, referer_len);
167 tmp_referer_buf[referer_len] = 0;
168 for (enabled_flag = false; ri; ri = ri->next) {
169 if (ri->any) {
170 enabled_flag = true;
171 if (!map->negative_referer) {
172 break;
173 }
174 } else if (ri->regx_valid && (pcre_exec(ri->regx, nullptr, tmp_referer_buf, referer_len, 0, 0, nullptr, 0) != -1)) {
175 enabled_flag = ri->negative ? false : true;
176 break;
177 }
178 }
179 }
180
181 if (!enabled_flag) {
182 if (!map->default_redirect_url) {
183 if ((s->filter_mask & URL_REMAP_FILTER_REDIRECT_FMT) != 0 && map->redir_chunk_list) {
184 redirect_tag_str *rc;
185 tmp_redirect_buf[(tmp = 0)] = 0;
186
187 for (rc = map->redir_chunk_list; rc; rc = rc->next) {
188 char *c = nullptr;
189 switch (rc->type) {
190 case 's':
191 c = rc->chunk_str;
192 break;
193 case 'r':
194 c = (referer_len && referer_hdr) ? &tmp_referer_buf[0] : nullptr;
195 break;
196 case 'f':
197 case 't':
198 remapped_host = (rc->type == 'f') ?
199 map->fromURL.string_get_buf(tmp_buf, static_cast<int>(sizeof(tmp_buf)), &from_len) :
200 ((s->url_map).getToURL())->string_get_buf(tmp_buf, static_cast<int>(sizeof(tmp_buf)), &from_len);
201 if (remapped_host && from_len > 0) {
202 c = &tmp_buf[0];
203 }
204 break;
205 case 'o':
206 c = s->unmapped_url.string_get_ref(nullptr);
207 break;
208 };
209
210 if (c && tmp < static_cast<int>(sizeof(tmp_redirect_buf) - 1)) {
211 tmp += snprintf(&tmp_redirect_buf[tmp], sizeof(tmp_redirect_buf) - tmp, "%s", c);
212 }
213 }
214 tmp_redirect_buf[sizeof(tmp_redirect_buf) - 1] = 0;
215 *redirect_url = ats_strdup(tmp_redirect_buf);
216 }
217 } else {
218 *redirect_url = ats_strdup(table->http_default_redirect_url);
219 }
220
221 if (*redirect_url == nullptr) {
222 *redirect_url = ats_strdup(map->filter_redirect_url ? map->filter_redirect_url : table->http_default_redirect_url);
223 }
224 if (HTTP_STATUS_NONE == s->http_return_code) {
225 s->http_return_code = HTTP_STATUS_MOVED_TEMPORARILY;
226 }
227 return false;
228 }
229 }
230
231 // We also need to rewrite the "Host:" header if it exists and
232 // pristine host hdr is not enabled
233 int host_len;
234 const char *host_hdr = request_header->value_get(MIME_FIELD_HOST, MIME_LEN_HOST, &host_len);
235
236 if (request_url && host_hdr != nullptr && s->txn_conf->maintain_pristine_host_hdr == 0) {
237 if (is_debug_tag_set("url_rewrite")) {
238 int old_host_hdr_len;
239 char *old_host_hdr = const_cast<char *>(request_header->value_get(MIME_FIELD_HOST, MIME_LEN_HOST, &old_host_hdr_len));
240 if (old_host_hdr) {
241 Debug("url_rewrite", "Host: Header before rewrite %.*s", old_host_hdr_len, old_host_hdr);
242 }
243 }
244 //
245 // Create the new host header field being careful that our
246 // temporary buffer has adequate length
247 //
248 remapped_host = request_url->host_get(&remapped_host_len);
249
250 int remapped_port = request_url->port_get_raw();
251
252 char host_hdr_buf[TS_MAX_HOST_NAME_LEN];
253 if (TS_MAX_HOST_NAME_LEN > remapped_host_len) {
254 tmp = remapped_host_len;
255 memcpy(host_hdr_buf, remapped_host, remapped_host_len);
256 if (remapped_port) {
257 tmp += snprintf(host_hdr_buf + remapped_host_len, TS_MAX_HOST_NAME_LEN - remapped_host_len - 1, ":%d", remapped_port);
258 }
259 } else {
260 tmp = TS_MAX_HOST_NAME_LEN;
261 }
262
263 // It is possible that the hostname is too long. If it is punt,
264 // and remove the host header. If it is too long the HostDB
265 // won't be able to resolve it and the request will not go
266 // through
267 if (tmp >= TS_MAX_HOST_NAME_LEN) {
268 request_header->field_delete(MIME_FIELD_HOST, MIME_LEN_HOST);
269 Debug("url_rewrite", "Host: Header too long after rewrite");
270 } else {
271 Debug("url_rewrite", "Host: Header after rewrite %.*s", tmp, host_hdr_buf);
272 request_header->value_set(MIME_FIELD_HOST, MIME_LEN_HOST, host_hdr_buf, tmp);
273 }
274 }
275
276 request_header->mark_target_dirty();
277
278 return true;
279 }
280
281 Action *
perform_remap(Continuation * cont,HttpTransact::State * s)282 RemapProcessor::perform_remap(Continuation *cont, HttpTransact::State *s)
283 {
284 Debug("url_rewrite", "Beginning RemapProcessor::perform_remap");
285 HTTPHdr *request_header = &s->hdr_info.client_request;
286 URL *request_url = request_header->url_get();
287 url_mapping *map = s->url_map.getMapping();
288 host_hdr_info *hh_info = &(s->hh_info);
289
290 if (!map) {
291 Error("Could not find corresponding url_mapping for this transaction %p", s);
292 Debug("url_rewrite", "Could not find corresponding url_mapping for this transaction");
293 ink_assert(!"this should never happen -- call setup_for_remap first");
294 cont->handleEvent(EVENT_REMAP_ERROR, nullptr);
295 return ACTION_RESULT_DONE;
296 }
297
298 RemapPlugins plugins(s, request_url, request_header, hh_info);
299
300 while (!plugins.run_single_remap()) {
301 ; // EMPTY
302 }
303
304 return ACTION_RESULT_DONE;
305 }
306