#include "ServerProcessor.h" #include "ServerLogger.h" #include "PathTraversal.h" #include "HTTPObjectsBridge.h" #include "UNSResponseBuilder.h" #include #include #include #include #ifdef __GNUG__ #include #include #endif // demangle(GNU)/回退(其他编译器) static std::string demangle_name(const char* name) { #ifdef __GNUG__ int status = 0; char *dem = abi::__cxa_demangle(name, nullptr, nullptr, &status); std::string ret = (status == 0 && dem) ? dem : name; std::free(dem); return ret; #else return name; #endif } // RTTI 名称缓存(线程安全) static const std::string& RTTISubClassName(const std::type_info &ti) { using key_t = std::type_index; static std::mutex m; static std::unordered_map cache; key_t k(ti); // 快速检查(持锁短时间) { std::lock_guard g(m); auto it = cache.find(k); if (it != cache.end()) return it->second; } // 若未缓存,先在无锁区做 demangle(可能较慢),然后再插入缓存 std::string dem = demangle_name(ti.name()); { std::lock_guard g(m); auto [it, inserted] = cache.emplace(k, std::move(dem)); return it->second; } } class ServerProcessor::Impl { public: bool IPCheck = false; IPTablePtr BlockedIPs = nullptr; std::map StreamInfo; }; ServerProcessor::ServerProcessor() : pimpl(std::make_unique()) { } ServerProcessor::~ServerProcessor() = default; void ServerProcessor::EnableIPCheck() { pimpl->IPCheck = true; return; } void ServerProcessor::DisableIPCheck() { pimpl->IPCheck = false; return; } void ServerProcessor::UpdateBlockedIPList(IPTablePtr ips) { pimpl->BlockedIPs = ips; return; } void ServerProcessor::AppenedBlockedIP(DateTime::Span block_time, std::string ip) { DateTime expr_time = (DateTime::Now() += block_time); IPList li{ ip }; pimpl->BlockedIPs->Appened(expr_time, li); pimpl->BlockedIPs->Update(); SCLOG_INFO("IP: [%s] has been blocked untill {%s}", ip.c_str(), std::string(expr_time).c_str()); return; } uns::HTTPMethod ServerProcessor::GetMethod(uns::RequestPtr request) { std::string method = request->GetImpl()->webcc_req->method(); if (method == "GET") return uns::HTTPMethod::H_GET; else if (method == "PUT") return uns::HTTPMethod::H_PUT; else if (method == "POST") return uns::HTTPMethod::H_POST; else if (method == "HEAD") return uns::HTTPMethod::H_HEAD; else if (method == "TRACE") return uns::HTTPMethod::H_TRACE; else if (method == "PATCH") return uns::HTTPMethod::H_PATCH; else if (method == "DELETE") return uns::HTTPMethod::H_DELETE; else if (method == "OPTIONS") return uns::HTTPMethod::H_OPTIONS; else if (method == "CONNECT") return uns::HTTPMethod::H_CONNECT; else return uns::HTTPMethod::H_UNKNOWN; } void ServerProcessor::AddStreamSettings(std::string method, bool stream) { pimpl->StreamInfo.insert(std::pair(method, stream)); return; } uns::PathTraversalDefenceLevel ServerProcessor::PTDefence() { return uns::PathTraversalDefenceLevel::DenyAll; } bool ServerProcessor::IsPathSafe(const std::string& raw_path) { return false; } uns::ResponsePtr ServerProcessor::Handle(uns::RequestPtr request) { std::string x_real_ip; if(request->GetImpl()->webcc_req->HasHeader("X-Real-IP")) x_real_ip = request->GetImpl()->webcc_req->GetHeader("X-Real-IP"); std::string req_ip = (x_real_ip.empty() ? request->GetImpl()->webcc_req->address() : x_real_ip); SCLOGF_INFO("Request recived by {{{}}}, ip: [{}], method: {}, URL: {}", RTTISubClassName(typeid(*this)), req_ip, request->GetImpl()->webcc_req->method(), request->GetImpl()->webcc_req->url().path()); if (pimpl->IPCheck && (pimpl->BlockedIPs != nullptr)) { pimpl->BlockedIPs->Update(); if (pimpl->BlockedIPs->IPExist(req_ip)) return uns::ResponseBuilder().IPBlocked()(); } // path test std::string path = request->GetImpl()->webcc_req->url().path(); auto status = PathTraversal::AnalyzeUrlTraversal(path); if(status != PathTraversal::UrlSafetyStatus::Safe) SCLOGF_WARNING("PathTraversal Detected: {}, Level: {}", path, PathTraversal::ToString(status)); switch(PTDefence()) { case uns::PathTraversalDefenceLevel::DenyAll: if(status != PathTraversal::UrlSafetyStatus::Safe) return uns::ResponseBuilder().Forbidden()(); break; case uns::PathTraversalDefenceLevel::AutoNormalize: { if(status == PathTraversal::UrlSafetyStatus::EvasiveTraversal) return uns::ResponseBuilder().Forbidden()(); auto decoded_path = PathTraversal::UrlDecode(path); if(!IsPathSafe(decoded_path)) return uns::ResponseBuilder().Forbidden()(); auto url = request->GetImpl()->webcc_req->url(); url.ForceSet_Path(PathTraversal::NormalizeUrlPath(decoded_path)); request->GetImpl()->webcc_req->set_url(std::move(url)); break; } case uns::PathTraversalDefenceLevel::AllowNormal: { if(status == PathTraversal::UrlSafetyStatus::EvasiveTraversal) return uns::ResponseBuilder().Forbidden()(); auto decoded_path = PathTraversal::UrlDecode(path); if(!IsPathSafe(decoded_path)) return uns::ResponseBuilder().Forbidden()(); auto url = request->GetImpl()->webcc_req->url(); url.ForceSet_Path(decoded_path); request->GetImpl()->webcc_req->set_url(std::move(url)); break; } default: break; //Check Bypassed by [AllowAll] } uns::ResponsePtr ptr = Processor(request); return ptr; } bool ServerProcessor::Stream(const std::string& method) { if (pimpl->StreamInfo.find(method) != pimpl->StreamInfo.end()) return pimpl->StreamInfo.at(method); else return false; }