Files

206 lines
5.8 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "ServerProcessor.h"
#include "ServerLogger.h"
#include "PathTraversal.h"
#include "HTTPObjectsBridge.h"
#include "UNSResponseBuilder.h"
#include <mutex>
#include <typeinfo>
#include <typeindex>
#include <unordered_map>
#ifdef __GNUG__
#include <cxxabi.h>
#include <cstdlib>
#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<key_t, std::string> cache;
key_t k(ti);
// 快速检查(持锁短时间)
{
std::lock_guard<std::mutex> 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<std::mutex> 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<std::string, bool> StreamInfo;
};
ServerProcessor::ServerProcessor() : pimpl(std::make_unique<Impl>())
{
}
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();
SCLOGF_INFO("IP: [{}] has been blocked untill {{{}}}", ip, std::string(expr_time));
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<std::string, bool>(method, stream));
return;
}
uns::PathTraversalDefenceLevel ServerProcessor::PTDefence()
{
return uns::PathTraversalDefenceLevel::DenyAll;
}
bool ServerProcessor::IsPathSafe(const std::string& raw_path)
{
return false;
}
bool ServerProcessor::IsHeaderValid(uns::RequestPtr request)
{
return true;
}
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;
}