feat: CDN Control Plane (ONCP) implementation

- Add REST API for node/user management (axum-based)
- Add NodeRegistry for server check-in and load balancing
- Add SniManager for dynamic SNI updates and emergency blocking
- Add CDN Dashboard CLI (oncp-master) with real-time monitoring
- Add ProbeDetector in ostp-guard for active probing detection
- Add iptables/nftables/Windows firewall ban integration
- Extend MimicryEngine with async SNI updates from control plane
- Fix all compilation warnings
- Update author to ospab.team
This commit is contained in:
2026-01-01 20:33:03 +03:00
parent fc00214b07
commit 6d4c06a013
19 changed files with 2671 additions and 15 deletions

View File

@@ -15,3 +15,4 @@ hmac.workspace = true
sha2.workspace = true
rand.workspace = true
uuid.workspace = true
serde.workspace = true

View File

@@ -1,10 +1,14 @@
//! Dynamic SNI & TLS Mimicry Engine
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
/// Geo-based SNI target selection
/// Geo-based SNI target selection with dynamic updates
pub struct MimicryEngine {
geo_sni_map: HashMap<String, Vec<String>>,
/// Blocked domains (received from control plane)
blocked: Arc<RwLock<std::collections::HashSet<String>>>,
}
impl MimicryEngine {
@@ -45,7 +49,10 @@ impl MimicryEngine {
vec!["qq.com".into(), "baidu.com".into(), "taobao.com".into()],
);
Self { geo_sni_map }
Self {
geo_sni_map,
blocked: Arc::new(RwLock::new(std::collections::HashSet::new())),
}
}
/// Select SNI based on geo-location
@@ -71,6 +78,76 @@ impl MimicryEngine {
pub fn add_mapping(&mut self, country: String, domains: Vec<String>) {
self.geo_sni_map.insert(country, domains);
}
/// Update SNI list from control plane (async)
pub async fn update_from_control_plane(&mut self, country: String, domains: Vec<String>) {
self.geo_sni_map.insert(country, domains);
}
/// Block a domain (emergency update from control plane)
pub async fn block_domain(&self, domain: &str) {
self.blocked.write().await.insert(domain.to_string());
}
/// Check if domain is blocked
pub async fn is_blocked(&self, domain: &str) -> bool {
self.blocked.read().await.contains(domain)
}
/// Get safe random SNI (excludes blocked domains)
pub async fn safe_random_sni(&self, country_code: &str) -> Option<String> {
let blocked = self.blocked.read().await;
self.geo_sni_map.get(country_code).and_then(|list| {
let safe: Vec<&String> = list.iter()
.filter(|d| !blocked.contains(*d))
.collect();
if safe.is_empty() {
None
} else {
let idx = rand::random::<usize>() % safe.len();
Some(safe[idx].clone())
}
})
}
/// Apply bulk SNI update
pub async fn apply_update(&mut self, update: SniUpdate) {
// Block removed domains
{
let mut blocked = self.blocked.write().await;
for domain in &update.remove {
blocked.insert(domain.clone());
}
}
// Add new domains
if !update.add.is_empty() {
let country = update.country.unwrap_or_else(|| "GLOBAL".to_string());
let entry = self.geo_sni_map.entry(country).or_insert_with(Vec::new);
for domain in update.add {
if !entry.contains(&domain) {
entry.push(domain);
}
}
}
}
/// Get all available SNIs for a country (excluding blocked)
pub async fn get_available_snis(&self, country_code: &str) -> Vec<String> {
let blocked = self.blocked.read().await;
self.geo_sni_map
.get(country_code)
.map(|list| {
list.iter()
.filter(|d| !blocked.contains(*d))
.cloned()
.collect()
})
.unwrap_or_default()
}
}
impl Default for MimicryEngine {
@@ -79,6 +156,16 @@ impl Default for MimicryEngine {
}
}
/// SNI update command from control plane
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SniUpdate {
pub remove: Vec<String>,
pub add: Vec<String>,
pub country: Option<String>,
#[serde(default)]
pub emergency: bool,
}
/// TLS ClientHello builder for REALITY-like mimicry
pub struct TlsHelloBuilder {
sni: String,