| /** @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. | |
| */ | |
| /***************************************************************************** | |
| * | |
| * ControlMatcher.cc - Implementation of general purpose matcher | |
| * | |
| * | |
| ****************************************************************************/ | |
| #include "swoc/bwf_ip.h" | |
| #include "swoc/swoc_file.h" | |
| #include "mgmt/config/ConfigContextDiags.h" | |
| #include "tscore/MatcherUtils.h" | |
| #include "tscore/Tokenizer.h" | |
| #include "proxy/ControlMatcher.h" | |
| #include "proxy/CacheControl.h" | |
| #include "proxy/ParentSelection.h" | |
| #include "tscore/HostLookup.h" | |
| #include "proxy/hdrs/HTTP.h" | |
| #include "../iocore/dns/P_SplitDNSProcessor.h" | |
| namespace | |
| { | |
| DbgCtl dbg_ctl_matcher("matcher"); | |
| } | |
| /**************************************************************** | |
| * Place all template instantiations at the bottom of the file | |
| ****************************************************************/ | |
| // HttpRequestData accessors | |
| // Can not be inlined due being virtual functions | |
| // | |
| char * | |
| HttpRequestData::get_string() | |
| { | |
| char *str = hdr->url_string_get(nullptr); | |
| if (str) { | |
| unescapifyStr(str); | |
| } | |
| return str; | |
| } | |
| const char * | |
| HttpRequestData::get_host() | |
| { | |
| return hostname_str; | |
| } | |
| sockaddr const * | |
| HttpRequestData::get_ip() | |
| { | |
| return &dest_ip.sa; | |
| } | |
| sockaddr const * | |
| HttpRequestData::get_client_ip() | |
| { | |
| return &src_ip.sa; | |
| } | |
| /************************************************************* | |
| * Begin class HostMatcher | |
| *************************************************************/ | |
| template <class Data, class MatchResult> | |
| HostMatcher<Data, MatchResult>::HostMatcher(const char *name, const char *filename) : BaseMatcher<Data>(name, filename) | |
| { | |
| host_lookup = std::make_unique<HostLookup>(name); | |
| } | |
| template <class Data, class MatchResult> HostMatcher<Data, MatchResult>::~HostMatcher() {} | |
| // | |
| // template <class Data,class MatchResult> | |
| // void HostMatcher<Data,MatchResult>::Print() | |
| // | |
| // Debugging Method | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| HostMatcher<Data, MatchResult>::Print() const | |
| { | |
| printf("\tHost/Domain Matcher with %d elements\n", num_el); | |
| host_lookup->Print(PrintFunc); | |
| } | |
| // | |
| // template <class Data,class MatchResult> | |
| // void HostMatcher<Data,MatchResult>::PrintFunc(void* opaque_data) | |
| // | |
| // Debugging Method | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| HostMatcher<Data, MatchResult>::PrintFunc(void *opaque_data) | |
| { | |
| Data *d = (Data *)opaque_data; | |
| d->Print(); | |
| } | |
| // void HostMatcher<Data,MatchResult>::AllocateSpace(int num_entries) | |
| // | |
| // Allocates the HostLeaf and Data arrays | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| HostMatcher<Data, MatchResult>::AllocateSpace(int num_entries) | |
| { | |
| // Should not have been allocated before | |
| ink_assert(array_len == -1); | |
| host_lookup->AllocateSpace(num_entries); | |
| data_array = new Data[num_entries]; | |
| array_len = num_entries; | |
| num_el = 0; | |
| } | |
| // void HostMatcher<Data,MatchResult>::Match(RequestData* rdata, MatchResult* result) | |
| // | |
| // Searches our tree and updates argresult for each element matching | |
| // arg hostname | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| HostMatcher<Data, MatchResult>::Match(RequestData *rdata, MatchResult *result) const | |
| { | |
| void *opaque_ptr; | |
| Data *data_ptr; | |
| bool r; | |
| // Check to see if there is any work to do before making the string copy | |
| if (this->num_el <= 0) { | |
| return; | |
| } | |
| HostLookupState s; | |
| r = host_lookup->MatchFirst(rdata->get_host(), &s, &opaque_ptr); | |
| while (r == true) { | |
| ink_assert(opaque_ptr != nullptr); | |
| data_ptr = (Data *)opaque_ptr; | |
| data_ptr->UpdateMatch(result, rdata); | |
| r = host_lookup->MatchNext(&s, &opaque_ptr); | |
| } | |
| } | |
| // | |
| // Result HostMatcher<Data,MatchResult>::NewEntry(bool domain_record, | |
| // char* match_data, char* match_info, int line_num) | |
| // | |
| // Creates a new host/domain record | |
| // | |
| // If successful, returns NULL | |
| // If not, returns a pointer to malloc allocated error string | |
| // that the caller MUST DEALLOCATE | |
| // | |
| template <class Data, class MatchResult> | |
| Result | |
| HostMatcher<Data, MatchResult>::NewEntry(matcher_line *line_info) | |
| { | |
| Data *cur_d; | |
| Result error = Result::ok(); | |
| char *match_data; | |
| // Make sure space has been allocated | |
| ink_assert(num_el >= 0); | |
| ink_assert(array_len >= 0); | |
| // Make sure we do not overrun the array; | |
| ink_assert(num_el < array_len); | |
| match_data = line_info->line[1][line_info->dest_entry]; | |
| // Make sure that the line_info is not bogus | |
| ink_assert(line_info->dest_entry < MATCHER_MAX_TOKENS); | |
| ink_assert(match_data != nullptr); | |
| // Remove our consumed label from the parsed line | |
| line_info->line[0][line_info->dest_entry] = nullptr; | |
| line_info->num_el--; | |
| // Fill in the parameter info | |
| cur_d = data_array + num_el; | |
| error = cur_d->Init(line_info); | |
| if (error.failed()) { | |
| // There was a problem so undo the effects this function | |
| new (cur_d) Data(); // reconstruct | |
| } else { | |
| // Fill in the matching info | |
| host_lookup->NewEntry(match_data, (line_info->type == MATCH_DOMAIN) ? true : false, cur_d); | |
| num_el++; | |
| } | |
| return error; | |
| } | |
| /************************************************************* | |
| * End class HostMatcher | |
| *************************************************************/ | |
| // | |
| // UrlMatcher<Data,MatchResult>::UrlMatcher() | |
| // | |
| template <class Data, class MatchResult> | |
| UrlMatcher<Data, MatchResult>::UrlMatcher(const char *name, const char *filename) : BaseMatcher<Data>(name, filename) | |
| { | |
| } | |
| // | |
| // UrlMatcher<Data,MatchResult>::~UrlMatcher() | |
| // | |
| template <class Data, class MatchResult> UrlMatcher<Data, MatchResult>::~UrlMatcher() | |
| { | |
| for (int i = 0; i < num_el; i++) { | |
| ats_free(url_str[i]); | |
| } | |
| delete[] url_str; | |
| delete[] url_value; | |
| } | |
| // | |
| // void UrlMatcher<Data,MatchResult>::Print() | |
| // | |
| // Debugging function | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| UrlMatcher<Data, MatchResult>::Print() const | |
| { | |
| printf("\tUrl Matcher with %d elements\n", num_el); | |
| for (int i = 0; i < num_el; i++) { | |
| printf("\t\tUrl: %s\n", url_str[i]); | |
| data_array[i].Print(); | |
| } | |
| } | |
| // | |
| // void UrlMatcher<Data,MatchResult>::AllocateSpace(int num_entries) | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| UrlMatcher<Data, MatchResult>::AllocateSpace(int num_entries) | |
| { | |
| // Should not have been allocated before | |
| ink_assert(array_len == -1); | |
| data_array = new Data[num_entries]; | |
| url_value = new int[num_entries]; | |
| url_str = new char *[num_entries]; | |
| memset(url_str, 0, sizeof(char *) * num_entries); | |
| array_len = num_entries; | |
| num_el = 0; | |
| } | |
| // | |
| // Result UrlMatcher<Data,MatchResult>::NewEntry(matcher_line* line_info) | |
| // | |
| template <class Data, class MatchResult> | |
| Result | |
| UrlMatcher<Data, MatchResult>::NewEntry(matcher_line *line_info) | |
| { | |
| Data *cur_d; | |
| char *pattern; | |
| Result error = Result::ok(); | |
| // Make sure space has been allocated | |
| ink_assert(num_el >= 0); | |
| ink_assert(array_len >= 0); | |
| // Make sure we do not overrun the array; | |
| ink_assert(num_el < array_len); | |
| pattern = line_info->line[1][line_info->dest_entry]; | |
| // Make sure that the line_info is not bogus | |
| ink_assert(line_info->dest_entry < MATCHER_MAX_TOKENS); | |
| ink_assert(pattern != nullptr); | |
| if (url_ht.find(pattern) != url_ht.end()) { | |
| return Result::failure("%s url expression error (have exist) at line %d position", matcher_name, line_info->line_num); | |
| } | |
| // Remove our consumed label from the parsed line | |
| line_info->line[0][line_info->dest_entry] = nullptr; | |
| line_info->num_el--; | |
| // Fill in the parameter info | |
| cur_d = data_array + num_el; | |
| error = cur_d->Init(line_info); | |
| if (!error.failed()) { | |
| url_str[num_el] = ats_strdup(pattern); | |
| url_value[num_el] = num_el; | |
| url_ht.emplace(url_str[num_el], url_value[num_el]); | |
| num_el++; | |
| } | |
| return error; | |
| } | |
| // | |
| // void UrlMatcher<Data,MatchResult>::Match(RD* rdata, MatchResult* result) | |
| // | |
| // Coduncts a linear search through the regex array and | |
| // updates arg result for each regex that matches arg URL | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| UrlMatcher<Data, MatchResult>::Match(RequestData *rdata, MatchResult *result) const | |
| { | |
| char *url_str; | |
| // Check to see there is any work to before we copy the URL | |
| if (num_el <= 0) { | |
| return; | |
| } | |
| url_str = rdata->get_string(); | |
| // Can't do a regex match with a NULL string so | |
| // use an empty one instead | |
| if (url_str == nullptr) { | |
| url_str = ats_strdup(""); | |
| } | |
| if (auto it = url_ht.find(url_str); it != url_ht.end()) { | |
| Dbg(dbg_ctl_matcher, "%s Matched %s with url at line %d", matcher_name, url_str, data_array[it->second].line_num); | |
| data_array[it->second].UpdateMatch(result, rdata); | |
| } | |
| ats_free(url_str); | |
| } | |
| // | |
| // RegexMatcher<Data,MatchResult>::RegexMatcher() | |
| // | |
| template <class Data, class MatchResult> | |
| RegexMatcher<Data, MatchResult>::RegexMatcher(const char *name, const char *filename) : BaseMatcher<Data>(name, filename) | |
| { | |
| } | |
| // | |
| // void RegexMatcher<Data,MatchResult>::Print() const | |
| // | |
| // Debugging function | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| RegexMatcher<Data, MatchResult>::Print() const | |
| { | |
| printf("\tRegex Matcher with %d elements\n", num_el); | |
| for (int i = 0; i < num_el; i++) { | |
| printf("\t\tRegex: %s\n", regex_strings[i].c_str()); | |
| data_array[i].Print(); | |
| } | |
| } | |
| // | |
| // void RegexMatcher<Data,MatchResult>::AllocateSpace(int num_entries) | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| RegexMatcher<Data, MatchResult>::AllocateSpace(int num_entries) | |
| { | |
| // Should not have been allocated before | |
| ink_assert(array_len == -1); | |
| regex_array.resize(num_entries); | |
| regex_strings.resize(num_entries); | |
| data_array = new Data[num_entries]; | |
| array_len = num_entries; | |
| num_el = 0; | |
| } | |
| // | |
| // Result RegexMatcher<Data,MatchResult>::NewEntry(matcher_line* line_info) | |
| // | |
| template <class Data, class MatchResult> | |
| Result | |
| RegexMatcher<Data, MatchResult>::NewEntry(matcher_line *line_info) | |
| { | |
| Data *cur_d; | |
| char *pattern; | |
| std::string error_msg; | |
| int erroffset; | |
| Result error = Result::ok(); | |
| // Make sure space has been allocated | |
| ink_assert(num_el >= 0); | |
| ink_assert(array_len >= 0); | |
| // Make sure we do not overrun the array; | |
| ink_assert(num_el < array_len); | |
| pattern = line_info->line[1][line_info->dest_entry]; | |
| // Make sure that the line_info is not bogus | |
| ink_assert(line_info->dest_entry < MATCHER_MAX_TOKENS); | |
| ink_assert(pattern != nullptr); | |
| // Create the compiled regular expression. This slot can still hold a previous line's | |
| // pattern, so ask compile() rather than empty(). | |
| if (!regex_array[num_el].compile(pattern, error_msg, erroffset)) { | |
| return Result::failure("%s regular expression error at line %d position %d : %s", matcher_name, line_info->line_num, erroffset, | |
| error_msg.c_str()); | |
| } | |
| regex_strings[num_el] = pattern; | |
| // Remove our consumed label from the parsed line | |
| line_info->line[0][line_info->dest_entry] = nullptr; | |
| line_info->num_el--; | |
| // Fill in the parameter info | |
| cur_d = data_array + num_el; | |
| error = cur_d->Init(line_info); | |
| if (error.failed()) { | |
| // There was a problem so undo the effects this function | |
| regex_strings[num_el] = ""; // reset the string | |
| } else { | |
| num_el++; | |
| } | |
| return error; | |
| } | |
| // | |
| // void RegexMatcher<Data,MatchResult>::Match(RequestData* rdata, MatchResult* result) | |
| // | |
| // Conducts a linear search through the regex array and | |
| // updates arg result for each regex that matches arg URL | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| RegexMatcher<Data, MatchResult>::Match(RequestData *rdata, MatchResult *result) const | |
| { | |
| // Check to see there is any work to before we copy the URL | |
| if (num_el <= 0) { | |
| return; | |
| } | |
| char *url_str = rdata->get_string(); | |
| // Can't do a regex match with a NULL string so | |
| // use an empty one instead | |
| if (url_str == nullptr) { | |
| url_str = ats_strdup(""); | |
| } | |
| // INKqa12980 | |
| // The function unescapifyStr() is already called in | |
| // HttpRequestData::get_string(); therefore, no need to call again here. | |
| for (int i = 0; i < num_el; i++) { | |
| RegexMatches matches; | |
| int r = regex_array[i].exec(url_str, matches); | |
| if (r >= 0) { | |
| Dbg(dbg_ctl_matcher, "%s Matched %s with regex at line %d", matcher_name, url_str, data_array[i].line_num); | |
| data_array[i].UpdateMatch(result, rdata); | |
| } else if (r != RE_ERROR_NOMATCH) { | |
| // An error has occurred | |
| Warning("Error matching regex for url: %s:%d (%d)", file_name ? file_name : "unknown", data_array[i].line_num, r); | |
| } // else: RE_ERROR_NOMATCH | |
| } | |
| ats_free(url_str); | |
| } | |
| // | |
| // HostRegexMatcher<Data,MatchResult>::HostRegexMatcher() | |
| // | |
| template <class Data, class MatchResult> | |
| HostRegexMatcher<Data, MatchResult>::HostRegexMatcher(const char *name, const char *filename) | |
| : RegexMatcher<Data, MatchResult>(name, filename) | |
| { | |
| } | |
| // | |
| // void HostRegexMatcher<Data,MatchResult>::Match(RequestData* rdata, MatchResult* result) | |
| // | |
| // Conducts a linear search through the regex array and | |
| // updates arg result for each regex that matches arg host_regex | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| HostRegexMatcher<Data, MatchResult>::Match(RequestData *rdata, MatchResult *result) const | |
| { | |
| const char *url_str; | |
| // Check to see there is any work to before we copy the URL | |
| if (this->num_el <= 0) { | |
| return; | |
| } | |
| url_str = rdata->get_host(); | |
| // Can't do a regex match with a NULL string so | |
| // use an empty one instead | |
| if (url_str == nullptr) { | |
| url_str = ""; | |
| } | |
| for (int i = 0; i < num_el; i++) { | |
| RegexMatches matches; | |
| int r = this->regex_array[i].exec(url_str, matches); | |
| if (r >= 0) { | |
| Dbg(dbg_ctl_matcher, "%s Matched %s with regex at line %d", const_cast<char *>(this->matcher_name), url_str, | |
| this->data_array[i].line_num); | |
| this->data_array[i].UpdateMatch(result, rdata); | |
| } else if (r != RE_ERROR_NOMATCH) { | |
| // An error has occurred | |
| Warning("Error matching regex for host: %s:%d (%d)", this->file_name ? this->file_name : "unknown", | |
| this->data_array[i].line_num, r); | |
| } // else: RE_ERROR_NOMATCH | |
| } | |
| } | |
| // | |
| // IpMatcher<Data,MatchResult>::IpMatcher() | |
| // | |
| template <class Data, class MatchResult> | |
| IpMatcher<Data, MatchResult>::IpMatcher(const char *name, const char *filename) : BaseMatcher<Data>(name, filename) | |
| { | |
| } | |
| // | |
| // void IpMatcher<Data,MatchResult>::AllocateSpace(int num_entries) | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| IpMatcher<Data, MatchResult>::AllocateSpace(int num_entries) | |
| { | |
| // Should not have been allocated before | |
| ink_assert(array_len == -1); | |
| data_array = new Data[num_entries]; | |
| array_len = num_entries; | |
| num_el = 0; | |
| } | |
| // | |
| // Result IpMatcher<Data,MatchResult>::NewEntry(matcher_line* line_info) | |
| // | |
| // Inserts a range the ip lookup table. | |
| // Creates new table levels as needed | |
| // | |
| // Returns NULL is all was OK. On error returns, a malloc | |
| // allocated error string which the CALLEE is responsible | |
| // for deallocating | |
| // | |
| template <class Data, class MatchResult> | |
| Result | |
| IpMatcher<Data, MatchResult>::NewEntry(matcher_line *line_info) | |
| { | |
| Data *cur_d; | |
| char *match_data; | |
| swoc::IPRange addrs; | |
| Result error = Result::ok(); | |
| // Make sure space has been allocated | |
| ink_assert(num_el >= 0); | |
| ink_assert(array_len >= 0); | |
| // Make sure we do not overrun the array; | |
| ink_assert(num_el < array_len); | |
| match_data = line_info->line[1][line_info->dest_entry]; | |
| // Make sure that the line_info is not bogus | |
| ink_assert(line_info->dest_entry < MATCHER_MAX_TOKENS); | |
| ink_assert(match_data != nullptr); | |
| // Extract the IP range | |
| if (!addrs.load(match_data)) { | |
| return Result::failure("%s not a valid IP address range at %s line %d", matcher_name, file_name, line_info->line_num); | |
| } | |
| // Remove our consumed label from the parsed line | |
| line_info->line[0][line_info->dest_entry] = nullptr; | |
| line_info->num_el--; | |
| // Fill in the parameter info | |
| cur_d = data_array + num_el; | |
| error = cur_d->Init(line_info); | |
| if (!error.failed()) { | |
| ip_addrs.mark(addrs, cur_d); | |
| ++num_el; | |
| } | |
| return error; | |
| } | |
| // | |
| // void IpMatcherData,MatchResult>::Match(in_addr_t addr, RequestData* rdata, MatchResult* result) | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| IpMatcher<Data, MatchResult>::Match(sockaddr const *addr, RequestData *rdata, MatchResult *result) const | |
| { | |
| if (auto [range, data]{*ip_addrs.find(swoc::IPAddr(addr))}; !range.empty()) { | |
| ink_assert(data != nullptr); | |
| data->UpdateMatch(result, rdata); | |
| } | |
| } | |
| template <class Data, class MatchResult> | |
| void | |
| IpMatcher<Data, MatchResult>::Print() const | |
| { | |
| std::string tmp; | |
| std::cout << swoc::bwprint(tmp, "\tIp Matcher with {} elements, {} ranges.", num_el, ip_addrs.count()) << std::endl; | |
| for (auto &&[range, data] : ip_addrs) { | |
| std::cout << swoc::bwprint(tmp, "Range {}", range); | |
| data->Print(); | |
| } | |
| } | |
| template <class Data, class MatchResult> | |
| ControlMatcher<Data, MatchResult>::ControlMatcher(const char *file_var, const char *name, const matcher_tags *tags, int flags_in, | |
| ConfigContext ctx) | |
| { | |
| flags = flags_in; | |
| ink_assert(flags & (ALLOW_HOST_TABLE | ALLOW_REGEX_TABLE | ALLOW_URL_TABLE | ALLOW_IP_TABLE)); | |
| config_tags = tags; | |
| ink_assert(config_tags != nullptr); | |
| matcher_name = name; | |
| config_file_path[0] = '\0'; | |
| if (!(flags & DONT_BUILD_TABLE)) { | |
| ats_scoped_str config_path(RecConfigReadConfigPath(file_var)); | |
| ink_release_assert(config_path); | |
| ink_strlcpy(config_file_path, config_path, sizeof(config_file_path)); | |
| } | |
| reMatch = nullptr; | |
| urlMatch = nullptr; | |
| hostMatch = nullptr; | |
| ipMatch = nullptr; | |
| hrMatch = nullptr; | |
| if (!(flags & DONT_BUILD_TABLE)) { | |
| m_numEntries = this->BuildTable(ctx); | |
| } else { | |
| m_numEntries = 0; | |
| } | |
| } | |
| template <class Data, class MatchResult> ControlMatcher<Data, MatchResult>::~ControlMatcher() {} | |
| // void ControlMatcher<Data, MatchResult>::Print() | |
| // | |
| // Debugging method | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| ControlMatcher<Data, MatchResult>::Print() const | |
| { | |
| printf("Control Matcher Table: %s\n", matcher_name); | |
| if (hostMatch != nullptr) { | |
| hostMatch->Print(); | |
| } | |
| if (reMatch != nullptr) { | |
| reMatch->Print(); | |
| } | |
| if (urlMatch != nullptr) { | |
| urlMatch->Print(); | |
| } | |
| if (ipMatch != nullptr) { | |
| ipMatch->Print(); | |
| } | |
| if (hrMatch != nullptr) { | |
| hrMatch->Print(); | |
| } | |
| } | |
| // void ControlMatcher<Data, MatchResult>::Match(RequestData* rdata | |
| // MatchResult* result) const | |
| // | |
| // Queries each table for the MatchResult* | |
| // | |
| template <class Data, class MatchResult> | |
| void | |
| ControlMatcher<Data, MatchResult>::Match(RequestData *rdata, MatchResult *result) const | |
| { | |
| if (hostMatch != nullptr) { | |
| hostMatch->Match(rdata, result); | |
| } | |
| if (reMatch != nullptr) { | |
| reMatch->Match(rdata, result); | |
| } | |
| if (urlMatch != nullptr) { | |
| urlMatch->Match(rdata, result); | |
| } | |
| if (ipMatch != nullptr) { | |
| ipMatch->Match(rdata->get_ip(), rdata, result); | |
| } | |
| if (hrMatch != nullptr) { | |
| hrMatch->Match(rdata, result); | |
| } | |
| } | |
| // int ControlMatcher::BuildTable() | |
| // | |
| // Reads the cache.config file and build the records array | |
| // from it | |
| // | |
| template <class Data, class MatchResult> | |
| int | |
| ControlMatcher<Data, MatchResult>::BuildTableFromString(char *file_buf, ConfigContext ctx) | |
| { | |
| // Table build locals | |
| Tokenizer bufTok("\n"); | |
| tok_iter_state i_state; | |
| const char *tmp; | |
| matcher_line *first = nullptr; | |
| matcher_line *current; | |
| matcher_line *last = nullptr; | |
| int line_num = 0; | |
| int second_pass = 0; | |
| int numEntries = 0; | |
| // type counts | |
| int hostDomain = 0; | |
| int regex = 0; | |
| int url = 0; | |
| int ip = 0; | |
| int hostregex = 0; | |
| if (bufTok.Initialize(file_buf, SHARE_TOKS | ALLOW_EMPTY_TOKS) == 0) { | |
| // We have an empty file | |
| return 0; | |
| } | |
| // First get the number of entries | |
| tmp = bufTok.iterFirst(&i_state); | |
| while (tmp != nullptr) { | |
| line_num++; | |
| // skip all blank spaces at beginning of line | |
| while (*tmp && isspace(*tmp)) { | |
| tmp++; | |
| } | |
| if (*tmp != '#' && *tmp != '\0') { | |
| const char *errptr; | |
| current = static_cast<matcher_line *>(ats_malloc(sizeof(matcher_line))); | |
| errptr = parseConfigLine(const_cast<char *>(tmp), current, config_tags); | |
| if (errptr != nullptr) { | |
| if (config_tags != &socks_server_tags) { | |
| Result error = | |
| Result::failure("%s discarding %s entry at line %d : %s", matcher_name, config_file_path, line_num, errptr); | |
| CfgLoadLog(ctx, DL_Error, "%s", error.message()); | |
| } | |
| ats_free(current); | |
| } else { | |
| // Line parsed ok. Figure out what the destination | |
| // type is and link it into our list | |
| numEntries++; | |
| current->line_num = line_num; | |
| switch (current->type) { | |
| case MATCH_HOST: | |
| case MATCH_DOMAIN: | |
| hostDomain++; | |
| break; | |
| case MATCH_IP: | |
| ip++; | |
| break; | |
| case MATCH_REGEX: | |
| regex++; | |
| break; | |
| case MATCH_URL: | |
| url++; | |
| break; | |
| case MATCH_HOST_REGEX: | |
| hostregex++; | |
| break; | |
| case MATCH_NONE: | |
| default: | |
| ink_assert(0); | |
| } | |
| if (first == nullptr) { | |
| ink_assert(last == nullptr); | |
| first = last = current; | |
| } else { | |
| last->next = current; | |
| last = current; | |
| } | |
| } | |
| } | |
| tmp = bufTok.iterNext(&i_state); | |
| } | |
| // Make we have something to do before going on | |
| if (numEntries == 0) { | |
| ats_free(first); | |
| return 0; | |
| } | |
| // Now allocate space for the record pointers | |
| if ((flags & ALLOW_REGEX_TABLE) && regex > 0) { | |
| reMatch = std::make_unique<RegexMatcher<Data, MatchResult>>(matcher_name, config_file_path); | |
| reMatch->AllocateSpace(regex); | |
| } | |
| if ((flags & ALLOW_URL_TABLE) && url > 0) { | |
| urlMatch = std::make_unique<UrlMatcher<Data, MatchResult>>(matcher_name, config_file_path); | |
| urlMatch->AllocateSpace(url); | |
| } | |
| if ((flags & ALLOW_HOST_TABLE) && hostDomain > 0) { | |
| hostMatch = std::make_unique<HostMatcher<Data, MatchResult>>(matcher_name, config_file_path); | |
| hostMatch->AllocateSpace(hostDomain); | |
| } | |
| if ((flags & ALLOW_IP_TABLE) && ip > 0) { | |
| ipMatch = std::make_unique<IpMatcher<Data, MatchResult>>(matcher_name, config_file_path); | |
| ipMatch->AllocateSpace(ip); | |
| } | |
| if ((flags & ALLOW_HOST_REGEX_TABLE) && hostregex > 0) { | |
| hrMatch = std::make_unique<HostRegexMatcher<Data, MatchResult>>(matcher_name, config_file_path); | |
| hrMatch->AllocateSpace(hostregex); | |
| } | |
| // Traverse the list and build the records table | |
| current = first; | |
| while (current != nullptr) { | |
| Result error = Result::ok(); | |
| second_pass++; | |
| if ((flags & ALLOW_HOST_TABLE) && current->type == MATCH_DOMAIN) { | |
| error = hostMatch->NewEntry(current); | |
| } else if ((flags & ALLOW_HOST_TABLE) && current->type == MATCH_HOST) { | |
| error = hostMatch->NewEntry(current); | |
| } else if ((flags & ALLOW_REGEX_TABLE) && current->type == MATCH_REGEX) { | |
| error = reMatch->NewEntry(current); | |
| } else if ((flags & ALLOW_URL_TABLE) && current->type == MATCH_URL) { | |
| error = urlMatch->NewEntry(current); | |
| } else if ((flags & ALLOW_IP_TABLE) && current->type == MATCH_IP) { | |
| error = ipMatch->NewEntry(current); | |
| } else if ((flags & ALLOW_HOST_REGEX_TABLE) && current->type == MATCH_HOST_REGEX) { | |
| error = hrMatch->NewEntry(current); | |
| } else { | |
| error = | |
| Result::failure("%s discarding %s entry with unknown type at line %d", matcher_name, config_file_path, current->line_num); | |
| } | |
| // Check to see if there was an error in creating the NewEntry | |
| if (error.failed()) { | |
| CfgLoadLog(ctx, DL_Error, "%s", error.message()); | |
| } | |
| // Deallocate the parsing structure | |
| last = current; | |
| current = current->next; | |
| ats_free(last); | |
| } | |
| ink_assert(second_pass == numEntries); | |
| if (dbg_ctl_matcher.on()) { | |
| Print(); | |
| } | |
| return numEntries; | |
| } | |
| template <class Data, class MatchResult> | |
| int | |
| ControlMatcher<Data, MatchResult>::BuildTable(ConfigContext ctx) | |
| { | |
| std::error_code ec; | |
| std::string content{swoc::file::load(swoc::file::path{config_file_path}, ec)}; | |
| if (ec) { | |
| switch (ec.value()) { | |
| case ENOENT: | |
| CfgLoadLog(ctx, DL_Warning, "ControlMatcher - Cannot open config file: %s - %s", config_file_path, strerror(ec.value())); | |
| break; | |
| default: | |
| CfgLoadFail(ctx, "ControlMatcher - %s failed to load: %s", config_file_path, strerror(ec.value())); | |
| return 1; | |
| } | |
| } | |
| return BuildTableFromString(content.data(), ctx); | |
| } | |
| /**************************************************************** | |
| * TEMPLATE INSTANTIATIONS GO HERE | |
| * | |
| ****************************************************************/ | |
| template class ControlMatcher<ParentRecord, ParentResult>; | |
| template class HostMatcher<ParentRecord, ParentResult>; | |
| template class RegexMatcher<ParentRecord, ParentResult>; | |
| template class UrlMatcher<ParentRecord, ParentResult>; | |
| template class IpMatcher<ParentRecord, ParentResult>; | |
| template class HostRegexMatcher<ParentRecord, ParentResult>; | |
| template class ControlMatcher<SplitDNSRecord, SplitDNSResult>; | |
| template class HostMatcher<SplitDNSRecord, SplitDNSResult>; | |
| template class RegexMatcher<SplitDNSRecord, SplitDNSResult>; | |
| template class UrlMatcher<SplitDNSRecord, SplitDNSResult>; | |
| template class IpMatcher<SplitDNSRecord, SplitDNSResult>; | |
| template class HostRegexMatcher<SplitDNSRecord, SplitDNSResult>; | |
| template class ControlMatcher<CacheControlRecord, CacheControlResult>; | |
| template class HostMatcher<CacheControlRecord, CacheControlResult>; | |
| template class RegexMatcher<CacheControlRecord, CacheControlResult>; | |
| template class UrlMatcher<CacheControlRecord, CacheControlResult>; | |
| template class IpMatcher<CacheControlRecord, CacheControlResult>; |