/* * \brief HTTP扫描业务模块 * * \author Qiuwen Lu * \date 2018-5-15 */ #include #include #include #include #include #include "util.h" #include "opts.h" #include "httpscan.h" #include "pxyconn.h" #include "http.h" #include "compat.h" #include "logger.h" static int __maat_table_register_or_throw(Maat_feather_t feather, const char * str_table) { int table_id = Maat_table_register(feather, str_table); if (table_id < 0) throw std::runtime_error("Failed at register maat table " + std::string(str_table)); return table_id; } HttpScan::HttpScan(struct tfe_instance * instance, struct tfe_config * config) : maat_feather_ref(instance->maat_feather) { table_id_ctrl_ip = __maat_table_register_or_throw(maat_feather_ref, "PXY_CTRL_IP"); table_id_ctrl_http_url = __maat_table_register_or_throw(maat_feather_ref, "PXY_CTRL_HTTP_URL"); table_id_ctrl_http_req_hdr = __maat_table_register_or_throw(maat_feather_ref, "PXY_CTRL_HTTP_REQ_HDR"); table_id_ctrl_http_req_body = __maat_table_register_or_throw(maat_feather_ref, "PXY_CTRL_HTTP_REQ_BODY"); table_id_ctrl_http_res_hdr = __maat_table_register_or_throw(maat_feather_ref, "PXY_CTRL_HTTP_RES_HDR"); table_id_ctrl_http_res_body = __maat_table_register_or_throw(maat_feather_ref, "PXY_CTRL_HTTP_RES_BODY"); auto & http_module = instance->http_module; http_module->SetHttpConnectionNewCallback([this](Http & ht, HttpConnection & ct) -> void { this->handlerConnectionCreate(ct); }); http_module->SetHttpConnectionCloseCallback([this](Http & ht, HttpConnection & ct) -> void { this->handlerConnectionClose(ct); }); } void HttpScan::handlerConnectionCreate(HttpConnection & ct) { /* 新Session的创建处理函数 */ auto __shared_this_ptr = shared_from_this(); ct.SetSessionNewCallback([__shared_this_ptr](HttpSession & session) -> void { /* 创建HttpScan的Session Ctx */ auto __scan_ctx = std::make_shared(*__shared_this_ptr); /* 设置回调函数,每个回调函数增加一次__scan_ctx的引用计数 * 注意在栈上定义一个指针,指向__scan_ctx。 * 这样,回调时始终保持__scan_ctx的引用计数大于等于1, * 避免在回调过程中变更回调函数导致ctx析构。 */ session.SetRequestHeaderCallback([__scan_ctx](HttpSession & session) { auto __scan_ctx_stack = __scan_ctx; __scan_ctx_stack->ScanRequestHeader(&session); }); session.SetRequestBodyCallback([__scan_ctx](HttpSession & session) { auto __scan_ctx_stack = __scan_ctx; __scan_ctx->ScanRequestBody(&session); }); session.SetResponseHeaderCallback([__scan_ctx](HttpSession & session) { auto __scan_ctx_stack = __scan_ctx; __scan_ctx->ScanResponseHeader(&session); }); session.SetResponseBodyCallback([__scan_ctx](HttpSession & session) { auto __scan_ctx_stack = __scan_ctx; __scan_ctx->ScanResponseBody(&session); }); }); /* 不设置SessionClose的回调函数,相应的逻辑在HttpScanSession的析构函数中处理 */ ct.SetSessionCloseCallback(nullptr); } void HttpScan::handlerConnectionClose(HttpConnection & ct) { return; } int HttpScan::connection_bypass_scan() { return 0; } int HttpScan::connection_bypass_do_action() { return 0; } HttpScanSession::HttpScanSession(const HttpScan & httpscan_module) : httpscan_module_ref_(httpscan_module) { } HttpScanSession::~HttpScanSession() { if (maat_scan_mid_ != nullptr) Maat_clean_status(&maat_scan_mid_); } void HttpScanSession::ScanRequestHeader(HttpSession * http_session_ctx) { auto & http_request = http_session_ctx->request(); int dummy[MAAT_SCAN_RESULT_]; /* 扫描IP地址,获取连接对应的四元组 */ const auto & connection = http_session_ctx->connection(); const auto * sockaddr_src = connection.SockAddrSource(); const auto * sockaddr_dst = connection.SockAddrDest(); /* 转换为Sapp中的四元组结构体 */ auto sapp_tuple4_ptr = sockaddr_to_sapp_ipaddr(sockaddr_src, sockaddr_dst); /* 扫描IP地址 */ nr_maat_scan_result_ = Maat_scan_addr(httpscan_module_ref_.maat_feather_ref, httpscan_module_ref_.table_id_ctrl_ip, sapp_tuple4_ptr.get(), maat_scan_result_, MAAT_SCAN_RESULT_, &maat_scan_mid_, 0); /* 以下所有扫描命中后,配置callback tag为repeat,在本函数返回后,再次调用RequestHeader处理回调 * 函数,执行命中动作 */ if (nr_maat_scan_result_ > 0) { http_session_ctx->SetRequestHeaderTag(HttpSession::kCallbackTagRepeat); return hit_config_and_do_action(http_session_ctx); } else if (nr_maat_scan_result_ == -1) { return hit_scan_error(); } /* 扫描HTTP URL */ const auto & __url = http_request.Url(); nr_maat_scan_result_ = Maat_full_scan_string(httpscan_module_ref_.maat_feather_ref, httpscan_module_ref_.table_id_ctrl_http_url, CHARSET_UTF8, __url.c_str(), (int) __url.length(), maat_scan_result_, dummy, MAAT_SCAN_RESULT_, &maat_scan_mid_, 0); if (nr_maat_scan_result_ > 0) { http_session_ctx->SetRequestHeaderTag(HttpSession::kCallbackTagRepeat); return hit_config_and_do_action(http_session_ctx); } else if (nr_maat_scan_result_ == -1) { return hit_scan_error(); } /* 未命中HTTP URL,继续扫描其他HTTP头部字段 */ http_request.cHeaders().ForEachHeader([this, http_session_ctx] (const std::string & field, const std::string & value) -> bool { /* 增强字符串表,设置区域字段,即Header字段 */ int ret = Maat_set_scan_status(httpscan_module_ref_.maat_feather_ref, &maat_scan_mid_, MAAT_SET_SCAN_DISTRICT, field.c_str(), (int) field.length()); /* 设置失败 */ if (ret < 0) { hit_scan_error(); return false; } int __dummy[MAAT_SCAN_RESULT_]; nr_maat_scan_result_ = Maat_full_scan_string(httpscan_module_ref_.maat_feather_ref, httpscan_module_ref_.table_id_ctrl_http_req_hdr, MAAT_DEFAULT_CHARSET_, value.c_str(), (int) value.length(), maat_scan_result_, __dummy, MAAT_SCAN_RESULT_, &maat_scan_mid_, 0); if (nr_maat_scan_result_ > 0) { http_session_ctx->SetRequestHeaderTag(HttpSession::kCallbackTagRepeat); hit_config_and_do_action(http_session_ctx); return false; } else if (nr_maat_scan_result_ == -1) { hit_scan_error(); return false; } return true; }); } void HttpScanSession::ScanRequestBody(HttpSession * http_session_ctx) { auto & http_request = http_session_ctx->request(); /* Body Content and length, prepare for maat */ const char * body_content_raw = http_request.Body()->data(); size_t body_content_length = http_request.Body()->size(); /* Dummy For maat */ int __dummy[MAAT_SCAN_RESULT_]; nr_maat_scan_result_ = Maat_full_scan_string(httpscan_module_ref_.maat_feather_ref, httpscan_module_ref_.table_id_ctrl_http_req_body, CHARSET_UTF8, body_content_raw, (int) body_content_length, maat_scan_result_, __dummy, MAAT_SCAN_RESULT_, &maat_scan_mid_, 0); if (nr_maat_scan_result_ > 0) { http_session_ctx->SetRequestBodyTag(HttpSession::kCallbackTagRepeat); return hit_config_and_do_action(http_session_ctx); } else if (nr_maat_scan_result_ == -1) { return hit_scan_error(); } return; } void HttpScanSession::ScanResponseHeader(HttpSession * http_session_ctx) { return; } void HttpScanSession::ScanResponseBody(HttpSession * http_session_ctx) { return; } void HttpScanSession::hit_config_and_do_action(HttpSession * session) { /* 判断命中数量,若为多命中,选择优先级最高的动作执行 */ enum HttpActionType action_type = HttpActionType::kActionMax; unsigned int do_action_id = 0; /* 选择最小的动作ID为实际执行的配置ID */ for (unsigned int i = 0; i < nr_maat_scan_result_; i++) { if (maat_scan_result_[i].action <= action_type) do_action_id = i; } Maat_rule_t * hit_maat_rule = &maat_scan_result_[do_action_id]; auto __action_type = (enum HttpActionType) hit_maat_rule->action; const char * __action_string = hit_maat_rule->service_defined; /* 创建HttpAction的对象 */ auto action_object = HttpActionFactory(__action_type, __action_string); /* HttpLogger对象 */ auto logger_object = HttpLoggerFactory(hit_maat_rule->service_id, hit_maat_rule->config_id); logger_object->ConstructByConnection(session->connection()); /* 设置Logger对象 */ action_object->LoggerSetup(std::move(logger_object)); /* 替换HttpSession的事件处理函数,以后的事件由HttpAction处理 */ session->SetRequestHeaderCallback([action_object](HttpSession & session) { auto __action_object = action_object; __action_object->OnRequestHeader(&session); }); session->SetRequestBodyCallback([action_object](HttpSession & session) { auto __action_object = action_object; __action_object->OnRequestBody(&session); }); session->SetResponseHeaderCallback([action_object](HttpSession & session) { auto __action_object = action_object; __action_object->OnResponseHeader(&session); }); session->SetResponseBodyCallback([action_object](HttpSession & session) { auto __action_object = action_object; __action_object->OnResponseBody(&session); }); /* 当前Session的处理函数指向Action对应的处理函数 */ CLOG(DEBUG, "conntrace") << string_format("hit rule: service_id = %d, config_id = %d, action = %d\n", hit_maat_rule->service_id, hit_maat_rule->config_id, hit_maat_rule->action); return; } void HttpScanSession::hit_scan_error() { return; }