engine update
This commit is contained in:
@@ -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(_) => {
|
||||||
ErrorStage::Tls("Parse error"),
|
self.staging.clear();
|
||||||
ErrorAction::Drop,
|
Err(TlsError::new(
|
||||||
Bytes::new(),
|
ErrorStage::Tls("Parse error"),
|
||||||
)),
|
ErrorAction::Drop,
|
||||||
|
Bytes::new(),
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ impl TunnelEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ = heartbeat.tick() => {
|
_ = heartbeat.tick() => {
|
||||||
let msg = MuxMessage { stream_id: 0, frame_type: FrameType::Heartbeat, data: Bytes::new() };
|
let msg = MuxMessage { stream_id: 0, frame_type: FrameType::Heartbeat, data: Bytes::new() };
|
||||||
Self::handle_outbound(&mut outbound, &mut codec, msg).await?;
|
Self::handle_outbound(&mut outbound, &mut codec, msg).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
msg_opt = mux_rx.recv() => {
|
msg_opt = mux_rx.recv() => {
|
||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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())?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user