1 /** @file
2  *
3  *  Remap configuration file parsing.
4  *
5  *  @section license License
6  *
7  *  Licensed to the Apache Software Foundation (ASF) under one
8  *  or more contributor license agreements.  See the NOTICE file
9  *  distributed with this work for additional information
10  *  regarding copyright ownership.  The ASF licenses this file
11  *  to you under the Apache License, Version 2.0 (the
12  *  "License"); you may not use this file except in compliance
13  *  with the License.  You may obtain a copy of the License at
14  *
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  *
17  *  Unless required by applicable law or agreed to in writing, software
18  *  distributed under the License is distributed on an "AS IS" BASIS,
19  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  *  See the License for the specific language governing permissions and
21  *  limitations under the License.
22  */
23 
24 #include "RemapConfig.h"
25 #include "UrlRewrite.h"
26 #include "ReverseProxy.h"
27 #include "tscore/I_Layout.h"
28 #include "HTTP.h"
29 #include "tscore/ink_platform.h"
30 #include "tscore/List.h"
31 #include "tscore/ink_cap.h"
32 #include "tscore/ink_file.h"
33 #include "tscore/Tokenizer.h"
34 #include "tscore/ts_file.h"
35 #include "tscore/Filenames.h"
36 #include "IPAllow.h"
37 #include "PluginFactory.h"
38 
39 #define modulePrefix "[ReverseProxy]"
40 
41 static bool remap_parse_config_bti(const char *path, BUILD_TABLE_INFO *bti);
42 
43 load_remap_file_func load_remap_file_cb = nullptr;
44 
45 /**
46   Returns the length of the URL.
47 
48   Will replace the terminator with a '/' if this is a full URL and
49   there are no '/' in it after the the host.  This ensures that class
50   URL parses the URL correctly.
51 
52 */
53 static int
UrlWhack(char * toWhack,int * origLength)54 UrlWhack(char *toWhack, int *origLength)
55 {
56   int length = strlen(toWhack);
57   char *tmp;
58   *origLength = length;
59 
60   // Check to see if this a full URL
61   tmp = strstr(toWhack, "://");
62   if (tmp != nullptr) {
63     if (strchr(tmp + 3, '/') == nullptr) {
64       toWhack[length] = '/';
65       length++;
66     }
67   }
68   return length;
69 }
70 
71 /**
72   Cleanup *char[] array - each item in array must be allocated via
73   ats_malloc or similar "x..." function.
74 
75 */
76 static void
clear_xstr_array(char * v[],size_t vsize)77 clear_xstr_array(char *v[], size_t vsize)
78 {
79   for (unsigned i = 0; i < vsize; i++) {
80     v[i] = static_cast<char *>(ats_free_null(v[i]));
81   }
82 }
83 
BUILD_TABLE_INFO()84 BUILD_TABLE_INFO::BUILD_TABLE_INFO()
85 
86 {
87   memset(this->paramv, 0, sizeof(this->paramv));
88   memset(this->argv, 0, sizeof(this->argv));
89 }
90 
~BUILD_TABLE_INFO()91 BUILD_TABLE_INFO::~BUILD_TABLE_INFO()
92 {
93   this->reset();
94 }
95 
96 void
reset()97 BUILD_TABLE_INFO::reset()
98 {
99   this->paramc = this->argc = 0;
100   clear_xstr_array(this->paramv, sizeof(this->paramv) / sizeof(char *));
101   clear_xstr_array(this->argv, sizeof(this->argv) / sizeof(char *));
102 }
103 
104 static const char *
process_filter_opt(url_mapping * mp,const BUILD_TABLE_INFO * bti,char * errStrBuf,int errStrBufSize)105 process_filter_opt(url_mapping *mp, const BUILD_TABLE_INFO *bti, char *errStrBuf, int errStrBufSize)
106 {
107   acl_filter_rule *rp, **rpp;
108   const char *errStr = nullptr;
109 
110   if (unlikely(!mp || !bti || !errStrBuf || errStrBufSize <= 0)) {
111     Debug("url_rewrite", "[process_filter_opt] Invalid argument(s)");
112     return (const char *)"[process_filter_opt] Invalid argument(s)";
113   }
114   for (rp = bti->rules_list; rp; rp = rp->next) {
115     if (rp->active_queue_flag) {
116       Debug("url_rewrite", "[process_filter_opt] Add active main filter \"%s\" (argc=%d)",
117             rp->filter_name ? rp->filter_name : "<nullptr>", rp->argc);
118       for (rpp = &mp->filter; *rpp; rpp = &((*rpp)->next)) {
119         ;
120       }
121       if ((errStr = remap_validate_filter_args(rpp, (const char **)rp->argv, rp->argc, errStrBuf, errStrBufSize)) != nullptr) {
122         break;
123       }
124     }
125   }
126   if (!errStr && (bti->remap_optflg & REMAP_OPTFLG_ALL_FILTERS) != 0) {
127     Debug("url_rewrite", "[process_filter_opt] Add per remap filter");
128     for (rpp = &mp->filter; *rpp; rpp = &((*rpp)->next)) {
129       ;
130     }
131     errStr = remap_validate_filter_args(rpp, (const char **)bti->argv, bti->argc, errStrBuf, errStrBufSize);
132   }
133   // Set the ip allow flag for this rule to the current ip allow flag state
134   mp->ip_allow_check_enabled_p = bti->ip_allow_check_enabled_p;
135 
136   return errStr;
137 }
138 
139 static bool
is_inkeylist(const char * key,...)140 is_inkeylist(const char *key, ...)
141 {
142   va_list ap;
143 
144   if (unlikely(key == nullptr || key[0] == '\0')) {
145     return false;
146   }
147 
148   va_start(ap, key);
149 
150   const char *str = va_arg(ap, const char *);
151   for (unsigned idx = 1; str; idx++) {
152     if (!strcasecmp(key, str)) {
153       va_end(ap);
154       return true;
155     }
156 
157     str = va_arg(ap, const char *);
158   }
159 
160   va_end(ap);
161   return false;
162 }
163 
164 static const char *
parse_define_directive(const char * directive,BUILD_TABLE_INFO * bti,char * errbuf,size_t errbufsize)165 parse_define_directive(const char *directive, BUILD_TABLE_INFO *bti, char *errbuf, size_t errbufsize)
166 {
167   bool flg;
168   acl_filter_rule *rp;
169   const char *cstr = nullptr;
170 
171   if (bti->paramc < 2) {
172     snprintf(errbuf, errbufsize, "Directive \"%s\" must have name argument", directive);
173     Debug("url_rewrite", "[parse_directive] %s", errbuf);
174     return (const char *)errbuf;
175   }
176   if (bti->argc < 1) {
177     snprintf(errbuf, errbufsize, "Directive \"%s\" must have filter parameter(s)", directive);
178     Debug("url_rewrite", "[parse_directive] %s", errbuf);
179     return (const char *)errbuf;
180   }
181 
182   flg = ((rp = acl_filter_rule::find_byname(bti->rules_list, (const char *)bti->paramv[1])) == nullptr) ? true : false;
183   // coverity[alloc_arg]
184   if ((cstr = remap_validate_filter_args(&rp, (const char **)bti->argv, bti->argc, errbuf, errbufsize)) == nullptr && rp) {
185     if (flg) { // new filter - add to list
186       acl_filter_rule **rpp = nullptr;
187       Debug("url_rewrite", "[parse_directive] new rule \"%s\" was created", bti->paramv[1]);
188       for (rpp = &bti->rules_list; *rpp; rpp = &((*rpp)->next)) {
189         ;
190       }
191       (*rpp = rp)->name(bti->paramv[1]);
192     }
193     Debug("url_rewrite", "[parse_directive] %d argument(s) were added to rule \"%s\"", bti->argc, bti->paramv[1]);
194     rp->add_argv(bti->argc, bti->argv); // store string arguments for future processing
195   }
196 
197   return cstr;
198 }
199 
200 static const char *
parse_delete_directive(const char * directive,BUILD_TABLE_INFO * bti,char * errbuf,size_t errbufsize)201 parse_delete_directive(const char *directive, BUILD_TABLE_INFO *bti, char *errbuf, size_t errbufsize)
202 {
203   if (bti->paramc < 2) {
204     snprintf(errbuf, errbufsize, "Directive \"%s\" must have name argument", directive);
205     Debug("url_rewrite", "[parse_directive] %s", errbuf);
206     return (const char *)errbuf;
207   }
208 
209   acl_filter_rule::delete_byname(&bti->rules_list, (const char *)bti->paramv[1]);
210   return nullptr;
211 }
212 
213 static const char *
parse_activate_directive(const char * directive,BUILD_TABLE_INFO * bti,char * errbuf,size_t errbufsize)214 parse_activate_directive(const char *directive, BUILD_TABLE_INFO *bti, char *errbuf, size_t errbufsize)
215 {
216   acl_filter_rule *rp;
217 
218   if (bti->paramc < 2) {
219     snprintf(errbuf, errbufsize, "Directive \"%s\" must have name argument", directive);
220     Debug("url_rewrite", "[parse_directive] %s", errbuf);
221     return (const char *)errbuf;
222   }
223 
224   // Check if for ip_allow filter
225   if (strcmp((const char *)bti->paramv[1], "ip_allow") == 0) {
226     bti->ip_allow_check_enabled_p = true;
227     return nullptr;
228   }
229 
230   if ((rp = acl_filter_rule::find_byname(bti->rules_list, (const char *)bti->paramv[1])) == nullptr) {
231     snprintf(errbuf, errbufsize, R"(Undefined filter "%s" in directive "%s")", bti->paramv[1], directive);
232     Debug("url_rewrite", "[parse_directive] %s", errbuf);
233     return (const char *)errbuf;
234   }
235 
236   acl_filter_rule::requeue_in_active_list(&bti->rules_list, rp);
237   return nullptr;
238 }
239 
240 static const char *
parse_deactivate_directive(const char * directive,BUILD_TABLE_INFO * bti,char * errbuf,size_t errbufsize)241 parse_deactivate_directive(const char *directive, BUILD_TABLE_INFO *bti, char *errbuf, size_t errbufsize)
242 {
243   acl_filter_rule *rp;
244 
245   if (bti->paramc < 2) {
246     snprintf(errbuf, errbufsize, "Directive \"%s\" must have name argument", directive);
247     Debug("url_rewrite", "[parse_directive] %s", errbuf);
248     return (const char *)errbuf;
249   }
250 
251   // Check if for ip_allow filter
252   if (strcmp((const char *)bti->paramv[1], "ip_allow") == 0) {
253     bti->ip_allow_check_enabled_p = false;
254     return nullptr;
255   }
256 
257   if ((rp = acl_filter_rule::find_byname(bti->rules_list, (const char *)bti->paramv[1])) == nullptr) {
258     snprintf(errbuf, errbufsize, R"(Undefined filter "%s" in directive "%s")", bti->paramv[1], directive);
259     Debug("url_rewrite", "[parse_directive] %s", errbuf);
260     return (const char *)errbuf;
261   }
262 
263   acl_filter_rule::requeue_in_passive_list(&bti->rules_list, rp);
264   return nullptr;
265 }
266 
267 static void
free_directory_list(int n_entries,struct dirent ** entrylist)268 free_directory_list(int n_entries, struct dirent **entrylist)
269 {
270   for (int i = 0; i < n_entries; ++i) {
271     free(entrylist[i]);
272   }
273 
274   free(entrylist);
275 }
276 
277 static const char *
parse_remap_fragment(const char * path,BUILD_TABLE_INFO * bti,char * errbuf,size_t errbufsize)278 parse_remap_fragment(const char *path, BUILD_TABLE_INFO *bti, char *errbuf, size_t errbufsize)
279 {
280   // We need to create a new bti so that we don't clobber any state in the parent parse, but we want
281   // to keep the ACL rules from the parent because ACLs must be global across the full set of config
282   // files.
283   BUILD_TABLE_INFO nbti;
284   bool success;
285 
286   if (access(path, R_OK) == -1) {
287     snprintf(errbuf, errbufsize, "%s: %s", path, strerror(errno));
288     return (const char *)errbuf;
289   }
290 
291   nbti.rules_list = bti->rules_list;
292   nbti.rewrite    = bti->rewrite;
293 
294   Debug("url_rewrite", "[%s] including remap configuration from %s", __func__, (const char *)path);
295   success = remap_parse_config_bti(path, &nbti);
296 
297   // The sub-parse might have updated the rules list, so push it up to the parent parse.
298   bti->rules_list = nbti.rules_list;
299 
300   if (success) {
301     // register the included file with the management subsystem so that we can correctly
302     // reload them when they change
303     load_remap_file_cb(ts::filename::REMAP, path);
304   } else {
305     snprintf(errbuf, errbufsize, "failed to parse included file %s", path);
306     return (const char *)errbuf;
307   }
308 
309   return nullptr;
310 }
311 
312 static const char *
parse_include_directive(const char * directive,BUILD_TABLE_INFO * bti,char * errbuf,size_t errbufsize)313 parse_include_directive(const char *directive, BUILD_TABLE_INFO *bti, char *errbuf, size_t errbufsize)
314 {
315   if (bti->paramc < 2) {
316     snprintf(errbuf, errbufsize, "Directive \"%s\" must have a path argument", directive);
317     Debug("url_rewrite", "[%s] %s", __func__, errbuf);
318     return (const char *)errbuf;
319   }
320 
321   for (unsigned i = 1; i < static_cast<unsigned>(bti->paramc); ++i) {
322     ats_scoped_str path;
323     const char *errmsg = nullptr;
324 
325     // The included path is relative to SYSCONFDIR, just like remap.config is.
326     path = RecConfigReadConfigPath(nullptr, bti->paramv[i]);
327 
328     if (ink_file_is_directory(path)) {
329       struct dirent **entrylist;
330       int n_entries;
331 
332       n_entries = scandir(path, &entrylist, nullptr, alphasort);
333       if (n_entries == -1) {
334         snprintf(errbuf, errbufsize, "failed to open %s: %s", path.get(), strerror(errno));
335         return (const char *)errbuf;
336       }
337 
338       for (int j = 0; j < n_entries; ++j) {
339         ats_scoped_str subpath;
340 
341         if (isdot(entrylist[j]->d_name) || isdotdot(entrylist[j]->d_name)) {
342           continue;
343         }
344 
345         subpath = Layout::relative_to(path.get(), entrylist[j]->d_name);
346 
347         if (ink_file_is_directory(subpath)) {
348           continue;
349         }
350 
351         errmsg = parse_remap_fragment(subpath, bti, errbuf, errbufsize);
352         if (errmsg != nullptr) {
353           break;
354         }
355       }
356 
357       free_directory_list(n_entries, entrylist);
358 
359     } else {
360       errmsg = parse_remap_fragment(path, bti, errbuf, errbufsize);
361     }
362 
363     if (errmsg) {
364       return errmsg;
365     }
366   }
367 
368   return nullptr;
369 }
370 
371 struct remap_directive {
372   const char *name;
373   const char *(*parser)(const char *, BUILD_TABLE_INFO *, char *, size_t);
374 };
375 
376 static const remap_directive directives[] = {
377 
378   {".definefilter", parse_define_directive},
379   {".deffilter", parse_define_directive},
380   {".defflt", parse_define_directive},
381 
382   {".deletefilter", parse_delete_directive},
383   {".delfilter", parse_delete_directive},
384   {".delflt", parse_delete_directive},
385 
386   {".usefilter", parse_activate_directive},
387   {".activefilter", parse_activate_directive},
388   {".activatefilter", parse_activate_directive},
389   {".useflt", parse_activate_directive},
390 
391   {".unusefilter", parse_deactivate_directive},
392   {".deactivatefilter", parse_deactivate_directive},
393   {".unactivefilter", parse_deactivate_directive},
394   {".deuseflt", parse_deactivate_directive},
395   {".unuseflt", parse_deactivate_directive},
396 
397   {".include", parse_include_directive},
398 };
399 
400 const char *
remap_parse_directive(BUILD_TABLE_INFO * bti,char * errbuf,size_t errbufsize)401 remap_parse_directive(BUILD_TABLE_INFO *bti, char *errbuf, size_t errbufsize)
402 {
403   const char *directive = nullptr;
404 
405   // Check arguments
406   if (unlikely(!bti || !errbuf || errbufsize == 0 || !bti->paramc || (directive = bti->paramv[0]) == nullptr)) {
407     Debug("url_rewrite", "[parse_directive] Invalid argument(s)");
408     return "Invalid argument(s)";
409   }
410 
411   for (unsigned i = 0; i < countof(directives); ++i) {
412     if (strcmp(directive, directives[i].name) == 0) {
413       return directives[i].parser(directive, bti, errbuf, errbufsize);
414     }
415   }
416 
417   snprintf(errbuf, errbufsize, "Unknown directive \"%s\"", directive);
418   Debug("url_rewrite", "[parse_directive] %s", errbuf);
419   return (const char *)errbuf;
420 }
421 
422 const char *
remap_validate_filter_args(acl_filter_rule ** rule_pp,const char ** argv,int argc,char * errStrBuf,size_t errStrBufSize)423 remap_validate_filter_args(acl_filter_rule **rule_pp, const char **argv, int argc, char *errStrBuf, size_t errStrBufSize)
424 {
425   acl_filter_rule *rule;
426   src_ip_info_t *ipi;
427   int i, j;
428   bool new_rule_flg = false;
429 
430   if (!rule_pp) {
431     Debug("url_rewrite", "[validate_filter_args] Invalid argument(s)");
432     return (const char *)"Invalid argument(s)";
433   }
434 
435   if (is_debug_tag_set("url_rewrite")) {
436     printf("validate_filter_args: ");
437     for (i = 0; i < argc; i++) {
438       printf("\"%s\" ", argv[i]);
439     }
440     printf("\n");
441   }
442 
443   if ((rule = *rule_pp) == nullptr) {
444     rule = new acl_filter_rule();
445     if (unlikely((*rule_pp = rule) == nullptr)) {
446       Debug("url_rewrite", "[validate_filter_args] Memory allocation error");
447       return (const char *)"Memory allocation Error";
448     }
449     new_rule_flg = true;
450     Debug("url_rewrite", "[validate_filter_args] new acl_filter_rule class was created during remap rule processing");
451   }
452 
453   for (i = 0; i < argc; i++) {
454     unsigned long ul;
455     bool hasarg;
456 
457     const char *argptr;
458     if ((ul = remap_check_option(&argv[i], 1, 0, nullptr, &argptr)) == 0) {
459       Debug("url_rewrite", "[validate_filter_args] Unknown remap option - %s", argv[i]);
460       snprintf(errStrBuf, errStrBufSize, "Unknown option - \"%s\"", argv[i]);
461       errStrBuf[errStrBufSize - 1] = 0;
462       if (new_rule_flg) {
463         delete rule;
464         *rule_pp = nullptr;
465       }
466       return (const char *)errStrBuf;
467     }
468 
469     // Every filter operator requires an argument except @internal.
470     hasarg = !(ul & REMAP_OPTFLG_INTERNAL);
471 
472     if (hasarg && (!argptr || !argptr[0])) {
473       Debug("url_rewrite", "[validate_filter_args] Empty argument in %s", argv[i]);
474       snprintf(errStrBuf, errStrBufSize, "Empty argument in \"%s\"", argv[i]);
475       errStrBuf[errStrBufSize - 1] = 0;
476       if (new_rule_flg) {
477         delete rule;
478         *rule_pp = nullptr;
479       }
480       return (const char *)errStrBuf;
481     }
482 
483     if (ul & REMAP_OPTFLG_METHOD) { /* "method=" option */
484       // Please remember that the order of hash idx creation is very important and it is defined
485       // in HTTP.cc file. 0 in our array is the first method, CONNECT
486       int m = hdrtoken_tokenize(argptr, strlen(argptr), nullptr) - HTTP_WKSIDX_CONNECT;
487 
488       if (m >= 0 && m < HTTP_WKSIDX_METHODS_CNT) {
489         rule->standard_method_lookup[m] = true;
490       } else {
491         Debug("url_rewrite", "[validate_filter_args] Using nonstandard method [%s]", argptr);
492         rule->nonstandard_methods.insert(argptr);
493       }
494       rule->method_restriction_enabled = true;
495     }
496 
497     if (ul & REMAP_OPTFLG_SRC_IP) { /* "src_ip=" option */
498       if (rule->src_ip_cnt >= ACL_FILTER_MAX_SRC_IP) {
499         Debug("url_rewrite", "[validate_filter_args] Too many \"src_ip=\" filters");
500         snprintf(errStrBuf, errStrBufSize, "Defined more than %d \"src_ip=\" filters!", ACL_FILTER_MAX_SRC_IP);
501         errStrBuf[errStrBufSize - 1] = 0;
502         if (new_rule_flg) {
503           delete rule;
504           *rule_pp = nullptr;
505         }
506         return (const char *)errStrBuf;
507       }
508       ipi = &rule->src_ip_array[rule->src_ip_cnt];
509       if (ul & REMAP_OPTFLG_INVERT) {
510         ipi->invert = true;
511       }
512       if (ats_ip_range_parse(argptr, ipi->start, ipi->end) != 0) {
513         Debug("url_rewrite", "[validate_filter_args] Unable to parse IP value in %s", argv[i]);
514         snprintf(errStrBuf, errStrBufSize, "Unable to parse IP value in %s", argv[i]);
515         errStrBuf[errStrBufSize - 1] = 0;
516         if (new_rule_flg) {
517           delete rule;
518           *rule_pp = nullptr;
519         }
520         return (const char *)errStrBuf;
521       }
522       for (j = 0; j < rule->src_ip_cnt; j++) {
523         if (rule->src_ip_array[j].start == ipi->start && rule->src_ip_array[j].end == ipi->end) {
524           ipi->reset();
525           ipi = nullptr;
526           break; /* we have the same src_ip in the list */
527         }
528       }
529       if (ipi) {
530         rule->src_ip_cnt++;
531         rule->src_ip_valid = 1;
532       }
533     }
534 
535     if (ul & REMAP_OPTFLG_IN_IP) { /* "dest_ip=" option */
536       if (rule->in_ip_cnt >= ACL_FILTER_MAX_IN_IP) {
537         Debug("url_rewrite", "[validate_filter_args] Too many \"in_ip=\" filters");
538         snprintf(errStrBuf, errStrBufSize, "Defined more than %d \"in_ip=\" filters!", ACL_FILTER_MAX_IN_IP);
539         errStrBuf[errStrBufSize - 1] = 0;
540         if (new_rule_flg) {
541           delete rule;
542           *rule_pp = nullptr;
543         }
544         return (const char *)errStrBuf;
545       }
546       ipi = &rule->in_ip_array[rule->in_ip_cnt];
547       if (ul & REMAP_OPTFLG_INVERT) {
548         ipi->invert = true;
549       }
550       // important! use copy of argument
551       if (ats_ip_range_parse(argptr, ipi->start, ipi->end) != 0) {
552         Debug("url_rewrite", "[validate_filter_args] Unable to parse IP value in %s", argv[i]);
553         snprintf(errStrBuf, errStrBufSize, "Unable to parse IP value in %s", argv[i]);
554         errStrBuf[errStrBufSize - 1] = 0;
555         if (new_rule_flg) {
556           delete rule;
557           *rule_pp = nullptr;
558         }
559         return (const char *)errStrBuf;
560       }
561       for (j = 0; j < rule->in_ip_cnt; j++) {
562         if (rule->in_ip_array[j].start == ipi->start && rule->in_ip_array[j].end == ipi->end) {
563           ipi->reset();
564           ipi = nullptr;
565           break; /* we have the same src_ip in the list */
566         }
567       }
568       if (ipi) {
569         rule->in_ip_cnt++;
570         rule->in_ip_valid = 1;
571       }
572     }
573 
574     if (ul & REMAP_OPTFLG_ACTION) { /* "action=" option */
575       if (is_inkeylist(argptr, "0", "off", "deny", "disable", nullptr)) {
576         rule->allow_flag = 0;
577       } else if (is_inkeylist(argptr, "1", "on", "allow", "enable", nullptr)) {
578         rule->allow_flag = 1;
579       } else {
580         Debug("url_rewrite", "[validate_filter_args] Unknown argument \"%s\"", argv[i]);
581         snprintf(errStrBuf, errStrBufSize, "Unknown argument \"%s\"", argv[i]);
582         errStrBuf[errStrBufSize - 1] = 0;
583         if (new_rule_flg) {
584           delete rule;
585           *rule_pp = nullptr;
586         }
587         return (const char *)errStrBuf;
588       }
589     }
590 
591     if (ul & REMAP_OPTFLG_INTERNAL) {
592       rule->internal = 1;
593     }
594   }
595 
596   if (is_debug_tag_set("url_rewrite")) {
597     rule->print();
598   }
599 
600   return nullptr; /* success */
601 }
602 
603 unsigned long
remap_check_option(const char ** argv,int argc,unsigned long findmode,int * _ret_idx,const char ** argptr)604 remap_check_option(const char **argv, int argc, unsigned long findmode, int *_ret_idx, const char **argptr)
605 {
606   unsigned long ret_flags = 0;
607   int idx                 = 0;
608 
609   if (argptr) {
610     *argptr = nullptr;
611   }
612   if (argv && argc > 0) {
613     for (int i = 0; i < argc; i++) {
614       if (!strcasecmp(argv[i], "map_with_referer")) {
615         if ((findmode & REMAP_OPTFLG_MAP_WITH_REFERER) != 0) {
616           idx = i;
617         }
618         ret_flags |= REMAP_OPTFLG_MAP_WITH_REFERER;
619       } else if (!strncasecmp(argv[i], "plugin=", 7)) {
620         if ((findmode & REMAP_OPTFLG_PLUGIN) != 0) {
621           idx = i;
622         }
623         if (argptr) {
624           *argptr = &argv[i][7];
625         }
626         ret_flags |= REMAP_OPTFLG_PLUGIN;
627       } else if (!strncasecmp(argv[i], "pparam=", 7)) {
628         if ((findmode & REMAP_OPTFLG_PPARAM) != 0) {
629           idx = i;
630         }
631         if (argptr) {
632           *argptr = &argv[i][7];
633         }
634         ret_flags |= REMAP_OPTFLG_PPARAM;
635       } else if (!strncasecmp(argv[i], "method=", 7)) {
636         if ((findmode & REMAP_OPTFLG_METHOD) != 0) {
637           idx = i;
638         }
639         if (argptr) {
640           *argptr = &argv[i][7];
641         }
642         ret_flags |= REMAP_OPTFLG_METHOD;
643       } else if (!strncasecmp(argv[i], "src_ip=~", 8)) {
644         if ((findmode & REMAP_OPTFLG_SRC_IP) != 0) {
645           idx = i;
646         }
647         if (argptr) {
648           *argptr = &argv[i][8];
649         }
650         ret_flags |= (REMAP_OPTFLG_SRC_IP | REMAP_OPTFLG_INVERT);
651       } else if (!strncasecmp(argv[i], "src_ip=", 7)) {
652         if ((findmode & REMAP_OPTFLG_SRC_IP) != 0) {
653           idx = i;
654         }
655         if (argptr) {
656           *argptr = &argv[i][7];
657         }
658         ret_flags |= REMAP_OPTFLG_SRC_IP;
659       } else if (!strncasecmp(argv[i], "in_ip=~", 7)) {
660         if ((findmode & REMAP_OPTFLG_IN_IP) != 0) {
661           idx = i;
662         }
663         if (argptr) {
664           *argptr = &argv[i][7];
665         }
666         ret_flags |= (REMAP_OPTFLG_IN_IP | REMAP_OPTFLG_INVERT);
667       } else if (!strncasecmp(argv[i], "in_ip=", 6)) {
668         if ((findmode & REMAP_OPTFLG_IN_IP) != 0) {
669           idx = i;
670         }
671         if (argptr) {
672           *argptr = &argv[i][6];
673         }
674         ret_flags |= REMAP_OPTFLG_IN_IP;
675       } else if (!strncasecmp(argv[i], "action=", 7)) {
676         if ((findmode & REMAP_OPTFLG_ACTION) != 0) {
677           idx = i;
678         }
679         if (argptr) {
680           *argptr = &argv[i][7];
681         }
682         ret_flags |= REMAP_OPTFLG_ACTION;
683       } else if (!strncasecmp(argv[i], "mapid=", 6)) {
684         if ((findmode & REMAP_OPTFLG_MAP_ID) != 0) {
685           idx = i;
686         }
687         if (argptr) {
688           *argptr = &argv[i][6];
689         }
690         ret_flags |= REMAP_OPTFLG_MAP_ID;
691       } else if (!strncasecmp(argv[i], "internal", 8)) {
692         if ((findmode & REMAP_OPTFLG_INTERNAL) != 0) {
693           idx = i;
694         }
695         ret_flags |= REMAP_OPTFLG_INTERNAL;
696       } else if (!strncasecmp(argv[i], "strategy=", 9)) {
697         if ((findmode & REMAP_OPTFLG_STRATEGY) != 0) {
698           idx = i;
699         }
700         if (argptr) {
701           *argptr = &argv[i][9];
702         }
703         ret_flags |= REMAP_OPTFLG_STRATEGY;
704       } else {
705         Warning("ignoring invalid remap option '%s'", argv[i]);
706       }
707 
708       if ((findmode & ret_flags) && !argptr) {
709         if (_ret_idx) {
710           *_ret_idx = idx;
711         }
712         return ret_flags;
713       }
714     }
715   }
716   if (_ret_idx) {
717     *_ret_idx = idx;
718   }
719   return ret_flags;
720 }
721 
722 /**
723  * @brief loads a remap plugin
724  *
725  * @pparam mp url mapping
726  * @pparam errbuf error buffer
727  * @pparam errbufsize size of the error buffer
728  * @pparam jump_to_argc
729  * @pparam plugin_found_at
730  * @return success - true, failure - false
731  */
732 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)733 remap_load_plugin(const char **argv, int argc, url_mapping *mp, char *errbuf, int errbufsize, int jump_to_argc,
734                   int *plugin_found_at, UrlRewrite *rewrite)
735 {
736   char *c, *err;
737   const char *new_argv[1024];
738   char *pargv[1024];
739   int idx          = 0;
740   int parc         = 0;
741   *plugin_found_at = 0;
742 
743   memset(pargv, 0, sizeof(pargv));
744   memset(new_argv, 0, sizeof(new_argv));
745 
746   ink_assert((unsigned)argc < countof(new_argv));
747 
748   if (jump_to_argc != 0) {
749     argc -= jump_to_argc;
750     int i = 0;
751     while (argv[i + jump_to_argc]) {
752       new_argv[i] = argv[i + jump_to_argc];
753       i++;
754     }
755     argv = &new_argv[0];
756     if (!remap_check_option(argv, argc, REMAP_OPTFLG_PLUGIN, &idx)) {
757       return false;
758     }
759   } else {
760     if (unlikely(!mp || (remap_check_option(argv, argc, REMAP_OPTFLG_PLUGIN, &idx) & REMAP_OPTFLG_PLUGIN) == 0)) {
761       snprintf(errbuf, errbufsize, "Can't find remap plugin keyword or \"url_mapping\" is nullptr");
762       return false; /* incorrect input data - almost impossible case */
763     }
764   }
765 
766   if (unlikely((c = (char *)strchr(argv[idx], (int)'=')) == nullptr || !(*(++c)))) {
767     snprintf(errbuf, errbufsize, "Can't find remap plugin file name in \"@%s\"", argv[idx]);
768     return false; /* incorrect input data */
769   }
770 
771   Debug("remap_plugin", "using path %s for plugin", c);
772 
773   /* Prepare remap plugin parameters from the config */
774   if ((err = mp->fromURL.string_get(nullptr)) == nullptr) {
775     snprintf(errbuf, errbufsize, "Can't load fromURL from URL class");
776     return false;
777   }
778   pargv[parc++] = ats_strdup(err);
779   ats_free(err);
780 
781   if ((err = mp->toURL.string_get(nullptr)) == nullptr) {
782     snprintf(errbuf, errbufsize, "Can't load toURL from URL class");
783     return false;
784   }
785   pargv[parc++] = ats_strdup(err);
786   ats_free(err);
787 
788   bool plugin_encountered = false;
789   // how many plugin parameters we have for this remapping
790   for (idx = 0; idx < argc && parc < static_cast<int>(countof(pargv) - 1); idx++) {
791     if (plugin_encountered && !strncasecmp("plugin=", argv[idx], 7) && argv[idx][7]) {
792       *plugin_found_at = idx;
793       break; // if there is another plugin, lets deal with that later
794     }
795 
796     if (!strncasecmp("plugin=", argv[idx], 7)) {
797       plugin_encountered = true;
798     }
799 
800     if (!strncasecmp("pparam=", argv[idx], 7) && argv[idx][7]) {
801       pargv[parc++] = const_cast<char *>(&(argv[idx][7]));
802     }
803   }
804 
805   Debug("url_rewrite", "Viewing all parameters for config line");
806   for (int k = 0; k < argc; k++) {
807     Debug("url_rewrite", "Argument %d: %s", k, argv[k]);
808   }
809 
810   Debug("url_rewrite", "Viewing parsed plugin parameters for %s: [%d]", c, *plugin_found_at);
811   for (int k = 0; k < parc; k++) {
812     Debug("url_rewrite", "Argument %d: %s", k, pargv[k]);
813   }
814 
815   RemapPluginInst *pi = nullptr;
816   std::string error;
817   {
818     uint32_t elevate_access = 0;
819     REC_ReadConfigInteger(elevate_access, "proxy.config.plugin.load_elevated");
820     ElevateAccess access(elevate_access ? ElevateAccess::FILE_PRIVILEGE : 0);
821 
822     pi = rewrite->pluginFactory.getRemapPlugin(ts::file::path(const_cast<const char *>(c)), parc, pargv, error,
823                                                isPluginDynamicReloadEnabled());
824   } // done elevating access
825 
826   bool result = true;
827   if (nullptr == pi) {
828     snprintf(errbuf, errbufsize, "%s", error.c_str());
829     result = false;
830   } else {
831     mp->add_plugin_instance(pi);
832   }
833 
834   ats_free(pargv[0]); // fromURL
835   ats_free(pargv[1]); // toURL
836 
837   return result;
838 }
839 /** will process the regex mapping configuration and create objects in
840     output argument reg_map. It assumes existing data in reg_map is
841     inconsequential and will be perfunctorily null-ed;
842 */
843 static bool
process_regex_mapping_config(const char * from_host_lower,url_mapping * new_mapping,UrlRewrite::RegexMapping * reg_map)844 process_regex_mapping_config(const char *from_host_lower, url_mapping *new_mapping, UrlRewrite::RegexMapping *reg_map)
845 {
846   const char *str;
847   int str_index;
848   const char *to_host;
849   int to_host_len;
850   int substitution_id;
851   int captures;
852 
853   reg_map->to_url_host_template     = nullptr;
854   reg_map->to_url_host_template_len = 0;
855   reg_map->n_substitutions          = 0;
856 
857   reg_map->url_map = new_mapping;
858 
859   // using from_host_lower (and not new_mapping->fromURL.host_get())
860   // as this one will be nullptr-terminated (required by pcre_compile)
861   if (reg_map->regular_expression.compile(from_host_lower) == false) {
862     Warning("pcre_compile failed! Regex has error starting at %s", from_host_lower);
863     goto lFail;
864   }
865 
866   captures = reg_map->regular_expression.get_capture_count();
867   if (captures == -1) {
868     Warning("pcre_fullinfo failed!");
869     goto lFail;
870   }
871   if (captures >= UrlRewrite::MAX_REGEX_SUBS) { // off by one for $0 (implicit capture)
872     Warning("regex has %d capturing subpatterns (including entire regex); Max allowed: %d", captures + 1,
873             UrlRewrite::MAX_REGEX_SUBS);
874     goto lFail;
875   }
876 
877   to_host = new_mapping->toURL.host_get(&to_host_len);
878   for (int i = 0; i < (to_host_len - 1); ++i) {
879     if (to_host[i] == '$') {
880       substitution_id = to_host[i + 1] - '0';
881       if ((substitution_id < 0) || (substitution_id > captures)) {
882         Warning("Substitution id [%c] has no corresponding capture pattern in regex [%s]", to_host[i + 1], from_host_lower);
883         goto lFail;
884       }
885       reg_map->substitution_markers[reg_map->n_substitutions] = i;
886       reg_map->substitution_ids[reg_map->n_substitutions]     = substitution_id;
887       ++reg_map->n_substitutions;
888     }
889   }
890 
891   // so the regex itself is stored in fromURL.host; string to match
892   // will be in the request; string to use for substitutions will be
893   // in this buffer
894   str                               = new_mapping->toURL.host_get(&str_index); // reusing str and str_index
895   reg_map->to_url_host_template_len = str_index;
896   reg_map->to_url_host_template     = static_cast<char *>(ats_malloc(str_index));
897   memcpy(reg_map->to_url_host_template, str, str_index);
898 
899   return true;
900 
901 lFail:
902   if (reg_map->to_url_host_template) {
903     ats_free(reg_map->to_url_host_template);
904     reg_map->to_url_host_template     = nullptr;
905     reg_map->to_url_host_template_len = 0;
906   }
907   return false;
908 }
909 
910 static bool
remap_parse_config_bti(const char * path,BUILD_TABLE_INFO * bti)911 remap_parse_config_bti(const char *path, BUILD_TABLE_INFO *bti)
912 {
913   char errBuf[1024];
914   char errStrBuf[1024];
915   const char *errStr;
916 
917   Tokenizer whiteTok(" \t");
918   bool alarm_already = false;
919 
920   // Vars to parse line in file
921   char *tok_state, *cur_line, *cur_line_tmp;
922   int rparse, cur_line_size, cln = 0; // Our current line number
923 
924   // Vars to build the mapping
925   const char *fromScheme, *toScheme;
926   int fromSchemeLen, toSchemeLen;
927   const char *fromHost, *toHost;
928   int fromHostLen, toHostLen;
929   char *map_from, *map_from_start;
930   char *map_to, *map_to_start;
931   const char *tmp; // Appease the DEC compiler
932   char *fromHost_lower     = nullptr;
933   char *fromHost_lower_ptr = nullptr;
934   char fromHost_lower_buf[1024];
935   url_mapping *new_mapping = nullptr;
936   mapping_type maptype;
937   referer_info *ri;
938   int origLength;
939   int length;
940   int tok_count;
941 
942   UrlRewrite::RegexMapping *reg_map;
943   bool is_cur_mapping_regex;
944   const char *type_id_str;
945 
946   std::error_code ec;
947   std::string content{ts::file::load(ts::file::path{path}, ec)};
948   if (ec) {
949     switch (ec.value()) {
950     case ENOENT:
951       Warning("Can't open remapping configuration file %s - %s", path, strerror(ec.value()));
952       break;
953     default:
954       Error("Failed load remapping configuration file %s - %s", path, strerror(ec.value()));
955       return false;
956     }
957   }
958 
959   Debug("url_rewrite", "[BuildTable] UrlRewrite::BuildTable()");
960 
961   for (cur_line = tokLine(content.data(), &tok_state, '\\'); cur_line != nullptr;) {
962     reg_map      = nullptr;
963     new_mapping  = nullptr;
964     errStrBuf[0] = 0;
965     bti->reset();
966 
967     // Strip leading whitespace
968     while (*cur_line && isascii(*cur_line) && isspace(*cur_line)) {
969       ++cur_line;
970     }
971 
972     if ((cur_line_size = strlen(cur_line)) <= 0) {
973       cur_line = tokLine(nullptr, &tok_state, '\\');
974       ++cln;
975       continue;
976     }
977 
978     // Strip trailing whitespace
979     cur_line_tmp = cur_line + cur_line_size - 1;
980     while (cur_line_tmp != cur_line && isascii(*cur_line_tmp) && isspace(*cur_line_tmp)) {
981       *cur_line_tmp = '\0';
982       --cur_line_tmp;
983     }
984 
985     if (strlen(cur_line) <= 0 || *cur_line == '#' || *cur_line == '\0') {
986       cur_line = tokLine(nullptr, &tok_state, '\\');
987       ++cln;
988       continue;
989     }
990 
991     Debug("url_rewrite", "[BuildTable] Parsing: \"%s\"", cur_line);
992 
993     tok_count = whiteTok.Initialize(cur_line, (SHARE_TOKS | ALLOW_SPACES));
994 
995     for (int j = 0; j < tok_count; j++) {
996       if ((const_cast<char *>(whiteTok[j]))[0] == '@') {
997         if ((const_cast<char *>(whiteTok[j]))[1]) {
998           bti->argv[bti->argc++] = ats_strdup(&(((char *)whiteTok[j])[1]));
999         }
1000       } else {
1001         bti->paramv[bti->paramc++] = ats_strdup((char *)whiteTok[j]);
1002       }
1003     }
1004 
1005     // Initial verification for number of arguments
1006     if (bti->paramc < 1 || (bti->paramc < 3 && bti->paramv[0][0] != '.') || bti->paramc > BUILD_TABLE_MAX_ARGS) {
1007       snprintf(errStrBuf, sizeof(errStrBuf), "malformed line %d in file %s", cln + 1, path);
1008       errStr = errStrBuf;
1009       goto MAP_ERROR;
1010     }
1011     // just check all major flags/optional arguments
1012     bti->remap_optflg = remap_check_option((const char **)bti->argv, bti->argc);
1013 
1014     // Check directive keywords (starting from '.')
1015     if (bti->paramv[0][0] == '.') {
1016       if ((errStr = remap_parse_directive(bti, errBuf, sizeof(errBuf))) != nullptr) {
1017         snprintf(errStrBuf, sizeof(errStrBuf), "error on line %d - %s", cln + 1, errStr);
1018         errStr = errStrBuf;
1019         goto MAP_ERROR;
1020       }
1021       // We skip the rest of the parsing here.
1022       cur_line = tokLine(nullptr, &tok_state, '\\');
1023       ++cln;
1024       continue;
1025     }
1026 
1027     is_cur_mapping_regex = (strncasecmp("regex_", bti->paramv[0], 6) == 0);
1028     type_id_str          = is_cur_mapping_regex ? (bti->paramv[0] + 6) : bti->paramv[0];
1029 
1030     // Check to see whether is a reverse or forward mapping
1031     if (!strcasecmp("reverse_map", type_id_str)) {
1032       Debug("url_rewrite", "[BuildTable] - REVERSE_MAP");
1033       maptype = REVERSE_MAP;
1034     } else if (!strcasecmp("map", type_id_str)) {
1035       Debug("url_rewrite", "[BuildTable] - %s",
1036             ((bti->remap_optflg & REMAP_OPTFLG_MAP_WITH_REFERER) == 0) ? "FORWARD_MAP" : "FORWARD_MAP_REFERER");
1037       maptype = ((bti->remap_optflg & REMAP_OPTFLG_MAP_WITH_REFERER) == 0) ? FORWARD_MAP : FORWARD_MAP_REFERER;
1038     } else if (!strcasecmp("redirect", type_id_str)) {
1039       Debug("url_rewrite", "[BuildTable] - PERMANENT_REDIRECT");
1040       maptype = PERMANENT_REDIRECT;
1041     } else if (!strcasecmp("redirect_temporary", type_id_str)) {
1042       Debug("url_rewrite", "[BuildTable] - TEMPORARY_REDIRECT");
1043       maptype = TEMPORARY_REDIRECT;
1044     } else if (!strcasecmp("map_with_referer", type_id_str)) {
1045       Debug("url_rewrite", "[BuildTable] - FORWARD_MAP_REFERER");
1046       maptype = FORWARD_MAP_REFERER;
1047     } else if (!strcasecmp("map_with_recv_port", type_id_str)) {
1048       Debug("url_rewrite", "[BuildTable] - FORWARD_MAP_WITH_RECV_PORT");
1049       maptype = FORWARD_MAP_WITH_RECV_PORT;
1050     } else {
1051       snprintf(errStrBuf, sizeof(errStrBuf), "unknown mapping type at line %d", cln + 1);
1052       errStr = errStrBuf;
1053       goto MAP_ERROR;
1054     }
1055 
1056     new_mapping = new url_mapping();
1057 
1058     // apply filter rules if we have to
1059     if (process_filter_opt(new_mapping, bti, errStrBuf, sizeof(errStrBuf)) != nullptr) {
1060       errStr = errStrBuf;
1061       goto MAP_ERROR;
1062     }
1063 
1064     // update sticky flag
1065     bti->accept_check_p = bti->accept_check_p && bti->ip_allow_check_enabled_p;
1066 
1067     new_mapping->map_id = 0;
1068     if ((bti->remap_optflg & REMAP_OPTFLG_MAP_ID) != 0) {
1069       int idx = 0;
1070       int ret = remap_check_option((const char **)bti->argv, bti->argc, REMAP_OPTFLG_MAP_ID, &idx);
1071       if (ret & REMAP_OPTFLG_MAP_ID) {
1072         char *c             = strchr(bti->argv[idx], static_cast<int>('='));
1073         new_mapping->map_id = static_cast<unsigned int>(atoi(++c));
1074       }
1075     }
1076 
1077     map_from = bti->paramv[1];
1078     length   = UrlWhack(map_from, &origLength);
1079 
1080     // FIX --- what does this comment mean?
1081     //
1082     // URL::create modified map_from so keep a point to
1083     //   the beginning of the string
1084     if ((tmp = (map_from_start = map_from)) != nullptr && length > 2 && tmp[length - 1] == '/' && tmp[length - 2] == '/') {
1085       new_mapping->unique = true;
1086       length -= 2;
1087     }
1088 
1089     new_mapping->fromURL.create(nullptr);
1090     rparse = new_mapping->fromURL.parse_regex(tmp, length);
1091 
1092     map_from_start[origLength] = '\0'; // Unwhack
1093 
1094     if (rparse != PARSE_RESULT_DONE) {
1095       snprintf(errStrBuf, sizeof(errStrBuf), "malformed From URL: %.*s", length, tmp);
1096       errStr = errStrBuf;
1097       goto MAP_ERROR;
1098     }
1099 
1100     map_to       = bti->paramv[2];
1101     length       = UrlWhack(map_to, &origLength);
1102     map_to_start = map_to;
1103     tmp          = map_to;
1104 
1105     new_mapping->toURL.create(nullptr);
1106     rparse                   = new_mapping->toURL.parse_no_host_check(std::string_view(tmp, length));
1107     map_to_start[origLength] = '\0'; // Unwhack
1108 
1109     if (rparse != PARSE_RESULT_DONE) {
1110       snprintf(errStrBuf, sizeof(errStrBuf), "malformed To URL: %.*s", length, tmp);
1111       errStr = errStrBuf;
1112       goto MAP_ERROR;
1113     }
1114 
1115     fromScheme = new_mapping->fromURL.scheme_get(&fromSchemeLen);
1116     // If the rule is "/" or just some other relative path
1117     //   we need to default the scheme to http
1118     if (fromScheme == nullptr || fromSchemeLen == 0) {
1119       new_mapping->fromURL.scheme_set(URL_SCHEME_HTTP, URL_LEN_HTTP);
1120       fromScheme                        = new_mapping->fromURL.scheme_get(&fromSchemeLen);
1121       new_mapping->wildcard_from_scheme = true;
1122     }
1123     toScheme = new_mapping->toURL.scheme_get(&toSchemeLen);
1124 
1125     // Include support for HTTPS scheme
1126     // includes support for FILE scheme
1127     if ((fromScheme != URL_SCHEME_HTTP && fromScheme != URL_SCHEME_HTTPS && fromScheme != URL_SCHEME_FILE &&
1128          fromScheme != URL_SCHEME_TUNNEL && fromScheme != URL_SCHEME_WS && fromScheme != URL_SCHEME_WSS) ||
1129         (toScheme != URL_SCHEME_HTTP && toScheme != URL_SCHEME_HTTPS && toScheme != URL_SCHEME_TUNNEL &&
1130          toScheme != URL_SCHEME_WS && toScheme != URL_SCHEME_WSS)) {
1131       errStr = "only http, https, ws, wss, and tunnel remappings are supported";
1132       goto MAP_ERROR;
1133     }
1134 
1135     // If mapping from WS or WSS we must map out to WS or WSS
1136     if ((fromScheme == URL_SCHEME_WSS || fromScheme == URL_SCHEME_WS) &&
1137         (toScheme != URL_SCHEME_WSS && toScheme != URL_SCHEME_WS)) {
1138       errStr = "WS or WSS can only be mapped out to WS or WSS.";
1139       goto MAP_ERROR;
1140     }
1141 
1142     // Check if a tag is specified.
1143     if (bti->paramv[3] != nullptr) {
1144       if (maptype == FORWARD_MAP_REFERER) {
1145         new_mapping->filter_redirect_url = ats_strdup(bti->paramv[3]);
1146         if (!strcasecmp(bti->paramv[3], "<default>") || !strcasecmp(bti->paramv[3], "default") ||
1147             !strcasecmp(bti->paramv[3], "<default_redirect_url>") || !strcasecmp(bti->paramv[3], "default_redirect_url")) {
1148           new_mapping->default_redirect_url = true;
1149         }
1150         new_mapping->redir_chunk_list = redirect_tag_str::parse_format_redirect_url(bti->paramv[3]);
1151         for (int j = bti->paramc; j > 4; j--) {
1152           if (bti->paramv[j - 1] != nullptr) {
1153             char refinfo_error_buf[1024];
1154             bool refinfo_error = false;
1155 
1156             ri = new referer_info(bti->paramv[j - 1], &refinfo_error, refinfo_error_buf, sizeof(refinfo_error_buf));
1157             if (refinfo_error) {
1158               snprintf(errStrBuf, sizeof(errStrBuf), "%s Incorrect Referer regular expression \"%s\" at line %d - %s", modulePrefix,
1159                        bti->paramv[j - 1], cln + 1, refinfo_error_buf);
1160               SignalError(errStrBuf, alarm_already);
1161               delete ri;
1162               ri = nullptr;
1163             }
1164 
1165             if (ri && ri->negative) {
1166               if (ri->any) {
1167                 new_mapping->optional_referer = true; /* referer header is optional */
1168                 delete ri;
1169                 ri = nullptr;
1170               } else {
1171                 new_mapping->negative_referer = true; /* we have negative referer in list */
1172               }
1173             }
1174             if (ri) {
1175               ri->next                  = new_mapping->referer_list;
1176               new_mapping->referer_list = ri;
1177             }
1178           }
1179         }
1180       } else {
1181         new_mapping->tag = ats_strdup(&(bti->paramv[3][0]));
1182       }
1183     }
1184 
1185     // Check to see the fromHost remapping is a relative one
1186     fromHost = new_mapping->fromURL.host_get(&fromHostLen);
1187     if (fromHost == nullptr || fromHostLen <= 0) {
1188       if (maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT) {
1189         if (*map_from_start != '/') {
1190           errStr = "relative remappings must begin with a /";
1191           goto MAP_ERROR;
1192         } else {
1193           fromHost    = "";
1194           fromHostLen = 0;
1195         }
1196       } else {
1197         errStr = "remap source in reverse mappings requires a hostname";
1198         goto MAP_ERROR;
1199       }
1200     }
1201 
1202     toHost = new_mapping->toURL.host_get(&toHostLen);
1203     if (toHost == nullptr || toHostLen <= 0) {
1204       errStr = "The remap destinations require a hostname";
1205       goto MAP_ERROR;
1206     }
1207     // Get rid of trailing slashes since they interfere
1208     //  with our ability to send redirects
1209 
1210     // You might be tempted to remove these lines but the new
1211     // optimized header system will introduce problems.  You
1212     // might get two slashes occasionally instead of one because
1213     // the rest of the system assumes that trailing slashes have
1214     // been removed.
1215 
1216     if (unlikely(fromHostLen >= (int)sizeof(fromHost_lower_buf))) {
1217       fromHost_lower = (fromHost_lower_ptr = static_cast<char *>(ats_malloc(fromHostLen + 1)));
1218     } else {
1219       fromHost_lower = &fromHost_lower_buf[0];
1220     }
1221     // Canonicalize the hostname by making it lower case
1222     memcpy(fromHost_lower, fromHost, fromHostLen);
1223     fromHost_lower[fromHostLen] = 0;
1224     LowerCaseStr(fromHost_lower);
1225 
1226     // set the normalized string so nobody else has to normalize this
1227     new_mapping->fromURL.host_set(fromHost_lower, fromHostLen);
1228 
1229     reg_map = nullptr;
1230     if (is_cur_mapping_regex) {
1231       reg_map = new UrlRewrite::RegexMapping();
1232       if (!process_regex_mapping_config(fromHost_lower, new_mapping, reg_map)) {
1233         errStr = "could not process regex mapping config line";
1234         goto MAP_ERROR;
1235       }
1236       Debug("url_rewrite_regex", "Configured regex rule for host [%s]", fromHost_lower);
1237     }
1238 
1239     // If a TS receives a request on a port which is set to tunnel mode
1240     // (ie, blind forwarding) and a client connects directly to the TS,
1241     // then the TS will use its IPv4 address and remap rules given
1242     // to send the request to its proper destination.
1243     // See HttpTransact::HandleBlindTunnel().
1244     // Therefore, for a remap rule like "map tunnel://hostname..."
1245     // in remap.config, we also needs to convert hostname to its IPv4 addr
1246     // and gives a new remap rule with the IPv4 addr.
1247     if ((maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT) &&
1248         fromScheme == URL_SCHEME_TUNNEL && (fromHost_lower[0] < '0' || fromHost_lower[0] > '9')) {
1249       addrinfo *ai_records; // returned records.
1250       ip_text_buffer ipb;   // buffer for address string conversion.
1251       if (0 == getaddrinfo(fromHost_lower, nullptr, nullptr, &ai_records)) {
1252         for (addrinfo *ai_spot = ai_records; ai_spot; ai_spot = ai_spot->ai_next) {
1253           if (ats_is_ip(ai_spot->ai_addr) && !ats_is_ip_any(ai_spot->ai_addr) && ai_spot->ai_protocol == IPPROTO_TCP) {
1254             url_mapping *u_mapping;
1255 
1256             ats_ip_ntop(ai_spot->ai_addr, ipb, sizeof ipb);
1257             u_mapping = new url_mapping;
1258             u_mapping->fromURL.create(nullptr);
1259             u_mapping->fromURL.copy(&new_mapping->fromURL);
1260             u_mapping->fromURL.host_set(ipb, strlen(ipb));
1261             u_mapping->toURL.create(nullptr);
1262             u_mapping->toURL.copy(&new_mapping->toURL);
1263 
1264             if (bti->paramv[3] != nullptr) {
1265               u_mapping->tag = ats_strdup(&(bti->paramv[3][0]));
1266             }
1267 
1268             if (!bti->rewrite->InsertForwardMapping(maptype, u_mapping, ipb)) {
1269               errStr = "unable to add mapping rule to lookup table";
1270               freeaddrinfo(ai_records);
1271               goto MAP_ERROR;
1272             }
1273           }
1274         }
1275 
1276         freeaddrinfo(ai_records);
1277       }
1278     }
1279 
1280     // check for a 'strategy' and if wire it up if one exists.
1281     if ((bti->remap_optflg & REMAP_OPTFLG_STRATEGY) != 0 &&
1282         (maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT)) {
1283       const char *strategy = strchr(bti->argv[0], static_cast<int>('='));
1284       if (strategy == nullptr) {
1285         errStr = "missing 'strategy' name argument, unable to add mapping rule";
1286         goto MAP_ERROR;
1287       } else {
1288         strategy++;
1289         new_mapping->strategy = bti->rewrite->strategyFactory->strategyInstance(strategy);
1290         if (!new_mapping->strategy) {
1291           snprintf(errStrBuf, sizeof(errStrBuf), "no strategy named '%s' is defined in the config", strategy);
1292           errStr = errStrBuf;
1293           goto MAP_ERROR;
1294         }
1295         Debug("url_rewrite_regex", "mapped the 'strategy' named %s", strategy);
1296       }
1297     }
1298 
1299     // Check "remap" plugin options and load .so object
1300     if ((bti->remap_optflg & REMAP_OPTFLG_PLUGIN) != 0 &&
1301         (maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT)) {
1302       if ((remap_check_option((const char **)bti->argv, bti->argc, REMAP_OPTFLG_PLUGIN, &tok_count) & REMAP_OPTFLG_PLUGIN) != 0) {
1303         int plugin_found_at = 0;
1304         int jump_to_argc    = 0;
1305 
1306         // this loads the first plugin
1307         if (!remap_load_plugin((const char **)bti->argv, bti->argc, new_mapping, errStrBuf, sizeof(errStrBuf), 0, &plugin_found_at,
1308                                bti->rewrite)) {
1309           Debug("remap_plugin", "Remap plugin load error - %s", errStrBuf[0] ? errStrBuf : "Unknown error");
1310           errStr = errStrBuf;
1311           goto MAP_ERROR;
1312         }
1313         // this loads any subsequent plugins (if present)
1314         while (plugin_found_at) {
1315           jump_to_argc += plugin_found_at;
1316           if (!remap_load_plugin((const char **)bti->argv, bti->argc, new_mapping, errStrBuf, sizeof(errStrBuf), jump_to_argc,
1317                                  &plugin_found_at, bti->rewrite)) {
1318             Debug("remap_plugin", "Remap plugin load error - %s", errStrBuf[0] ? errStrBuf : "Unknown error");
1319             errStr = errStrBuf;
1320             goto MAP_ERROR;
1321           }
1322         }
1323       }
1324     }
1325 
1326     // Now add the mapping to appropriate container
1327     if (!bti->rewrite->InsertMapping(maptype, new_mapping, reg_map, fromHost_lower, is_cur_mapping_regex)) {
1328       errStr = "unable to add mapping rule to lookup table";
1329       goto MAP_ERROR;
1330     }
1331 
1332     fromHost_lower_ptr = static_cast<char *>(ats_free_null(fromHost_lower_ptr));
1333 
1334     cur_line = tokLine(nullptr, &tok_state, '\\');
1335     ++cln;
1336     continue;
1337 
1338   // Deal with error / warning scenarios
1339   MAP_ERROR:
1340 
1341     snprintf(errBuf, sizeof(errBuf), "%s failed to add remap rule at %s line %d: %s", modulePrefix, path, cln + 1, errStr);
1342     SignalError(errBuf, alarm_already);
1343 
1344     delete reg_map;
1345     delete new_mapping;
1346     return false;
1347   } /* end of while(cur_line != nullptr) */
1348 
1349   IpAllow::enableAcceptCheck(bti->accept_check_p);
1350   return true;
1351 }
1352 
1353 bool
remap_parse_config(const char * path,UrlRewrite * rewrite)1354 remap_parse_config(const char *path, UrlRewrite *rewrite)
1355 {
1356   BUILD_TABLE_INFO bti;
1357 
1358   /* If this happens to be a config reload, the list of loaded remap plugins is non-empty, and we
1359    * can signal all these plugins that a reload has begun. */
1360   rewrite->pluginFactory.indicatePreReload();
1361 
1362   bti.rewrite = rewrite;
1363   bool status = remap_parse_config_bti(path, &bti);
1364 
1365   /* Now after we parsed the configuration and (re)loaded plugins and plugin instances
1366    * accordingly notify all plugins that we are done */
1367   rewrite->pluginFactory.indicatePostReload(status);
1368 
1369   return status;
1370 }
1371