engine update

This commit is contained in:
2026-03-20 17:26:47 +07:00
parent e234bd4006
commit 5aed291375
4 changed files with 90 additions and 21 deletions
+13 -3
View File
@@ -57,6 +57,7 @@ impl Codec {
Ok(server_hello_record) Ok(server_hello_record)
} }
pub fn process_handshake(&mut self, buffer: &mut BytesMut) -> Result<(), TlsError> { pub fn process_handshake(&mut self, buffer: &mut BytesMut) -> Result<(), TlsError> {
let mes_opt = TlsBridge::unpack_handshake(buffer)?; let mes_opt = TlsBridge::unpack_handshake(buffer)?;
let mes = mes_opt.ok_or_else(|| { let mes = mes_opt.ok_or_else(|| {
@@ -150,10 +151,14 @@ impl Codec {
} }
} }
while let Some(app_data) = TlsBridge::unpack_app_data(buffer)? { while let Some(app_data) = TlsBridge::unpack_app_data(buffer).map_err(|e| {
self.staging.clear();
e
})? {
let mut data_to_decrypt = BytesMut::from(app_data.payload); let mut data_to_decrypt = BytesMut::from(app_data.payload);
let decrypted = self.crypto.decrypt(&mut data_to_decrypt).map_err(|_| { let decrypted = self.crypto.decrypt(&mut data_to_decrypt).map_err(|_| {
self.staging.clear();
TlsError::new( TlsError::new(
ErrorStage::Tls("Decr error"), ErrorStage::Tls("Decr error"),
ErrorAction::Drop, ErrorAction::Drop,
@@ -162,6 +167,7 @@ impl Codec {
})?; })?;
if decrypted.len() < 16 { if decrypted.len() < 16 {
self.staging.clear();
return Err(TlsError::new( return Err(TlsError::new(
ErrorStage::Tls("Packet too short for auth"), ErrorStage::Tls("Packet too short for auth"),
ErrorAction::Drop, ErrorAction::Drop,
@@ -178,6 +184,7 @@ impl Codec {
received = %hex::encode(&received_tag[..4]), received = %hex::encode(&received_tag[..4]),
"AUTH MISMATCH: Potential replay or MITM attack. Dropping connection." "AUTH MISMATCH: Potential replay or MITM attack. Dropping connection."
); );
self.staging.clear();
return Err(TlsError::new( return Err(TlsError::new(
ErrorStage::Tls("Auth tag mismatch"), ErrorStage::Tls("Auth tag mismatch"),
ErrorAction::Drop, ErrorAction::Drop,
@@ -199,11 +206,14 @@ impl Codec {
match Frame::parse(&mut self.staging) { match Frame::parse(&mut self.staging) {
Ok(Some(frame)) => Ok(Some(frame)), Ok(Some(frame)) => Ok(Some(frame)),
Ok(None) => Ok(None), Ok(None) => Ok(None),
Err(_) => Err(TlsError::new( Err(_) => {
self.staging.clear();
Err(TlsError::new(
ErrorStage::Tls("Parse error"), ErrorStage::Tls("Parse error"),
ErrorAction::Drop, ErrorAction::Drop,
Bytes::new(), Bytes::new(),
)), ))
}
} }
} }
} }
+61 -11
View File
@@ -115,7 +115,7 @@ impl ClientHandler {
let ch = conn let ch = conn
.codec .codec
.make_client_handshake(&BrowserProfile::CHROME_131, "google.com") .make_client_handshake(&BrowserProfile::CHROME_131, "ubuntu.com")
.map_err(|e| format!("{:?}", e))?; .map_err(|e| format!("{:?}", e))?;
conn.outbound conn.outbound
.write_all(&ch) .write_all(&ch)
@@ -257,6 +257,26 @@ pub struct ServerHandler {
pub token: CancellationToken, pub token: CancellationToken,
} }
impl ServerHandler {
async fn handle_fallback(outbound: &mut OwnedWriteHalf) {
// Формируем правдоподобный HTTP-ответ.
// Заголовок "Server: nginx" помогает мимикрировать под обычный веб-сервер.
let fallback_response = "HTTP/1.1 302 Found\r\n\
Server: nginx/1.18.0 (Ubuntu)\r\n\
Location: https://www.ubuntu.com/\r\n\
Content-Length: 0\r\n\
Connection: close\r\n\
\r\n";
// Пытаемся отправить ответ сканеру/цензору, игнорируя ошибки (если он уже отключился)
let _ = outbound.write_all(fallback_response.as_bytes()).await;
let _ = outbound.flush().await;
// Корректно закрываем соединение (отправляем FIN/RST)
let _ = outbound.shutdown().await;
}
}
#[async_trait::async_trait] #[async_trait::async_trait]
impl TunnelHandler for ServerHandler { impl TunnelHandler for ServerHandler {
async fn run(mut self) -> Result<(), String> { async fn run(mut self) -> Result<(), String> {
@@ -264,6 +284,9 @@ impl TunnelHandler for ServerHandler {
let (mux_tx, mux_rx) = mpsc::channel(BUF_SIZE); let (mux_tx, mux_rx) = mpsc::channel(BUF_SIZE);
let muxer = Muxer::new(mux_tx, false); let muxer = Muxer::new(mux_tx, false);
// Тайм-аут для защиты от "сканеров-молчунов" (Active Probing)
let handshake_timeout = std::time::Duration::from_secs(5);
let hello = loop { let hello = loop {
match self match self
.conn .conn
@@ -272,21 +295,47 @@ impl TunnelHandler for ServerHandler {
{ {
Ok(b) => break b, Ok(b) => break b,
Err(e) if e.action == ErrorAction::Wait => { Err(e) if e.action == ErrorAction::Wait => {
if self // Используем timeout: если за 5 сек не пришел полный handshake - рвем/фолбечим
.conn let read_res = tokio::time::timeout(
.inbound handshake_timeout,
.read_buf(&mut self.conn.read_buf) self.conn.inbound.read_buf(&mut self.conn.read_buf),
.await )
.map_err(|e| e.to_string())? .await;
== 0
{ match read_res {
return Err("Closed".into()); Ok(Ok(n)) if n == 0 => {
return Err("Client closed connection before handshake".into());
}
Ok(Ok(_)) => {
// Прочитали кусок, идем на следующий круг проверять handshake
continue;
}
Ok(Err(e)) => {
return Err(format!("Socket read error: {}", e));
}
Err(_) => {
// СРАБОТАЛ ТАЙМ-АУТ
netrunner_logger::warn!(
"Handshake timeout (Scanner detected). Triggering fallback."
);
ServerHandler::handle_fallback(&mut self.conn.outbound).await;
return Ok(()); // Выходим без ошибки, чтобы не крашить сервер
} }
} }
Err(e) => return Err(format!("{:?}", e)), }
Err(e) => {
// ПРИШЕЛ МУСОР ИЛИ НЕВЕРНЫЙ ФОРМАТ (HTTP сканер)
netrunner_logger::warn!(
error = ?e,
"Invalid handshake format (Scanner detected). Triggering fallback."
);
ServerHandler::handle_fallback(&mut self.conn.outbound).await;
return Ok(()); // Выходим без ошибки
}
} }
}; };
// --- ЕСЛИ МЫ ЗДЕСЬ, ХЕНДШЕЙК ПРОШЕЛ УСПЕШНО ---
self.conn self.conn
.outbound .outbound
.write_all(&hello) .write_all(&hello)
@@ -294,6 +343,7 @@ impl TunnelHandler for ServerHandler {
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
let handler = std::sync::Arc::new(StreamHandler::new(muxer, ConnectionRole::Server)); let handler = std::sync::Arc::new(StreamHandler::new(muxer, ConnectionRole::Server));
TunnelEngine { TunnelEngine {
inbound: self.conn.inbound, inbound: self.conn.inbound,
outbound: self.conn.outbound, outbound: self.conn.outbound,
+10 -1
View File
@@ -78,7 +78,7 @@ impl TunnelEngine {
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
if n == 0 && read_buf.is_empty() { if n == 0 && read_buf.is_empty() {
netrunner_logger::error!("Connection closed by peer (EOF detected)"); netrunner_logger::info!("Connection closed by peer (EOF detected)");
return Err("EOF".into()); return Err("EOF".into());
} }
@@ -95,6 +95,15 @@ impl TunnelEngine {
break; break;
} }
if e.action == ErrorAction::Drop {
// ТСПУ подмешал мусор ИЛИ ключи не совпали.
// Мгновенный выход с ошибкой приведет к обрыву TCP-соединения.
netrunner_logger::error!(
"CRITICAL: Crypto tampering or sync lost. Hard dropping tunnel!"
);
return Err("Crypto drop".into());
}
error!(error = ?e, "Codec inbound failed"); error!(error = ?e, "Codec inbound failed");
return Err(format!("Codec error: {:?}", e)); return Err(format!("Codec error: {:?}", e));
} }
+1 -1
View File
@@ -111,7 +111,7 @@ impl Network {
let mut codec = crate::protocol::codec::codec::Codec::new(false); let mut codec = crate::protocol::codec::codec::Codec::new(false);
let ch = codec let ch = codec
.make_client_handshake(&BrowserProfile::CHROME_131, "google.com") .make_client_handshake(&BrowserProfile::CHROME_131, "ubuntu.com")
.map_err(|e| format!("{:?}", e))?; .map_err(|e| format!("{:?}", e))?;
outbound.write_all(&ch).await.map_err(|e| e.to_string())?; outbound.write_all(&ch).await.map_err(|e| e.to_string())?;