#include "pattern_replace.h" #include #define PCRE2_CODE_UNIT_WIDTH 8 #include #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 *__attribute__((__unused__)) 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 *__attribute__((__unused__)) 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; } char *rt_read_file(const char* filename, size_t *input_sz) { FILE* fp=NULL; struct stat file_info; stat(filename, &file_info); *input_sz=file_info.st_size; fp=fopen(filename,"r"); if(fp==NULL) { return NULL; } char* data=(char*)malloc((*input_sz)); fread(data,1,*input_sz,fp); fclose(fp); return data; } size_t __attribute__((__unused__)) 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) { 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; } } size_t insert_string(char * in, size_t in_sz, const char *insert_on, const char *stype, const char *type, char** out) { char *target=NULL; size_t outlen=0, target_size=0; char position[]=""; /* ""*/ int js_type_len = 58; /*""*/ int css_type_len = 49; char* head_string=NULL; if (0==strcasecmp(type, "css")) { target_size = in_sz+strlen(stype)+css_type_len; target = ALLOC(char, target_size); } if (0==strcasecmp(type, "js")) { target_size = in_sz+strlen(stype)+js_type_len; target = ALLOC(char, target_size); } if (insert_on != NULL && 0==strcasecmp(insert_on, "after-page-load")) { memcpy(position, "", sizeof(position)); } head_string=strstr(in, position); if (head_string != NULL) { strncat(target, in, MIN((unsigned int)(head_string-in), target_size)); size_t style_len = 0; char *style_msg = NULL; if (0==strcasecmp(type, "js")) { style_len = strlen(stype)+js_type_len+1; style_msg = ALLOC(char, style_len); snprintf(style_msg, style_len, "", stype); } if (0==strcasecmp(type, "css")) { style_len = strlen(stype)+css_type_len+1; style_msg = ALLOC(char, style_len); snprintf(style_msg, style_len, "\n", stype); } strncat(target, style_msg, target_size); free(style_msg); style_msg = NULL; strncat(target, head_string, target_size); *out = target; outlen = strlen(target) + 1; }else { free(target); target = NULL; outlen = 0; } return outlen; } size_t execute_insert_rule(char * in, size_t in_sz, const struct insert_rule * rules, char** out) { return insert_string(in, in_sz, rules->position, rules->stype, rules->type, out); } void simple_replace(const char* find, const char* replacement, const char* input, size_t in_sz, char** output, size_t *output_sz) { size_t n_got_rule=1; struct replace_rule rules[16]; rules[0].zone = kZoneResponseBody; rules[0].find = tfe_strdup(find); rules[0].replace_with = tfe_strdup(replacement); *output_sz=execute_replace_rule(input, strlen(input), kZoneResponseBody, rules, n_got_rule, output); return; }