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