#include "pattern_replace.h" #include #define PCRE2_CODE_UNIT_WIDTH 8 #include #include #include #include #include #define MAX_EDIT_MATCHES 16 enum replace_zone zone_name_to_id(const char * name) { const char * std_name[] = {"http_req_uri", "http_req_header", "http_req_body", "http_resp_header", "http_resp_body", "http_resp_body"}; size_t i = 0; for (i = 0; i < sizeof(std_name) / sizeof(const char *); i++) { if (0 == strcasecmp(name, std_name[i])) { break; } } return (enum replace_zone) i; } static char * strchr_esc(char * s, const char delim) { char * token; if (s == NULL) return NULL; for (token = s; *token != '\0'; token++) { if (*token == '\\') { token++; continue; } if (*token == delim) break; } if (*token == '\0') { return NULL; } else { return token; } } static char * strtok_r_esc(char * s, const char delim, char ** save_ptr) { char * token; if (s == NULL) s = *save_ptr; /* Scan leading delimiters. */ token = strchr_esc(s, delim); if (token == NULL) { *save_ptr = token; return s; } /* Find the end of the token. */ *token = '\0'; token++; *save_ptr = token; return s; } size_t format_replace_rule(const char * exec_para, struct replace_rule * replace, size_t n_replace) { char * tmp = ALLOC(char, strlen(exec_para) + 1); char * token = NULL, * sub_token = NULL, * saveptr = NULL, * saveptr2 = NULL; size_t idx = 0; const char * str_zone = "zone="; const char * str_subs = "substitute="; memcpy(tmp, exec_para, strlen(exec_para)); for (token = tmp;; token = NULL) { sub_token = strtok_r(token, ";", &saveptr); if (sub_token == NULL) break; if (0 == strncasecmp(sub_token, str_zone, strlen(str_zone))) { replace[idx].zone = zone_name_to_id(sub_token + strlen(str_zone)); if (replace[idx].zone == kZoneMax) { break; } } sub_token = strtok_r(NULL, ";", &saveptr); if (0 == strncasecmp(sub_token, str_subs, strlen(str_subs))) { sub_token += strlen(str_subs) + 1; replace[idx].find = tfe_strdup(strtok_r_esc(sub_token, '/', &saveptr2)); replace[idx].replace_with = tfe_strdup(strtok_r_esc(NULL, '/', &saveptr2)); idx++; if (idx == n_replace) { break; } } } free(tmp); tmp = NULL; return idx; } size_t select_replace_rule(enum replace_zone zone, const struct replace_rule * replace, size_t n_replace, const struct replace_rule ** selected, size_t n_selected) { size_t i = 0, j = 0; for (i = 0; i < n_replace && j < n_selected; i++) { if (replace[i].zone == zone) { selected[j] = replace + i; j++; } } return j; } size_t replace_string(const char * in, size_t in_sz, const struct replace_rule * zone, char** out) { size_t replace_len = strlen(zone->replace_with); assert(strlen(zone->find) != 0); int error; PCRE2_SIZE erroffset; const PCRE2_SPTR pattern = (PCRE2_SPTR)zone->find; const PCRE2_SPTR subject = (PCRE2_SPTR)in; const PCRE2_SPTR replacement = (PCRE2_SPTR)zone->replace_with; pcre2_code *re = pcre2_compile(pattern, PCRE2_ZERO_TERMINATED, 0, &error, &erroffset, 0); if (re == 0) return -1; pcre2_jit_compile(re, PCRE2_JIT_COMPLETE); PCRE2_SIZE outbuff_size = in_sz+sizeof(replacement)*MAX_EDIT_MATCHES; PCRE2_SIZE outlen = 0; PCRE2_UCHAR* out_buffer = NULL; not_enough_mem_retry: out_buffer = (PCRE2_UCHAR*)malloc(sizeof(PCRE2_UCHAR)*outbuff_size); outlen = outbuff_size; int rc = pcre2_substitute(re, subject, in_sz, 0, PCRE2_SUBSTITUTE_GLOBAL | PCRE2_SUBSTITUTE_EXTENDED | PCRE2_SUBSTITUTE_OVERFLOW_LENGTH, 0, 0, replacement, PCRE2_ZERO_TERMINATED, out_buffer, &outlen); if(outlen>outbuff_size) { outbuff_size=outlen; free(out_buffer); out_buffer=NULL; goto not_enough_mem_retry; } if(rc<=0) { free(out_buffer); outlen=rc; } else { *out=(char*)out_buffer; } pcre2_code_free(re); return outlen; } size_t execute_replace_rule(const char * in, size_t in_sz, enum replace_zone zone, const struct replace_rule * rules, size_t n_rule, char** out) { const struct replace_rule * todo[n_rule]; size_t n_todo = 0, i = 0, interator_sz=0, pre_out_sz=0; const char * interator = NULL; char* new_out = NULL, * pre_out = NULL; size_t output_size=0; if (in_sz == 0 || in==NULL) { return 0; } n_todo = select_replace_rule(zone, rules, n_rule, todo, n_rule); interator = in; interator_sz = in_sz; for (i = 0; i < n_todo; i++) { output_size = replace_string(interator, interator_sz, todo[i], &new_out); if (output_size == 0) { continue; } if (pre_out != NULL) { free(pre_out); pre_out = NULL; } pre_out = new_out; pre_out_sz = output_size; interator = new_out; interator_sz = output_size; new_out=NULL; output_size=0; } if(pre_out_sz>0) { *out=pre_out; return pre_out_sz; } else { return 0; } } void simple_replace(const char* find, const char* replacement, const char* input, size_t in_sz, char** output, size_t *output_sz) { char* exec_para=NULL; asprintf(&exec_para,"zone=http_resp_body;substitute=/%s/%s", find, replacement); size_t n_got_rule=0; struct replace_rule rules[16]; n_got_rule=format_replace_rule(exec_para, rules, sizeof(rules)/sizeof(rules[0])); *output_sz=execute_replace_rule(input, strlen(input), kZoneResponseBody, rules, n_got_rule, output); free(exec_para); return; }