This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
tango-tfe/plugin/business/pangu-http/src/pangu_http.cpp

1416 lines
42 KiB
C++
Raw Normal View History

2018-09-14 16:33:36 +08:00
#include "pangu_logger.h"
#include "pattern_replace.h"
2018-10-14 18:45:02 +08:00
#include "pangu_web_cache.h"
2018-09-14 11:42:22 +08:00
#include <tfe_proxy.h>
#include <tfe_stream.h>
#include <tfe_utils.h>
#include <tfe_http.h>
2018-09-18 12:01:56 +08:00
#include <tfe_plugin.h>
#include <MESA/Maat_rule.h>
#include <MESA/MESA_handle_logger.h>
#include <MESA/MESA_prof_load.h>
#include <MESA/stream.h>
2018-09-09 15:21:26 +08:00
#include <event2/event.h>
#include <event2/buffer.h>
#include <ctemplate/template.h>
#include <assert.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#define MAX_SCAN_RESULT 16
#define MAX_EDIT_ZONE_NUM 64
enum pangu_action//Bigger action number is prior.
{
PG_ACTION_NONE = 0x00,
PG_ACTION_MONIT = 0x01,
PG_ACTION_FORWARD = 0x02, /* N/A */
PG_ACTION_REJECT = 0x10,
PG_ACTION_DROP = 0x20, /* N/A */
PG_ACTION_REDIRECT = 0x30,
PG_ACTION_RATELIMIT = 0x40, /* N/A */
PG_ACTION_REPLACE = 0x50,
PG_ACTION_LOOP = 0x60, /* N/A */
PG_ACTION_WHITELIST = 0x80,
__PG_ACTION_MAX
};
enum scan_table
{
PXY_CTRL_IP,
PXY_CTRL_HTTP_URL,
PXY_CTRL_HTTP_REQ_HDR,
PXY_CTRL_HTTP_REQ_BODY,
PXY_CTRL_HTTP_RES_HDR,
PXY_CTRL_HTTP_RES_BODY,
__SCAN_TABLE_MAX
};
enum pangu_http_stat
{
STAT_SESSION,
STAT_LOG_NUM,
STAT_ACTION_MONIT,
STAT_ACTION_REJECT,
STAT_ACTION_REDIRECT,
STAT_ACTION_PRE_REPLACE,
STAT_ACTION_REPLACE,
STAT_ACTION_WHITELSIT,
STAT_SUSPENDING,
__PG_STAT_MAX
};
struct pangu_rt
{
Maat_feather_t maat;
struct pangu_logger * send_logger;
void * local_logger;
int log_level;
int thread_num;
int scan_table_id[__SCAN_TABLE_MAX];
ctemplate::Template * tpl_403, * tpl_404, * tpl_451;
char * reject_page;
int page_size;
long long suspend_max;
int cache_enabled;
struct cache_handle* cache;
screen_stat_handle_t fs_handle;
long long stat_val[__PG_STAT_MAX];
int fs_id[__PG_STAT_MAX];
struct event_base* gc_evbase;
struct event* gcev;
int ca_store_reseting;
};
struct pangu_rt * g_pangu_rt;
#define MAAT_INPUT_JSON 0
#define MAAT_INPUT_REDIS 1
#define MAAT_INPUT_FILE 2
static Maat_feather_t create_maat_feather(const char * profile, const char * section, int max_thread, void * logger)
2018-09-09 15:21:26 +08:00
{
Maat_feather_t target;
int input_mode = 0, maat_stat_on = 0, maat_perf_on = 0;
int ret = 0, scan_detail = 0, effect_interval = 60;
char table_info[TFE_STRING_MAX] = {0}, inc_cfg_dir[TFE_STRING_MAX] = {0}, ful_cfg_dir[TFE_STRING_MAX] = {0};
char redis_server[TFE_STRING_MAX] = {0};
int redis_port = 0;
int redis_db_idx = 0;
char json_cfg_file[TFE_STRING_MAX] = {0}, maat_stat_file[TFE_STRING_MAX] = {0};
const char * instance_name = "pangu";
MESA_load_profile_int_def(profile, section, "MAAT_INPUT_MODE", &(input_mode), 0);
MESA_load_profile_int_def(profile, section, "STAT_SWITCH", &(maat_stat_on), 1);
MESA_load_profile_int_def(profile, section, "PERF_SWITCH", &(maat_perf_on), 1);
MESA_load_profile_string_def(profile, section, "TABLE_INFO", table_info, sizeof(table_info), "");
MESA_load_profile_string_def(profile, section, "JSON_CFG_FILE", json_cfg_file, sizeof(json_cfg_file), "");
MESA_load_profile_string_def(profile, section, "MAAT_REDIS_SERVER", redis_server, sizeof(redis_server), "");
MESA_load_profile_int_def(profile, section, "MAAT_REDIS_PORT", &(redis_port), 6379);
MESA_load_profile_int_def(profile, section, "MAAT_REDIS_DB_INDEX", &(redis_db_idx), 0);
MESA_load_profile_string_def(profile, section, "INC_CFG_DIR", inc_cfg_dir, sizeof(inc_cfg_dir), "");
MESA_load_profile_string_def(profile, section, "FULL_CFG_DIR", ful_cfg_dir, sizeof(ful_cfg_dir), "");
MESA_load_profile_string_def(profile, section, "STAT_FILE", maat_stat_file, sizeof(maat_stat_file), "");
MESA_load_profile_int_def(profile, section, "EFFECT_INTERVAL_S", &(effect_interval), 60);
effect_interval *= 1000;//convert s to ms
assert(strlen(inc_cfg_dir) != 0 && strlen(ful_cfg_dir) != 0);
target = Maat_feather(max_thread, table_info, logger);
Maat_set_feather_opt(target, MAAT_OPT_INSTANCE_NAME, instance_name, strlen(instance_name) + 1);
switch (input_mode)
{
case MAAT_INPUT_JSON:
Maat_set_feather_opt(target, MAAT_OPT_JSON_FILE_PATH, json_cfg_file, strlen(json_cfg_file) + 1);
break;
case MAAT_INPUT_REDIS:
Maat_set_feather_opt(target, MAAT_OPT_REDIS_IP, redis_server, strlen(redis_server) + 1);
Maat_set_feather_opt(target, MAAT_OPT_REDIS_PORT, &redis_port, sizeof(redis_port));
Maat_set_feather_opt(target, MAAT_OPT_REDIS_INDEX, &redis_db_idx, sizeof(redis_db_idx));
break;
case MAAT_INPUT_FILE: Maat_set_feather_opt(target, MAAT_OPT_FULL_CFG_DIR, ful_cfg_dir, strlen(ful_cfg_dir) + 1);
Maat_set_feather_opt(target, MAAT_OPT_INC_CFG_DIR, inc_cfg_dir, strlen(inc_cfg_dir) + 1);
break;
default: TFE_LOG_ERROR(logger, "Invalid MAAT Input Mode: %d.", input_mode);
goto error_out;
break;
2018-09-09 15:21:26 +08:00
}
Maat_set_feather_opt(target, MAAT_OPT_FOREIGN_CONT_DIR, "./pangu_files", strlen("./pangu_files")+1);
if (maat_stat_on)
2018-09-09 15:21:26 +08:00
{
Maat_set_feather_opt(target, MAAT_OPT_STAT_FILE_PATH, maat_stat_file, strlen(maat_stat_file) + 1);
2018-09-09 15:21:26 +08:00
Maat_set_feather_opt(target, MAAT_OPT_STAT_ON, NULL, 0);
if (maat_perf_on)
2018-09-09 15:21:26 +08:00
{
Maat_set_feather_opt(target, MAAT_OPT_PERF_ON, NULL, 0);
}
}
Maat_set_feather_opt(target, MAAT_OPT_EFFECT_INVERVAL_MS, &effect_interval, sizeof(effect_interval));
Maat_set_feather_opt(target, MAAT_OPT_SCAN_DETAIL, &scan_detail, sizeof(scan_detail));
ret = Maat_initiate_feather(target);
if (ret < 0)
2018-09-09 15:21:26 +08:00
{
TFE_LOG_ERROR(logger, "%s MAAT init failed.", __FUNCTION__);
goto error_out;
2018-09-09 15:21:26 +08:00
}
2018-09-09 15:21:26 +08:00
return target;
error_out:
Maat_burn_feather(target);
return NULL;
2018-09-09 15:21:26 +08:00
}
static void pangu_http_gc_cb(evutil_socket_t fd, short what, void * arg)
{
int i=0;
for(i=0;i<__PG_STAT_MAX;i++)
{
FS_operate(g_pangu_rt->fs_handle, g_pangu_rt->fs_id[i], 0, FS_OP_SET, ATOMIC_READ(&(g_pangu_rt->stat_val[i])));
}
return;
}
static void pangu_http_stat_init(struct pangu_rt * pangu_runtime)
{
int i=0;
struct timeval gc_delay = {0, 500*1000}; //Microseconds, we set 500 miliseconds here.
const char* spec[__PG_STAT_MAX]={0};
spec[STAT_SESSION]="http_sess";
spec[STAT_LOG_NUM]="log_num";
spec[STAT_ACTION_MONIT]="monitor";
spec[STAT_ACTION_REJECT]="reject";
spec[STAT_ACTION_REDIRECT]="redirect";
spec[STAT_ACTION_PRE_REPLACE]="pre_replace";
spec[STAT_ACTION_REPLACE]="replace";
spec[STAT_ACTION_WHITELSIT]="whitelist";
spec[STAT_SUSPENDING]="suspending";
for(i=0;i<__PG_STAT_MAX;i++)
{
if(spec[i]!=NULL)
{
pangu_runtime->fs_id[i]=FS_register(pangu_runtime->fs_handle, FS_STYLE_STATUS, FS_CALC_CURRENT, spec[i]);
}
}
g_pangu_rt->gcev = event_new(pangu_runtime->gc_evbase, -1, EV_PERSIST, pangu_http_gc_cb, NULL);
evtimer_add(g_pangu_rt->gcev, &gc_delay);
return;
}
void trusted_CA_update_start_cb(int update_type, void* u_para)
{
if(update_type==MAAT_RULE_UPDATE_TYPE_FULL)
{
if(g_pangu_rt->ca_store_reseting==0)
{
tfe_proxy_ssl_reset_trust_ca();
TFE_LOG_INFO(g_pangu_rt->local_logger, "Trusted CA Store Reset Start.");
}
g_pangu_rt->ca_store_reseting++;
}
}
void trusted_CA_update_cert_cb(int table_id, const char* table_line, void* u_para)
{
int ret=0, cfg_id=0, is_valid=0;
char cert_name[128]={0}, cert_file[1024]={0};
ret=sscanf(table_line, "%d\t%s\t%s\t%d", &cfg_id, cert_name, cert_file, &is_valid);
if(ret!=4)
{
TFE_LOG_ERROR(g_pangu_rt->local_logger, "Trusted CA Store parse cert config failed: %s", table_line);
return;
}
if(is_valid==1)
{
ret=tfe_proxy_ssl_add_trust_ca(cert_file);
if(ret<0)
{
TFE_LOG_ERROR(g_pangu_rt->local_logger, "Trusted CA Store add cert failed %d:%s:%s", cfg_id, cert_name, cert_file);
}
else
{
TFE_LOG_INFO(g_pangu_rt->local_logger, "Trusted CA Store add cert success %d:%s:%s", cfg_id, cert_name, cert_file);
}
}
else
{
ret=tfe_proxy_ssl_del_trust_ca(cert_file);
if(ret<0)
{
TFE_LOG_ERROR(g_pangu_rt->local_logger, "Trusted CA Store del cert failed %d:%s:%s", cfg_id, cert_name, cert_file);
}
else
{
TFE_LOG_INFO(g_pangu_rt->local_logger, "Trusted CA Store del cert success %d:%s:%s", cfg_id, cert_name, cert_file);
}
}
return;
}
void trusted_CA_update_crl_cb(int table_id,const char* table_line,void* u_para)
{
int ret=0, crl_id=0, cert_id=0, is_valid=0;
char crl_file[1024]={0};
ret=sscanf(table_line, "%d\t%d\t%s\t%d", &crl_id, &cert_id, crl_file, &is_valid);
if(ret!=4)
{
TFE_LOG_ERROR(g_pangu_rt->local_logger, "Trusted CA Store parse crl config failed: %s", table_line);
return;
}
if(is_valid==1)
{
ret=tfe_proxy_ssl_add_crl(crl_file);
if(ret<0)
{
TFE_LOG_ERROR(g_pangu_rt->local_logger, "Trusted CA Store add crl failed %d:%d:%s", crl_id, cert_id, crl_file);
}
else
{
TFE_LOG_INFO(g_pangu_rt->local_logger, "Trusted CA Store add crl success %d:%d:%s", crl_id, cert_id, crl_file);
}
}
else
{
ret=tfe_proxy_ssl_del_crl(crl_file);
if(ret<0)
{
TFE_LOG_ERROR(g_pangu_rt->local_logger, "Trusted CA Store del crl failed %d:%d:%s", crl_id, cert_id, crl_file);
}
else
{
TFE_LOG_INFO(g_pangu_rt->local_logger, "Trusted CA Store del crl success %d:%d:%s", crl_id, cert_id, crl_file);
}
}
return;
}
void trusted_CA_update_finish_cb(void* u_para)
{
if(g_pangu_rt->ca_store_reseting>0)
{
g_pangu_rt->ca_store_reseting--;
if(g_pangu_rt->ca_store_reseting==0)
{
TFE_LOG_INFO(g_pangu_rt->local_logger, "Trusted CA Store Reset Finish.");
}
}
}
2018-09-18 12:01:56 +08:00
int pangu_http_init(struct tfe_proxy * proxy)
{
const char * profile = "./conf/pangu/pangu_pxy.conf";
const char * logfile = "./log/pangu_pxy.log";
int table_id=0, temp=0;
g_pangu_rt = ALLOC(struct pangu_rt, 1);
g_pangu_rt->thread_num = tfe_proxy_get_work_thread_count();
g_pangu_rt->gc_evbase=tfe_proxy_get_gc_evbase();
MESA_load_profile_int_def(profile, "DEBUG", "LOG_LEVEL", &(g_pangu_rt->log_level), 0);
g_pangu_rt->local_logger = MESA_create_runtime_log_handle(logfile, g_pangu_rt->log_level);
g_pangu_rt->send_logger = pangu_log_handle_create(profile, "LOG", g_pangu_rt->local_logger);
if (!g_pangu_rt->send_logger)
{
goto error_out;
}
g_pangu_rt->fs_handle = tfe_proxy_get_fs_handle();
pangu_http_stat_init(g_pangu_rt);
g_pangu_rt->maat = create_maat_feather(profile, "MAAT", g_pangu_rt->thread_num, g_pangu_rt->local_logger);
if (!g_pangu_rt->maat)
{
goto error_out;
}
const char * table_name[__SCAN_TABLE_MAX];
table_name[PXY_CTRL_IP] = "PXY_CTRL_IP";
table_name[PXY_CTRL_HTTP_URL] = "PXY_CTRL_HTTP_URL";
table_name[PXY_CTRL_HTTP_REQ_HDR] = "PXY_CTRL_HTTP_REQ_HDR";
table_name[PXY_CTRL_HTTP_REQ_BODY] = "PXY_CTRL_HTTP_REQ_BODY";
table_name[PXY_CTRL_HTTP_RES_HDR] = "PXY_CTRL_HTTP_RES_HDR";
table_name[PXY_CTRL_HTTP_RES_BODY] = "PXY_CTRL_HTTP_RES_BODY";
for (int i = 0; i < __SCAN_TABLE_MAX; i++)
{
g_pangu_rt->scan_table_id[i] = Maat_table_register(g_pangu_rt->maat, table_name[i]);
if (g_pangu_rt->scan_table_id[i] < 0)
{
2018-09-14 16:33:36 +08:00
TFE_LOG_ERROR(NULL, "Pangu HTTP Maat table %s register failed.", table_name[i]);
goto error_out;
}
}
char page_path[256];
memset(page_path, 0, sizeof(page_path));
MESA_load_profile_string_def(profile, "TEMPLATE", "PAGE_403", page_path, sizeof(page_path),
"./resource/pangu/HTTP403.html");
g_pangu_rt->tpl_403 = ctemplate::Template::GetTemplate(page_path, ctemplate::DO_NOT_STRIP);
memset(page_path, 0, sizeof(page_path));
MESA_load_profile_string_def(profile, "TEMPLATE", "PAGE_404", page_path, sizeof(page_path),
"./resource/pangu/HTTP404.html");
g_pangu_rt->tpl_404 = ctemplate::Template::GetTemplate(page_path, ctemplate::DO_NOT_STRIP);
memset(page_path, 0, sizeof(page_path));
MESA_load_profile_string_def(profile, "TEMPLATE", "PAGE_451", page_path, sizeof(page_path),
"./resource/pangu/HTTP451.html");
g_pangu_rt->tpl_451 = ctemplate::Template::GetTemplate(page_path, ctemplate::DO_NOT_STRIP);
MESA_load_profile_int_def(profile, "TANGO_CACHE", "suspend_max", &(temp), 1024*1024);
g_pangu_rt->suspend_max=temp;
MESA_load_profile_int_def(profile, "TANGO_CACHE", "enable_cache", &(g_pangu_rt->cache_enabled), 1);
if(g_pangu_rt->cache_enabled)
{
g_pangu_rt->cache = create_web_cache_handle(profile, "TANGO_CACHE", g_pangu_rt->gc_evbase,
g_pangu_rt->maat, g_pangu_rt->local_logger);
if(!g_pangu_rt->cache)
{
TFE_LOG_INFO(NULL, "Tango Cache init failed.");
goto error_out;
}
TFE_LOG_INFO(NULL, "Tango Cache Enabled.");
}
table_id=Maat_table_register(g_pangu_rt->maat, "PXY_OBJ_TRUSTED_CA_CERT");
if(table_id<0)
{
TFE_LOG_INFO(NULL, "Pangu HTTP register table PXY_OBJ_TRUSTED_CA_CERT failed.");
goto error_out;
}
Maat_table_callback_register(g_pangu_rt->maat, table_id,
trusted_CA_update_start_cb,
trusted_CA_update_cert_cb,
trusted_CA_update_finish_cb,
g_pangu_rt);
table_id=Maat_table_register(g_pangu_rt->maat, "PXY_OBJ_TRUSTED_CA_CRL");
if(table_id<0)
{
TFE_LOG_INFO(NULL, "Pangu HTTP register table PXY_OBJ_TRUSTED_CA_CRL failed.");
goto error_out;
}
Maat_table_callback_register(g_pangu_rt->maat, table_id,
trusted_CA_update_start_cb,
trusted_CA_update_crl_cb,
trusted_CA_update_finish_cb,
g_pangu_rt);
2018-09-14 16:33:36 +08:00
TFE_LOG_INFO(NULL, "Pangu HTTP init success.");
2018-09-18 12:01:56 +08:00
return 0;
error_out:
2018-09-14 16:33:36 +08:00
TFE_LOG_ERROR(NULL, "Pangu HTTP init failed.");
2018-09-18 12:01:56 +08:00
return -1;
}
2018-10-25 18:45:33 +08:00
struct replace_ctx
{
struct replace_rule * rule;
size_t n_rule;
struct tfe_http_half * replacing;
struct evbuffer * http_body;
int actually_replaced;
};
struct pangu_http_ctx
{
int magic_num;
enum pangu_action action;
char * action_para;
scan_status_t scan_mid;
stream_para_t sp;
struct cache_mid* cmid;
struct Maat_rule_t * enforce_rules;
size_t n_enforce;
char * enforce_para;
struct replace_ctx * rep_ctx;
2018-10-25 18:45:33 +08:00
int (* resumed_cb)(const struct tfe_stream * stream,
const struct tfe_http_session * session, enum tfe_http_event event, const unsigned char * data,
size_t datalen, unsigned int thread_id, struct pangu_http_ctx* ctx);
enum cache_pending_result pending_result;
struct future *f_cache_pending, *f_cache_query;
struct tfe_http_session * ref_session;
2018-10-25 18:45:33 +08:00
struct tfe_http_half* cache_revalidate_req;
struct tfe_http_half* cached_response;
size_t cache_result_declared_sz, cache_result_actual_sz;
struct cache_update_context* cache_update_ctx;
int thread_id;
};
2018-10-25 18:45:33 +08:00
void http_repl_ctx_free(struct replace_ctx* rep_ctx)
{
for (size_t i = 0; i < rep_ctx->n_rule; i++)
{
FREE(&(rep_ctx->rule[i].find));
FREE(&(rep_ctx->rule[i].replace_with));
}
if (rep_ctx->http_body)
{
evbuffer_free(rep_ctx->http_body);
rep_ctx->http_body = NULL;
}
//On session recycled.
if(rep_ctx->replacing)
{
tfe_http_half_free(rep_ctx->replacing);
rep_ctx->replacing=NULL;
}
FREE(&rep_ctx);
return;
}
#define HTTP_CTX_MAGIC_NUM 20181021
static struct pangu_http_ctx * pangu_http_ctx_new(unsigned int thread_id)
{
struct pangu_http_ctx * ctx = ALLOC(struct pangu_http_ctx, 1);
ctx->magic_num=HTTP_CTX_MAGIC_NUM;
ctx->scan_mid = NULL;
ctx->thread_id = (int) thread_id;
return ctx;
}
static void pangu_http_ctx_free(struct pangu_http_ctx * ctx)
{
assert(ctx->magic_num==HTTP_CTX_MAGIC_NUM);
2018-10-14 18:45:02 +08:00
if (ctx->rep_ctx)
{
http_repl_ctx_free(ctx->rep_ctx);
ctx->rep_ctx = NULL;
}
FREE(&ctx->enforce_rules);
FREE(&ctx->enforce_para);
Maat_clean_status(&(ctx->scan_mid));
ctx->scan_mid = NULL;
2018-10-14 18:45:02 +08:00
if(ctx->sp)
{
Maat_stream_scan_string_end(&(ctx->sp));
}
2018-10-14 18:45:02 +08:00
if(ctx->cache_update_ctx)
{
web_cache_write_end(ctx->cache_update_ctx);
ctx->cache_update_ctx=NULL;
}
//On session recycle
if(ctx->cache_revalidate_req)
{
tfe_http_half_free(ctx->cache_revalidate_req);
ctx->cache_revalidate_req=NULL;
}
if(ctx->cached_response)
{
//Dirty close
ctx->cached_response=NULL;
}
2018-10-14 18:45:02 +08:00
if(ctx->f_cache_query)
{
future_destroy(ctx->f_cache_query);
ctx->f_cache_query=NULL;
}
if(ctx->f_cache_pending)
{
future_destroy(ctx->f_cache_pending);
ctx->f_cache_pending=NULL;
}
ctx->magic_num=0;
FREE(&ctx);
}
static inline void addr_tfe2sapp(const struct tfe_stream_addr * tfe_addr, struct ipaddr * sapp_addr)
{
if(tfe_addr->addrtype==TFE_ADDR_STREAM_TUPLE4_V4||tfe_addr->addrtype==TFE_ADDR_IPV4)
{
sapp_addr->addrtype = ADDR_TYPE_IPV4;
}
else
{
sapp_addr->addrtype=ADDR_TYPE_IPV6;
}
sapp_addr->paddr = (char *) tfe_addr->paddr;
return;
}
static int pangu_action_weight[__PG_ACTION_MAX] = {0};
void __pangu_action_weight_init() __attribute__((constructor, used));
void __pangu_action_weight_init()
{
pangu_action_weight[PG_ACTION_NONE] = 0;
pangu_action_weight[PG_ACTION_MONIT] = 1;
pangu_action_weight[PG_ACTION_REPLACE] = 2;
pangu_action_weight[PG_ACTION_REDIRECT] = 3;
pangu_action_weight[PG_ACTION_REJECT] = 4;
pangu_action_weight[PG_ACTION_WHITELIST] = 5;
}
static inline int action_cmp(enum pangu_action a1, enum pangu_action a2)
{
return pangu_action_weight[a1] - pangu_action_weight[a2];
}
//enforce_rules[0] contains execute action.
static enum pangu_action decide_ctrl_action(const struct Maat_rule_t * hit_rules, size_t n_hit,
struct Maat_rule_t ** enforce_rules, size_t * n_enforce, char** enforce_para)
{
size_t n_monit = 0, exist_enforce_num = 0, i = 0;
const struct Maat_rule_t * prior_rule = hit_rules;
struct Maat_rule_t monit_rule[n_hit];
enum pangu_action prior_action = PG_ACTION_NONE;
for (i = 0; i < n_hit; i++)
{
unsigned char __expand_action = (unsigned char) hit_rules[i].action;
enum pangu_action __action = (enum pangu_action) __expand_action;
if (__action == PG_ACTION_MONIT)
{
memcpy(monit_rule + n_monit, hit_rules + i, sizeof(struct Maat_rule_t));
n_monit++;
}
if (action_cmp(__action, prior_action) > 0)
{
prior_rule = hit_rules + i;
prior_action = __action;
}
else if (action_cmp(__action, prior_action) == 0)
{
if (hit_rules[i].config_id > prior_rule->config_id)
{
prior_rule = hit_rules + i;
}
}
else
{
continue;
}
}
if (prior_action == PG_ACTION_WHITELIST)
{
return PG_ACTION_WHITELIST;
}
exist_enforce_num = *n_enforce;
if (prior_action == PG_ACTION_MONIT)
{
*n_enforce += n_monit;
}
else
{
*n_enforce += n_monit + 1;
}
*enforce_rules = (struct Maat_rule_t *) realloc(*enforce_rules, sizeof(struct Maat_rule_t) * (*n_enforce));
if (prior_action == PG_ACTION_MONIT)
{
memcpy(*enforce_rules + exist_enforce_num, monit_rule, n_monit * sizeof(struct Maat_rule_t));
}
else
{
memmove(*enforce_rules+1, *enforce_rules, exist_enforce_num*sizeof(struct Maat_rule_t));
memcpy(*enforce_rules, prior_rule, sizeof(struct Maat_rule_t));
memcpy(*enforce_rules + exist_enforce_num + 1, monit_rule, n_monit * sizeof(struct Maat_rule_t));
}
if(*enforce_para!=NULL)
{
free(*enforce_para);
}
size_t __serv_def_len = (size_t)prior_rule->serv_def_len;
*enforce_para = ALLOC(char, __serv_def_len);
if (__serv_def_len > MAX_SERVICE_DEFINE_LEN)
{
Maat_read_rule(g_pangu_rt->maat,prior_rule, MAAT_RULE_SERV_DEFINE, *enforce_para, __serv_def_len);
}
else
{
strcpy(*enforce_para, prior_rule->service_defined);
}
return prior_action;
}
//HTML template is downloaded from https://github.com/AndiDittrich/HttpErrorPages
static void html_generate(int status_code, int cfg_id, const char* msg, char ** page_buff, size_t * page_size)
{
ctemplate::TemplateDictionary dict("pg_page_dict"); //dict is automatically finalized after function returned.
dict.SetIntValue("cfg_id", cfg_id);
dict.SetValue("msg", msg);
std::string output;
ctemplate::Template * tpl = NULL;
switch (status_code)
{
case 403: tpl = g_pangu_rt->tpl_403;
break;
case 404: tpl = g_pangu_rt->tpl_404;
break;
case 451: tpl = g_pangu_rt->tpl_451;
break;
default: return;
}
tpl->Expand(&output, &dict);
2018-10-11 10:42:05 +08:00
*page_size = output.length() + 1;
*page_buff = tfe_strdup(output.c_str());
}
static void html_free(char ** page_buff)
{
FREE(page_buff);
return;
}
void http_replace(const struct tfe_stream * stream, const struct tfe_http_session * session,
enum tfe_http_event events, const unsigned char * body_frag, size_t frag_size, struct pangu_http_ctx * ctx)
{
struct tfe_http_session * to_write_sess = NULL;
2018-09-28 15:41:29 +08:00
char * rewrite_buff = NULL;
size_t rewrite_sz = 0;
to_write_sess = tfe_http_session_allow_write(session);
if (to_write_sess == NULL) //fail to wirte, abandon.
{
TFE_STREAM_LOG_INFO(stream, "tfe_http_session_allow_write() %s failed.", session->req->req_spec.uri);
ctx->action = PG_ACTION_NONE;
tfe_http_session_detach(session); return;
2018-09-14 16:33:36 +08:00
}
struct replace_ctx * rep_ctx = ctx->rep_ctx;
if (ctx->rep_ctx == NULL)
{
/* we must determinate the replace action on HTTP header, otherwise,
* the header has been forwarded, only replace the body but not modify header will raise exception */
if ((events & EV_HTTP_REQ_HDR) || (events & EV_HTTP_RESP_HDR))
{
ctx->rep_ctx = rep_ctx = ALLOC(struct replace_ctx, 1);
rep_ctx->rule = ALLOC(struct replace_rule, MAX_EDIT_ZONE_NUM);
rep_ctx->n_rule = format_replace_rule(ctx->enforce_para, rep_ctx->rule, MAX_EDIT_ZONE_NUM);
}
else
{
TFE_STREAM_LOG_INFO(stream, "Can only setup replace on REQ/RESP headers, detached.");
ctx->action = PG_ACTION_NONE;
tfe_http_session_detach(session); return;
}
}
struct tfe_http_half * in_req_half = session->req;
struct tfe_http_half * in_resp_half = session->resp;
struct tfe_http_req_spec * in_req_spec = &in_req_half->req_spec;
struct tfe_http_resp_spec * in_resp_spec = &in_resp_half->resp_spec;
if ((events & EV_HTTP_REQ_HDR) || (events & EV_HTTP_RESP_HDR))
{
2018-09-28 15:41:29 +08:00
char * rewrite_uri = NULL;
size_t rewrite_uri_sz=0;
if (tfe_http_in_request(events))
{
2018-09-28 15:41:29 +08:00
rewrite_uri_sz = execute_replace_rule(in_req_spec->uri, strlen(in_req_spec->uri),
kZoneRequestUri, rep_ctx->rule, rep_ctx->n_rule, &rewrite_uri);
if(rewrite_uri_sz>0) rep_ctx->actually_replaced=1;
rep_ctx->replacing = tfe_http_session_request_create(to_write_sess, in_req_spec->method,
2018-09-28 15:41:29 +08:00
rewrite_uri_sz >0 ? rewrite_uri : in_req_spec->uri);
tfe_http_session_request_set(to_write_sess, rep_ctx->replacing);
}
else
{
rep_ctx->replacing = tfe_http_session_response_create(to_write_sess, in_resp_spec->resp_code);
tfe_http_session_response_set(to_write_sess, rep_ctx->replacing);
}
if (rewrite_uri != NULL)
{
2018-09-28 15:41:29 +08:00
FREE(&rewrite_uri);
}
enum replace_zone zone = tfe_http_in_request(events) ? kZoneRequestHeaders : kZoneResponseHeader;
struct tfe_http_half * in_half = tfe_http_in_request(events) ? in_req_half : in_resp_half;
struct http_field_name in_header_field{};
const char * in_header_value = NULL;
void * iterator = NULL;
while (true)
{
if ((in_header_value = tfe_http_field_iterate(in_half, &iterator, &in_header_field)) == NULL)
{
break;
}
2018-09-28 15:41:29 +08:00
rewrite_buff = NULL;
rewrite_sz = 0;
rewrite_sz=execute_replace_rule(in_header_value,
strlen(in_header_value), zone, rep_ctx->rule, rep_ctx->n_rule, &rewrite_buff);
if(rewrite_sz>0) rep_ctx->actually_replaced=1;
2018-09-28 15:41:29 +08:00
tfe_http_field_write(rep_ctx->replacing, &in_header_field, rewrite_sz>0? rewrite_buff : in_header_value);
if(rewrite_buff != NULL)
{
2018-09-28 15:41:29 +08:00
FREE(&rewrite_buff);
}
}
}
if ((events & EV_HTTP_REQ_BODY_BEGIN) || (events & EV_HTTP_RESP_BODY_BEGIN))
{
assert(rep_ctx->http_body == NULL);
rep_ctx->http_body = evbuffer_new();
}
if ((events & EV_HTTP_REQ_BODY_CONT) || (events & EV_HTTP_RESP_BODY_CONT))
{
evbuffer_add(rep_ctx->http_body, body_frag, frag_size);
}
if ((events & EV_HTTP_REQ_BODY_END) || (events & EV_HTTP_RESP_BODY_END))
{
char * __http_body = (char *) evbuffer_pullup(rep_ctx->http_body, -1);
size_t __http_body_len = evbuffer_get_length(rep_ctx->http_body);
enum replace_zone r_zone = tfe_http_in_request(events) ? kZoneRequestBody : kZoneResponseBody;
2018-09-28 15:41:29 +08:00
rewrite_buff = NULL;
rewrite_sz = 0;
rewrite_sz = execute_replace_rule(__http_body, __http_body_len, r_zone,
rep_ctx->rule, rep_ctx->n_rule, &rewrite_buff);
2018-09-28 15:41:29 +08:00
if (rewrite_sz >0 )
{
2018-09-28 15:41:29 +08:00
tfe_http_half_append_body(rep_ctx->replacing, rewrite_buff, rewrite_sz, 0);
rep_ctx->actually_replaced=1;
}
else
{
tfe_http_half_append_body(rep_ctx->replacing, __http_body, __http_body_len, 0);
}
if (rewrite_buff != NULL)
{
2018-09-28 15:41:29 +08:00
FREE(&rewrite_buff);
}
if (rep_ctx->http_body != NULL)
{
evbuffer_free(rep_ctx->http_body);
rep_ctx->http_body = NULL;
}
}
if ((events & EV_HTTP_REQ_END) || (events & EV_HTTP_RESP_END))
{
tfe_http_half_append_body(rep_ctx->replacing, NULL, 0, 0);
rep_ctx->replacing = NULL;
}
}
static void http_reject(const struct tfe_http_session * session, enum tfe_http_event events,
struct pangu_http_ctx * ctx)
{
int resp_code = 0, ret = 0;
struct tfe_http_half * response = NULL;
char * page_buff = NULL;
size_t page_size = 0;
char cont_len_str[16];
char msg[TFE_STRING_MAX] = "";
struct tfe_http_session * to_write_sess = NULL;
ret = sscanf(ctx->enforce_para, "code=%d;content=%[^\n]", &resp_code, msg);
if (ret != 1 && ret != 2)
{
TFE_LOG_ERROR(g_pangu_rt->local_logger, "Invalid reject rule %d paramter %s",
ctx->enforce_rules[0].config_id, ctx->enforce_para);
2018-10-11 10:42:05 +08:00
ctx->action = PG_ACTION_NONE;
goto error_out;
}
to_write_sess = tfe_http_session_allow_write(session);
response = tfe_http_session_response_create(to_write_sess, resp_code);
html_generate(resp_code, ctx->enforce_rules[0].config_id, msg, &page_buff, &page_size);
tfe_http_std_field_write(response, TFE_HTTP_CONT_TYPE, "text/html; charset=utf-8");
snprintf(cont_len_str, sizeof(cont_len_str), "%lu", page_size);
tfe_http_std_field_write(response, TFE_HTTP_CONT_LENGTH, cont_len_str);
tfe_http_half_append_body(response, page_buff, page_size, 0);
tfe_http_half_append_body(response, NULL, 0, 0);
2018-09-14 16:33:36 +08:00
tfe_http_session_response_set(to_write_sess, response);
tfe_http_session_detach(session);
error_out:
2018-09-14 16:33:36 +08:00
html_free(&page_buff);
}
static void http_redirect(const struct tfe_http_session * session, enum tfe_http_event events,
struct pangu_http_ctx * ctx)
{
int resp_code = 0, ret = 0;
char * url = NULL;
struct tfe_http_half * response = NULL;
struct tfe_http_session * to_write = NULL;
url = ALLOC(char, ctx->enforce_rules[0].serv_def_len);
ret = sscanf(ctx->enforce_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_rules[0].config_id, ctx->enforce_para);
goto error_out;
}
to_write = tfe_http_session_allow_write(session);
if (to_write == NULL)
{
assert(0);
}
response = tfe_http_session_response_create(to_write, resp_code);
tfe_http_std_field_write(response, TFE_HTTP_LOCATION, url);
tfe_http_half_append_body(response, NULL, 0, 0);
tfe_http_session_response_set(to_write, response);
tfe_http_session_detach(session);
error_out:
free(url);
}
enum pangu_action http_scan(const struct tfe_http_session * session, enum tfe_http_event events,
const unsigned char * body_frag, size_t frag_size, struct pangu_http_ctx * ctx)
{
void * interator = NULL;
const char * field_val = NULL;
struct http_field_name field_name;
2018-09-14 16:33:36 +08:00
struct Maat_rule_t result[MAX_SCAN_RESULT];
char buff[TFE_STRING_MAX], * p = NULL;
int scan_ret = 0, table_id = 0;
size_t hit_cnt = 0, i = 0;
if (events & EV_HTTP_REQ_HDR)
{
const char * str_url = session->req->req_spec.url;
int str_url_length = (int) (strlen(session->req->req_spec.url));
scan_ret = Maat_full_scan_string(g_pangu_rt->maat, g_pangu_rt->scan_table_id[PXY_CTRL_HTTP_URL],
CHARSET_UTF8, str_url, str_url_length, result, NULL, MAX_SCAN_RESULT, &(ctx->scan_mid), ctx->thread_id);
if (scan_ret > 0)
{
hit_cnt += scan_ret;
}
}
if ((events & EV_HTTP_REQ_HDR) || (events & EV_HTTP_RESP_HDR))
{
table_id = events & EV_HTTP_REQ_HDR ? g_pangu_rt->scan_table_id[PXY_CTRL_HTTP_REQ_HDR] : g_pangu_rt
->scan_table_id[PXY_CTRL_HTTP_RES_HDR];
while (hit_cnt < MAX_SCAN_RESULT)
{
field_val = tfe_http_field_iterate(session->req, &interator, &field_name);
if (field_val == NULL)
2018-09-14 16:33:36 +08:00
{
break;
}
const char * str_field_name = http_field_to_string(&field_name);
scan_ret = Maat_set_scan_status(g_pangu_rt->maat, &(ctx->scan_mid), MAAT_SET_SCAN_DISTRICT,
str_field_name, strlen(str_field_name));
assert(scan_ret == 0);
scan_ret = Maat_full_scan_string(g_pangu_rt->maat, table_id,
CHARSET_UTF8, field_val, strlen(field_val),
result + hit_cnt, NULL, MAX_SCAN_RESULT - hit_cnt, &(ctx->scan_mid), ctx->thread_id);
if (scan_ret > 0)
{
hit_cnt += scan_ret;
2018-09-14 16:33:36 +08:00
}
}
}
if ((events & EV_HTTP_REQ_BODY_BEGIN) | (events & EV_HTTP_RESP_BODY_BEGIN))
{
assert(ctx->sp == NULL);
table_id = events & EV_HTTP_REQ_BODY_BEGIN ? g_pangu_rt->scan_table_id[PXY_CTRL_HTTP_REQ_BODY] : g_pangu_rt
->scan_table_id[PXY_CTRL_HTTP_RES_BODY];
ctx->sp = Maat_stream_scan_string_start(g_pangu_rt->maat, table_id, ctx->thread_id);
}
if (body_frag != NULL)
{
scan_ret = Maat_stream_scan_string(&(ctx->sp), CHARSET_UTF8, (const char *) body_frag, (int) frag_size,
result + hit_cnt, NULL, MAX_SCAN_RESULT - hit_cnt, &(ctx->scan_mid));
if (scan_ret > 0)
{
hit_cnt += scan_ret;
}
}
if ((events & EV_HTTP_REQ_BODY_END) | (events & EV_HTTP_RESP_BODY_END))
{
2018-09-14 16:33:36 +08:00
Maat_stream_scan_string_end(&(ctx->sp));
ctx->sp = NULL;
}
if (hit_cnt > 0)
{
ctx->action = decide_ctrl_action(result, hit_cnt, &ctx->enforce_rules, &ctx->n_enforce, &ctx->enforce_para);
if (ctx->action == PG_ACTION_WHITELIST) goto __out;
if (hit_cnt > 1)
{
p = buff;
for (i = 0; i < hit_cnt; i++)
{
p += snprintf(p, sizeof(buff) - (p - buff), "%d:", result[i].config_id);
}
*p = '\0';
TFE_LOG_INFO(g_pangu_rt->local_logger, "Multiple rules matched: url=%s num=%lu ids=%s execute=%d.",
session->req->req_spec.url, hit_cnt, buff, ctx->enforce_rules[0].config_id);
}
}
__out:
return ctx->action;
}
void enforce_control_policy(const struct tfe_stream * stream, const struct tfe_http_session * session,
enum tfe_http_event events, const unsigned char * body_frag, size_t frag_size, unsigned int thread_id,
struct pangu_http_ctx * ctx)
{
if(ctx->action==PG_ACTION_NONE||ctx->action==PG_ACTION_MONIT)
{
//ctx->action changed in http_scan.
http_scan(session, events, body_frag, frag_size, ctx);
}
switch (ctx->action)
{
2018-10-14 18:45:02 +08:00
case PG_ACTION_NONE:
break;
case PG_ACTION_MONIT:
ATOMIC_INC(&(g_pangu_rt->stat_val[STAT_ACTION_MONIT]));
//send log on close.
break;
case PG_ACTION_REJECT:
http_reject(session, events, ctx);
ATOMIC_INC(&(g_pangu_rt->stat_val[STAT_ACTION_REJECT]));
break;
case PG_ACTION_REDIRECT:
http_redirect(session, events, ctx);
ATOMIC_INC(&(g_pangu_rt->stat_val[STAT_ACTION_REDIRECT]));
break;
case PG_ACTION_REPLACE:
http_replace(stream, session, events, body_frag, frag_size, ctx);
ATOMIC_INC(&(g_pangu_rt->stat_val[STAT_ACTION_PRE_REPLACE]));
break;
case PG_ACTION_WHITELIST:
tfe_http_session_detach(session);
ATOMIC_INC(&(g_pangu_rt->stat_val[STAT_ACTION_WHITELSIT]));
break;
default: assert(0);
break;
}
return;
}
2018-10-25 18:45:33 +08:00
#define RESUMED_CB_NO_MORE_CALLS 0
#define RESUMED_CB_MORE_CALLS 1
int make_revalidate_request(const struct tfe_stream * stream, const struct tfe_http_session * session,
enum tfe_http_event events, const unsigned char * body_frag, size_t frag_size, unsigned int thread_id, struct pangu_http_ctx * ctx)
{
2018-10-25 18:45:33 +08:00
assert(ctx->cache_revalidate_req);
if(events & EV_HTTP_REQ_BODY_BEGIN)
{
tfe_http_half_write_body_begin(ctx->cache_revalidate_req, 1);
}
if(events & EV_HTTP_REQ_BODY_CONT)
{
tfe_http_half_write_body_data(ctx->cache_revalidate_req, body_frag, frag_size);
}
if(events & EV_HTTP_REQ_BODY_END)
{
tfe_http_half_write_body_end(ctx->cache_revalidate_req);
ctx->cache_revalidate_req=NULL;
return RESUMED_CB_NO_MORE_CALLS;
}
if(events & EV_HTTP_REQ_END && ctx->cache_revalidate_req)
{
ctx->cache_revalidate_req=NULL;
return RESUMED_CB_NO_MORE_CALLS;
}
2018-10-25 18:45:33 +08:00
return RESUMED_CB_MORE_CALLS;
}
int dummy_resume(const struct tfe_stream * stream, const struct tfe_http_session * session,
enum tfe_http_event events, const unsigned char * body_frag, size_t frag_size, unsigned int thread_id, struct pangu_http_ctx * ctx)
{
return RESUMED_CB_NO_MORE_CALLS;
}
static void cache_read_on_succ(future_result_t * result, void * user)
2018-10-25 18:45:33 +08:00
{
struct pangu_http_ctx * ctx = (struct pangu_http_ctx *)user;
const struct cached_meta* meta=NULL;
enum cache_query_result_type type=cache_query_result_get_type(result);
const unsigned char* data=NULL;
size_t data_sz=0;
char temp[TFE_STRING_MAX];
switch(type)
{
case CACHE_QUERY_RESULT_META:
meta=cache_query_result_read_meta(result);
ctx->cache_result_declared_sz=meta->content_length;
ctx->resumed_cb=dummy_resume;
tfe_http_session_resume(ctx->ref_session);
ATOMIC_DEC(&(g_pangu_rt->stat_val[STAT_SUSPENDING]));
2018-10-25 18:45:33 +08:00
ctx->cached_response=tfe_http_session_response_create(ctx->ref_session, 200);
tfe_http_std_field_write(ctx->cached_response, TFE_HTTP_CONT_TYPE, meta->content_type);
tfe_http_std_field_write(ctx->cached_response, TFE_HTTP_LAST_MODIFIED, meta->last_modified);
tfe_http_std_field_write(ctx->cached_response, TFE_HTTP_ETAG, meta->etag);
tfe_http_nonstd_field_write(ctx->cached_response, "X-Cache-Lookup", "Hit From TFE");
2018-10-25 18:45:33 +08:00
snprintf(temp, sizeof(temp), "%lu", meta->content_length);
tfe_http_std_field_write(ctx->cached_response, TFE_HTTP_CONT_LENGTH, temp);
//Dirty code here.
2018-10-25 18:45:33 +08:00
tfe_http_session_response_set(ctx->ref_session, ctx->cached_response);
//From now, ownership of cached_response has been transfered to http session,
//bussines plugin only hold this pointer as an reference for writing response body.
2018-10-25 18:45:33 +08:00
tfe_http_half_write_body_begin(ctx->cached_response, 1);
meta=NULL;
break;
case CACHE_QUERY_RESULT_DATA:
data_sz=cache_query_result_get_data(result, &data);
tfe_http_half_write_body_data(ctx->cached_response, data, data_sz);
ctx->cache_result_actual_sz+=data_sz;
break;
case CACHE_QUERY_RESULT_END:
assert(ctx->cached_response!=NULL);
tfe_http_half_write_body_end(ctx->cached_response);
//ownership has been transferred to http session, set to NULL.
ctx->pending_result=PENDING_RESULT_HIT;
2018-10-25 18:45:33 +08:00
ctx->cached_response=NULL;
assert(ctx->cache_result_actual_sz==ctx->cache_result_declared_sz);
future_destroy(ctx->f_cache_query);
ctx->f_cache_query=NULL;
break;
case CACHE_QUERY_RESULT_MISS:
ctx->pending_result=PENDING_RESULT_MISS;
2018-10-25 18:45:33 +08:00
ctx->resumed_cb=dummy_resume;
tfe_http_session_resume(ctx->ref_session);
ATOMIC_DEC(&(g_pangu_rt->stat_val[STAT_SUSPENDING]));
2018-10-25 18:45:33 +08:00
future_destroy(ctx->f_cache_query);
ctx->f_cache_query=NULL;
break;
default:
break;
}
return;
}
static void cache_read_on_fail(enum e_future_error err, const char * what, void * user)
2018-10-25 18:45:33 +08:00
{
struct pangu_http_ctx * ctx = (struct pangu_http_ctx *)user;
future_destroy(ctx->f_cache_query);
ctx->f_cache_query=NULL;
if(!ctx->cached_response)
{
2018-10-25 18:45:33 +08:00
tfe_http_session_resume(ctx->ref_session);
ctx->resumed_cb=dummy_resume;
ATOMIC_DEC(&(g_pangu_rt->stat_val[STAT_SUSPENDING]));
}
else
{
2018-10-25 18:45:33 +08:00
tfe_http_half_write_body_end(ctx->cached_response);
ctx->cached_response=NULL;
}
ctx->pending_result=PENDING_RESULT_MISS;
2018-10-25 18:45:33 +08:00
printf("cache query failed: %s %s\n", ctx->ref_session->req->req_spec.url, what);
}
static void cache_pend_on_succ(future_result_t * result, void * user)
2018-10-25 18:45:33 +08:00
{
struct pangu_http_ctx * ctx = (struct pangu_http_ctx *)user;
const struct cached_meta* meta=NULL;
meta=cache_pending_result_read_meta(result);
ctx->resumed_cb=dummy_resume;
tfe_http_session_resume(ctx->ref_session);
ATOMIC_DEC(&(g_pangu_rt->stat_val[STAT_SUSPENDING]));
2018-10-25 18:45:33 +08:00
future_destroy(ctx->f_cache_pending);
ctx->f_cache_pending=NULL;
if(meta==NULL)
{
ctx->pending_result = PENDING_RESULT_MISS;
2018-10-25 18:45:33 +08:00
return;
}
if( meta->etag==NULL && meta->last_modified==NULL)
2018-10-25 18:45:33 +08:00
{
ctx->pending_result = PENDING_RESULT_MISS;
2018-10-25 18:45:33 +08:00
return;
}
2018-10-25 18:45:33 +08:00
ctx->pending_result=PENDING_RESULT_REVALIDATE;
struct http_field_name in_field_name;
const char * in_header_value = NULL;
void * iterator = NULL;
ctx->cache_revalidate_req=tfe_http_session_request_create(ctx->ref_session,
ctx->ref_session->req->req_spec.method, ctx->ref_session->req->req_spec.uri);
while (true)
{
if ((in_header_value = tfe_http_field_iterate(ctx->ref_session->req, &iterator, &in_field_name)) == NULL)
{
break;
}
if(in_field_name.field_id==TFE_HTTP_IF_MATCH || in_field_name.field_id==TFE_HTTP_IF_NONE_MATCH
|| in_field_name.field_id==TFE_HTTP_IF_MODIFIED_SINCE
|| in_field_name.field_id==TFE_HTTP_IF_UNMODIFIED_SINCE)
{
continue;
}
tfe_http_field_write(ctx->cache_revalidate_req, &in_field_name, in_header_value);
}
if(meta->etag) tfe_http_std_field_write(ctx->cache_revalidate_req, TFE_HTTP_IF_NONE_MATCH, meta->etag);
if(meta->last_modified) tfe_http_std_field_write(ctx->cache_revalidate_req, TFE_HTTP_IF_MODIFIED_SINCE, meta->last_modified);
tfe_http_session_request_set(ctx->ref_session, ctx->cache_revalidate_req);
2018-10-25 18:45:33 +08:00
ctx->resumed_cb=make_revalidate_request;
return;
}
static void cache_pend_on_fail(enum e_future_error err, const char * what, void * user)
2018-10-25 18:45:33 +08:00
{
struct pangu_http_ctx * ctx = (struct pangu_http_ctx *)user;
ctx->pending_result=PENDING_RESULT_FOBIDDEN;
tfe_http_session_resume(ctx->ref_session);
ATOMIC_DEC(&(g_pangu_rt->stat_val[STAT_SUSPENDING]));
2018-10-25 18:45:33 +08:00
ctx->resumed_cb=dummy_resume;
future_destroy(ctx->f_cache_pending);
ctx->f_cache_pending=NULL;
return;
}
void cache_pend(const struct tfe_http_session * session, unsigned int thread_id, struct pangu_http_ctx * ctx)
2018-10-25 18:45:33 +08:00
{
if(g_pangu_rt->stat_val[STAT_SUSPENDING]>g_pangu_rt->suspend_max)
{
ctx->pending_result=PENDING_RESULT_FOBIDDEN;
return;
}
ctx->f_cache_pending=future_create("cache_pend", cache_pend_on_succ, cache_pend_on_fail, ctx);
ctx->ref_session=tfe_http_session_allow_write(session);
ctx->pending_result=web_cache_async_pending(g_pangu_rt->cache, thread_id, session->req, &(ctx->cmid), ctx->f_cache_pending);
2018-10-25 18:45:33 +08:00
switch(ctx->pending_result)
{
case PENDING_RESULT_REVALIDATE:
tfe_http_session_suspend(ctx->ref_session);
ATOMIC_INC(&(g_pangu_rt->stat_val[STAT_SUSPENDING]));
2018-10-25 18:45:33 +08:00
break;
case PENDING_RESULT_ALLOWED:
case PENDING_RESULT_FOBIDDEN:
case PENDING_RESULT_MISS:
future_destroy(ctx->f_cache_pending);
ctx->f_cache_pending=NULL;
2018-10-25 18:45:33 +08:00
break;
default:
break;
}
return;
}
void cache_read(const struct tfe_http_session * session, unsigned int thread_id, struct pangu_http_ctx * ctx)
2018-10-25 18:45:33 +08:00
{
if(g_pangu_rt->stat_val[STAT_SUSPENDING]>g_pangu_rt->suspend_max)
{
return;
}
ctx->f_cache_query=future_create("cache_read", cache_read_on_succ, cache_read_on_fail, ctx);
int ret=web_cache_async_read(g_pangu_rt->cache, thread_id, session->req, &(ctx->cmid), ctx->f_cache_query);
if(ret==0)
{
ctx->ref_session=tfe_http_session_allow_write(session);
tfe_http_session_suspend(ctx->ref_session);
ATOMIC_INC(&(g_pangu_rt->stat_val[STAT_SUSPENDING]));
}
else
{
future_destroy(ctx->f_cache_query);
ctx->f_cache_query=NULL;
}
2018-10-25 18:45:33 +08:00
}
void cache_write(const struct tfe_http_session * session, enum tfe_http_event events,
const unsigned char * body_frag, size_t frag_size,
unsigned int thread_id, struct pangu_http_ctx * ctx)
{
2018-10-25 18:45:33 +08:00
if(events & EV_HTTP_RESP_BODY_BEGIN)
{
ctx->cache_update_ctx=web_cache_write_start(g_pangu_rt->cache, thread_id, session, &(ctx->cmid));
}
if(events & EV_HTTP_RESP_BODY_CONT && ctx->cache_update_ctx!=NULL)
{
web_cache_write(ctx->cache_update_ctx, body_frag, frag_size);
}
if(events & EV_HTTP_RESP_BODY_END && ctx->cache_update_ctx!=NULL)
{
web_cache_write_end(ctx->cache_update_ctx);
ctx->cache_update_ctx=NULL;
2018-10-18 21:42:53 +08:00
//printf("cache update success: %s\n", ctx->ref_session->req->req_spec.url);
}
}
2018-09-14 16:33:36 +08:00
void pangu_on_http_begin(const struct tfe_stream * stream,
const struct tfe_http_session * session, unsigned int thread_id, void ** pme)
{
struct pangu_http_ctx * ctx = *(struct pangu_http_ctx **) pme;
struct Maat_rule_t result[MAX_SCAN_RESULT];
struct ipaddr sapp_addr;
int hit_cnt = 0;
assert(ctx == NULL);
ATOMIC_INC(&(g_pangu_rt->stat_val[STAT_SESSION]));
ctx = pangu_http_ctx_new(thread_id);
2018-09-14 16:33:36 +08:00
addr_tfe2sapp(stream->addr, &sapp_addr);
hit_cnt = Maat_scan_proto_addr(g_pangu_rt->maat, g_pangu_rt->scan_table_id[PXY_CTRL_IP], &sapp_addr, 0,
result, MAX_SCAN_RESULT, &(ctx->scan_mid), (int) thread_id);
if (hit_cnt > 0)
{
ctx->action = decide_ctrl_action(result, hit_cnt, &ctx->enforce_rules, &ctx->n_enforce, &ctx->enforce_para);
}
if (ctx->action == PG_ACTION_WHITELIST)
{
2018-09-14 16:33:36 +08:00
tfe_http_session_detach(session);
}
*pme = ctx;
2018-09-18 12:01:56 +08:00
return;
}
void pangu_on_http_end(const struct tfe_stream * stream,
const struct tfe_http_session * session, unsigned int thread_id, void ** pme)
{
struct pangu_http_ctx * ctx = *(struct pangu_http_ctx **) pme;
size_t i=0, j=0;
int ret=0;
if(ctx->action == PG_ACTION_REPLACE && ctx->rep_ctx->actually_replaced==0)
{
for(i=0; i< ctx->n_enforce; i++)
{
if((unsigned char)ctx->enforce_rules[i].action == PG_ACTION_REPLACE)
{
if(i+1 > ctx->n_enforce)
{
memmove(ctx->enforce_rules+i, ctx->enforce_rules+i+1, sizeof(struct Maat_rule_t));
}
j++;
}
}
ctx->n_enforce-=j;
if(ctx->n_enforce==0)
{
ctx->action = PG_ACTION_NONE;
FREE(&(ctx->enforce_rules));
}
}
struct pangu_log log_msg = {.stream=stream, .http=session, .result=ctx->enforce_rules, .result_num=ctx->n_enforce};
if (ctx->action != PG_ACTION_NONE&& !(ctx->action == PG_ACTION_REPLACE && ctx->n_enforce==1 && ctx->rep_ctx->actually_replaced==0))
{
ret=pangu_send_log(g_pangu_rt->send_logger, &log_msg);
ATOMIC_ADD(&(g_pangu_rt->stat_val[STAT_LOG_NUM]), ret);
}
if(ctx->rep_ctx && ctx->rep_ctx->actually_replaced==0)
{
ATOMIC_INC(&(g_pangu_rt->stat_val[STAT_ACTION_REPLACE]));
}
cache_mid_clear(&(ctx->cmid));
pangu_http_ctx_free(ctx);
*pme = NULL;
return;
}
void pangu_on_http_data(const struct tfe_stream * stream, const struct tfe_http_session * session,
enum tfe_http_event events, const unsigned char * body_frag, size_t frag_size, unsigned int thread_id, void ** pme)
{
struct pangu_http_ctx * ctx = *(struct pangu_http_ctx **) pme;
2018-10-25 18:45:33 +08:00
int ret=0;
if(ctx->resumed_cb)
{
2018-10-25 18:45:33 +08:00
ret=ctx->resumed_cb(stream, session, events, body_frag, frag_size,thread_id, ctx);
if(ret==RESUMED_CB_NO_MORE_CALLS)
{
ctx->resumed_cb=NULL;
}
return;
}
2018-10-25 18:45:33 +08:00
enforce_control_policy(stream, session, events, body_frag, frag_size,thread_id, ctx);
if(g_pangu_rt->cache_enabled && ctx->action == PG_ACTION_NONE)
{
2018-10-25 18:45:33 +08:00
if(events & EV_HTTP_REQ_HDR)
{
cache_pend(session, thread_id, ctx);
2018-10-25 18:45:33 +08:00
if(ctx->pending_result==PENDING_RESULT_ALLOWED)
{
cache_read(session, thread_id, ctx);
2018-10-25 18:45:33 +08:00
}
}
2018-10-25 18:45:33 +08:00
if(events & EV_HTTP_RESP_HDR && ctx->pending_result==PENDING_RESULT_REVALIDATE)
{
if(session->resp->resp_spec.resp_code==TFE_HTTP_STATUS_NOT_MODIFIED)
{
cache_read(session, thread_id, ctx);
2018-10-25 18:45:33 +08:00
}
}
2018-11-20 16:43:17 +08:00
if(tfe_http_in_response(events) && ctx->pending_result==PENDING_RESULT_MISS)
{
cache_write(session, events, body_frag, frag_size, thread_id, ctx);
2018-10-25 18:45:33 +08:00
}
}
return;
}
struct tfe_plugin pangu_http_spec = {
.symbol=NULL,
.type = TFE_PLUGIN_TYPE_BUSINESS,
.on_init = pangu_http_init,
.on_deinit = NULL,
.on_open = NULL,
.on_data = NULL,
.on_close = NULL,
.on_session_begin=pangu_on_http_begin,
.on_session_data=pangu_on_http_data,
.on_session_end=pangu_on_http_end
};
2018-09-18 12:01:56 +08:00
TFE_PLUGIN_REGISTER(pangu_http, pangu_http_spec)
2018-09-09 15:21:26 +08:00