From 50e0afc3700d4cef493639ae6f04049a7ccf6d22 Mon Sep 17 00:00:00 2001 From: zhengchao Date: Thu, 13 Sep 2018 19:28:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90replace=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E7=9A=84=E7=BC=96=E5=86=99=EF=BC=8C=E9=80=82=E9=85=8D=E6=96=B0?= =?UTF-8?q?=E7=9A=84http=E8=A7=A3=E6=9E=90=E5=B1=82=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/include/tfe_utils.h | 1 + plugin/business/pangu-http/pangu_http.cpp | 408 +++++++++++++++++----- 2 files changed, 331 insertions(+), 78 deletions(-) diff --git a/common/include/tfe_utils.h b/common/include/tfe_utils.h index ec5a27a..7c33f52 100644 --- a/common/include/tfe_utils.h +++ b/common/include/tfe_utils.h @@ -15,6 +15,7 @@ #endif #define ALLOC(type, number) ((type *)calloc(sizeof(type), number)) +#define FREE(p) {free(*p);*p=NULL} #define TFE_STRUCT_ALLOC(struct_type) __extension__ \ ({ \ diff --git a/plugin/business/pangu-http/pangu_http.cpp b/plugin/business/pangu-http/pangu_http.cpp index b5be075..1c15538 100644 --- a/plugin/business/pangu-http/pangu_http.cpp +++ b/plugin/business/pangu-http/pangu_http.cpp @@ -7,12 +7,20 @@ #include #include +#include +#include + + #include #include #include #include +#include +#include + #define MAX_SCAN_RESULT 16 #define MAX_EDIT_ZONE_NUM 64 +#define MAX_EDIT_MATCHES 16 enum pangu_action//Bigger action number is prior. { PG_ACTION_NONE = 0x00, @@ -159,18 +167,44 @@ static void _wrap_non_std_field_write(struct tfe_http_half * half, const char* f tfe_http_field_write(half, tmp_name, value); return; } +enum replace_zone +{ + kZoneRequestUri=0, + kZoneRequestHeaders, + kZoneRequestBody, + kZoneResponseHeader, + kZoneResponseBody, + kZoneMax +}; +struct replace_rule +{ + enum replace_zone zone; + char* find; + char* replace_with; +}; +struct replace_ctx +{ + struct replace_rule* rule; + size_t n_rule; + struct tfe_http_half * replacing; + struct evbuffer* http_body; + size_t body_size; +}; struct pangu_http_ctx { enum pangu_action action; char* action_para; scan_status_t mid; stream_para_t sp; - struct Maat_rule_t* enforce_rule; - char* enforce_para; + struct Maat_rule_t* exec_rule; struct Maat_rule_t* monit_rule; int monit_num; + + char* exec_para; + struct replace_ctx *rep_ctx; int thread_id; }; + static struct pangu_http_ctx* pangu_http_ctx_new(unsigned int thread_id) { struct pangu_http_ctx* ctx=ALLOC(struct pangu_http_ctx,1); @@ -180,7 +214,19 @@ static struct pangu_http_ctx* pangu_http_ctx_new(unsigned int thread_id) } static void pangu_http_ctx_free(struct pangu_http_ctx* ctx) { - + if(ctx->rep_ctx!=NULL) + { + for(size_t i=0;irep_ctx->n_rule;i++) + { + FREE(&(ctx->rep_ctx->rule[i].find)); + FREE(&(ctx->rep_ctx->rule[i].replace_with)); + } + evbuffer_free(ctx->rep_ctx->http_body); + ctx->rep_ctx->http_body=NULL; + //todo destroy http_half; + assert(ctx->rep_ctx->replacing==NULL); + FREE(&(ctx->rep_ctx)); + } Maat_clean_status(&(ctx->mid)); ctx->mid=NULL; free(ctx); @@ -259,21 +305,7 @@ static int is_http_request(uint64 events) return 0; } } -enum replace_zone -{ - kZoneRequestUri=0, - kZoneRequestHeaders, - kZoneRequestBody, - kZoneResponseHeader, - kZoneResponseBody, - kZoneMax -}; -struct replace_rule -{ - enum replace_zone zone; - char* find; - char* replace_with; -}; + enum replace_zone zone_name_to_id(const char* name) { const char* std_name[]={"http_req_uri", @@ -336,15 +368,15 @@ static char *strtok_r_esc(char *s, const char delim, char **save_ptr) { return s; } -size_t format_replace_rule(const char* enforce_para,struct replace_rule* zone, size_t n_zone) +size_t format_replace_rule(const char* exec_para, struct replace_rule* replace, size_t n_replace) { - char* tmp=ALLOC(char, strlen(enforce_para)+1); + 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_zone="replace="; const char* str_subs="substitute="; - memcpy(tmp,enforce_para,strlen(enforce_para)); + memcpy(tmp,exec_para,strlen(exec_para)); for (token = tmp; ; token= NULL) { sub_token= strtok_r(token,";", &saveptr); @@ -352,8 +384,8 @@ size_t format_replace_rule(const char* enforce_para,struct replace_rule* zone, s break; if(0=strncasecmp(sub_token,str_zone,strlen(str_zone))) { - zone[idx].zone=zone_name_to_id(sub_token+strlen(str_zone)); - if(zone[idx].zone==kZoneMax) + replace[idx].zone=zone_name_to_id(sub_token+strlen(str_zone)); + if(replace[idx].zone==kZoneMax) { break; } @@ -361,10 +393,10 @@ size_t format_replace_rule(const char* enforce_para,struct replace_rule* zone, s if(0==strncasecmp(sub_token,str_subs,strlen(str_subs))) { sub_token=+=strlen(str_subs); - zone[idx].find=tfe_strdup(strtok_r_esc(sub_token,"/", &saveptr2)); - zone[idx].replace_with==tfe_strdup(strtok_r_esc(NULL,"/", &saveptr2)); + 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_zone) + if(idx==n_replace) { break; } @@ -374,15 +406,240 @@ size_t format_replace_rule(const char* enforce_para,struct replace_rule* zone, s tmp=NULL; return idx; } -static enum tfe_bussiness_action http_replace(const struct tfe_stream * stream, const struct tfe_http_session * session, - uint64_t events, const char* body_frag, size_t frag_size,struct pangu_http_ctx* ctx) +size_t select_replace_rule(enum replace_zone zone, struct replace_rule* replace, size_t n_replace, + const struct replace_rule** selected, size_t n_selected) { - enum tfe_bussiness_action plugin_ret=BIZ_ACTION_FORWARD; - struct replace_rule* zone=ALLOC(struct replace_rule, MAX_EDIT_ZONE_NUM); - size_t zone_num=format_replace_rule(ctx->enforce_para, zone, MAX_EDIT_ZONE_NUM); + size_t i=0,j=0; + for(i=0;ireplace_with); + + status=regcomp(®, zone->find, REG_EXTENDED|REG_NEWLINE); + if(status!=0) + { + char error_message[TFE_STRING_MAX]; + regerror (status, reg, error_message, sizeof(error_message)); + TFE_LOG_ERROR(g_pangu_rt->local_logger,"Regex error compiling '%s': %s\n", + zone->find, error_message); + goto error_out; + } + + /* "p" is a pointer into the string which points to the end of the previous match. */ + const char *p = in; + /* "pre_sub_expr_end" is a pointer into the string which points to the end of the previous sub expression match. */ + const char *pre_sub_expr_end=NULL; + + /* "N_matches" is the maximum number of matches allowed. */ + const int n_matches = MAX_EDIT_MATCHES; + /* "M" contains the matches found. */ + regmatch_t m[n_matches]; + int i = 0; + + while (1) { + int nomatch = regexec (reg, p, n_matches, m, 0); + if (nomatch) + { + break; + } + if(is_replaced==0) + { + out=evbuffer_new(); + is_replaced=1; + } + assert(m[0].rm_so!=-1); + pre_sub_expr_end=p; + if(m[1].rm_so == -1)//no sub expr, replace the entire expr. + { + evbuffer_add(out, pre_sub_expr_end, m[0].rm_so-(pre_sub_expr_end-p)); + evbuffer_add(out, zone->replace_with, replace_len); + pre_sub_expr_end=p + m[0].rm_eo; + } + else //have sub expr, replace the sub expr. + { + for (i = 1, pre_sub_expr_end=p; i < n_matches; i++) + { + if (m[i].rm_so == -1) + { + break; + } + evbuffer_add(out, pre_sub_expr_end, m[i].rm_so-(pre_sub_expr_end-p)); + evbuffer_add(out, zone->replace_with, replace_len); + pre_sub_expr_end = p + m[i].rm_eo; + } + } + p += m[0].rm_eo; + } + if(is_replaced) + { + evbuffer_add(out, pre_sub_expr_end, in_sz-(pre_sub_expr_end-p)); + } + +error_out: + regfree(®); + return out; } -static enum tfe_bussiness_action http_reject(const struct tfe_http_session * session, uint64_t events, struct pangu_http_ctx* ctx) +struct evbuffer* execute_replace_rule(const char* in, size_t in_sz, + enum replace_zone zone, const struct replace_rule* rules, size_t n_rule) +{ + const struct replace_rule** todo[MAX_EDIT_ZONE_NUM]; + enum replace_zone zone; + int ret=0, is_replaced=0; + size_t n_todo=0, i=0, tmp_size=0; + struct evbuffer* out=NULL; + const char* interator=NULL; + size_t interator_sz=0; + struct evbuffer* new_out=NULL, *pre_out=NULL; + if(in==0) + { + return NULL; + } + //Do not process buffer that contains '\0'. + if(0!=memchr(in, '\0', in_sz)) + { + return NULL; + } + n_todo=select_replace_rule(zone, rules, n_rule, todo, MAX_EDIT_ZONE_NUM); + interator=in; + for(i=0; irep_ctx=ALLOC(struct replace_rule, MAX_EDIT_ZONE_NUM); + struct tfe_http_half * req=NULL; + void* interator=NULL; + struct http_field_name tmp_name; + const struct replace_rule** todo[MAX_EDIT_ZONE_NUM]; + size_t n_todo=0,i=0, out_size=0; + char* buff_in=NULL, buff_out=NULL; + struct evbuffer* rewrite_url=NULL, *rewrite_buff=NULL; + struct replace_ctx* rep_ctx=NULL; + int is_replaced=0; + if(ctx->rep_ctx==NULL) + { + ctx->rep_ctx=rep_ctx=ALLOC(struct replace_ctx, 1); + + rep_ctx->n_rule=format_replace_rule(ctx->exec_para, rep_ctx->rule, MAX_EDIT_ZONE_NUM); + + } + if(events&EV_HTTP_REQ_HDR) + { + rewrite_url=execute_replace_rule(session->req->req_spec.uri, strlen(session->req->req_spec.uri), + kZoneRequestUri, rep_ctx->rule, rep_ctx->n_rule); + } + if((events&EV_HTTP_REQ_HDR)|(events&EV_HTTP_RESP_HDR)) + { + if(events&EV_HTTP_REQ_HDR) + { + rep_ctx->replacing=tfe_http_session_request_create(session, session->req->req_spec.method, + rewrite_url!=NULL ? evbuffer_pullup(rewrite_url,-1) : session->req->req_spec.uri); + evbuffer_free(rewrite_url); + rewrite_url=NULL; + tfe_http_allow_write(session->req); + } + else + { + rep_ctx->replacing=tfe_http_session_response_create(session, session->resp->resp_spec.resp_code); + tfe_http_allow_write(session->resp); + } + while(1) + { + buff_in=tfe_http_field_iterate(session->req, &interator, &tmp_name); + if(tmp_name->field_id==TFE_HTTP_CONT_LENGTH) + { + continue; + } + if(buff_in!=NULL) + { + rewrite_buff=execute_replace_rule(buff_in, strlen(buff_in), + events&EV_HTTP_REQ_HDR?kZoneRequestHeaders:kZoneResponseHeader, rep_ctx->rule, rep_ctx->n_rule); + tfe_http_field_write(rep_ctx->replacing, tmp_name, + rewrite_buff!=NULL ? evbuffer_pullup(rewrite_buff) : buff_in); + evbuffer_free(rewrite_buff); + rewrite_buff=NULL; + + } + else + { + break; + } + } + } + if((events&EV_HTTP_REQ_BODY_BEGIN)|(events&EV_HTTP_RESP_BODY_BEGIN)) + { + assert(rep_ctx->http_body==NULL); + assert(rep_ctx->body_size=0); + rep_ctx->http_body=evbuffer_new(); + } + if(body_frag!=NULL) + { + evbuffer_add(rep_ctx->http_body, body_frag, frag_size); + rep_ctx->body_size++; + } + if((events&EV_HTTP_REQ_BODY_END)|(events&EV_HTTP_RESP_BODY_END)) + { + + assert(rep_ctx->body_size==evbuffer_get_length(rep_ctx->http_body)); + buff_in=evbuffer_pullup(rep_ctx->http_body, -1); + rewrite_buff=execute_replace_rule(buff_in, rep_ctx->body_size, + events&EV_HTTP_REQ_HDR?kZoneRequestHeaders:kZoneResponseHeader, rep_ctx->rule, rep_ctx->n_rule); + char cont_len_str[TFE_SYMBOL_MAX]; + snprintf(cont_len_str, "%llu", evbuffer_get_length(rewrite_buff)); + _wrap_std_field_write(rep_ctx->replacing, TFE_HTTP_CONT_LENGTH, cont_len_str); + tfe_http_half_append_body(rep_ctx->replacing, + evbuffer_pullup(rewrite_buff, -1), evbuffer_get_length(rewrite_buff),0); + evbuffer_free(rewrite_buff); + rewrite_buff=NULL; + if(is_http_request(events)) + { + tfe_http_session_request_set(session, rep_ctx->replacing); + } + else + { + tfe_http_session_response_set(session, rep_ctx->replacing); + } + rep_ctx->replacing=NULL;//http half's ownership has been transfered to session. + + evbuffer_free(rep_ctx->http_body); + rep_ctx->http_body=NULL; + rep_ctx->body_size=0; + } + + + +} +static void http_reject(const struct tfe_http_session * session, uint64_t events, struct pangu_http_ctx* ctx) { enum tfe_bussiness_action plugin_ret=BIZ_ACTION_FORWARD; @@ -391,56 +648,54 @@ static enum tfe_bussiness_action http_reject(const struct tfe_http_session * ses struct http_field_name tmp_name; char* page_buff=NULL; size_t page_size=0; - char buff[TFE_STRING_MAX]; + char cont_len_str[TFE_STRING_MAX]; + struct tfe_http_session* to_write=NULL; - ret=sscanf(ctx->enforce_para,"code=%d%[^;];",&resp_code); + ret=sscanf(ctx->exec_para,"code=%d%[^;];",&resp_code); if(ret!=1) { TFE_LOG_ERROR(g_pangu_rt->local_logger, "Invalid reject rule %d paramter %s", - ctx->enforce_rule->config_id, ctx->enforce_para); + ctx->exec_rule->config_id, ctx->exec_para); goto error_out; } + to_write=tfe_http_session_allow_write(session); + response=tfe_http_session_response_create(to_write, resp_code); - response=tfe_http_response_create(session->major_version, resp_code); - - html_generate(ctx->enforce_para, &page_buff, &page_size); + html_generate(ctx->exec_para, &page_buff, &page_size); _wrap_std_field_write(response, TFE_HTTP_CONT_TYPE, "text/html; charset=utf-8"); - snprinf(buff,sizeof(buff),"%d",page_size); - _wrap_std_field_write(response, TFE_HTTP_CONT_LENGTH, buff); - - tfe_http_append_body(response, page_buff, page_size, EV_HTTP_RESP_BODY_FULL); - - tfe_http_session_set_half(session,response); - plugin_ret=is_http_request(events)?BIZ_ACTION_ANSWER:BIZ_ACTION_MODIFIED; - + snprinf(cont_len_str,sizeof(cont_len_str), "%llu", page_size); + _wrap_std_field_write(response, TFE_HTTP_CONT_LENGTH, cont_len_str); + tfe_http_half_append_body(response, page_buff, page_size, 0); + tfe_http_session_response_set(session, response); + response=NULL; error_out: html_free(page_buff); return plugin_ret; } -static enum tfe_bussiness_action http_redirect(const struct tfe_http_session * session, uint64_t events, struct pangu_http_ctx* ctx) +static void http_redirect(const struct tfe_http_session * session, uint64_t events, struct pangu_http_ctx* ctx) { - - enum tfe_bussiness_action plugin_ret=BIZ_ACTION_FORWARD; int resp_code=0,ret=0; char* url=NULL; struct tfe_http_half* response=NULL; - - url=ALLOC(char, ctx->enforce_rule->serv_def_len); - ret=sscanf(ctx->enforce_para,"code=%d%[^;];url=%*[^;];",&resp_code,url); + struct tfe_http_session* to_write=NULL; + url=ALLOC(char, ctx->exec_rule->serv_def_len); + ret=sscanf(ctx->exec_para,"code=%d%[^;];url=%*[^;];",&resp_code,url); if(ret!=2) { TFE_LOG_ERROR(g_pangu_rt->local_logger, "Invalid redirect rule %d paramter %s", - ctx->enforce_rule->config_id, ctx->enforce_para); + ctx->exec_rule->config_id, ctx->exec_para); goto error_out; } - response=tfe_http_response_create(session->major_version, resp_code); - _wrap_std_field_write(response, TFE_HTTP_LOCATION, url); - tfe_http_session_set_half(session,response); - plugin_ret=is_http_request(events)?BIZ_ACTION_ANSWER:BIZ_ACTION_MODIFIED; + to_write=tfe_http_session_allow_write(session); + response=tfe_http_session_response_create(to_write, resp_code); + _wrap_std_field_write(response, TFE_HTTP_LOCATION, url); + tfe_http_session_response_set(to_write, response); + response=NULL; + error_out: free(url); - return plugin_ret; + return; } enum pangu_action http_scan(const struct tfe_http_session * session, uint64_t events, @@ -496,7 +751,7 @@ enum pangu_action http_scan(const struct tfe_http_session * session, uint64_t ev hit_cnt+=scan_ret; } } - if(events&EV_HTTP_REQ_BODY_END) + if((events&EV_HTTP_REQ_BODY_END)|(events&EV_HTTP_RESP_BODY_END)) { Maat_stream_scan_string_end(&(ctx->sp); ctx->sp=NULL; @@ -504,14 +759,14 @@ enum pangu_action http_scan(const struct tfe_http_session * session, uint64_t ev if(hit_cnt>0) { ctx->action=decide_ctrl_action(result, hit_cnt, &choosen); - ctx->enforce_rule=ALLOC(struct Maat_rule_t, 1); - memcpy(ctx->enforce_rule, choosen, sizeof(struct Maat_rule_t)); - if(ctx->enforce_rule.serv_def_len>MAX_SERVICE_DEFINE_LEN) + ctx->exec_rule=ALLOC(struct Maat_rule_t, 1); + memcpy(ctx->exec_rule, choosen, sizeof(struct Maat_rule_t)); + if(ctx->exec_rule.serv_def_len>MAX_SERVICE_DEFINE_LEN) { - ctx->enforce_para=ALLOC(char, ctx->enforce_rule.serv_def_len); - read_rule_ret=Maat_read_rule(g_pangu_rt->maat, ctx->enforce_rule, - MAAT_RULE_SERV_DEFINE, ctx->enforce_para, ctx->enforce_rule.serv_def_len); - assert(read_rule_ret== ctx->enforce_rule.serv_def_len); + ctx->exec_para=ALLOC(char, ctx->exec_rule.serv_def_len); + read_rule_ret=Maat_read_rule(g_pangu_rt->maat, ctx->exec_rule, + MAAT_RULE_SERV_DEFINE, ctx->exec_para, ctx->exec_rule.serv_def_len); + assert(read_rule_ret== ctx->exec_rule.serv_def_len); } if(hit_cnt>1) { @@ -522,7 +777,7 @@ enum pangu_action http_scan(const struct tfe_http_session * session, uint64_t ev } *p='\0'; TFE_LOG_INFO(g_pangu_rt->local_logger, "Multiple rules matched: url=%s num=%d ids=%s enforce=%d .", - session->req->req_spec.url, buff, hit_cnt, ctx->enforce_rule->config_id); + session->req->req_spec.url, buff, hit_cnt, ctx->exec_rule->config_id); } } return ctx->action; @@ -558,7 +813,7 @@ void pangu_on_http_end(const struct tfe_stream * stream, { struct pangu_http_ctx* ctx=*(struct pangu_http_ctx**)pme; - struct pangu_log log_msg={.stream=stream, .http=session, .result=ctx->enforce_rule, .result_num=1}; + struct pangu_log log_msg={.stream=stream, .http=session, .result=ctx->exec_rule, .result_num=1}; if(ctx->action!=PG_ACTION_NONE) { pangu_send_log(g_pangu_rt->send_logger, &log_msg, NULL, 0); @@ -568,13 +823,12 @@ void pangu_on_http_end(const struct tfe_stream * stream, return; } -enum tfe_bussiness_action pangu_on_http_data(const struct tfe_stream * stream, const struct tfe_http_session * session, +void pangu_on_http_data(const struct tfe_stream * stream, const struct tfe_http_session * session, uint64_t events, const char* body_frag, size_t frag_size, unsigned int thread_id, void ** pme) { struct pangu_http_ctx* ctx=*(struct pangu_http_ctx**)pme; int hit_cnt=0; enum pangu_action hit_action=PG_ACTION_NONE; - enum tfe_bussiness_action plugin_ret=BIZ_ACTION_FORWARD; Re_Enter: switch(ctx->action) @@ -589,25 +843,23 @@ Re_Enter: break; case PG_ACTION_MONIT: //send log on close. - plugin_ret=BIZ_ACTION_FORWARD; break; case PG_ACTION_REJECT: - plugin_ret=http_reject(session, events, ctx); + http_reject(session, events, ctx); break; case PG_ACTION_REDIRECT: - plugin_ret=http_redirect(session, events, ctx); + http_redirect(session, events, ctx); case PG_ACTION_REPLACE: - plugin_ret=http_replace(stream, session, events, body_frag, frag_size,ctx); + http_replace(stream, session, events, body_frag, frag_size,ctx); break; case PG_ACTION_WHITELIST: - plugin_ret=BIZ_ACTION_PASSTHROUGH; + tfe_http_session_detach(session); break; default: assert(0); - plugin_ret=BIZ_ACTION_FORWARD break; } - return plugin_ret; + return; }