| /** @file |
| * |
| * Remap configuration file parsing. |
| * |
| * @section license License |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "RemapConfig.h" |
| #include "UrlRewrite.h" |
| #include "ReverseProxy.h" |
| #include "tscore/I_Layout.h" |
| #include "HTTP.h" |
| #include "tscore/ink_platform.h" |
| #include "tscore/List.h" |
| #include "tscore/ink_cap.h" |
| #include "tscore/ink_file.h" |
| #include "tscore/Tokenizer.h" |
| #include "IPAllow.h" |
| #include "PluginFactory.h" |
| |
| #define modulePrefix "[ReverseProxy]" |
| |
| static bool remap_parse_config_bti(const char *path, BUILD_TABLE_INFO *bti); |
| |
| load_remap_file_func load_remap_file_cb = nullptr; |
| |
| /** |
| Returns the length of the URL. |
| |
| Will replace the terminator with a '/' if this is a full URL and |
| there are no '/' in it after the the host. This ensures that class |
| URL parses the URL correctly. |
| |
| */ |
| static int |
| UrlWhack(char *toWhack, int *origLength) |
| { |
| int length = strlen(toWhack); |
| char *tmp; |
| *origLength = length; |
| |
| // Check to see if this a full URL |
| tmp = strstr(toWhack, "://"); |
| if (tmp != nullptr) { |
| if (strchr(tmp + 3, '/') == nullptr) { |
| toWhack[length] = '/'; |
| length++; |
| } |
| } |
| return length; |
| } |
| |
| /** |
| Cleanup *char[] array - each item in array must be allocated via |
| ats_malloc or similar "x..." function. |
| |
| */ |
| static void |
| clear_xstr_array(char *v[], size_t vsize) |
| { |
| for (unsigned i = 0; i < vsize; i++) { |
| v[i] = static_cast<char *>(ats_free_null(v[i])); |
| } |
| } |
| |
| BUILD_TABLE_INFO::BUILD_TABLE_INFO() |
| |
| { |
| memset(this->paramv, 0, sizeof(this->paramv)); |
| memset(this->argv, 0, sizeof(this->argv)); |
| } |
| |
| BUILD_TABLE_INFO::~BUILD_TABLE_INFO() |
| { |
| this->reset(); |
| } |
| |
| void |
| BUILD_TABLE_INFO::reset() |
| { |
| this->paramc = this->argc = 0; |
| clear_xstr_array(this->paramv, sizeof(this->paramv) / sizeof(char *)); |
| clear_xstr_array(this->argv, sizeof(this->argv) / sizeof(char *)); |
| } |
| |
| static const char * |
| process_filter_opt(url_mapping *mp, const BUILD_TABLE_INFO *bti, char *errStrBuf, int errStrBufSize) |
| { |
| acl_filter_rule *rp, **rpp; |
| const char *errStr = nullptr; |
| |
| if (unlikely(!mp || !bti || !errStrBuf || errStrBufSize <= 0)) { |
| Debug("url_rewrite", "[process_filter_opt] Invalid argument(s)"); |
| return (const char *)"[process_filter_opt] Invalid argument(s)"; |
| } |
| for (rp = bti->rules_list; rp; rp = rp->next) { |
| if (rp->active_queue_flag) { |
| Debug("url_rewrite", "[process_filter_opt] Add active main filter \"%s\" (argc=%d)", |
| rp->filter_name ? rp->filter_name : "<nullptr>", rp->argc); |
| for (rpp = &mp->filter; *rpp; rpp = &((*rpp)->next)) { |
| ; |
| } |
| if ((errStr = remap_validate_filter_args(rpp, (const char **)rp->argv, rp->argc, errStrBuf, errStrBufSize)) != nullptr) { |
| break; |
| } |
| } |
| } |
| if (!errStr && (bti->remap_optflg & REMAP_OPTFLG_ALL_FILTERS) != 0) { |
| Debug("url_rewrite", "[process_filter_opt] Add per remap filter"); |
| for (rpp = &mp->filter; *rpp; rpp = &((*rpp)->next)) { |
| ; |
| } |
| errStr = remap_validate_filter_args(rpp, (const char **)bti->argv, bti->argc, errStrBuf, errStrBufSize); |
| } |
| // Set the ip allow flag for this rule to the current ip allow flag state |
| mp->ip_allow_check_enabled_p = bti->ip_allow_check_enabled_p; |
| |
| return errStr; |
| } |
| |
| static bool |
| is_inkeylist(const char *key, ...) |
| { |
| va_list ap; |
| |
| if (unlikely(key == nullptr || key[0] == '\0')) { |
| return false; |
| } |
| |
| va_start(ap, key); |
| |
| const char *str = va_arg(ap, const char *); |
| for (unsigned idx = 1; str; idx++) { |
| if (!strcasecmp(key, str)) { |
| va_end(ap); |
| return true; |
| } |
| |
| str = va_arg(ap, const char *); |
| } |
| |
| va_end(ap); |
| return false; |
| } |
| |
| static const char * |
| parse_define_directive(const char *directive, BUILD_TABLE_INFO *bti, char *errbuf, size_t errbufsize) |
| { |
| bool flg; |
| acl_filter_rule *rp; |
| const char *cstr = nullptr; |
| |
| if (bti->paramc < 2) { |
| snprintf(errbuf, errbufsize, "Directive \"%s\" must have name argument", directive); |
| Debug("url_rewrite", "[parse_directive] %s", errbuf); |
| return (const char *)errbuf; |
| } |
| if (bti->argc < 1) { |
| snprintf(errbuf, errbufsize, "Directive \"%s\" must have filter parameter(s)", directive); |
| Debug("url_rewrite", "[parse_directive] %s", errbuf); |
| return (const char *)errbuf; |
| } |
| |
| flg = ((rp = acl_filter_rule::find_byname(bti->rules_list, (const char *)bti->paramv[1])) == nullptr) ? true : false; |
| // coverity[alloc_arg] |
| if ((cstr = remap_validate_filter_args(&rp, (const char **)bti->argv, bti->argc, errbuf, errbufsize)) == nullptr && rp) { |
| if (flg) { // new filter - add to list |
| acl_filter_rule **rpp = nullptr; |
| Debug("url_rewrite", "[parse_directive] new rule \"%s\" was created", bti->paramv[1]); |
| for (rpp = &bti->rules_list; *rpp; rpp = &((*rpp)->next)) { |
| ; |
| } |
| (*rpp = rp)->name(bti->paramv[1]); |
| } |
| Debug("url_rewrite", "[parse_directive] %d argument(s) were added to rule \"%s\"", bti->argc, bti->paramv[1]); |
| rp->add_argv(bti->argc, bti->argv); // store string arguments for future processing |
| } |
| |
| return cstr; |
| } |
| |
| static const char * |
| parse_delete_directive(const char *directive, BUILD_TABLE_INFO *bti, char *errbuf, size_t errbufsize) |
| { |
| if (bti->paramc < 2) { |
| snprintf(errbuf, errbufsize, "Directive \"%s\" must have name argument", directive); |
| Debug("url_rewrite", "[parse_directive] %s", errbuf); |
| return (const char *)errbuf; |
| } |
| |
| acl_filter_rule::delete_byname(&bti->rules_list, (const char *)bti->paramv[1]); |
| return nullptr; |
| } |
| |
| static const char * |
| parse_activate_directive(const char *directive, BUILD_TABLE_INFO *bti, char *errbuf, size_t errbufsize) |
| { |
| acl_filter_rule *rp; |
| |
| if (bti->paramc < 2) { |
| snprintf(errbuf, errbufsize, "Directive \"%s\" must have name argument", directive); |
| Debug("url_rewrite", "[parse_directive] %s", errbuf); |
| return (const char *)errbuf; |
| } |
| |
| // Check if for ip_allow filter |
| if (strcmp((const char *)bti->paramv[1], "ip_allow") == 0) { |
| bti->ip_allow_check_enabled_p = true; |
| return nullptr; |
| } |
| |
| if ((rp = acl_filter_rule::find_byname(bti->rules_list, (const char *)bti->paramv[1])) == nullptr) { |
| snprintf(errbuf, errbufsize, R"(Undefined filter "%s" in directive "%s")", bti->paramv[1], directive); |
| Debug("url_rewrite", "[parse_directive] %s", errbuf); |
| return (const char *)errbuf; |
| } |
| |
| acl_filter_rule::requeue_in_active_list(&bti->rules_list, rp); |
| return nullptr; |
| } |
| |
| static const char * |
| parse_deactivate_directive(const char *directive, BUILD_TABLE_INFO *bti, char *errbuf, size_t errbufsize) |
| { |
| acl_filter_rule *rp; |
| |
| if (bti->paramc < 2) { |
| snprintf(errbuf, errbufsize, "Directive \"%s\" must have name argument", directive); |
| Debug("url_rewrite", "[parse_directive] %s", errbuf); |
| return (const char *)errbuf; |
| } |
| |
| // Check if for ip_allow filter |
| if (strcmp((const char *)bti->paramv[1], "ip_allow") == 0) { |
| bti->ip_allow_check_enabled_p = false; |
| return nullptr; |
| } |
| |
| if ((rp = acl_filter_rule::find_byname(bti->rules_list, (const char *)bti->paramv[1])) == nullptr) { |
| snprintf(errbuf, errbufsize, R"(Undefined filter "%s" in directive "%s")", bti->paramv[1], directive); |
| Debug("url_rewrite", "[parse_directive] %s", errbuf); |
| return (const char *)errbuf; |
| } |
| |
| acl_filter_rule::requeue_in_passive_list(&bti->rules_list, rp); |
| return nullptr; |
| } |
| |
| static void |
| free_directory_list(int n_entries, struct dirent **entrylist) |
| { |
| for (int i = 0; i < n_entries; ++i) { |
| free(entrylist[i]); |
| } |
| |
| free(entrylist); |
| } |
| |
| static const char * |
| parse_remap_fragment(const char *path, BUILD_TABLE_INFO *bti, char *errbuf, size_t errbufsize) |
| { |
| // We need to create a new bti so that we don't clobber any state in the parent parse, but we want |
| // to keep the ACL rules from the parent because ACLs must be global across the full set of config |
| // files. |
| BUILD_TABLE_INFO nbti; |
| bool success; |
| |
| if (access(path, R_OK) == -1) { |
| snprintf(errbuf, errbufsize, "%s: %s", path, strerror(errno)); |
| return (const char *)errbuf; |
| } |
| |
| nbti.rules_list = bti->rules_list; |
| nbti.rewrite = bti->rewrite; |
| |
| Debug("url_rewrite", "[%s] including remap configuration from %s", __func__, (const char *)path); |
| success = remap_parse_config_bti(path, &nbti); |
| |
| // The sub-parse might have updated the rules list, so push it up to the parent parse. |
| bti->rules_list = nbti.rules_list; |
| |
| if (success) { |
| // register the included file with the management subsystem so that we can correctly |
| // reload them when they change |
| load_remap_file_cb(path); |
| } else { |
| snprintf(errbuf, errbufsize, "failed to parse included file %s", path); |
| return (const char *)errbuf; |
| } |
| |
| return nullptr; |
| } |
| |
| static const char * |
| parse_include_directive(const char *directive, BUILD_TABLE_INFO *bti, char *errbuf, size_t errbufsize) |
| { |
| if (bti->paramc < 2) { |
| snprintf(errbuf, errbufsize, "Directive \"%s\" must have a path argument", directive); |
| Debug("url_rewrite", "[%s] %s", __func__, errbuf); |
| return (const char *)errbuf; |
| } |
| |
| for (unsigned i = 1; i < static_cast<unsigned>(bti->paramc); ++i) { |
| ats_scoped_str path; |
| const char *errmsg = nullptr; |
| |
| // The included path is relative to SYSCONFDIR, just like remap.config is. |
| path = RecConfigReadConfigPath(nullptr, bti->paramv[i]); |
| |
| if (ink_file_is_directory(path)) { |
| struct dirent **entrylist; |
| int n_entries; |
| |
| n_entries = scandir(path, &entrylist, nullptr, alphasort); |
| if (n_entries == -1) { |
| snprintf(errbuf, errbufsize, "failed to open %s: %s", path.get(), strerror(errno)); |
| return (const char *)errbuf; |
| } |
| |
| for (int j = 0; j < n_entries; ++j) { |
| ats_scoped_str subpath; |
| |
| if (isdot(entrylist[j]->d_name) || isdotdot(entrylist[j]->d_name)) { |
| continue; |
| } |
| |
| subpath = Layout::relative_to(path.get(), entrylist[j]->d_name); |
| |
| if (ink_file_is_directory(subpath)) { |
| continue; |
| } |
| |
| errmsg = parse_remap_fragment(subpath, bti, errbuf, errbufsize); |
| if (errmsg != nullptr) { |
| break; |
| } |
| } |
| |
| free_directory_list(n_entries, entrylist); |
| |
| } else { |
| errmsg = parse_remap_fragment(path, bti, errbuf, errbufsize); |
| } |
| |
| if (errmsg) { |
| return errmsg; |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| struct remap_directive { |
| const char *name; |
| const char *(*parser)(const char *, BUILD_TABLE_INFO *, char *, size_t); |
| }; |
| |
| static const remap_directive directives[] = { |
| |
| {".definefilter", parse_define_directive}, |
| {".deffilter", parse_define_directive}, |
| {".defflt", parse_define_directive}, |
| |
| {".deletefilter", parse_delete_directive}, |
| {".delfilter", parse_delete_directive}, |
| {".delflt", parse_delete_directive}, |
| |
| {".usefilter", parse_activate_directive}, |
| {".activefilter", parse_activate_directive}, |
| {".activatefilter", parse_activate_directive}, |
| {".useflt", parse_activate_directive}, |
| |
| {".unusefilter", parse_deactivate_directive}, |
| {".deactivatefilter", parse_deactivate_directive}, |
| {".unactivefilter", parse_deactivate_directive}, |
| {".deuseflt", parse_deactivate_directive}, |
| {".unuseflt", parse_deactivate_directive}, |
| |
| {".include", parse_include_directive}, |
| }; |
| |
| const char * |
| remap_parse_directive(BUILD_TABLE_INFO *bti, char *errbuf, size_t errbufsize) |
| { |
| const char *directive = nullptr; |
| |
| // Check arguments |
| if (unlikely(!bti || !errbuf || errbufsize == 0 || !bti->paramc || (directive = bti->paramv[0]) == nullptr)) { |
| Debug("url_rewrite", "[parse_directive] Invalid argument(s)"); |
| return "Invalid argument(s)"; |
| } |
| |
| for (unsigned i = 0; i < countof(directives); ++i) { |
| if (strcmp(directive, directives[i].name) == 0) { |
| return directives[i].parser(directive, bti, errbuf, errbufsize); |
| } |
| } |
| |
| snprintf(errbuf, errbufsize, "Unknown directive \"%s\"", directive); |
| Debug("url_rewrite", "[parse_directive] %s", errbuf); |
| return (const char *)errbuf; |
| } |
| |
| const char * |
| remap_validate_filter_args(acl_filter_rule **rule_pp, const char **argv, int argc, char *errStrBuf, size_t errStrBufSize) |
| { |
| acl_filter_rule *rule; |
| src_ip_info_t *ipi; |
| int i, j; |
| bool new_rule_flg = false; |
| |
| if (!rule_pp) { |
| Debug("url_rewrite", "[validate_filter_args] Invalid argument(s)"); |
| return (const char *)"Invalid argument(s)"; |
| } |
| |
| if (is_debug_tag_set("url_rewrite")) { |
| printf("validate_filter_args: "); |
| for (i = 0; i < argc; i++) { |
| printf("\"%s\" ", argv[i]); |
| } |
| printf("\n"); |
| } |
| |
| if ((rule = *rule_pp) == nullptr) { |
| rule = new acl_filter_rule(); |
| if (unlikely((*rule_pp = rule) == nullptr)) { |
| Debug("url_rewrite", "[validate_filter_args] Memory allocation error"); |
| return (const char *)"Memory allocation Error"; |
| } |
| new_rule_flg = true; |
| Debug("url_rewrite", "[validate_filter_args] new acl_filter_rule class was created during remap rule processing"); |
| } |
| |
| for (i = 0; i < argc; i++) { |
| unsigned long ul; |
| bool hasarg; |
| |
| const char *argptr; |
| if ((ul = remap_check_option(&argv[i], 1, 0, nullptr, &argptr)) == 0) { |
| Debug("url_rewrite", "[validate_filter_args] Unknown remap option - %s", argv[i]); |
| snprintf(errStrBuf, errStrBufSize, "Unknown option - \"%s\"", argv[i]); |
| errStrBuf[errStrBufSize - 1] = 0; |
| if (new_rule_flg) { |
| delete rule; |
| *rule_pp = nullptr; |
| } |
| return (const char *)errStrBuf; |
| } |
| |
| // Every filter operator requires an argument except @internal. |
| hasarg = !(ul & REMAP_OPTFLG_INTERNAL); |
| |
| if (hasarg && (!argptr || !argptr[0])) { |
| Debug("url_rewrite", "[validate_filter_args] Empty argument in %s", argv[i]); |
| snprintf(errStrBuf, errStrBufSize, "Empty argument in \"%s\"", argv[i]); |
| errStrBuf[errStrBufSize - 1] = 0; |
| if (new_rule_flg) { |
| delete rule; |
| *rule_pp = nullptr; |
| } |
| return (const char *)errStrBuf; |
| } |
| |
| if (ul & REMAP_OPTFLG_METHOD) { /* "method=" option */ |
| // Please remember that the order of hash idx creation is very important and it is defined |
| // in HTTP.cc file. 0 in our array is the first method, CONNECT |
| int m = hdrtoken_tokenize(argptr, strlen(argptr), nullptr) - HTTP_WKSIDX_CONNECT; |
| |
| if (m >= 0 && m < HTTP_WKSIDX_METHODS_CNT) { |
| rule->standard_method_lookup[m] = true; |
| } else { |
| Debug("url_rewrite", "[validate_filter_args] Using nonstandard method [%s]", argptr); |
| rule->nonstandard_methods.insert(argptr); |
| } |
| rule->method_restriction_enabled = true; |
| } |
| |
| if (ul & REMAP_OPTFLG_SRC_IP) { /* "src_ip=" option */ |
| if (rule->src_ip_cnt >= ACL_FILTER_MAX_SRC_IP) { |
| Debug("url_rewrite", "[validate_filter_args] Too many \"src_ip=\" filters"); |
| snprintf(errStrBuf, errStrBufSize, "Defined more than %d \"src_ip=\" filters!", ACL_FILTER_MAX_SRC_IP); |
| errStrBuf[errStrBufSize - 1] = 0; |
| if (new_rule_flg) { |
| delete rule; |
| *rule_pp = nullptr; |
| } |
| return (const char *)errStrBuf; |
| } |
| ipi = &rule->src_ip_array[rule->src_ip_cnt]; |
| if (ul & REMAP_OPTFLG_INVERT) { |
| ipi->invert = true; |
| } |
| if (ats_ip_range_parse(argptr, ipi->start, ipi->end) != 0) { |
| Debug("url_rewrite", "[validate_filter_args] Unable to parse IP value in %s", argv[i]); |
| snprintf(errStrBuf, errStrBufSize, "Unable to parse IP value in %s", argv[i]); |
| errStrBuf[errStrBufSize - 1] = 0; |
| if (new_rule_flg) { |
| delete rule; |
| *rule_pp = nullptr; |
| } |
| return (const char *)errStrBuf; |
| } |
| for (j = 0; j < rule->src_ip_cnt; j++) { |
| if (rule->src_ip_array[j].start == ipi->start && rule->src_ip_array[j].end == ipi->end) { |
| ipi->reset(); |
| ipi = nullptr; |
| break; /* we have the same src_ip in the list */ |
| } |
| } |
| if (ipi) { |
| rule->src_ip_cnt++; |
| rule->src_ip_valid = 1; |
| } |
| } |
| |
| if (ul & REMAP_OPTFLG_IN_IP) { /* "dest_ip=" option */ |
| if (rule->in_ip_cnt >= ACL_FILTER_MAX_IN_IP) { |
| Debug("url_rewrite", "[validate_filter_args] Too many \"in_ip=\" filters"); |
| snprintf(errStrBuf, errStrBufSize, "Defined more than %d \"in_ip=\" filters!", ACL_FILTER_MAX_IN_IP); |
| errStrBuf[errStrBufSize - 1] = 0; |
| if (new_rule_flg) { |
| delete rule; |
| *rule_pp = nullptr; |
| } |
| return (const char *)errStrBuf; |
| } |
| ipi = &rule->in_ip_array[rule->in_ip_cnt]; |
| if (ul & REMAP_OPTFLG_INVERT) { |
| ipi->invert = true; |
| } |
| // important! use copy of argument |
| if (ats_ip_range_parse(argptr, ipi->start, ipi->end) != 0) { |
| Debug("url_rewrite", "[validate_filter_args] Unable to parse IP value in %s", argv[i]); |
| snprintf(errStrBuf, errStrBufSize, "Unable to parse IP value in %s", argv[i]); |
| errStrBuf[errStrBufSize - 1] = 0; |
| if (new_rule_flg) { |
| delete rule; |
| *rule_pp = nullptr; |
| } |
| return (const char *)errStrBuf; |
| } |
| for (j = 0; j < rule->in_ip_cnt; j++) { |
| if (rule->in_ip_array[j].start == ipi->start && rule->in_ip_array[j].end == ipi->end) { |
| ipi->reset(); |
| ipi = nullptr; |
| break; /* we have the same src_ip in the list */ |
| } |
| } |
| if (ipi) { |
| rule->in_ip_cnt++; |
| rule->in_ip_valid = 1; |
| } |
| } |
| |
| if (ul & REMAP_OPTFLG_ACTION) { /* "action=" option */ |
| if (is_inkeylist(argptr, "0", "off", "deny", "disable", nullptr)) { |
| rule->allow_flag = 0; |
| } else if (is_inkeylist(argptr, "1", "on", "allow", "enable", nullptr)) { |
| rule->allow_flag = 1; |
| } else { |
| Debug("url_rewrite", "[validate_filter_args] Unknown argument \"%s\"", argv[i]); |
| snprintf(errStrBuf, errStrBufSize, "Unknown argument \"%s\"", argv[i]); |
| errStrBuf[errStrBufSize - 1] = 0; |
| if (new_rule_flg) { |
| delete rule; |
| *rule_pp = nullptr; |
| } |
| return (const char *)errStrBuf; |
| } |
| } |
| |
| if (ul & REMAP_OPTFLG_INTERNAL) { |
| rule->internal = 1; |
| } |
| } |
| |
| if (is_debug_tag_set("url_rewrite")) { |
| rule->print(); |
| } |
| |
| return nullptr; /* success */ |
| } |
| |
| unsigned long |
| remap_check_option(const char **argv, int argc, unsigned long findmode, int *_ret_idx, const char **argptr) |
| { |
| unsigned long ret_flags = 0; |
| int idx = 0; |
| |
| if (argptr) { |
| *argptr = nullptr; |
| } |
| if (argv && argc > 0) { |
| for (int i = 0; i < argc; i++) { |
| if (!strcasecmp(argv[i], "map_with_referer")) { |
| if ((findmode & REMAP_OPTFLG_MAP_WITH_REFERER) != 0) { |
| idx = i; |
| } |
| ret_flags |= REMAP_OPTFLG_MAP_WITH_REFERER; |
| } else if (!strncasecmp(argv[i], "plugin=", 7)) { |
| if ((findmode & REMAP_OPTFLG_PLUGIN) != 0) { |
| idx = i; |
| } |
| if (argptr) { |
| *argptr = &argv[i][7]; |
| } |
| ret_flags |= REMAP_OPTFLG_PLUGIN; |
| } else if (!strncasecmp(argv[i], "pparam=", 7)) { |
| if ((findmode & REMAP_OPTFLG_PPARAM) != 0) { |
| idx = i; |
| } |
| if (argptr) { |
| *argptr = &argv[i][7]; |
| } |
| ret_flags |= REMAP_OPTFLG_PPARAM; |
| } else if (!strncasecmp(argv[i], "method=", 7)) { |
| if ((findmode & REMAP_OPTFLG_METHOD) != 0) { |
| idx = i; |
| } |
| if (argptr) { |
| *argptr = &argv[i][7]; |
| } |
| ret_flags |= REMAP_OPTFLG_METHOD; |
| } else if (!strncasecmp(argv[i], "src_ip=~", 8)) { |
| if ((findmode & REMAP_OPTFLG_SRC_IP) != 0) { |
| idx = i; |
| } |
| if (argptr) { |
| *argptr = &argv[i][8]; |
| } |
| ret_flags |= (REMAP_OPTFLG_SRC_IP | REMAP_OPTFLG_INVERT); |
| } else if (!strncasecmp(argv[i], "src_ip=", 7)) { |
| if ((findmode & REMAP_OPTFLG_SRC_IP) != 0) { |
| idx = i; |
| } |
| if (argptr) { |
| *argptr = &argv[i][7]; |
| } |
| ret_flags |= REMAP_OPTFLG_SRC_IP; |
| } else if (!strncasecmp(argv[i], "in_ip=~", 7)) { |
| if ((findmode & REMAP_OPTFLG_IN_IP) != 0) { |
| idx = i; |
| } |
| if (argptr) { |
| *argptr = &argv[i][7]; |
| } |
| ret_flags |= (REMAP_OPTFLG_IN_IP | REMAP_OPTFLG_INVERT); |
| } else if (!strncasecmp(argv[i], "in_ip=", 6)) { |
| if ((findmode & REMAP_OPTFLG_IN_IP) != 0) { |
| idx = i; |
| } |
| if (argptr) { |
| *argptr = &argv[i][6]; |
| } |
| ret_flags |= REMAP_OPTFLG_IN_IP; |
| } else if (!strncasecmp(argv[i], "action=", 7)) { |
| if ((findmode & REMAP_OPTFLG_ACTION) != 0) { |
| idx = i; |
| } |
| if (argptr) { |
| *argptr = &argv[i][7]; |
| } |
| ret_flags |= REMAP_OPTFLG_ACTION; |
| } else if (!strncasecmp(argv[i], "mapid=", 6)) { |
| if ((findmode & REMAP_OPTFLG_MAP_ID) != 0) { |
| idx = i; |
| } |
| if (argptr) { |
| *argptr = &argv[i][6]; |
| } |
| ret_flags |= REMAP_OPTFLG_MAP_ID; |
| } else if (!strncasecmp(argv[i], "internal", 8)) { |
| if ((findmode & REMAP_OPTFLG_INTERNAL) != 0) { |
| idx = i; |
| } |
| ret_flags |= REMAP_OPTFLG_INTERNAL; |
| } else { |
| Warning("ignoring invalid remap option '%s'", argv[i]); |
| } |
| |
| if ((findmode & ret_flags) && !argptr) { |
| if (_ret_idx) { |
| *_ret_idx = idx; |
| } |
| return ret_flags; |
| } |
| } |
| } |
| if (_ret_idx) { |
| *_ret_idx = idx; |
| } |
| return ret_flags; |
| } |
| |
| /** |
| * @brief loads a remap plugin |
| * |
| * @pparam mp url mapping |
| * @pparam errbuf error buffer |
| * @pparam errbufsize size of the error buffer |
| * @pparam jump_to_argc |
| * @pparam plugin_found_at |
| * @return success - true, failure - false |
| */ |
| bool |
| remap_load_plugin(const char **argv, int argc, url_mapping *mp, char *errbuf, int errbufsize, int jump_to_argc, |
| int *plugin_found_at, UrlRewrite *rewrite) |
| { |
| char *c, *err; |
| const char *new_argv[1024]; |
| char *pargv[1024]; |
| int idx = 0; |
| int parc = 0; |
| *plugin_found_at = 0; |
| |
| memset(pargv, 0, sizeof(pargv)); |
| memset(new_argv, 0, sizeof(new_argv)); |
| |
| ink_assert((unsigned)argc < countof(new_argv)); |
| |
| if (jump_to_argc != 0) { |
| argc -= jump_to_argc; |
| int i = 0; |
| while (argv[i + jump_to_argc]) { |
| new_argv[i] = argv[i + jump_to_argc]; |
| i++; |
| } |
| argv = &new_argv[0]; |
| if (!remap_check_option(argv, argc, REMAP_OPTFLG_PLUGIN, &idx)) { |
| return false; |
| } |
| } else { |
| if (unlikely(!mp || (remap_check_option(argv, argc, REMAP_OPTFLG_PLUGIN, &idx) & REMAP_OPTFLG_PLUGIN) == 0)) { |
| snprintf(errbuf, errbufsize, "Can't find remap plugin keyword or \"url_mapping\" is nullptr"); |
| return false; /* incorrect input data - almost impossible case */ |
| } |
| } |
| |
| if (unlikely((c = (char *)strchr(argv[idx], (int)'=')) == nullptr || !(*(++c)))) { |
| snprintf(errbuf, errbufsize, "Can't find remap plugin file name in \"@%s\"", argv[idx]); |
| return false; /* incorrect input data */ |
| } |
| |
| Debug("remap_plugin", "using path %s for plugin", c); |
| |
| /* Prepare remap plugin parameters from the config */ |
| if ((err = mp->fromURL.string_get(nullptr)) == nullptr) { |
| snprintf(errbuf, errbufsize, "Can't load fromURL from URL class"); |
| return false; |
| } |
| pargv[parc++] = ats_strdup(err); |
| ats_free(err); |
| |
| if ((err = mp->toURL.string_get(nullptr)) == nullptr) { |
| snprintf(errbuf, errbufsize, "Can't load toURL from URL class"); |
| return false; |
| } |
| pargv[parc++] = ats_strdup(err); |
| ats_free(err); |
| |
| bool plugin_encountered = false; |
| // how many plugin parameters we have for this remapping |
| for (idx = 0; idx < argc && parc < static_cast<int>(countof(pargv) - 1); idx++) { |
| if (plugin_encountered && !strncasecmp("plugin=", argv[idx], 7) && argv[idx][7]) { |
| *plugin_found_at = idx; |
| break; // if there is another plugin, lets deal with that later |
| } |
| |
| if (!strncasecmp("plugin=", argv[idx], 7)) { |
| plugin_encountered = true; |
| } |
| |
| if (!strncasecmp("pparam=", argv[idx], 7) && argv[idx][7]) { |
| pargv[parc++] = const_cast<char *>(&(argv[idx][7])); |
| } |
| } |
| |
| Debug("url_rewrite", "Viewing all parameters for config line"); |
| for (int k = 0; k < argc; k++) { |
| Debug("url_rewrite", "Argument %d: %s", k, argv[k]); |
| } |
| |
| Debug("url_rewrite", "Viewing parsed plugin parameters for %s: [%d]", c, *plugin_found_at); |
| for (int k = 0; k < parc; k++) { |
| Debug("url_rewrite", "Argument %d: %s", k, pargv[k]); |
| } |
| |
| RemapPluginInst *pi = nullptr; |
| std::string error; |
| { |
| uint32_t elevate_access = 0; |
| REC_ReadConfigInteger(elevate_access, "proxy.config.plugin.load_elevated"); |
| ElevateAccess access(elevate_access ? ElevateAccess::FILE_PRIVILEGE : 0); |
| |
| pi = rewrite->pluginFactory.getRemapPlugin(ts::file::path(const_cast<const char *>(c)), parc, pargv, error); |
| } // done elevating access |
| |
| bool result = true; |
| if (nullptr == pi) { |
| snprintf(errbuf, errbufsize, "%s", error.c_str()); |
| } else { |
| mp->add_plugin_instance(pi); |
| } |
| |
| ats_free(pargv[0]); // fromURL |
| ats_free(pargv[1]); // toURL |
| |
| return result; |
| } |
| /** will process the regex mapping configuration and create objects in |
| output argument reg_map. It assumes existing data in reg_map is |
| inconsequential and will be perfunctorily null-ed; |
| */ |
| static bool |
| process_regex_mapping_config(const char *from_host_lower, url_mapping *new_mapping, UrlRewrite::RegexMapping *reg_map) |
| { |
| const char *str; |
| int str_index; |
| const char *to_host; |
| int to_host_len; |
| int substitution_id; |
| int substitution_count = 0; |
| int captures; |
| |
| reg_map->to_url_host_template = nullptr; |
| reg_map->to_url_host_template_len = 0; |
| reg_map->n_substitutions = 0; |
| |
| reg_map->url_map = new_mapping; |
| |
| // using from_host_lower (and not new_mapping->fromURL.host_get()) |
| // as this one will be nullptr-terminated (required by pcre_compile) |
| if (reg_map->regular_expression.compile(from_host_lower) == false) { |
| Warning("pcre_compile failed! Regex has error starting at %s", from_host_lower); |
| goto lFail; |
| } |
| |
| captures = reg_map->regular_expression.get_capture_count(); |
| if (captures == -1) { |
| Warning("pcre_fullinfo failed!"); |
| goto lFail; |
| } |
| if (captures >= UrlRewrite::MAX_REGEX_SUBS) { // off by one for $0 (implicit capture) |
| Warning("regex has %d capturing subpatterns (including entire regex); Max allowed: %d", captures + 1, |
| UrlRewrite::MAX_REGEX_SUBS); |
| goto lFail; |
| } |
| |
| to_host = new_mapping->toURL.host_get(&to_host_len); |
| for (int i = 0; i < (to_host_len - 1); ++i) { |
| if (to_host[i] == '$') { |
| if (substitution_count > UrlRewrite::MAX_REGEX_SUBS) { |
| Warning("Cannot have more than %d substitutions in mapping with host [%s]", UrlRewrite::MAX_REGEX_SUBS, from_host_lower); |
| goto lFail; |
| } |
| substitution_id = to_host[i + 1] - '0'; |
| if ((substitution_id < 0) || (substitution_id > captures)) { |
| Warning("Substitution id [%c] has no corresponding capture pattern in regex [%s]", to_host[i + 1], from_host_lower); |
| goto lFail; |
| } |
| reg_map->substitution_markers[reg_map->n_substitutions] = i; |
| reg_map->substitution_ids[reg_map->n_substitutions] = substitution_id; |
| ++reg_map->n_substitutions; |
| } |
| } |
| |
| // so the regex itself is stored in fromURL.host; string to match |
| // will be in the request; string to use for substitutions will be |
| // in this buffer |
| str = new_mapping->toURL.host_get(&str_index); // reusing str and str_index |
| reg_map->to_url_host_template_len = str_index; |
| reg_map->to_url_host_template = static_cast<char *>(ats_malloc(str_index)); |
| memcpy(reg_map->to_url_host_template, str, str_index); |
| |
| return true; |
| |
| lFail: |
| if (reg_map->to_url_host_template) { |
| ats_free(reg_map->to_url_host_template); |
| reg_map->to_url_host_template = nullptr; |
| reg_map->to_url_host_template_len = 0; |
| } |
| return false; |
| } |
| |
| static bool |
| remap_parse_config_bti(const char *path, BUILD_TABLE_INFO *bti) |
| { |
| char errBuf[1024]; |
| char errStrBuf[1024]; |
| const char *errStr; |
| |
| Tokenizer whiteTok(" \t"); |
| bool alarm_already = false; |
| |
| // Vars to parse line in file |
| char *tok_state, *cur_line, *cur_line_tmp; |
| int rparse, cur_line_size, cln = 0; // Our current line number |
| |
| // Vars to build the mapping |
| const char *fromScheme, *toScheme; |
| int fromSchemeLen, toSchemeLen; |
| const char *fromHost, *toHost; |
| int fromHostLen, toHostLen; |
| char *map_from, *map_from_start; |
| char *map_to, *map_to_start; |
| const char *tmp; // Appease the DEC compiler |
| char *fromHost_lower = nullptr; |
| char *fromHost_lower_ptr = nullptr; |
| char fromHost_lower_buf[1024]; |
| url_mapping *new_mapping = nullptr; |
| mapping_type maptype; |
| referer_info *ri; |
| int origLength; |
| int length; |
| int tok_count; |
| |
| UrlRewrite::RegexMapping *reg_map; |
| bool is_cur_mapping_regex; |
| const char *type_id_str; |
| |
| ats_scoped_str file_buf(readIntoBuffer(path, modulePrefix, nullptr)); |
| if (!file_buf) { |
| Warning("can't load remapping configuration file %s", path); |
| return false; |
| } |
| |
| Debug("url_rewrite", "[BuildTable] UrlRewrite::BuildTable()"); |
| |
| for (cur_line = tokLine(file_buf, &tok_state, '\\'); cur_line != nullptr;) { |
| reg_map = nullptr; |
| new_mapping = nullptr; |
| errStrBuf[0] = 0; |
| bti->reset(); |
| |
| // Strip leading whitespace |
| while (*cur_line && isascii(*cur_line) && isspace(*cur_line)) { |
| ++cur_line; |
| } |
| |
| if ((cur_line_size = strlen(cur_line)) <= 0) { |
| cur_line = tokLine(nullptr, &tok_state, '\\'); |
| ++cln; |
| continue; |
| } |
| |
| // Strip trailing whitespace |
| cur_line_tmp = cur_line + cur_line_size - 1; |
| while (cur_line_tmp != cur_line && isascii(*cur_line_tmp) && isspace(*cur_line_tmp)) { |
| *cur_line_tmp = '\0'; |
| --cur_line_tmp; |
| } |
| |
| if ((cur_line_size = strlen(cur_line)) <= 0 || *cur_line == '#' || *cur_line == '\0') { |
| cur_line = tokLine(nullptr, &tok_state, '\\'); |
| ++cln; |
| continue; |
| } |
| |
| Debug("url_rewrite", "[BuildTable] Parsing: \"%s\"", cur_line); |
| |
| tok_count = whiteTok.Initialize(cur_line, (SHARE_TOKS | ALLOW_SPACES)); |
| |
| for (int j = 0; j < tok_count; j++) { |
| if ((const_cast<char *>(whiteTok[j]))[0] == '@') { |
| if ((const_cast<char *>(whiteTok[j]))[1]) { |
| bti->argv[bti->argc++] = ats_strdup(&(((char *)whiteTok[j])[1])); |
| } |
| } else { |
| bti->paramv[bti->paramc++] = ats_strdup((char *)whiteTok[j]); |
| } |
| } |
| |
| // Initial verification for number of arguments |
| if (bti->paramc < 1 || (bti->paramc < 3 && bti->paramv[0][0] != '.') || bti->paramc > BUILD_TABLE_MAX_ARGS) { |
| snprintf(errStrBuf, sizeof(errStrBuf), "malformed line %d in file %s", cln + 1, path); |
| errStr = errStrBuf; |
| goto MAP_ERROR; |
| } |
| // just check all major flags/optional arguments |
| bti->remap_optflg = remap_check_option((const char **)bti->argv, bti->argc); |
| |
| // Check directive keywords (starting from '.') |
| if (bti->paramv[0][0] == '.') { |
| if ((errStr = remap_parse_directive(bti, errBuf, sizeof(errBuf))) != nullptr) { |
| snprintf(errStrBuf, sizeof(errStrBuf), "error on line %d - %s", cln + 1, errStr); |
| errStr = errStrBuf; |
| goto MAP_ERROR; |
| } |
| // We skip the rest of the parsing here. |
| cur_line = tokLine(nullptr, &tok_state, '\\'); |
| ++cln; |
| continue; |
| } |
| |
| is_cur_mapping_regex = (strncasecmp("regex_", bti->paramv[0], 6) == 0); |
| type_id_str = is_cur_mapping_regex ? (bti->paramv[0] + 6) : bti->paramv[0]; |
| |
| // Check to see whether is a reverse or forward mapping |
| if (!strcasecmp("reverse_map", type_id_str)) { |
| Debug("url_rewrite", "[BuildTable] - REVERSE_MAP"); |
| maptype = REVERSE_MAP; |
| } else if (!strcasecmp("map", type_id_str)) { |
| Debug("url_rewrite", "[BuildTable] - %s", |
| ((bti->remap_optflg & REMAP_OPTFLG_MAP_WITH_REFERER) == 0) ? "FORWARD_MAP" : "FORWARD_MAP_REFERER"); |
| maptype = ((bti->remap_optflg & REMAP_OPTFLG_MAP_WITH_REFERER) == 0) ? FORWARD_MAP : FORWARD_MAP_REFERER; |
| } else if (!strcasecmp("redirect", type_id_str)) { |
| Debug("url_rewrite", "[BuildTable] - PERMANENT_REDIRECT"); |
| maptype = PERMANENT_REDIRECT; |
| } else if (!strcasecmp("redirect_temporary", type_id_str)) { |
| Debug("url_rewrite", "[BuildTable] - TEMPORARY_REDIRECT"); |
| maptype = TEMPORARY_REDIRECT; |
| } else if (!strcasecmp("map_with_referer", type_id_str)) { |
| Debug("url_rewrite", "[BuildTable] - FORWARD_MAP_REFERER"); |
| maptype = FORWARD_MAP_REFERER; |
| } else if (!strcasecmp("map_with_recv_port", type_id_str)) { |
| Debug("url_rewrite", "[BuildTable] - FORWARD_MAP_WITH_RECV_PORT"); |
| maptype = FORWARD_MAP_WITH_RECV_PORT; |
| } else { |
| snprintf(errStrBuf, sizeof(errStrBuf), "unknown mapping type at line %d", cln + 1); |
| errStr = errStrBuf; |
| goto MAP_ERROR; |
| } |
| |
| new_mapping = new url_mapping(); |
| |
| // apply filter rules if we have to |
| if ((errStr = process_filter_opt(new_mapping, bti, errStrBuf, sizeof(errStrBuf))) != nullptr) { |
| errStr = errStrBuf; |
| goto MAP_ERROR; |
| } |
| |
| // update sticky flag |
| bti->accept_check_p = bti->accept_check_p && bti->ip_allow_check_enabled_p; |
| |
| new_mapping->map_id = 0; |
| if ((bti->remap_optflg & REMAP_OPTFLG_MAP_ID) != 0) { |
| int idx = 0; |
| int ret = remap_check_option((const char **)bti->argv, bti->argc, REMAP_OPTFLG_MAP_ID, &idx); |
| if (ret & REMAP_OPTFLG_MAP_ID) { |
| char *c = strchr(bti->argv[idx], static_cast<int>('=')); |
| new_mapping->map_id = static_cast<unsigned int>(atoi(++c)); |
| } |
| } |
| |
| map_from = bti->paramv[1]; |
| length = UrlWhack(map_from, &origLength); |
| |
| // FIX --- what does this comment mean? |
| // |
| // URL::create modified map_from so keep a point to |
| // the beginning of the string |
| if ((tmp = (map_from_start = map_from)) != nullptr && length > 2 && tmp[length - 1] == '/' && tmp[length - 2] == '/') { |
| new_mapping->unique = true; |
| length -= 2; |
| } |
| |
| new_mapping->fromURL.create(nullptr); |
| rparse = new_mapping->fromURL.parse_no_path_component_breakdown(tmp, length); |
| |
| map_from_start[origLength] = '\0'; // Unwhack |
| |
| if (rparse != PARSE_RESULT_DONE) { |
| errStr = "malformed From URL"; |
| goto MAP_ERROR; |
| } |
| |
| map_to = bti->paramv[2]; |
| length = UrlWhack(map_to, &origLength); |
| map_to_start = map_to; |
| tmp = map_to; |
| |
| new_mapping->toURL.create(nullptr); |
| rparse = new_mapping->toURL.parse_no_path_component_breakdown(tmp, length); |
| map_to_start[origLength] = '\0'; // Unwhack |
| |
| if (rparse != PARSE_RESULT_DONE) { |
| errStr = "malformed To URL"; |
| goto MAP_ERROR; |
| } |
| |
| fromScheme = new_mapping->fromURL.scheme_get(&fromSchemeLen); |
| // If the rule is "/" or just some other relative path |
| // we need to default the scheme to http |
| if (fromScheme == nullptr || fromSchemeLen == 0) { |
| new_mapping->fromURL.scheme_set(URL_SCHEME_HTTP, URL_LEN_HTTP); |
| fromScheme = new_mapping->fromURL.scheme_get(&fromSchemeLen); |
| new_mapping->wildcard_from_scheme = true; |
| } |
| toScheme = new_mapping->toURL.scheme_get(&toSchemeLen); |
| |
| // Include support for HTTPS scheme |
| // includes support for FILE scheme |
| if ((fromScheme != URL_SCHEME_HTTP && fromScheme != URL_SCHEME_HTTPS && fromScheme != URL_SCHEME_FILE && |
| fromScheme != URL_SCHEME_TUNNEL && fromScheme != URL_SCHEME_WS && fromScheme != URL_SCHEME_WSS) || |
| (toScheme != URL_SCHEME_HTTP && toScheme != URL_SCHEME_HTTPS && toScheme != URL_SCHEME_TUNNEL && |
| toScheme != URL_SCHEME_WS && toScheme != URL_SCHEME_WSS)) { |
| errStr = "only http, https, ws, wss, and tunnel remappings are supported"; |
| goto MAP_ERROR; |
| } |
| |
| // If mapping from WS or WSS we must map out to WS or WSS |
| if ((fromScheme == URL_SCHEME_WSS || fromScheme == URL_SCHEME_WS) && |
| (toScheme != URL_SCHEME_WSS && toScheme != URL_SCHEME_WS)) { |
| errStr = "WS or WSS can only be mapped out to WS or WSS."; |
| goto MAP_ERROR; |
| } |
| |
| // Check if a tag is specified. |
| if (bti->paramv[3] != nullptr) { |
| if (maptype == FORWARD_MAP_REFERER) { |
| new_mapping->filter_redirect_url = ats_strdup(bti->paramv[3]); |
| if (!strcasecmp(bti->paramv[3], "<default>") || !strcasecmp(bti->paramv[3], "default") || |
| !strcasecmp(bti->paramv[3], "<default_redirect_url>") || !strcasecmp(bti->paramv[3], "default_redirect_url")) { |
| new_mapping->default_redirect_url = true; |
| } |
| new_mapping->redir_chunk_list = redirect_tag_str::parse_format_redirect_url(bti->paramv[3]); |
| for (int j = bti->paramc; j > 4; j--) { |
| if (bti->paramv[j - 1] != nullptr) { |
| char refinfo_error_buf[1024]; |
| bool refinfo_error = false; |
| |
| ri = new referer_info(bti->paramv[j - 1], &refinfo_error, refinfo_error_buf, sizeof(refinfo_error_buf)); |
| if (refinfo_error) { |
| snprintf(errStrBuf, sizeof(errStrBuf), "%s Incorrect Referer regular expression \"%s\" at line %d - %s", modulePrefix, |
| bti->paramv[j - 1], cln + 1, refinfo_error_buf); |
| SignalError(errStrBuf, alarm_already); |
| delete ri; |
| ri = nullptr; |
| } |
| |
| if (ri && ri->negative) { |
| if (ri->any) { |
| new_mapping->optional_referer = true; /* referer header is optional */ |
| delete ri; |
| ri = nullptr; |
| } else { |
| new_mapping->negative_referer = true; /* we have negative referer in list */ |
| } |
| } |
| if (ri) { |
| ri->next = new_mapping->referer_list; |
| new_mapping->referer_list = ri; |
| } |
| } |
| } |
| } else { |
| new_mapping->tag = ats_strdup(&(bti->paramv[3][0])); |
| } |
| } |
| |
| // Check to see the fromHost remapping is a relative one |
| fromHost = new_mapping->fromURL.host_get(&fromHostLen); |
| if (fromHost == nullptr || fromHostLen <= 0) { |
| if (maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT) { |
| if (*map_from_start != '/') { |
| errStr = "relative remappings must begin with a /"; |
| goto MAP_ERROR; |
| } else { |
| fromHost = ""; |
| fromHostLen = 0; |
| } |
| } else { |
| errStr = "remap source in reverse mappings requires a hostname"; |
| goto MAP_ERROR; |
| } |
| } |
| |
| toHost = new_mapping->toURL.host_get(&toHostLen); |
| if (toHost == nullptr || toHostLen <= 0) { |
| errStr = "The remap destinations require a hostname"; |
| goto MAP_ERROR; |
| } |
| // Get rid of trailing slashes since they interfere |
| // with our ability to send redirects |
| |
| // You might be tempted to remove these lines but the new |
| // optimized header system will introduce problems. You |
| // might get two slashes occasionally instead of one because |
| // the rest of the system assumes that trailing slashes have |
| // been removed. |
| |
| if (unlikely(fromHostLen >= (int)sizeof(fromHost_lower_buf))) { |
| fromHost_lower = (fromHost_lower_ptr = static_cast<char *>(ats_malloc(fromHostLen + 1))); |
| } else { |
| fromHost_lower = &fromHost_lower_buf[0]; |
| } |
| // Canonicalize the hostname by making it lower case |
| memcpy(fromHost_lower, fromHost, fromHostLen); |
| fromHost_lower[fromHostLen] = 0; |
| LowerCaseStr(fromHost_lower); |
| |
| // set the normalized string so nobody else has to normalize this |
| new_mapping->fromURL.host_set(fromHost_lower, fromHostLen); |
| |
| reg_map = nullptr; |
| if (is_cur_mapping_regex) { |
| reg_map = new UrlRewrite::RegexMapping(); |
| if (!process_regex_mapping_config(fromHost_lower, new_mapping, reg_map)) { |
| errStr = "could not process regex mapping config line"; |
| goto MAP_ERROR; |
| } |
| Debug("url_rewrite_regex", "Configured regex rule for host [%s]", fromHost_lower); |
| } |
| |
| // If a TS receives a request on a port which is set to tunnel mode |
| // (ie, blind forwarding) and a client connects directly to the TS, |
| // then the TS will use its IPv4 address and remap rules given |
| // to send the request to its proper destination. |
| // See HttpTransact::HandleBlindTunnel(). |
| // Therefore, for a remap rule like "map tunnel://hostname..." |
| // in remap.config, we also needs to convert hostname to its IPv4 addr |
| // and gives a new remap rule with the IPv4 addr. |
| if ((maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT) && |
| fromScheme == URL_SCHEME_TUNNEL && (fromHost_lower[0] < '0' || fromHost_lower[0] > '9')) { |
| addrinfo *ai_records; // returned records. |
| ip_text_buffer ipb; // buffer for address string conversion. |
| if (0 == getaddrinfo(fromHost_lower, nullptr, nullptr, &ai_records)) { |
| for (addrinfo *ai_spot = ai_records; ai_spot; ai_spot = ai_spot->ai_next) { |
| if (ats_is_ip(ai_spot->ai_addr) && !ats_is_ip_any(ai_spot->ai_addr) && ai_spot->ai_protocol == IPPROTO_TCP) { |
| url_mapping *u_mapping; |
| |
| ats_ip_ntop(ai_spot->ai_addr, ipb, sizeof ipb); |
| u_mapping = new url_mapping; |
| u_mapping->fromURL.create(nullptr); |
| u_mapping->fromURL.copy(&new_mapping->fromURL); |
| u_mapping->fromURL.host_set(ipb, strlen(ipb)); |
| u_mapping->toURL.create(nullptr); |
| u_mapping->toURL.copy(&new_mapping->toURL); |
| |
| if (bti->paramv[3] != nullptr) { |
| u_mapping->tag = ats_strdup(&(bti->paramv[3][0])); |
| } |
| |
| if (!bti->rewrite->InsertForwardMapping(maptype, u_mapping, ipb)) { |
| errStr = "unable to add mapping rule to lookup table"; |
| freeaddrinfo(ai_records); |
| goto MAP_ERROR; |
| } |
| } |
| } |
| |
| freeaddrinfo(ai_records); |
| } |
| } |
| |
| // Check "remap" plugin options and load .so object |
| if ((bti->remap_optflg & REMAP_OPTFLG_PLUGIN) != 0 && |
| (maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT)) { |
| if ((remap_check_option((const char **)bti->argv, bti->argc, REMAP_OPTFLG_PLUGIN, &tok_count) & REMAP_OPTFLG_PLUGIN) != 0) { |
| int plugin_found_at = 0; |
| int jump_to_argc = 0; |
| |
| // this loads the first plugin |
| if (!remap_load_plugin((const char **)bti->argv, bti->argc, new_mapping, errStrBuf, sizeof(errStrBuf), 0, &plugin_found_at, |
| bti->rewrite)) { |
| Debug("remap_plugin", "Remap plugin load error - %s", errStrBuf[0] ? errStrBuf : "Unknown error"); |
| errStr = errStrBuf; |
| goto MAP_ERROR; |
| } |
| // this loads any subsequent plugins (if present) |
| while (plugin_found_at) { |
| jump_to_argc += plugin_found_at; |
| if (!remap_load_plugin((const char **)bti->argv, bti->argc, new_mapping, errStrBuf, sizeof(errStrBuf), jump_to_argc, |
| &plugin_found_at, bti->rewrite)) { |
| Debug("remap_plugin", "Remap plugin load error - %s", errStrBuf[0] ? errStrBuf : "Unknown error"); |
| errStr = errStrBuf; |
| goto MAP_ERROR; |
| } |
| } |
| } |
| } |
| |
| // Now add the mapping to appropriate container |
| if (!bti->rewrite->InsertMapping(maptype, new_mapping, reg_map, fromHost_lower, is_cur_mapping_regex)) { |
| errStr = "unable to add mapping rule to lookup table"; |
| goto MAP_ERROR; |
| } |
| |
| fromHost_lower_ptr = static_cast<char *>(ats_free_null(fromHost_lower_ptr)); |
| |
| cur_line = tokLine(nullptr, &tok_state, '\\'); |
| ++cln; |
| continue; |
| |
| // Deal with error / warning scenarios |
| MAP_ERROR: |
| |
| snprintf(errBuf, sizeof(errBuf), "%s failed to add remap rule at %s line %d: %s", modulePrefix, path, cln + 1, errStr); |
| SignalError(errBuf, alarm_already); |
| |
| delete reg_map; |
| delete new_mapping; |
| return false; |
| } /* end of while(cur_line != nullptr) */ |
| |
| IpAllow::enableAcceptCheck(bti->accept_check_p); |
| return true; |
| } |
| |
| bool |
| remap_parse_config(const char *path, UrlRewrite *rewrite) |
| { |
| BUILD_TABLE_INFO bti; |
| |
| /* If this happens to be a config reload, the list of loaded remap plugins is non-empty, and we |
| * can signal all these plugins that a reload has begun. */ |
| rewrite->pluginFactory.indicatePreReload(); |
| |
| bti.rewrite = rewrite; |
| bool status = remap_parse_config_bti(path, &bti); |
| |
| /* Now after we parsed the configuration and (re)loaded plugins and plugin instances |
| * accordingly notify all plugins that we are done */ |
| rewrite->pluginFactory.indicatePostReload(status); |
| |
| return status; |
| } |