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