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
+16 -6
View File
@@ -57,6 +57,7 @@ impl Codec {
Ok(server_hello_record)
}
pub fn process_handshake(&mut self, buffer: &mut BytesMut) -> Result<(), TlsError> {
let mes_opt = TlsBridge::unpack_handshake(buffer)?;
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 decrypted = self.crypto.decrypt(&mut data_to_decrypt).map_err(|_| {
self.staging.clear();
TlsError::new(
ErrorStage::Tls("Decr error"),
ErrorAction::Drop,
@@ -162,6 +167,7 @@ impl Codec {
})?;
if decrypted.len() < 16 {
self.staging.clear();
return Err(TlsError::new(
ErrorStage::Tls("Packet too short for auth"),
ErrorAction::Drop,
@@ -178,6 +184,7 @@ impl Codec {
received = %hex::encode(&received_tag[..4]),
"AUTH MISMATCH: Potential replay or MITM attack. Dropping connection."
);
self.staging.clear();
return Err(TlsError::new(
ErrorStage::Tls("Auth tag mismatch"),
ErrorAction::Drop,
@@ -199,11 +206,14 @@ impl Codec {
match Frame::parse(&mut self.staging) {
Ok(Some(frame)) => Ok(Some(frame)),
Ok(None) => Ok(None),
Err(_) => Err(TlsError::new(
ErrorStage::Tls("Parse error"),
ErrorAction::Drop,
Bytes::new(),
)),
Err(_) => {
self.staging.clear();
Err(TlsError::new(
ErrorStage::Tls("Parse error"),
ErrorAction::Drop,
Bytes::new(),
))
}
}
}
}
+61 -11
View File
@@ -115,7 +115,7 @@ impl ClientHandler {
let ch = conn
.codec
.make_client_handshake(&BrowserProfile::CHROME_131, "google.com")
.make_client_handshake(&BrowserProfile::CHROME_131, "ubuntu.com")
.map_err(|e| format!("{:?}", e))?;
conn.outbound
.write_all(&ch)
@@ -257,6 +257,26 @@ pub struct ServerHandler {
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]
impl TunnelHandler for ServerHandler {
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 muxer = Muxer::new(mux_tx, false);
// Тайм-аут для защиты от "сканеров-молчунов" (Active Probing)
let handshake_timeout = std::time::Duration::from_secs(5);
let hello = loop {
match self
.conn
@@ -272,21 +295,47 @@ impl TunnelHandler for ServerHandler {
{
Ok(b) => break b,
Err(e) if e.action == ErrorAction::Wait => {
if self
.conn
.inbound
.read_buf(&mut self.conn.read_buf)
.await
.map_err(|e| e.to_string())?
== 0
{
return Err("Closed".into());
// Используем timeout: если за 5 сек не пришел полный handshake - рвем/фолбечим
let read_res = tokio::time::timeout(
handshake_timeout,
self.conn.inbound.read_buf(&mut self.conn.read_buf),
)
.await;
match read_res {
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
.outbound
.write_all(&hello)
@@ -294,6 +343,7 @@ impl TunnelHandler for ServerHandler {
.map_err(|e| e.to_string())?;
let handler = std::sync::Arc::new(StreamHandler::new(muxer, ConnectionRole::Server));
TunnelEngine {
inbound: self.conn.inbound,
outbound: self.conn.outbound,
+12 -3
View File
@@ -50,8 +50,8 @@ impl TunnelEngine {
}
_ = heartbeat.tick() => {
let msg = MuxMessage { stream_id: 0, frame_type: FrameType::Heartbeat, data: Bytes::new() };
Self::handle_outbound(&mut outbound, &mut codec, msg).await?;
let msg = MuxMessage { stream_id: 0, frame_type: FrameType::Heartbeat, data: Bytes::new() };
Self::handle_outbound(&mut outbound, &mut codec, msg).await?;
}
msg_opt = mux_rx.recv() => {
@@ -78,7 +78,7 @@ impl TunnelEngine {
.map_err(|e| e.to_string())?;
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());
}
@@ -95,6 +95,15 @@ impl TunnelEngine {
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");
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 ch = codec
.make_client_handshake(&BrowserProfile::CHROME_131, "google.com")
.make_client_handshake(&BrowserProfile::CHROME_131, "ubuntu.com")
.map_err(|e| format!("{:?}", e))?;
outbound.write_all(&ch).await.map_err(|e| e.to_string())?;