• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

UrlRewrite.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   A brief file description
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
00022  */
00023 
00024 #include "UrlRewrite.h"
00025 #include "ProxyConfig.h"
00026 #include "ReverseProxy.h"
00027 #include "UrlMappingPathIndex.h"
00028 #include "RemapConfig.h"
00029 #include "I_Layout.h"
00030 
00031 #define modulePrefix "[ReverseProxy]"
00032 
00033 /**
00034   Determines where we are in a situation where a virtual path is
00035   being mapped to a server home page. If it is, we set a special flag
00036   instructing us to be on the lookout for the need to send a redirect
00037   to if the request URL is a object, opposed to a directory. We need
00038   the redirect for an object so that the browser is aware that it is
00039   real accessing a directory (albeit a virtual one).
00040 
00041 */
00042 static void
00043 SetHomePageRedirectFlag(url_mapping *new_mapping, URL &new_to_url)
00044 {
00045   int fromLen, toLen;
00046   const char *from_path = new_mapping->fromURL.path_get(&fromLen);
00047   const char *to_path = new_to_url.path_get(&toLen);
00048 
00049   new_mapping->homePageRedirect = (from_path && !to_path) ? true : false;
00050 }
00051 
00052 //
00053 // CTOR / DTOR for the UrlRewrite class.
00054 //
00055 UrlRewrite::UrlRewrite()
00056  : nohost_rules(0), reverse_proxy(0), backdoor_enabled(0),
00057    mgmt_autoconf_port(0), default_to_pac(0), default_to_pac_port(0), ts_name(NULL),
00058    http_default_redirect_url(NULL), num_rules_forward(0), num_rules_reverse(0), num_rules_redirect_permanent(0),
00059    num_rules_redirect_temporary(0), num_rules_forward_with_recv_port(0), _valid(false)
00060 {
00061 
00062   forward_mappings.hash_lookup = reverse_mappings.hash_lookup =
00063     permanent_redirects.hash_lookup = temporary_redirects.hash_lookup =
00064     forward_mappings_with_recv_port.hash_lookup = NULL;
00065 
00066   char * config_file = NULL;
00067   char * config_file_path = NULL;
00068 
00069   REC_ReadConfigStringAlloc(config_file, "proxy.config.url_remap.filename");
00070   if (config_file == NULL) {
00071     pmgmt->signalManager(MGMT_SIGNAL_CONFIG_ERROR, "Unable to find proxy.config.url_remap.filename");
00072     Warning("%s Unable to locate remap.config.  No remappings in effect", modulePrefix);
00073     return;
00074   }
00075 
00076   this->ts_name = NULL;
00077   REC_ReadConfigStringAlloc(this->ts_name, "proxy.config.proxy_name");
00078   if (this->ts_name == NULL) {
00079     pmgmt->signalManager(MGMT_SIGNAL_CONFIG_ERROR, "Unable to read proxy.config.proxy_name");
00080     Warning("%s Unable to determine proxy name.  Incorrect redirects could be generated", modulePrefix);
00081     this->ts_name = ats_strdup("");
00082   }
00083 
00084   this->http_default_redirect_url = NULL;
00085   REC_ReadConfigStringAlloc(this->http_default_redirect_url, "proxy.config.http.referer_default_redirect");
00086   if (this->http_default_redirect_url == NULL) {
00087     pmgmt->signalManager(MGMT_SIGNAL_CONFIG_ERROR, "Unable to read proxy.config.http.referer_default_redirect");
00088     Warning("%s Unable to determine default redirect url for \"referer\" filter.", modulePrefix);
00089     this->http_default_redirect_url = ats_strdup("http://www.apache.org");
00090   }
00091 
00092   REC_ReadConfigInteger(reverse_proxy, "proxy.config.reverse_proxy.enabled");
00093   REC_ReadConfigInteger(mgmt_autoconf_port, "proxy.config.admin.autoconf_port");
00094   REC_ReadConfigInteger(default_to_pac, "proxy.config.url_remap.default_to_server_pac");
00095   REC_ReadConfigInteger(default_to_pac_port, "proxy.config.url_remap.default_to_server_pac_port");
00096   REC_ReadConfigInteger(url_remap_mode, "proxy.config.url_remap.url_remap_mode");
00097   REC_ReadConfigInteger(backdoor_enabled, "proxy.config.url_remap.handle_backdoor_urls");
00098 
00099   config_file_path = Layout::relative_to(Layout::get()->sysconfdir, config_file);
00100 
00101   if (0 == this->BuildTable(config_file_path)) {
00102     _valid = true;
00103     if (is_debug_tag_set("url_rewrite")) {
00104       Print();
00105     }
00106   } else {
00107     Warning("something failed during BuildTable() -- check your remap plugins!");
00108   }
00109 
00110   ats_free(config_file_path);
00111   ats_free(config_file);
00112 }
00113 
00114 UrlRewrite::~UrlRewrite()
00115 {
00116   ats_free(this->ts_name);
00117   ats_free(this->http_default_redirect_url);
00118 
00119   DestroyStore(forward_mappings);
00120   DestroyStore(reverse_mappings);
00121   DestroyStore(permanent_redirects);
00122   DestroyStore(temporary_redirects);
00123   DestroyStore(forward_mappings_with_recv_port);
00124   _valid = false;
00125 }
00126 
00127 /** Sets the reverse proxy flag. */
00128 void
00129 UrlRewrite::SetReverseFlag(int flag)
00130 {
00131   reverse_proxy = flag;
00132   if (is_debug_tag_set("url_rewrite"))
00133     Print();
00134 }
00135 
00136 /**
00137   Allocaites via new, and setups the default mapping to the PAC generator
00138   port which is used to serve the PAC (proxy autoconfig) file.
00139 
00140 */
00141 url_mapping *
00142 UrlRewrite::SetupPacMapping()
00143 {
00144   const char *from_url = "http:///";
00145   const char *local_url = "http://127.0.0.1/";
00146 
00147   url_mapping *mapping;
00148   int pac_generator_port;
00149 
00150   mapping = new url_mapping;
00151 
00152   mapping->fromURL.create(NULL);
00153   mapping->fromURL.parse(from_url, strlen(from_url));
00154 
00155   mapping->toUrl.create(NULL);
00156   mapping->toUrl.parse(local_url, strlen(local_url));
00157 
00158   pac_generator_port = (default_to_pac_port < 0) ? mgmt_autoconf_port : default_to_pac_port;
00159 
00160   mapping->toUrl.port_set(pac_generator_port);
00161 
00162   return mapping;
00163 }
00164 
00165 /**
00166   Allocaites via new, and adds a mapping like this map /ink/rh
00167   http://{backdoor}/ink/rh
00168 
00169   These {backdoor} things are then rewritten in a request-hdr hook.  (In the
00170   future it might make sense to move the rewriting into HttpSM directly.)
00171 
00172 */
00173 url_mapping *
00174 UrlRewrite::SetupBackdoorMapping()
00175 {
00176   const char from_url[] = "/ink/rh";
00177   const char to_url[] = "http://{backdoor}/ink/rh";
00178 
00179   url_mapping *mapping = new url_mapping;
00180 
00181   mapping->fromURL.create(NULL);
00182   mapping->fromURL.parse(from_url, sizeof(from_url) - 1);
00183   mapping->fromURL.scheme_set(URL_SCHEME_HTTP, URL_LEN_HTTP);
00184 
00185   mapping->toUrl.create(NULL);
00186   mapping->toUrl.parse(to_url, sizeof(to_url) - 1);
00187 
00188   return mapping;
00189 }
00190 
00191 /** Deallocated a hash table and all the url_mappings in it. */
00192 void
00193 UrlRewrite::_destroyTable(InkHashTable *h_table)
00194 {
00195   InkHashTableEntry *ht_entry;
00196   InkHashTableIteratorState ht_iter;
00197   UrlMappingPathIndex *item;
00198 
00199   if (h_table != NULL) {        // Iterate over the hash tabel freeing up the all the url_mappings
00200     //   contained with in
00201     for (ht_entry = ink_hash_table_iterator_first(h_table, &ht_iter); ht_entry != NULL;) {
00202       item = (UrlMappingPathIndex *)ink_hash_table_entry_value(h_table, ht_entry);
00203       delete item;
00204       ht_entry = ink_hash_table_iterator_next(h_table, &ht_iter);
00205     }
00206     ink_hash_table_destroy(h_table);
00207   }
00208 }
00209 
00210 /** Debugging Method. */
00211 void
00212 UrlRewrite::Print()
00213 {
00214   printf("URL Rewrite table with %d entries\n", num_rules_forward + num_rules_reverse +
00215          num_rules_redirect_temporary + num_rules_redirect_permanent + num_rules_forward_with_recv_port);
00216   printf("  Reverse Proxy is %s\n", (reverse_proxy == 0) ? "Off" : "On");
00217 
00218   printf("  Forward Mapping Table with %d entries\n", num_rules_forward);
00219   PrintStore(forward_mappings);
00220 
00221   printf("  Reverse Mapping Table with %d entries\n", num_rules_reverse);
00222   PrintStore(reverse_mappings);
00223 
00224   printf("  Permanent Redirect Mapping Table with %d entries\n", num_rules_redirect_permanent);
00225   PrintStore(permanent_redirects);
00226 
00227   printf("  Temporary Redirect Mapping Table with %d entries\n", num_rules_redirect_temporary);
00228   PrintStore(temporary_redirects);
00229 
00230   printf("  Forward Mapping With Recv Port Table with %d entries\n", num_rules_forward_with_recv_port);
00231   PrintStore(forward_mappings_with_recv_port);
00232 
00233   if (http_default_redirect_url != NULL) {
00234     printf("  Referer filter default redirect URL: \"%s\"\n", http_default_redirect_url);
00235   }
00236 }
00237 
00238 /** Debugging method. */
00239 void
00240 UrlRewrite::PrintStore(MappingsStore &store)
00241 {
00242   if (store.hash_lookup != NULL) {
00243     InkHashTableEntry *ht_entry;
00244     InkHashTableIteratorState ht_iter;
00245     UrlMappingPathIndex *value;
00246 
00247     for (ht_entry = ink_hash_table_iterator_first(store.hash_lookup, &ht_iter); ht_entry != NULL;) {
00248       value = (UrlMappingPathIndex *) ink_hash_table_entry_value(store.hash_lookup, ht_entry);
00249       value->Print();
00250       ht_entry = ink_hash_table_iterator_next(store.hash_lookup, &ht_iter);
00251     }
00252   }
00253 
00254   if (!store.regex_list.empty()) {
00255     printf("    Regex mappings:\n");
00256     forl_LL(RegexMapping, list_iter, store.regex_list) {
00257       list_iter->url_map->Print();
00258     }
00259   }
00260 }
00261 
00262 /**
00263   If a remapping is found, returns a pointer to it otherwise NULL is
00264   returned.
00265 
00266 */
00267 url_mapping *
00268 UrlRewrite::_tableLookup(InkHashTable *h_table, URL *request_url,
00269                         int request_port, char *request_host, int request_host_len)
00270 {
00271   UrlMappingPathIndex *ht_entry;
00272   url_mapping *um = NULL;
00273   int ht_result;
00274 
00275   ht_result = ink_hash_table_lookup(h_table, request_host, (void **) &ht_entry);
00276 
00277   if (likely(ht_result && ht_entry)) {
00278     // for empty host don't do a normal search, get a mapping arbitrarily
00279     um = ht_entry->Search(request_url, request_port, request_host_len ? true : false);
00280   }
00281   return um;
00282 }
00283 
00284 // This is only used for redirects and reverse rules, and the homepageredirect flag
00285 // can never be set. The end result is that request_url is modified per remap container.
00286 void
00287 url_rewrite_remap_request(const UrlMappingContainer& mapping_container, URL *request_url)
00288 {
00289   const char *requestPath;
00290   int requestPathLen = 0;
00291   int fromPathLen = 0;
00292 
00293   URL *map_to = mapping_container.getToURL();
00294   URL *map_from = mapping_container.getFromURL();
00295   const char *toHost;
00296   const char *toPath;
00297   const char *toScheme;
00298   int toPathLen;
00299   int toHostLen;
00300   int toSchemeLen;
00301 
00302   map_from->path_get(&fromPathLen);
00303 
00304   toHost = map_to->host_get(&toHostLen);
00305   toPath = map_to->path_get(&toPathLen);
00306   toScheme = map_to->scheme_get(&toSchemeLen);
00307 
00308   Debug("url_rewrite", "%s: Remapping rule id: %d matched", __func__, mapping_container.getMapping()->map_id);
00309 
00310   request_url->host_set(toHost, toHostLen);
00311   request_url->port_set(map_to->port_get_raw());
00312   request_url->scheme_set(toScheme, toSchemeLen);
00313 
00314   requestPath = request_url->path_get(&requestPathLen);
00315 
00316   // Should be +3, little extra padding won't hurt. Use the stack allocation
00317   // for better performance (bummer that arrays of variable length is not supported
00318   // on Solaris CC.
00319   char *newPath = static_cast<char*>(alloca(sizeof(char*)*((requestPathLen - fromPathLen) + toPathLen + 8)));
00320   int newPathLen = 0;
00321 
00322   *newPath = 0;
00323   if (toPath) {
00324     memcpy(newPath, toPath, toPathLen);
00325     newPathLen += toPathLen;
00326   }
00327 
00328   // We might need to insert a trailing slash in the new portion of the path
00329   // if more will be added and none is present and one will be needed.
00330   if (!fromPathLen && requestPathLen && newPathLen && toPathLen && *(newPath + newPathLen - 1) != '/') {
00331     *(newPath + newPathLen) = '/';
00332     newPathLen++;
00333   }
00334 
00335   if (requestPath) {
00336     //avoid adding another trailing slash if the requestPath already had one and so does the toPath
00337     if (requestPathLen < fromPathLen) {
00338       if (toPathLen && requestPath[requestPathLen - 1] == '/' && toPath[toPathLen - 1] == '/') {
00339         fromPathLen++;
00340       }
00341     } else {
00342       if (toPathLen && requestPath[fromPathLen] == '/' && toPath[toPathLen - 1] == '/') {
00343         fromPathLen++;
00344       }
00345     }
00346 
00347     // copy the end of the path past what has been mapped
00348     if ((requestPathLen - fromPathLen) > 0) {
00349       memcpy(newPath + newPathLen, requestPath + fromPathLen, requestPathLen - fromPathLen);
00350       newPathLen += (requestPathLen - fromPathLen);
00351     }
00352   }
00353 
00354   // Skip any leading / in the path when setting the new URL path
00355   if (*newPath == '/') {
00356     request_url->path_set(newPath + 1, newPathLen - 1);
00357   } else {
00358     request_url->path_set(newPath, newPathLen);
00359   }
00360 }
00361 
00362 /** Used to do the backwards lookups. */
00363 #define N_URL_HEADERS 4
00364 bool
00365 UrlRewrite::ReverseMap(HTTPHdr *response_header)
00366 {
00367   const char *location_hdr;
00368   URL location_url;
00369   int loc_length;
00370   bool remap_found = false;
00371   const char *host;
00372   int host_len;
00373   char *new_loc_hdr;
00374   int new_loc_length;
00375   int i;
00376   const struct {
00377     const char *const field;
00378     const int len;
00379   } url_headers[N_URL_HEADERS] = {
00380     { MIME_FIELD_LOCATION, MIME_LEN_LOCATION } ,
00381     { MIME_FIELD_CONTENT_LOCATION, MIME_LEN_CONTENT_LOCATION } ,
00382     { "URI", 3 } ,
00383     { "Destination", 11 }
00384   };
00385 
00386   if (unlikely(num_rules_reverse == 0)) {
00387     ink_assert(reverse_mappings.empty());
00388     return false;
00389   }
00390 
00391   for (i = 0; i < N_URL_HEADERS; ++i) {
00392     location_hdr = response_header->value_get(url_headers[i].field, url_headers[i].len, &loc_length);
00393 
00394     if (location_hdr == NULL) {
00395       continue;
00396     }
00397 
00398     location_url.create(NULL);
00399     location_url.parse(location_hdr, loc_length);
00400 
00401     host = location_url.host_get(&host_len);
00402 
00403     UrlMappingContainer reverse_mapping(response_header->m_heap);
00404 
00405     if (reverseMappingLookup(&location_url, location_url.port_get(), host, host_len, reverse_mapping)) {
00406       if (i == 0)
00407         remap_found = true;
00408       url_rewrite_remap_request(reverse_mapping, &location_url);
00409       new_loc_hdr = location_url.string_get_ref(&new_loc_length);
00410       response_header->value_set(url_headers[i].field, url_headers[i].len, new_loc_hdr, new_loc_length);
00411     }
00412 
00413     location_url.destroy();
00414   }
00415   return remap_found;
00416 }
00417 
00418 /** Perform fast ACL filtering. */
00419 void
00420 UrlRewrite::PerformACLFiltering(HttpTransact::State *s, url_mapping *map)
00421 {
00422   if (unlikely(!s || s->acl_filtering_performed || !s->client_connection_enabled))
00423     return;
00424 
00425   s->acl_filtering_performed = true;    // small protection against reverse mapping
00426 
00427   if (map->filter) {
00428     int res;
00429     int method = s->hdr_info.client_request.method_get_wksidx();
00430     int method_wksidx = (method != -1) ? (method - HTTP_WKSIDX_CONNECT) : -1;
00431     bool client_enabled_flag = true;
00432     ink_release_assert(ats_is_ip(&s->client_info.addr));
00433     for (acl_filter_rule * rp = map->filter; rp; rp = rp->next) {
00434       bool match = true;
00435       if (rp->method_restriction_enabled) {
00436         if (method_wksidx != -1) {
00437           match = rp->standard_method_lookup[method_wksidx];
00438         }
00439         else if (!rp->nonstandard_methods.empty()) {
00440           int method_str_len;
00441           const char *method_str = s->hdr_info.client_request.method_get(&method_str_len);
00442           match = rp->nonstandard_methods.count(std::string(method_str, method_str_len));
00443         }
00444       }
00445       if (match && rp->src_ip_valid) {
00446         match = false;
00447         for (int j = 0; j < rp->src_ip_cnt && !match; j++) {
00448           res = rp->src_ip_array[j].contains(s->client_info.addr) ? 1 : 0;
00449           if (rp->src_ip_array[j].invert) {
00450             if (res != 1)
00451               match = true;
00452           } else {
00453             if (res == 1)
00454               match = true;
00455           }
00456         }
00457       }
00458       if (match && client_enabled_flag) {     //make sure that a previous filter did not DENY
00459         Debug("url_rewrite", "matched ACL filter rule, %s request", rp->allow_flag ? "allowing" : "denying");
00460         client_enabled_flag = rp->allow_flag ? true : false;
00461       } else {
00462         if (!client_enabled_flag) {
00463           Debug("url_rewrite", "Previous ACL filter rule denied request, continuing to deny it");
00464         } else {
00465           Debug("url_rewrite", "did NOT match ACL filter rule, %s request", rp->allow_flag ? "denying" : "allowing");
00466           client_enabled_flag = rp->allow_flag ? false : true;
00467         }
00468       }
00469       
00470     }                         /* end of for(rp = map->filter;rp;rp = rp->next) */
00471     s->client_connection_enabled = client_enabled_flag;
00472   }
00473 }
00474 
00475 /**
00476    Determines if a redirect is to occur and if so, figures out what the
00477    redirect is. This was plaguiarized from UrlRewrite::Remap. redirect_url
00478    ought to point to the new, mapped URL when the function exits.
00479 */
00480 mapping_type
00481 UrlRewrite::Remap_redirect(HTTPHdr *request_header, URL *redirect_url)
00482 {
00483   URL *request_url;
00484   mapping_type mappingType;
00485   const char *host = NULL;
00486   int host_len = 0, request_port = 0;
00487   bool prt, trt;                        // existence of permanent and temporary redirect tables, respectively
00488 
00489   prt = (num_rules_redirect_permanent != 0);
00490   trt = (num_rules_redirect_temporary != 0);
00491 
00492   if (prt + trt == 0)
00493     return NONE;
00494 
00495   // Since are called before request validity checking
00496   //  occurs, make sure that we have both a valid request
00497   //  header and a valid URL
00498   //
00499   if (request_header == NULL) {
00500     Debug("url_rewrite", "request_header was invalid.  UrlRewrite::Remap_redirect bailing out.");
00501     return NONE;
00502   }
00503   request_url = request_header->url_get();
00504   if (!request_url->valid()) {
00505     Debug("url_rewrite", "request_url was invalid.  UrlRewrite::Remap_redirect bailing out.");
00506     return NONE;
00507   }
00508 
00509   host = request_url->host_get(&host_len);
00510   request_port = request_url->port_get();
00511 
00512   if (host_len == 0 && reverse_proxy != 0) {    // Server request.  Use the host header to figure out where
00513                                                 // it goes.  Host header parsing is same as in ::Remap
00514     int host_hdr_len;
00515     const char *host_hdr = request_header->value_get(MIME_FIELD_HOST, MIME_LEN_HOST, &host_hdr_len);
00516 
00517     if (!host_hdr) {
00518       host_hdr = "";
00519       host_hdr_len = 0;
00520     }
00521 
00522     const char *tmp = (const char *) memchr(host_hdr, ':', host_hdr_len);
00523 
00524     if (tmp == NULL) {
00525       host_len = host_hdr_len;
00526     } else {
00527       host_len = tmp - host_hdr;
00528       request_port = ink_atoi(tmp + 1, host_hdr_len - host_len);
00529 
00530       // If atoi fails, try the default for the
00531       //   protocol
00532       if (request_port == 0) {
00533         request_port = request_url->port_get();
00534       }
00535     }
00536 
00537     host = host_hdr;
00538   }
00539   // Temporary Redirects have precedence over Permanent Redirects
00540   // the rationale behind this is that network administrators might
00541   // want quick redirects and not want to worry about all the existing
00542   // permanent rules
00543   mappingType = NONE;
00544 
00545   UrlMappingContainer redirect_mapping(request_header->m_heap);
00546 
00547   if (trt) {
00548     if (temporaryRedirectLookup(request_url, request_port, host, host_len, redirect_mapping)) {
00549       mappingType = TEMPORARY_REDIRECT;
00550     }
00551   }
00552   if ((mappingType == NONE) && prt) {
00553     if (permanentRedirectLookup(request_url, request_port, host, host_len, redirect_mapping)) {
00554       mappingType = PERMANENT_REDIRECT;
00555     }
00556   }
00557 
00558   if (mappingType != NONE) {
00559     ink_assert((mappingType == PERMANENT_REDIRECT) || (mappingType == TEMPORARY_REDIRECT));
00560 
00561     // Make a copy of the request url so that we can munge it
00562     //   for the redirect
00563     redirect_url->create(NULL);
00564     redirect_url->copy(request_url);
00565 
00566     // Perform the actual URL rewrite
00567     url_rewrite_remap_request(redirect_mapping, redirect_url);
00568 
00569     return mappingType;
00570   }
00571   ink_assert(mappingType == NONE);
00572 
00573   return NONE;
00574 }
00575 
00576 bool
00577 UrlRewrite::_addToStore(MappingsStore &store, url_mapping *new_mapping, RegexMapping *reg_map,
00578                         const char * src_host, bool is_cur_mapping_regex, int &count)
00579 {
00580   bool retval;
00581   if (is_cur_mapping_regex) {
00582     store.regex_list.enqueue(reg_map);
00583     retval = true;
00584   } else {
00585     retval = TableInsert(store.hash_lookup, new_mapping, src_host);
00586   }
00587   if (retval) {
00588     ++count;
00589   }
00590   return retval;
00591 }
00592 
00593 bool
00594 UrlRewrite::InsertMapping(mapping_type maptype, url_mapping *new_mapping, RegexMapping *reg_map,
00595                         const char * src_host, bool is_cur_mapping_regex)
00596 {
00597   bool success = false;
00598 
00599   // Now add the mapping to appropriate container
00600   switch (maptype) {
00601   case FORWARD_MAP:
00602   case FORWARD_MAP_REFERER:
00603     success = _addToStore(forward_mappings, new_mapping, reg_map, src_host,
00604                                   is_cur_mapping_regex, num_rules_forward);
00605     if (success) {
00606       // @todo: is this applicable to regex mapping too?
00607       SetHomePageRedirectFlag(new_mapping, new_mapping->toUrl);
00608     }
00609     break;
00610   case REVERSE_MAP:
00611     success = _addToStore(reverse_mappings, new_mapping, reg_map, src_host,
00612                              is_cur_mapping_regex, num_rules_reverse);
00613     new_mapping->homePageRedirect = false;
00614     break;
00615   case PERMANENT_REDIRECT:
00616     success = _addToStore(permanent_redirects, new_mapping, reg_map, src_host,
00617                              is_cur_mapping_regex, num_rules_redirect_permanent);
00618     break;
00619   case TEMPORARY_REDIRECT:
00620     success = _addToStore(temporary_redirects, new_mapping, reg_map, src_host,
00621                              is_cur_mapping_regex, num_rules_redirect_temporary);
00622     break;
00623   case FORWARD_MAP_WITH_RECV_PORT:
00624     success = _addToStore(forward_mappings_with_recv_port, new_mapping, reg_map, src_host,
00625                              is_cur_mapping_regex, num_rules_forward_with_recv_port);
00626     break;
00627   default:
00628     // 'default' required to avoid compiler warning; unsupported map
00629     // type would have been dealt with much before this
00630     return false;
00631   }
00632 
00633   return success;
00634 }
00635 
00636 bool
00637 UrlRewrite::InsertForwardMapping(mapping_type maptype, url_mapping * mapping, const char * src_host)
00638 {
00639   bool success;
00640 
00641   if (maptype == FORWARD_MAP_WITH_RECV_PORT) {
00642     success = TableInsert(forward_mappings_with_recv_port.hash_lookup, mapping, src_host);
00643   } else {
00644     success = TableInsert(forward_mappings.hash_lookup, mapping, src_host);
00645   }
00646 
00647   if (success) {
00648     switch (maptype) {
00649       case FORWARD_MAP:
00650       case FORWARD_MAP_REFERER:
00651       case FORWARD_MAP_WITH_RECV_PORT:
00652         SetHomePageRedirectFlag(mapping, mapping->toUrl);
00653         break;
00654       default:
00655         break;
00656     }
00657 
00658     (maptype != FORWARD_MAP_WITH_RECV_PORT) ? ++num_rules_forward
00659                                             : ++num_rules_forward_with_recv_port;
00660   }
00661 
00662   return success;
00663 }
00664 
00665 /**
00666   Reads the configuration file and creates a new hash table.
00667 
00668   @return zero on success and non-zero on failure.
00669 
00670 */
00671 int
00672 UrlRewrite::BuildTable(const char * path)
00673 {
00674   BUILD_TABLE_INFO bti;
00675   url_mapping * new_mapping = NULL;
00676 
00677   ink_assert(forward_mappings.empty());
00678   ink_assert(reverse_mappings.empty());
00679   ink_assert(permanent_redirects.empty());
00680   ink_assert(temporary_redirects.empty());
00681   ink_assert(forward_mappings_with_recv_port.empty());
00682   ink_assert(num_rules_forward == 0);
00683   ink_assert(num_rules_reverse == 0);
00684   ink_assert(num_rules_redirect_permanent == 0);
00685   ink_assert(num_rules_redirect_temporary == 0);
00686   ink_assert(num_rules_forward_with_recv_port == 0);
00687 
00688 
00689   forward_mappings.hash_lookup = ink_hash_table_create(InkHashTableKeyType_String);
00690   reverse_mappings.hash_lookup = ink_hash_table_create(InkHashTableKeyType_String);
00691   permanent_redirects.hash_lookup = ink_hash_table_create(InkHashTableKeyType_String);
00692   temporary_redirects.hash_lookup = ink_hash_table_create(InkHashTableKeyType_String);
00693   forward_mappings_with_recv_port.hash_lookup = ink_hash_table_create(InkHashTableKeyType_String);
00694 
00695   if (!remap_parse_config(path, this)) {
00696     // XXX handle file reload error
00697     return 3;
00698   }
00699 
00700   // Add the mapping for backdoor urls if enabled.
00701   // This needs to be before the default PAC mapping for ""
00702   // since this is more specific
00703   if (unlikely(backdoor_enabled)) {
00704     new_mapping = SetupBackdoorMapping();
00705     if (TableInsert(forward_mappings.hash_lookup, new_mapping, "")) {
00706       num_rules_forward++;
00707     } else {
00708       Warning("Could not insert backdoor mapping into store");
00709       delete new_mapping;
00710       return 3;
00711     }
00712   }
00713   // Add the default mapping to the manager PAC file
00714   //  if we need it
00715   if (default_to_pac) {
00716     new_mapping = SetupPacMapping();
00717     if (TableInsert(forward_mappings.hash_lookup, new_mapping, "")) {
00718       num_rules_forward++;
00719     } else {
00720       Warning("Could not insert pac mapping into store");
00721       delete new_mapping;
00722       return 3;
00723     }
00724   }
00725   // Destroy unused tables
00726   if (num_rules_forward == 0) {
00727     forward_mappings.hash_lookup = ink_hash_table_destroy(forward_mappings.hash_lookup);
00728   } else {
00729     if (ink_hash_table_isbound(forward_mappings.hash_lookup, "")) {
00730       nohost_rules = 1;
00731     }
00732   }
00733 
00734   if (num_rules_reverse == 0) {
00735     reverse_mappings.hash_lookup = ink_hash_table_destroy(reverse_mappings.hash_lookup);
00736   }
00737 
00738   if (num_rules_redirect_permanent == 0) {
00739     permanent_redirects.hash_lookup = ink_hash_table_destroy(permanent_redirects.hash_lookup);
00740   }
00741 
00742   if (num_rules_redirect_temporary == 0) {
00743     temporary_redirects.hash_lookup = ink_hash_table_destroy(temporary_redirects.hash_lookup);
00744   }
00745 
00746   if (num_rules_forward_with_recv_port == 0) {
00747     forward_mappings_with_recv_port.hash_lookup = ink_hash_table_destroy(
00748       forward_mappings_with_recv_port.hash_lookup);
00749   }
00750 
00751   return 0;
00752 }
00753 
00754 /**
00755   Inserts arg mapping in h_table with key src_host chaining the mapping
00756   of existing entries bound to src_host if necessary.
00757 
00758 */
00759 bool
00760 UrlRewrite::TableInsert(InkHashTable *h_table, url_mapping *mapping, const char *src_host)
00761 {
00762   char src_host_tmp_buf[1];
00763   UrlMappingPathIndex *ht_contents;
00764 
00765   if (!src_host) {
00766     src_host = &src_host_tmp_buf[0];
00767     src_host_tmp_buf[0] = 0;
00768   }
00769   // Insert the new_mapping into hash table
00770   if (ink_hash_table_lookup(h_table, src_host, (void**) &ht_contents)) {
00771     // There is already a path index for this host
00772     if (ht_contents == NULL) {
00773       // why should this happen?
00774       Warning("Found entry cannot be null!");
00775       return false;
00776     }
00777   } else {
00778     ht_contents = new UrlMappingPathIndex();
00779     ink_hash_table_insert(h_table, src_host, ht_contents);
00780   }
00781   if (!ht_contents->Insert(mapping)) {
00782     Warning("Could not insert new mapping");
00783     return false;
00784   }
00785   return true;
00786 }
00787 
00788 /**  First looks up the hash table for "simple" mappings and then the
00789      regex mappings.  Only higher-ranked regex mappings are examined if
00790      a hash mapping is found; or else all regex mappings are examined
00791 
00792      Returns highest-ranked mapping on success, NULL on failure
00793 */
00794 bool
00795 UrlRewrite::_mappingLookup(MappingsStore &mappings, URL *request_url,
00796                            int request_port, const char *request_host, int request_host_len,
00797                            UrlMappingContainer &mapping_container)
00798 {
00799   char request_host_lower[TS_MAX_HOST_NAME_LEN];
00800 
00801   if (!request_host || !request_url ||
00802       (request_host_len < 0) || (request_host_len >= TS_MAX_HOST_NAME_LEN)) {
00803     Debug("url_rewrite", "Invalid arguments!");
00804     return false;
00805   }
00806 
00807   // lowercase
00808   for (int i = 0; i < request_host_len; ++i) {
00809     request_host_lower[i] = tolower(request_host[i]);
00810   }
00811   request_host_lower[request_host_len] = 0;
00812 
00813   bool retval = false;
00814   int rank_ceiling = -1;
00815   url_mapping *mapping = _tableLookup(mappings.hash_lookup, request_url, request_port, request_host_lower,
00816                                       request_host_len);
00817   if (mapping != NULL) {
00818     rank_ceiling = mapping->getRank();
00819     Debug("url_rewrite", "Found 'simple' mapping with rank %d", rank_ceiling);
00820     mapping_container.set(mapping);
00821     retval = true;
00822   }
00823   if (_regexMappingLookup(mappings.regex_list, request_url, request_port, request_host_lower, request_host_len,
00824                           rank_ceiling, mapping_container)) {
00825     Debug("url_rewrite", "Using regex mapping with rank %d", (mapping_container.getMapping())->getRank());
00826     retval = true;
00827   }
00828   return retval;
00829 }
00830 
00831 // does not null terminate return string
00832 int
00833 UrlRewrite::_expandSubstitutions(int *matches_info, const RegexMapping *reg_map,
00834                                  const char *matched_string,
00835                                  char *dest_buf, int dest_buf_size)
00836 {
00837   int cur_buf_size = 0;
00838   int token_start = 0;
00839   int n_bytes_needed;
00840   int match_index;
00841   for (int i = 0; i < reg_map->n_substitutions; ++i) {
00842     // first copy preceding bytes
00843     n_bytes_needed = reg_map->substitution_markers[i] - token_start;
00844     if ((cur_buf_size + n_bytes_needed) > dest_buf_size) {
00845       goto lOverFlow;
00846     }
00847     memcpy(dest_buf + cur_buf_size, reg_map->to_url_host_template + token_start, n_bytes_needed);
00848     cur_buf_size += n_bytes_needed;
00849 
00850     // then copy the sub pattern match
00851     match_index = reg_map->substitution_ids[i] * 2;
00852     n_bytes_needed = matches_info[match_index + 1] - matches_info[match_index];
00853     if ((cur_buf_size + n_bytes_needed) > dest_buf_size) {
00854       goto lOverFlow;
00855     }
00856     memcpy(dest_buf + cur_buf_size, matched_string + matches_info[match_index], n_bytes_needed);
00857     cur_buf_size += n_bytes_needed;
00858 
00859     token_start = reg_map->substitution_markers[i] + 2; // skip the place holder
00860   }
00861 
00862   // copy last few bytes (if any)
00863   if (token_start < reg_map->to_url_host_template_len) {
00864     n_bytes_needed = reg_map->to_url_host_template_len - token_start;
00865     if ((cur_buf_size + n_bytes_needed) > dest_buf_size) {
00866       goto lOverFlow;
00867     }
00868     memcpy(dest_buf + cur_buf_size, reg_map->to_url_host_template + token_start, n_bytes_needed);
00869     cur_buf_size += n_bytes_needed;
00870   }
00871   Debug("url_rewrite_regex", "Expanded substitutions and returning string [%.*s] with length %d",
00872         cur_buf_size, dest_buf, cur_buf_size);
00873   return cur_buf_size;
00874 
00875  lOverFlow:
00876   Warning("Overflow while expanding substitutions");
00877   return 0;
00878 }
00879 
00880 bool
00881 UrlRewrite::_regexMappingLookup(RegexMappingList &regex_mappings, URL *request_url, int request_port,
00882                                 const char *request_host, int request_host_len, int rank_ceiling,
00883                                 UrlMappingContainer &mapping_container)
00884 {
00885   bool retval = false;
00886 
00887   if (rank_ceiling == -1) { // we will now look at all regex mappings
00888     rank_ceiling = INT_MAX;
00889     Debug("url_rewrite_regex", "Going to match all regexes");
00890   }
00891   else {
00892     Debug("url_rewrite_regex", "Going to match regexes with rank <= %d", rank_ceiling);
00893   }
00894 
00895   int request_scheme_len, reg_map_scheme_len;
00896   const char *request_scheme = request_url->scheme_get(&request_scheme_len), *reg_map_scheme;
00897 
00898   int request_path_len, reg_map_path_len;
00899   const char *request_path = request_url->path_get(&request_path_len), *reg_map_path;
00900 
00901   // Loop over the entire linked list, or until we're satisfied
00902   forl_LL(RegexMapping, list_iter, regex_mappings) {
00903     int reg_map_rank = list_iter->url_map->getRank();
00904 
00905     if (reg_map_rank > rank_ceiling) {
00906       break;
00907     }
00908 
00909     reg_map_scheme = list_iter->url_map->fromURL.scheme_get(&reg_map_scheme_len);
00910     if ((request_scheme_len != reg_map_scheme_len) ||
00911         strncmp(request_scheme, reg_map_scheme, request_scheme_len)) {
00912       Debug("url_rewrite_regex", "Skipping regex with rank %d as scheme does not match request scheme",
00913             reg_map_rank);
00914       continue;
00915     }
00916 
00917     if (list_iter->url_map->fromURL.port_get() != request_port) {
00918       Debug("url_rewrite_regex", "Skipping regex with rank %d as regex map port does not match request port. "
00919             "regex map port: %d, request port %d",
00920             reg_map_rank, list_iter->url_map->fromURL.port_get(), request_port);
00921       continue;
00922     }
00923 
00924     reg_map_path = list_iter->url_map->fromURL.path_get(&reg_map_path_len);
00925     if ((request_path_len < reg_map_path_len) ||
00926         strncmp(reg_map_path, request_path, reg_map_path_len)) { // use the shorter path length here
00927       Debug("url_rewrite_regex", "Skipping regex with rank %d as path does not cover request path",
00928             reg_map_rank);
00929       continue;
00930     }
00931 
00932     int matches_info[MAX_REGEX_SUBS * 3];
00933     int match_result = pcre_exec(list_iter->re, list_iter->re_extra, request_host, request_host_len,
00934                                  0, 0, matches_info, (sizeof(matches_info) / sizeof(int)));
00935     if (match_result > 0) {
00936       Debug("url_rewrite_regex", "Request URL host [%.*s] matched regex in mapping of rank %d "
00937             "with %d possible substitutions", request_host_len, request_host, reg_map_rank, match_result);
00938 
00939       mapping_container.set(list_iter->url_map);
00940 
00941       char buf[4096];
00942       int buf_len;
00943 
00944       // Expand substitutions in the host field from the stored template
00945       buf_len = _expandSubstitutions(matches_info, list_iter, request_host, buf, sizeof(buf));
00946       URL *expanded_url = mapping_container.createNewToURL();
00947       expanded_url->copy(&((list_iter->url_map)->toUrl));
00948       expanded_url->host_set(buf, buf_len);
00949 
00950       Debug("url_rewrite_regex", "Expanded toURL to [%.*s]",
00951             expanded_url->length_get(), expanded_url->string_get_ref());
00952       retval = true;
00953       break;
00954     } else if (match_result == PCRE_ERROR_NOMATCH) {
00955       Debug("url_rewrite_regex", "Request URL host [%.*s] did NOT match regex in mapping of rank %d",
00956             request_host_len, request_host, reg_map_rank);
00957     } else {
00958       Warning("pcre_exec() failed with error code %d", match_result);
00959       break;
00960     }
00961   }
00962 
00963   return retval;
00964 }
00965 
00966 void
00967 UrlRewrite::_destroyList(RegexMappingList &mappings)
00968 {
00969   RegexMapping *list_iter;
00970   while ((list_iter=mappings.pop()) != NULL) {
00971     delete list_iter->url_map;
00972     if (list_iter->re) {
00973       pcre_free(list_iter->re);
00974     }
00975     if (list_iter->re_extra) {
00976       pcre_free(list_iter->re_extra);
00977     }
00978     if (list_iter->to_url_host_template) {
00979       ats_free(list_iter->to_url_host_template);
00980     }
00981     delete list_iter;
00982   }
00983   mappings.clear();
00984 }

Generated by  doxygen 1.7.1