🌐 SNIProxy 轻量级匿名CDN代理架构与实现
🏗️ 1. 整体架构设计
🔹 1.1 系统架构概览
SNIProxy核心架构 HTTP 80 HTTPS 443 CDN域名 直连域名 协议分发层 SNIProxy核心 HTTP处理引擎 TLS处理引擎 HTTP头解析 SNI提取 路由决策引擎 CDN转发器 直连转发器 CDN节点池 目标服务器 数据转发引擎 客户端 响应处理 配置管理 性能监控
🔹 1.2 组件交互关系
客户端 监听器(server) SNIProxy(sniproxy) 解析器(Ipep) 目标服务器 TCP连接(80/443) 创建代理会话 协议检测(HTTP/TLS) 解析Host头 提取SNI扩展 alt [HTTP协议] [TLS协议] 域名解析 返回IP地址 CDN路由决策 连接到CDN节点 直连目标服务器 alt [CDN路由] [直连] 建立双向通道 请求数据 转发请求 响应数据 转发响应 客户端 监听器(server) SNIProxy(sniproxy) 解析器(Ipep) 目标服务器
⚙️ 2. 核心模块深度分析
📝 2.1 配置管理系统
⚡ 配置加载流程
是 否 启动参数 配置文件路径 文件存在? 读取JSON内容 使用默认配置 JSON解析 配置项验证 CDN域名处理 端口有效性检查 内存化配置对象
💾 关键配置数据结构
cpp
class server_configuration {
public:
int concurrent; // 并发线程数
int backlog; // 最大待处理连接
bool fast_open; // TCP快速打开
struct {
bool lan; // 局域网优化
bool wan; // 广域网优化
} turbo;
struct {
int http; // HTTP监听端口
int http_ssl; // HTTPS监听端口
} listen;
struct {
std::string host; // CDN主域名
std::string http; // HTTP CDN节点
std::string http_ssl;// HTTPS CDN节点
} reverse_proxy;
struct {
int timeout; // 连接超时(秒)
} connect;
};
🔍 配置验证算法
- 端口范围:1-65535
- CDN域名有效性示例:
cpp
if (reverse_host.size()) {
std::size_t index = reverse_host.find('.');
if (index == std::string::npos) {
reverse_host.clear(); // 无效域名
}
}
- CDN节点地址转换:
cpp
IPEndPoint ipep = Ipep::GetEndPoint(reverse_server, false);
if (!IPEndPoint::IsInvalid(ipep)) {
// 转换为标准IP:PORT格式
}
🖧 2.2 网络IO核心(Hosting)
🧱 线程模型
管理 1 * Hosting -int concurrent_ -ContextList contexts_ -Mutex lockobj_ +GetContext() +Timeout() io_context_wrapper -boost::asio::io_context context -std::thread worker_thread +run()
🔄 IO上下文分配
cpp
std::shared_ptr<boost::asio::io_context> Hosting::GetContext() noexcept {
MutexScope scope_(lockobj_);
if (contexts_.empty()) return nullptr;
auto context_ = contexts_.front();
contexts_.pop_front();
contexts_.push_back(context_);
return context_;
}
⏱️ 超时管理机制
cpp
bool Hosting::WaitTimeout() noexcept {
timeout_->expires_from_now(boost::posix_time::milliseconds(10));
timeout_->async_wait([this](const boost::system::error_code& ec) {
if (!ec) {
now_ += 10; // 更新时间戳
WaitTimeout(); // 递归设置下一个超时点
}
});
return true;
}
🔐 2.3 协议处理引擎
🔹 2.3.1 TLS流程图
0x01 是 否 接收ClientHello 验证记录类型=0x16 读取TLS长度 加载完整握手消息 解析Handshake类型 解析ClientHello 跳过Random32字节 处理SessionID 跳过CipherSuites 跳过CompressionMethods 解析Extensions 找到SNI扩展? 提取主机名 返回空字符串
🔑 SNI提取关键代码
cpp
std::string sniproxy::fetch_sniaddr(size_t tls_payload) noexcept {
Byte* data = (Byte*)local_socket_buf_;
if (*data++ != 0x01) return ""; // 非ClientHello
int length = fetch_length(data);
data += 2; // 跳过版本
data += 32; // 跳过Random
// 处理Session ID
Byte session_len = *data++;
data += session_len;
// 处理Cipher Suites
int cipher_len = fetch_uint16(data);
data += cipher_len;
// 处理Extensions
int extensions_len = fetch_uint16(data);
Byte* extensions_end = data + extensions_len;
while (data < extensions_end) {
int ext_type = fetch_uint16(data);
int ext_len = fetch_uint16(data);
if (ext_type == 0x0000) { // SNI扩展
int name_list_len = fetch_uint16(data);
int name_type = *data++;
if (name_type != 0x00) continue; // 非主机名类型
int name_len = fetch_uint16(data);
return std::string((char*)data, name_len);
}
data += ext_len;
}
return "";
}
🔹 2.3.2 HTTP处理流程
完整URL 路径 是 否 接收请求行 解析方法+URI+版本 URI格式检查 提取主机部分 查找Host头 返回主机名 遍历头部 找到Host头? 提取主机名 返回空
🔑 Host头提取示例代码
cpp
std::string sniproxy::do_httpd_handshake_host(MemoryStream& messages_) {
// 解析请求行
std::vector<std::string> headers;
Tokenize(std::string((char*)messages_.GetBuffer().get()), headers, "\r\n");
// 解析请求行: METHOD URI VERSION
std::vector<std::string> request_line;
Tokenize(headers[0], request_line, " ");
if (request_line.size() < 3) return "";
// 检查URI格式
const std::string& uri = request_line[1];
if (uri.find("://") != std::string::npos) {
size_t pos = uri.find("://") + 3;
size_t end = uri.find('/', pos);
if (end == std::string::npos)
return uri.substr(pos);
else
return uri.substr(pos, end - pos);
}
// 查找Host头
for (size_t i = 1; i < headers.size(); i++) {
if (headers[i].find("Host:") == 0) {
return headers[i].substr(6); // "Host: "长度
}
}
return "";
}
🧭 3. 路由决策引擎
🚦 路由示意表
条件 | 动作 | 目标端点 |
---|---|---|
主机匹配CDN域名且协议为HTTP | 转发到CDN | reverse_proxy.http |
主机匹配CDN域名且协议为HTTPS | 转发到CDN | reverse_proxy.http_ssl |
主机不匹配CDN域名 | 直连 | 解析的IP地址 |
无效主机名 | 拒绝连接 | - |
保留IP地址(环回/组播) | 拒绝连接 | - |
🔑 域名匹配算法
cpp
bool sniproxy::be_host(std::string host, std::string domain) noexcept {
host = ToLower(RTrim(LTrim(host)));
domain = ToLower(RTrim(LTrim(domain)));
if (host == domain) return true;
std::vector<std::string> labels;
if (Tokenize(domain, labels, ".") < 2) return false;
std::vector<std::string> host_labels;
Tokenize(host, host_labels, ".");
if (host_labels.size() <= labels.size()) return false;
size_t diff = host_labels.size() - labels.size();
for (size_t i = 0; i < labels.size(); i++) {
if (host_labels[i + diff] != labels[i])
return false;
}
return true;
}
🔑 端点选择逻辑
cpp
IPEndPoint sniproxy::select_endpoint(
const std::string& hostname,
int port,
bool is_https) noexcept
{
if (be_host(hostname, configuration_->reverse_proxy.host)) {
const std::string& cdn_node = is_https ?
configuration_->reverse_proxy.http_ssl :
configuration_->reverse_proxy.http;
return Ipep::GetEndPoint(cdn_node, false);
}
boost::system::error_code ec;
boost::asio::ip::address address =
boost::asio::ip::address::from_string(hostname, ec);
if (ec) {
return Ipep::GetEndPoint(hostname, port, true);
}
return IPEndPoint(address.to_string().c_str(), port);
}
🔥 4. 性能优化体系
🚀 4.1 网络栈优化
参数 | 选项 | 适用场景 | 示例代码 |
---|---|---|---|
TCP_NODELAY | 开启 | 高吞吐/低延迟 | socket->set_option(no_delay(true)) |
TCP_FASTOPEN | 开启 | 连接快速建立 | setsockopt(IPPROTO_TCP, TCP_FASTOPEN, ...) |
IP_TOS | 0x68 | QoS优化 | setsockopt(SOL_IP, IP_TOS, 0x68) |
SO_REUSEADDR | 开启 | 端口复用 | acceptor->set_option(reuse_address(true)) |
IP_DONTFRAG | 禁用 | 路径MTU发现 | setsockopt(IPPROTO_IP, IP_DONTFRAG, 0) |
🛠️ 4.2 内存管理优化
MemoryStream -std::shared_ptr _buffer -int _position -int _length -int _capacity +Write(const void*, int, int) +Read(void*, int, int) +GetBuffer()
cpp
template<typename T>
std::shared_ptr<T> make_shared_alloc(int length) noexcept {
if (length < 1) return nullptr;
size_t aligned_size = (length * sizeof(T) + 15) & ~15;
#ifdef JEMALLOC
T* p = (T*)je_malloc(aligned_size);
#else
T* p = (T*)malloc(aligned_size);
#endif
return std::shared_ptr<T>(p, [](T* ptr) {
if (ptr) {
#ifdef JEMALLOC
je_free(ptr);
#else
free(ptr);
#endif
}
});
}
🕸️ 5. CDN转发机制深度解析
🌍 CDN架构示意图
CDN域名 客户端 SNIProxy 路由决策 CDN适配层 节点选择策略 健康检查 负载均衡 CDN节点1 CDN节点2 CDN节点3 源服务器
🔑 节点选择算法
cpp
class CDNSelector {
public:
struct Node {
IPEndPoint endpoint;
int weight;
int current_connections;
std::chrono::steady_clock::time_point last_checked;
bool healthy;
};
CDNSelector(const std::vector<IPEndPoint>& nodes) {
for (const auto& ep : nodes) {
available_nodes.push_back({ep, 10, 0,
std::chrono::steady_clock::now(), true});
}
}
IPEndPoint select_node() {
auto now = std::chrono::steady_clock::now();
for (auto& node : available_nodes) {
if (now - node.last_checked > std::chrono::seconds(30)) {
node.healthy = check_node_health(node.endpoint);
node.last_checked = now;
}
}
Node* selected = nullptr;
int min_score = std::numeric_limits<int>::max();
for (auto& node : available_nodes) {
if (!node.healthy) continue;
int score = node.current_connections * 100 / node.weight;
if (score < min_score) {
min_score = score;
selected = &node;
}
}
if (selected) {
selected->current_connections++;
return selected->endpoint;
}
throw std::runtime_error("No available CDN nodes");
}
private:
bool check_node_health(const IPEndPoint& ep) {
// 实现TCP/HTTP健康检查
return true;
}
std::vector<Node> available_nodes;
};
🔥 6. 安全机制深度分析
🛡️ 威胁模型
- DDoS攻击(SYN洪水、连接耗尽)
- 协议攻击(畸形TLS/HTTP包)
- 信息泄露(内存暴露、错误信息)
- 中间人攻击(TLS拦截)
- 资源耗尽(文件描述符、内存)
🛡️ 防御措施
网络层 连接限制 SYN Cookies Backlog队列管理 协议层 严格格式校验 超时控制 5秒连接超时 应用层 内存隔离 安全关闭 双重关闭验证 运维层 权限最小化 实时监控
✅ 7. 扩展性与未来演进
🚀 可扩展架构
IPlugin +on_connection_start() +on_headers_parsed() +on_data_received() +on_connection_end() ProtocolPlugin +on_connection_start() RoutingPlugin +on_headers_parsed() LoggingPlugin +on_connection_end() SNIProxyCore -std::vector plugins +register_plugin(IPlugin*) +notify_event(EventType)
结论 🌟
SNIProxy 通过架构创新 和深度优化,实现了:
- 🚀 高性能:零拷贝、智能内存池
- 🔐 安全保障:分层防御、协议校验、隐私保护
- ✅ 高可靠:智能路由、健康检查
- 🌱 易扩展:模块化、插件系统