comments remove
This commit is contained in:
@@ -40,10 +40,10 @@ impl DnsHandler {
|
|||||||
|
|
||||||
if needs_update {
|
if needs_update {
|
||||||
let p_clone = self.cache_path.clone();
|
let p_clone = self.cache_path.clone();
|
||||||
// Просто спавним асинхронную задачу
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||||
// Теперь вызываем асинхронно через .await
|
|
||||||
if let Err(e) = Self::download_blocklist_async(p_clone).await {
|
if let Err(e) = Self::download_blocklist_async(p_clone).await {
|
||||||
netrunner_logger::error!("DNS: Background update failed: {}", e);
|
netrunner_logger::error!("DNS: Background update failed: {}", e);
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,6 @@ impl DnsHandler {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Делаем функцию асинхронной и убираем block_on
|
|
||||||
async fn download_blocklist_async(cache_path: String) -> anyhow::Result<()> {
|
async fn download_blocklist_async(cache_path: String) -> anyhow::Result<()> {
|
||||||
info!("DNS: Starting background download to {}", cache_path);
|
info!("DNS: Starting background download to {}", cache_path);
|
||||||
|
|
||||||
@@ -112,7 +111,6 @@ impl DnsHandler {
|
|||||||
.set_recursion_available(true)
|
.set_recursion_available(true)
|
||||||
.add_query(query.clone());
|
.add_query(query.clone());
|
||||||
|
|
||||||
// Проверка блокировок
|
|
||||||
if self.forbidden_suffixes.iter().any(|s| name.ends_with(s))
|
if self.forbidden_suffixes.iter().any(|s| name.ends_with(s))
|
||||||
|| self.block_list.contains(&name)
|
|| self.block_list.contains(&name)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::connections::dns::DnsHandler; // проверь путь
|
use crate::connections::dns::DnsHandler;
|
||||||
use crate::connections::ip_store::FakeIpStore;
|
use crate::connections::ip_store::FakeIpStore;
|
||||||
use smoltcp::socket::udp;
|
use smoltcp::socket::udp;
|
||||||
|
|
||||||
|
|||||||
@@ -14,18 +14,14 @@ use tokio_util::sync::CancellationToken;
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
// 1. Инициализация логгера (теперь он внутри асинхронного контекста)
|
|
||||||
netrunner_logger::Logger::init();
|
netrunner_logger::Logger::init();
|
||||||
info!("Initializing NetRunner Stack...");
|
info!("Initializing NetRunner Stack...");
|
||||||
|
|
||||||
// 2. Инициализация DNS асинхронно
|
|
||||||
// Передаем пустую строку или путь к кэшу (на десктопе можно ".")
|
|
||||||
let mut dns_handler = DnsHandler::new(".");
|
let mut dns_handler = DnsHandler::new(".");
|
||||||
if let Err(e) = dns_handler.init().await {
|
if let Err(e) = dns_handler.init().await {
|
||||||
error!("Failed to initialize DNS blocklist: {}", e);
|
error!("Failed to initialize DNS blocklist: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Настройка TUN устройства
|
|
||||||
let tun_device = Tun::create(|config| {
|
let tun_device = Tun::create(|config| {
|
||||||
config
|
config
|
||||||
.tun_name("netr0")
|
.tun_name("netr0")
|
||||||
@@ -41,7 +37,6 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
info!("TUN interface is UP: 10.0.0.1/24");
|
info!("TUN interface is UP: 10.0.0.1/24");
|
||||||
|
|
||||||
// 4. Конфигурация стека протоколов (MTU 1280 для стабильности)
|
|
||||||
let config = Config::new(smoltcp::wire::HardwareAddress::Ip);
|
let config = Config::new(smoltcp::wire::HardwareAddress::Ip);
|
||||||
let mut caps = DeviceCapabilities::default();
|
let mut caps = DeviceCapabilities::default();
|
||||||
caps.max_transmission_unit = 1280;
|
caps.max_transmission_unit = 1280;
|
||||||
@@ -56,7 +51,6 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
let proxy_ip = network.get_self_local_address();
|
let proxy_ip = network.get_self_local_address();
|
||||||
|
|
||||||
// 5. Запуск сетевого прокси в отдельной задаче
|
|
||||||
let network_token = CancellationToken::new();
|
let network_token = CancellationToken::new();
|
||||||
let net_token_for_spawn = network_token.clone();
|
let net_token_for_spawn = network_token.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@@ -64,7 +58,6 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
network.run(net_token_for_spawn).await;
|
network.run(net_token_for_spawn).await;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 6. Инициализация Engine
|
|
||||||
let mut engine = Engine::new(config, caps, proxy_ip, dns_handler);
|
let mut engine = Engine::new(config, caps, proxy_ip, dns_handler);
|
||||||
engine.set_any_ip(true);
|
engine.set_any_ip(true);
|
||||||
engine.set_transparent_mode();
|
engine.set_transparent_mode();
|
||||||
@@ -84,7 +77,6 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. Очистка ресурсов
|
|
||||||
network_token.cancel();
|
network_token.cancel();
|
||||||
info!("Restoring system routing...");
|
info!("Restoring system routing...");
|
||||||
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
|
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ impl SessionManager {
|
|||||||
pub(crate) fn spawn_session(
|
pub(crate) fn spawn_session(
|
||||||
&self,
|
&self,
|
||||||
remote_address: String,
|
remote_address: String,
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))] // Путь обязателен для мобилок
|
#[cfg(any(target_os = "android", target_os = "ios"))] tun_fd: Option<i32>,
|
||||||
tun_fd: Option<i32>,
|
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))] cache_dir: String,
|
#[cfg(any(target_os = "android", target_os = "ios"))] cache_dir: String,
|
||||||
) -> Arc<Session> {
|
) -> Arc<Session> {
|
||||||
let runtime = get_runtime();
|
let runtime = get_runtime();
|
||||||
@@ -62,11 +61,11 @@ impl SessionManager {
|
|||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
{
|
{
|
||||||
cache_dir
|
cache_dir
|
||||||
} // Используем путь из мобильного приложения
|
}
|
||||||
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||||
{
|
{
|
||||||
".".to_string()
|
".".to_string()
|
||||||
} // На ПК пишем в локальную папку
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut dns_handler = DnsHandler::new(&cache_path);
|
let mut dns_handler = DnsHandler::new(&cache_path);
|
||||||
@@ -132,9 +131,9 @@ impl SessionManager {
|
|||||||
info!("Engine async task started");
|
info!("Engine async task started");
|
||||||
|
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
// Добавляем обработку результата прямо здесь
|
|
||||||
res = engine.run(tun_device) => {
|
res = engine.run(tun_device) => {
|
||||||
// Теперь эта ветка возвращает (), так как мы обработали результат
|
|
||||||
info!("Engine loop finished: {:?}", res);
|
info!("Engine loop finished: {:?}", res);
|
||||||
},
|
},
|
||||||
_ = cancel_token_for_engine.cancelled() => {
|
_ = cancel_token_for_engine.cancelled() => {
|
||||||
|
|||||||
@@ -114,22 +114,18 @@ impl ConnectionManager {
|
|||||||
fn handle_tcp(&mut self, handle: SocketHandle, socket: &mut tcp::Socket) {
|
fn handle_tcp(&mut self, handle: SocketHandle, socket: &mut tcp::Socket) {
|
||||||
use tcp::State;
|
use tcp::State;
|
||||||
|
|
||||||
// 1. Очистка закрытых сокетов
|
|
||||||
if socket.state() == State::Closed {
|
if socket.state() == State::Closed {
|
||||||
// Если сокет закрыт, удаляем его сессию и помечаем на удаление из сета
|
|
||||||
if self.active_tcp_sessions.contains_key(&handle) {
|
if self.active_tcp_sessions.contains_key(&handle) {
|
||||||
debug!(%handle, "TCP session closed, removing from active sessions");
|
debug!(%handle, "TCP session closed, removing from active sessions");
|
||||||
self.active_tcp_sessions.remove(&handle);
|
self.active_tcp_sessions.remove(&handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Добавляем в очередь на удаление из SocketSet (чтобы освободить память)
|
|
||||||
if !self.sockets_to_remove.contains(&handle) {
|
if !self.sockets_to_remove.contains(&handle) {
|
||||||
self.sockets_to_remove.push(handle);
|
self.sockets_to_remove.push(handle);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Инициализация сессии при установке соединения
|
|
||||||
if socket.state() == State::Established && !self.active_tcp_sessions.contains_key(&handle) {
|
if socket.state() == State::Established && !self.active_tcp_sessions.contains_key(&handle) {
|
||||||
let target = self.resolve_target(socket);
|
let target = self.resolve_target(socket);
|
||||||
|
|
||||||
@@ -145,12 +141,10 @@ impl ConnectionManager {
|
|||||||
self.active_tcp_sessions.insert(handle, conn);
|
self.active_tcp_sessions.insert(handle, conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Тик активной сессии (проброс данных в прокси)
|
|
||||||
if let Some(conn) = self.active_tcp_sessions.get_mut(&handle) {
|
if let Some(conn) = self.active_tcp_sessions.get_mut(&handle) {
|
||||||
if !conn.tick(socket) {
|
if !conn.tick(socket) {
|
||||||
debug!(%handle, "Connection tick failed, aborting.");
|
debug!(%handle, "Connection tick failed, aborting.");
|
||||||
socket.abort();
|
socket.abort();
|
||||||
// Сессия удалится на следующем проходе, когда стейт станет Closed
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,9 +184,9 @@ impl ConnectionManager {
|
|||||||
|
|
||||||
fn create_dynamic_tcp_socket<'a>(port: u16) -> tcp::Socket<'a> {
|
fn create_dynamic_tcp_socket<'a>(port: u16) -> tcp::Socket<'a> {
|
||||||
let buf_size = match port {
|
let buf_size = match port {
|
||||||
443 | 80 => 256 * 1024, // 256 KB для веба
|
443 | 80 => 256 * 1024,
|
||||||
22 | 53 | 123 => 16 * 1024, // 16 KB для мелких протоколов (SSH, DNS over TCP, NTP)
|
22 | 53 | 123 => 16 * 1024,
|
||||||
_ => 64 * 1024, // Дефолт
|
_ => 64 * 1024,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut socket = tcp::Socket::new(
|
let mut socket = tcp::Socket::new(
|
||||||
@@ -200,7 +194,7 @@ impl ConnectionManager {
|
|||||||
tcp::SocketBuffer::new(vec![0; buf_size]),
|
tcp::SocketBuffer::new(vec![0; buf_size]),
|
||||||
);
|
);
|
||||||
|
|
||||||
socket.set_nagle_enabled(false); // Для отзывчивости (особенно в играх типа Silent Hill, если через VPN)
|
socket.set_nagle_enabled(false);
|
||||||
socket.set_ack_delay(None);
|
socket.set_ack_delay(None);
|
||||||
socket
|
socket
|
||||||
}
|
}
|
||||||
@@ -216,12 +210,10 @@ impl ConnectionManager {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ищем только SYN (начало соединения)
|
|
||||||
if tcp_packet.syn() && !tcp_packet.ack() {
|
if tcp_packet.syn() && !tcp_packet.ack() {
|
||||||
let dst_port = tcp_packet.dst_port();
|
let dst_port = tcp_packet.dst_port();
|
||||||
let dst_addr = ip_packet.dst_addr();
|
let dst_addr = ip_packet.dst_addr();
|
||||||
|
|
||||||
// Проверяем, не создали ли мы уже такой сокет на предыдущем шаге
|
|
||||||
if !self.has_active_session(socket_set, dst_addr.into(), dst_port) {
|
if !self.has_active_session(socket_set, dst_addr.into(), dst_port) {
|
||||||
debug!(target: "netrunner", "Dynamic TCP: Creating socket for {}:{}", dst_addr, dst_port);
|
debug!(target: "netrunner", "Dynamic TCP: Creating socket for {}:{}", dst_addr, dst_port);
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ use smoltcp::{
|
|||||||
};
|
};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
// --- TokenBuffer (без изменений, он у тебя отличный) ---
|
|
||||||
const TOKEN_BUFFER_LIST_MAX_SIZE: usize = 64;
|
const TOKEN_BUFFER_LIST_MAX_SIZE: usize = 64;
|
||||||
static TOKEN_BUFFER_LIST: LazyLock<Mutex<Vec<BytesMut>>> = LazyLock::new(|| Mutex::new(Vec::new()));
|
static TOKEN_BUFFER_LIST: LazyLock<Mutex<Vec<BytesMut>>> = LazyLock::new(|| Mutex::new(Vec::new()));
|
||||||
|
|
||||||
@@ -61,11 +60,10 @@ impl DerefMut for TokenBuffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- VirtTunDevice ---
|
|
||||||
pub struct VirtTunDevice {
|
pub struct VirtTunDevice {
|
||||||
capabilities: DeviceCapabilities,
|
capabilities: DeviceCapabilities,
|
||||||
rx_queue: mpsc::UnboundedReceiver<TokenBuffer>, // smoltcp читает отсюда
|
rx_queue: mpsc::UnboundedReceiver<TokenBuffer>,
|
||||||
tx_queue: mpsc::UnboundedSender<TokenBuffer>, // smoltcp пишет сюда
|
tx_queue: mpsc::UnboundedSender<TokenBuffer>,
|
||||||
rx_avail: Arc<AtomicBool>,
|
rx_avail: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,8 +72,8 @@ impl VirtTunDevice {
|
|||||||
capabilities: DeviceCapabilities,
|
capabilities: DeviceCapabilities,
|
||||||
) -> (
|
) -> (
|
||||||
Self,
|
Self,
|
||||||
mpsc::UnboundedSender<TokenBuffer>, // Канал, чтобы закидывать пакеты в smoltcp
|
mpsc::UnboundedSender<TokenBuffer>,
|
||||||
mpsc::UnboundedReceiver<TokenBuffer>, // Канал, чтобы забирать готовые пакеты из smoltcp
|
mpsc::UnboundedReceiver<TokenBuffer>,
|
||||||
Arc<AtomicBool>,
|
Arc<AtomicBool>,
|
||||||
) {
|
) {
|
||||||
let (to_smoltcp_tx, to_smoltcp_rx) = mpsc::unbounded_channel();
|
let (to_smoltcp_tx, to_smoltcp_rx) = mpsc::unbounded_channel();
|
||||||
|
|||||||
@@ -29,11 +29,7 @@ pub struct Engine {
|
|||||||
socket_set: SocketSet<'static>,
|
socket_set: SocketSet<'static>,
|
||||||
manager: ConnectionManager,
|
manager: ConnectionManager,
|
||||||
device: VirtTunDevice,
|
device: VirtTunDevice,
|
||||||
|
|
||||||
// Канал для инъекции пакетов в smoltcp
|
|
||||||
to_smoltcp_tx: UnboundedSender<TokenBuffer>,
|
to_smoltcp_tx: UnboundedSender<TokenBuffer>,
|
||||||
|
|
||||||
// Временное хранилище канала выхода из smoltcp (отдадим воркеру в run)
|
|
||||||
from_smoltcp_rx: Option<UnboundedReceiver<TokenBuffer>>,
|
from_smoltcp_rx: Option<UnboundedReceiver<TokenBuffer>>,
|
||||||
|
|
||||||
avail: Arc<AtomicBool>,
|
avail: Arc<AtomicBool>,
|
||||||
@@ -47,7 +43,7 @@ impl Engine {
|
|||||||
dns_handler: DnsHandler,
|
dns_handler: DnsHandler,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let now = Engine::current_time();
|
let now = Engine::current_time();
|
||||||
// Смотри, как чисто мы получаем компоненты:
|
|
||||||
let (mut device, to_smoltcp_tx, from_smoltcp_rx, avail) = VirtTunDevice::new(caps);
|
let (mut device, to_smoltcp_tx, from_smoltcp_rx, avail) = VirtTunDevice::new(caps);
|
||||||
let interface = Interface::new(config, &mut device, now);
|
let interface = Interface::new(config, &mut device, now);
|
||||||
|
|
||||||
@@ -69,25 +65,19 @@ impl Engine {
|
|||||||
info!("Current routes: {:?}", self.interface.routes());
|
info!("Current routes: {:?}", self.interface.routes());
|
||||||
let (writer, reader) = tun.split().expect("Failed to split TUN");
|
let (writer, reader) = tun.split().expect("Failed to split TUN");
|
||||||
|
|
||||||
// 1. Создаем трубу для ВХОДЯЩЕГО трафика: TUN -> Engine
|
|
||||||
let (tun_to_engine_tx, mut tun_to_engine_rx) = mpsc::unbounded_channel();
|
let (tun_to_engine_tx, mut tun_to_engine_rx) = mpsc::unbounded_channel();
|
||||||
Self::spawn_tun_reader(reader, tun_to_engine_tx, self.avail.clone());
|
Self::spawn_tun_reader(reader, tun_to_engine_tx, self.avail.clone());
|
||||||
|
|
||||||
// 2. Запускаем трубу ИСХОДЯЩЕГО трафика: smoltcp -> TUN
|
|
||||||
let from_smoltcp_rx = self.from_smoltcp_rx.take().expect("Engine started twice");
|
let from_smoltcp_rx = self.from_smoltcp_rx.take().expect("Engine started twice");
|
||||||
Self::spawn_tun_writer(writer, from_smoltcp_rx);
|
Self::spawn_tun_writer(writer, from_smoltcp_rx);
|
||||||
|
|
||||||
let mut last_log = StdInstant::now();
|
let mut last_log = StdInstant::now();
|
||||||
|
|
||||||
// 3. Главный цикл (ДИСПЕТЧЕР)
|
|
||||||
loop {
|
loop {
|
||||||
// Читаем всё, что прилетело из операционной системы
|
|
||||||
while let Ok(token) = tun_to_engine_rx.try_recv() {
|
while let Ok(token) = tun_to_engine_rx.try_recv() {
|
||||||
// АНАЛИЗ: Создаем сокет, если это новый SYN
|
|
||||||
self.manager
|
self.manager
|
||||||
.try_create_socket_from_packet(&token, &mut self.socket_set);
|
.try_create_socket_from_packet(&token, &mut self.socket_set);
|
||||||
|
|
||||||
// ПЕРЕДАЧА: Закидываем пакет в виртуальную сетевую карту smoltcp
|
|
||||||
if self.to_smoltcp_tx.send(token).is_ok() {
|
if self.to_smoltcp_tx.send(token).is_ok() {
|
||||||
self.device.mark_rx_available();
|
self.device.mark_rx_available();
|
||||||
}
|
}
|
||||||
@@ -131,7 +121,6 @@ impl Engine {
|
|||||||
sleep(sleep_duration).await;
|
sleep(sleep_duration).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Воркер: Читает ОС -> Шлет в Engine
|
|
||||||
fn spawn_tun_reader(
|
fn spawn_tun_reader(
|
||||||
mut reader: DeviceReader,
|
mut reader: DeviceReader,
|
||||||
to_engine: UnboundedSender<TokenBuffer>,
|
to_engine: UnboundedSender<TokenBuffer>,
|
||||||
@@ -173,7 +162,6 @@ impl Engine {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Воркер: Читает smoltcp -> Шлет в ОС
|
|
||||||
fn spawn_tun_writer(
|
fn spawn_tun_writer(
|
||||||
mut writer: DeviceWriter,
|
mut writer: DeviceWriter,
|
||||||
mut from_smoltcp: UnboundedReceiver<TokenBuffer>,
|
mut from_smoltcp: UnboundedReceiver<TokenBuffer>,
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ fn get_default_gateway() -> Option<String> {
|
|||||||
.output()
|
.output()
|
||||||
.ok()?;
|
.ok()?;
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
// Простой парсинг: ищем строку с 0.0.0.0 и берем IP из колонки шлюза
|
|
||||||
stdout
|
stdout
|
||||||
.lines()
|
.lines()
|
||||||
.find(|line| line.contains("0.0.0.0"))
|
.find(|line| line.contains("0.0.0.0"))
|
||||||
@@ -59,19 +59,15 @@ pub fn setup_platform_routing(remote_address: &str) -> io::Result<()> {
|
|||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
// 1. Ядро
|
|
||||||
let _ = run_cmd_ext("sysctl -w net.ipv4.conf.all.rp_filter=0", true);
|
let _ = run_cmd_ext("sysctl -w net.ipv4.conf.all.rp_filter=0", true);
|
||||||
let _ = run_cmd_ext("sysctl -w net.ipv4.conf.netr0.rp_filter=0", true);
|
let _ = run_cmd_ext("sysctl -w net.ipv4.conf.netr0.rp_filter=0", true);
|
||||||
let _ = run_cmd_ext("sysctl -w net.ipv4.ip_forward=1", true);
|
let _ = run_cmd_ext("sysctl -w net.ipv4.ip_forward=1", true);
|
||||||
|
|
||||||
// 2. Маршрутизация
|
|
||||||
let _ = run_cmd_ext("ip rule add fwmark 0x1 table 100", true);
|
let _ = run_cmd_ext("ip rule add fwmark 0x1 table 100", true);
|
||||||
let _ = run_cmd_ext("ip route add default dev netr0 table 100", true);
|
let _ = run_cmd_ext("ip route add default dev netr0 table 100", true);
|
||||||
|
|
||||||
// 3. NFTables (Чиним ошибку удаления и синтаксис)
|
|
||||||
// Добавляем таблицу (если есть — ничего не сделает, если нет — создаст)
|
|
||||||
run_cmd_ext("nft add table ip netrunner", true)?;
|
run_cmd_ext("nft add table ip netrunner", true)?;
|
||||||
// Очищаем таблицу перед работой, чтобы не плодить дубликаты
|
|
||||||
run_cmd_ext("nft flush table ip netrunner", true)?;
|
run_cmd_ext("nft flush table ip netrunner", true)?;
|
||||||
|
|
||||||
run_cmd_ext(
|
run_cmd_ext(
|
||||||
@@ -95,7 +91,6 @@ pub fn setup_platform_routing(remote_address: &str) -> io::Result<()> {
|
|||||||
);
|
);
|
||||||
run_cmd_ext(&dns_redir, false)?;
|
run_cmd_ext(&dns_redir, false)?;
|
||||||
|
|
||||||
// 4. DNS (resolvectl не знает 'metric', используем стандартный синтаксис)
|
|
||||||
let _ = run_cmd_ext("resolvectl dns netr0 10.0.0.2", true);
|
let _ = run_cmd_ext("resolvectl dns netr0 10.0.0.2", true);
|
||||||
let _ = run_cmd_ext("resolvectl domain netr0 ~.", true);
|
let _ = run_cmd_ext("resolvectl domain netr0 ~.", true);
|
||||||
|
|
||||||
@@ -106,17 +101,14 @@ pub fn setup_platform_routing(remote_address: &str) -> io::Result<()> {
|
|||||||
{
|
{
|
||||||
let gateway = get_default_gateway().unwrap_or_else(|| "192.168.1.1".to_string());
|
let gateway = get_default_gateway().unwrap_or_else(|| "192.168.1.1".to_string());
|
||||||
|
|
||||||
// 1. Устанавливаем IP для адаптера
|
|
||||||
let _ = run_cmd_ext(
|
let _ = run_cmd_ext(
|
||||||
"netsh interface ipv4 set address name=\"netr0\" static 10.0.0.1 255.255.255.0 none",
|
"netsh interface ipv4 set address name=\"netr0\" static 10.0.0.1 255.255.255.0 none",
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 2. Ждем и получаем индекс именно нашего туннеля
|
|
||||||
let tun_idx = get_adapter_index("netr0")
|
let tun_idx = get_adapter_index("netr0")
|
||||||
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Interface netr0 not found"))?;
|
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Interface netr0 not found"))?;
|
||||||
|
|
||||||
// 3. Маршрут-исключение для прокси через физический шлюз
|
|
||||||
let _ = run_cmd_ext(
|
let _ = run_cmd_ext(
|
||||||
&format!(
|
&format!(
|
||||||
"route add {} mask 255.255.255.255 {} metric 1",
|
"route add {} mask 255.255.255.255 {} metric 1",
|
||||||
@@ -125,7 +117,6 @@ pub fn setup_platform_routing(remote_address: &str) -> io::Result<()> {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 4. Глобальные маршруты через TUN (теперь с правильным if_idx)
|
|
||||||
run_cmd_ext(
|
run_cmd_ext(
|
||||||
&format!(
|
&format!(
|
||||||
"route add 0.0.0.0 mask 128.0.0.0 10.0.0.2 if {} metric 5",
|
"route add 0.0.0.0 mask 128.0.0.0 10.0.0.2 if {} metric 5",
|
||||||
@@ -141,7 +132,6 @@ pub fn setup_platform_routing(remote_address: &str) -> io::Result<()> {
|
|||||||
true,
|
true,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// 5. DNS
|
|
||||||
let _ = run_cmd_ext(
|
let _ = run_cmd_ext(
|
||||||
"netsh interface ipv4 set dnsservers name=\"netr0\" static 10.0.0.2 primary",
|
"netsh interface ipv4 set dnsservers name=\"netr0\" static 10.0.0.2 primary",
|
||||||
true,
|
true,
|
||||||
@@ -172,7 +162,6 @@ pub fn reset_platform_routing(proxy_ip: Option<&str>) -> io::Result<()> {
|
|||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
// Очищаем глобальные маршруты, иначе интернет не вернется
|
|
||||||
let _ = run_cmd_ext("route delete 0.0.0.0 mask 128.0.0.0", true);
|
let _ = run_cmd_ext("route delete 0.0.0.0 mask 128.0.0.0", true);
|
||||||
let _ = run_cmd_ext("route delete 128.0.0.0 mask 128.0.0.0", true);
|
let _ = run_cmd_ext("route delete 128.0.0.0 mask 128.0.0.0", true);
|
||||||
|
|
||||||
@@ -180,7 +169,6 @@ pub fn reset_platform_routing(proxy_ip: Option<&str>) -> io::Result<()> {
|
|||||||
let _ = run_cmd_ext(&format!("route delete {}", ip), true);
|
let _ = run_cmd_ext(&format!("route delete {}", ip), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сбрасываем DNS на автоматический (или удаляем наш)
|
|
||||||
let _ = run_cmd_ext(
|
let _ = run_cmd_ext(
|
||||||
"netsh interface ipv4 set dnsservers name=\"netr0\" source=dhcp",
|
"netsh interface ipv4 set dnsservers name=\"netr0\" source=dhcp",
|
||||||
true,
|
true,
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ pub struct SessionKeys {
|
|||||||
pub ecdh: ECDH,
|
pub ecdh: ECDH,
|
||||||
pub auth_key: [u8; 32],
|
pub auth_key: [u8; 32],
|
||||||
pub current_aead: Option<([u8; 32], [u8; 12], [u8; 32], [u8; 12])>,
|
pub current_aead: Option<([u8; 32], [u8; 12], [u8; 32], [u8; 12])>,
|
||||||
is_initiator: bool, // Сохраним роль для правильного возврата параметров
|
is_initiator: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SessionKeys {
|
impl SessionKeys {
|
||||||
@@ -159,7 +159,6 @@ impl SessionKeys {
|
|||||||
|
|
||||||
self.auth_key = HKDF::expand_key::<32>(&hkdf, b"auth_key").map_err(|e| e.to_string())?;
|
self.auth_key = HKDF::expand_key::<32>(&hkdf, b"auth_key").map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
// Определяем порядок: (Write Key, Write IV, Read Key, Read IV)
|
|
||||||
let keys = if is_server {
|
let keys = if is_server {
|
||||||
(s_key, s_iv, c_key, c_iv)
|
(s_key, s_iv, c_key, c_iv)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -144,7 +144,6 @@ impl TlsBridge {
|
|||||||
profile: &ServerProfile,
|
profile: &ServerProfile,
|
||||||
) -> Result<Bytes, TlsError> {
|
) -> Result<Bytes, TlsError> {
|
||||||
if let HandshakeMessage::Client { base, extensions } = client_msg {
|
if let HandshakeMessage::Client { base, extensions } = client_msg {
|
||||||
// 1. Проверка Auth Tag в Session ID (последние 16 байт)
|
|
||||||
if base.session_id.len() != 32 {
|
if base.session_id.len() != 32 {
|
||||||
return Err(TlsError::new(
|
return Err(TlsError::new(
|
||||||
ErrorStage::Handshake("Invalid SessionID len"),
|
ErrorStage::Handshake("Invalid SessionID len"),
|
||||||
@@ -165,8 +164,6 @@ impl TlsBridge {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Выполняем Key Exchange (находим KeyShare клиента и считаем Shared Secret)
|
|
||||||
// Это обновит внутреннее состояние keys (auth_key и AEAD ключи)
|
|
||||||
keys.update_keys(base.random, extensions, true)
|
keys.update_keys(base.random, extensions, true)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
netrunner_logger::error!(error = %e, "Server failed key update");
|
netrunner_logger::error!(error = %e, "Server failed key update");
|
||||||
@@ -177,7 +174,6 @@ impl TlsBridge {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// 3. Генерируем ServerHello, используя наш свежий публичный ключ и локальную соль
|
|
||||||
let server_pub_key = keys.ecdh.public_key.to_bytes();
|
let server_pub_key = keys.ecdh.public_key.to_bytes();
|
||||||
|
|
||||||
Ok(ServerHello::make_server_hello(
|
Ok(ServerHello::make_server_hello(
|
||||||
|
|||||||
@@ -46,16 +46,12 @@ impl Codec {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// ВАЖНО: TlsBridge сам проверит Auth Tag в Session ID клиента
|
|
||||||
// и вызовет update_keys для генерации общего секрета.
|
|
||||||
let server_hello_record = TlsBridge::wrap_server_hello(
|
let server_hello_record = TlsBridge::wrap_server_hello(
|
||||||
&client_msg,
|
&client_msg,
|
||||||
&mut self.session_keys,
|
&mut self.session_keys,
|
||||||
&ServerProfile::MODERN,
|
&ServerProfile::MODERN,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Ключи уже обновлены внутри session_keys.
|
|
||||||
// Просто забираем их и устанавливаем в AEAD шифратор.
|
|
||||||
let (w_key, w_iv, r_key, r_iv) = self.session_keys.get_aead_parameters();
|
let (w_key, w_iv, r_key, r_iv) = self.session_keys.get_aead_parameters();
|
||||||
self.crypto.set_keys(w_key, w_iv, r_key, r_iv);
|
self.crypto.set_keys(w_key, w_iv, r_key, r_iv);
|
||||||
|
|
||||||
|
|||||||
@@ -115,29 +115,25 @@ impl Parser for ClientHello {
|
|||||||
fn can_parse(bytes: &BytesMut) -> bool {
|
fn can_parse(bytes: &BytesMut) -> bool {
|
||||||
let mut reader = &bytes[..];
|
let mut reader = &bytes[..];
|
||||||
|
|
||||||
// 1. Version (2) + Random (32) + SessionID Len (1) = 35
|
|
||||||
if reader.len() < 35 {
|
if reader.len() < 35 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
reader.advance(34);
|
reader.advance(34);
|
||||||
|
|
||||||
// 2. Session ID
|
|
||||||
let sid_len = reader[0] as usize;
|
let sid_len = reader[0] as usize;
|
||||||
reader.advance(1);
|
reader.advance(1);
|
||||||
if reader.len() < sid_len + 2 {
|
if reader.len() < sid_len + 2 {
|
||||||
return false;
|
return false;
|
||||||
} // +2 для Cipher Suites Len
|
}
|
||||||
reader.advance(sid_len);
|
reader.advance(sid_len);
|
||||||
|
|
||||||
// 3. Cipher Suites
|
|
||||||
let ciphers_len = u16::from_be_bytes([reader[0], reader[1]]) as usize;
|
let ciphers_len = u16::from_be_bytes([reader[0], reader[1]]) as usize;
|
||||||
reader.advance(2);
|
reader.advance(2);
|
||||||
if reader.len() < ciphers_len + 1 {
|
if reader.len() < ciphers_len + 1 {
|
||||||
return false;
|
return false;
|
||||||
} // +1 для Compression Len
|
}
|
||||||
reader.advance(ciphers_len);
|
reader.advance(ciphers_len);
|
||||||
|
|
||||||
// 4. Compression Methods
|
|
||||||
let comp_len = reader[0] as usize;
|
let comp_len = reader[0] as usize;
|
||||||
reader.advance(1);
|
reader.advance(1);
|
||||||
if reader.len() < comp_len {
|
if reader.len() < comp_len {
|
||||||
@@ -145,7 +141,6 @@ impl Parser for ClientHello {
|
|||||||
}
|
}
|
||||||
reader.advance(comp_len);
|
reader.advance(comp_len);
|
||||||
|
|
||||||
// 5. Extensions (опционально в TLS, но обычно есть)
|
|
||||||
if reader.len() >= 2 {
|
if reader.len() >= 2 {
|
||||||
let ext_len = u16::from_be_bytes([reader[0], reader[1]]) as usize;
|
let ext_len = u16::from_be_bytes([reader[0], reader[1]]) as usize;
|
||||||
reader.advance(2);
|
reader.advance(2);
|
||||||
@@ -158,8 +153,6 @@ impl Parser for ClientHello {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
|
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
|
||||||
// Мы уже проверили всё в can_parse, поэтому здесь просто
|
|
||||||
// последовательно забираем данные через get_*
|
|
||||||
if !Self::can_parse(bytes) {
|
if !Self::can_parse(bytes) {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ impl Connection {
|
|||||||
|
|
||||||
pub fn new_raw(inbound: OwnedReadHalf, outbound: OwnedWriteHalf) -> Self {
|
pub fn new_raw(inbound: OwnedReadHalf, outbound: OwnedWriteHalf) -> Self {
|
||||||
Self {
|
Self {
|
||||||
addr: "0.0.0.0:0".parse().unwrap(), // заглушка, если адрес не важен
|
addr: "0.0.0.0:0".parse().unwrap(),
|
||||||
inbound,
|
inbound,
|
||||||
outbound,
|
outbound,
|
||||||
read_buf: BytesMut::with_capacity(BUF_SIZE),
|
read_buf: BytesMut::with_capacity(BUF_SIZE),
|
||||||
@@ -105,7 +105,6 @@ pub struct ClientHandler {
|
|||||||
pub muxer: Muxer,
|
pub muxer: Muxer,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Внутри connection.rs или где у тебя ClientHandler
|
|
||||||
impl ClientHandler {
|
impl ClientHandler {
|
||||||
pub async fn connect(
|
pub async fn connect(
|
||||||
remote_proxy_addr: &str,
|
remote_proxy_addr: &str,
|
||||||
@@ -118,7 +117,6 @@ impl ClientHandler {
|
|||||||
|
|
||||||
let mut conn = Connection::new_raw(inbound, outbound);
|
let mut conn = Connection::new_raw(inbound, outbound);
|
||||||
|
|
||||||
// TLS Handshake
|
|
||||||
let ch = conn
|
let ch = conn
|
||||||
.codec
|
.codec
|
||||||
.make_client_handshake(&BrowserProfile::CHROME_131, "google.com")
|
.make_client_handshake(&BrowserProfile::CHROME_131, "google.com")
|
||||||
@@ -177,7 +175,6 @@ impl ClientHandler {
|
|||||||
|
|
||||||
let mut buf = [0u8; 1024];
|
let mut buf = [0u8; 1024];
|
||||||
loop {
|
loop {
|
||||||
// Просто ждем, пока клиент не разорвет TCP-соединение
|
|
||||||
if self
|
if self
|
||||||
.conn
|
.conn
|
||||||
.inbound
|
.inbound
|
||||||
@@ -198,17 +195,14 @@ impl TunnelHandler for ClientHandler {
|
|||||||
async fn run(mut self) -> Result<(), String> {
|
async fn run(mut self) -> Result<(), String> {
|
||||||
info!("Starting SOCKS multiplexed handling");
|
info!("Starting SOCKS multiplexed handling");
|
||||||
|
|
||||||
// 1. Приветствие (Handshake)
|
|
||||||
self.conn.read_socks_request().await?;
|
self.conn.read_socks_request().await?;
|
||||||
self.conn
|
self.conn
|
||||||
.send_socks_reply(SocksReply::HandshakeSelect { method: 0x00 })
|
.send_socks_reply(SocksReply::HandshakeSelect { method: 0x00 })
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// 2. Получаем основной запрос (Connect или UDP Associate)
|
|
||||||
let req = self.conn.read_socks_request().await?;
|
let req = self.conn.read_socks_request().await?;
|
||||||
|
|
||||||
match req {
|
match req {
|
||||||
// Ветка TCP CONNECT
|
|
||||||
SocksRequest::Connect {
|
SocksRequest::Connect {
|
||||||
command: 0x01,
|
command: 0x01,
|
||||||
target,
|
target,
|
||||||
@@ -253,13 +247,11 @@ impl TunnelHandler for ClientHandler {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ветка UDP ASSOCIATE
|
|
||||||
SocksRequest::Connect { command: 0x03, .. } => {
|
SocksRequest::Connect { command: 0x03, .. } => {
|
||||||
info!("Handling UDP Associate request");
|
info!("Handling UDP Associate request");
|
||||||
self.handle_udp_associate().await
|
self.handle_udp_associate().await
|
||||||
}
|
}
|
||||||
|
|
||||||
// Всё остальное (BIND и т.д.)
|
|
||||||
_ => Err("Unsupported SOCKS command".into()),
|
_ => Err("Unsupported SOCKS command".into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ impl StreamHandler {
|
|||||||
let target_str = String::from_utf8_lossy(&payload).to_string();
|
let target_str = String::from_utf8_lossy(&payload).to_string();
|
||||||
let muxer = self.muxer.clone();
|
let muxer = self.muxer.clone();
|
||||||
|
|
||||||
// Канал для передачи данных из мультиплексора в мост (bridge)
|
|
||||||
let (v_tx, v_rx) = tokio::sync::mpsc::channel(512);
|
let (v_tx, v_rx) = tokio::sync::mpsc::channel(512);
|
||||||
muxer.register_stream(stream_id, v_tx).await;
|
muxer.register_stream(stream_id, v_tx).await;
|
||||||
|
|
||||||
@@ -43,7 +42,6 @@ impl StreamHandler {
|
|||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
info!(stream_id, target = %target_str, "Attempting remote connection");
|
info!(stream_id, target = %target_str, "Attempting remote connection");
|
||||||
|
|
||||||
// Обертываем коннект в таймаут, чтобы не плодить зомби-таски
|
|
||||||
let connect_timeout = tokio::time::timeout(
|
let connect_timeout = tokio::time::timeout(
|
||||||
std::time::Duration::from_secs(5),
|
std::time::Duration::from_secs(5),
|
||||||
tokio::net::TcpStream::connect(&target_str),
|
tokio::net::TcpStream::connect(&target_str),
|
||||||
@@ -62,7 +60,7 @@ impl StreamHandler {
|
|||||||
|
|
||||||
let mut reply_buf = BytesMut::with_capacity(10);
|
let mut reply_buf = BytesMut::with_capacity(10);
|
||||||
let reply = SocksReply::ConnectResult {
|
let reply = SocksReply::ConnectResult {
|
||||||
reply_code: 0x00, // Success
|
reply_code: 0x00,
|
||||||
atyp: 0x01,
|
atyp: 0x01,
|
||||||
addr: [0, 0, 0, 0],
|
addr: [0, 0, 0, 0],
|
||||||
port: 0,
|
port: 0,
|
||||||
@@ -78,11 +76,11 @@ impl StreamHandler {
|
|||||||
}
|
}
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
error!(stream_id, target = %target_str, error = %e, "TCP connection failed");
|
error!(stream_id, target = %target_str, error = %e, "TCP connection failed");
|
||||||
Self::send_error_reply(&muxer, stream_id, 0x01).await; // 0x01 = General failure
|
Self::send_error_reply(&muxer, stream_id, 0x01).await;
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!(stream_id, target = %target_str, "Connection timed out (DNS/TCP)");
|
error!(stream_id, target = %target_str, "Connection timed out (DNS/TCP)");
|
||||||
Self::send_error_reply(&muxer, stream_id, 0x04).await; // 0x04 = Host unreachable
|
Self::send_error_reply(&muxer, stream_id, 0x04).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -260,14 +260,13 @@ impl ExtensionBuilder {
|
|||||||
TlsExtensions::RENEGOTIATION_INFO => self.renegotiation_info(),
|
TlsExtensions::RENEGOTIATION_INFO => self.renegotiation_info(),
|
||||||
TlsExtensions::PADDING => {
|
TlsExtensions::PADDING => {
|
||||||
if profile.target_padding_len > 0 {
|
if profile.target_padding_len > 0 {
|
||||||
// Передаем накопленный оверхед всего пакета
|
|
||||||
self.padding(profile.target_padding_len as usize, overhead);
|
self.padding(profile.target_padding_len as usize, overhead);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id if TlsExtensions::is_grease(id) => {
|
id if TlsExtensions::is_grease(id) => {
|
||||||
if profile.has_grease {
|
if profile.has_grease {
|
||||||
self.grease_with_id(id); // Используем конкретный ID из ExtensionOrder
|
self.grease_with_id(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => self.apply_generic_extension(ext_id, profile),
|
_ => self.apply_generic_extension(ext_id, profile),
|
||||||
|
|||||||
@@ -36,36 +36,29 @@ impl ClientHello {
|
|||||||
|
|
||||||
buf.put_u8(HANDSHAKE_TYPE_CLIENT_HELLO);
|
buf.put_u8(HANDSHAKE_TYPE_CLIENT_HELLO);
|
||||||
|
|
||||||
// Резервируем 3 байта под длину Handshake (U24)
|
|
||||||
let length_pos = buf.len();
|
let length_pos = buf.len();
|
||||||
buf.put_bytes(0, 3);
|
buf.put_bytes(0, 3);
|
||||||
|
|
||||||
buf.put_u16(0x0303); // Legacy Version
|
buf.put_u16(0x0303);
|
||||||
buf.put_slice(&self.random);
|
buf.put_slice(&self.random);
|
||||||
|
|
||||||
// Session ID
|
|
||||||
buf.put_u8(self.session_id.len() as u8);
|
buf.put_u8(self.session_id.len() as u8);
|
||||||
buf.put_slice(&self.session_id);
|
buf.put_slice(&self.session_id);
|
||||||
|
|
||||||
// Cipher Suites
|
|
||||||
buf.put_u16((self.cipher_suites.len() * 2) as u16);
|
buf.put_u16((self.cipher_suites.len() * 2) as u16);
|
||||||
for &suite in &self.cipher_suites {
|
for &suite in &self.cipher_suites {
|
||||||
buf.put_u16(suite);
|
buf.put_u16(suite);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compression Methods (всегда 0x01 0x00 для маскировки)
|
|
||||||
buf.put_u8(1);
|
buf.put_u8(1);
|
||||||
buf.put_u8(0x00);
|
buf.put_u8(0x00);
|
||||||
|
|
||||||
// Extensions
|
|
||||||
buf.put_u16(self.extensions.len() as u16);
|
buf.put_u16(self.extensions.len() as u16);
|
||||||
buf.put_slice(&self.extensions);
|
buf.put_slice(&self.extensions);
|
||||||
|
|
||||||
// ПРАВИЛЬНЫЙ РАСЧЕТ ДЛИНЫ:
|
|
||||||
// Длина сообщения Handshake не включает сам тип (1 байт) и поле длины (3 байта)
|
|
||||||
let total_len = (buf.len() - length_pos - 3) as u32;
|
let total_len = (buf.len() - length_pos - 3) as u32;
|
||||||
let len_bytes = total_len.to_be_bytes();
|
let len_bytes = total_len.to_be_bytes();
|
||||||
// Записываем 3 байта длины (Big Endian)
|
|
||||||
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
|
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
|
||||||
|
|
||||||
buf.freeze()
|
buf.freeze()
|
||||||
@@ -126,14 +119,12 @@ impl ServerHello {
|
|||||||
salt: [u8; 32],
|
salt: [u8; 32],
|
||||||
profile: &ServerProfile,
|
profile: &ServerProfile,
|
||||||
) -> Bytes {
|
) -> Bytes {
|
||||||
// Создаем инстанс ServerHello
|
|
||||||
let server_hello = Self::from_client_hello(client_hello, server_public_key, salt, profile);
|
let server_hello = Self::from_client_hello(client_hello, server_public_key, salt, profile);
|
||||||
|
|
||||||
// Оборачиваем в Record Layer
|
|
||||||
let record = TlsRecord::new(
|
let record = TlsRecord::new(
|
||||||
ContentType::Handshake,
|
ContentType::Handshake,
|
||||||
profile.record_layer_version, // 0x0303 обычно
|
profile.record_layer_version,
|
||||||
server_hello.serialize(), // Сериализация самого SH
|
server_hello.serialize(),
|
||||||
);
|
);
|
||||||
|
|
||||||
record.serialize()
|
record.serialize()
|
||||||
@@ -145,10 +136,8 @@ impl ServerHello {
|
|||||||
salt: [u8; 32],
|
salt: [u8; 32],
|
||||||
profile: &ServerProfile,
|
profile: &ServerProfile,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// 1. Используем соль как рандом сервера
|
|
||||||
let server_random = salt;
|
let server_random = salt;
|
||||||
|
|
||||||
// 2. Выбор Cipher Suite (твой код здесь хорош, оставляем)
|
|
||||||
let selected_suite = if profile.honor_cipher_order {
|
let selected_suite = if profile.honor_cipher_order {
|
||||||
profile
|
profile
|
||||||
.cipher_suites
|
.cipher_suites
|
||||||
@@ -167,27 +156,22 @@ impl ServerHello {
|
|||||||
|
|
||||||
let mut extensions = BytesMut::new();
|
let mut extensions = BytesMut::new();
|
||||||
|
|
||||||
// 3. Работа с версией.
|
|
||||||
// Выбираем макс. версию из профиля (например, TLS 1.3)
|
|
||||||
let selected_version = profile.versions.max();
|
let selected_version = profile.versions.max();
|
||||||
|
|
||||||
// Добавляем расширение Supported Versions (обязательно для TLS 1.3)
|
|
||||||
extensions.put_u16(0x002b);
|
extensions.put_u16(0x002b);
|
||||||
extensions.put_u16(2);
|
extensions.put_u16(2);
|
||||||
extensions.put_u16(selected_version as u16);
|
extensions.put_u16(selected_version as u16);
|
||||||
|
|
||||||
// 4. Key Share (исправляем расчет длины, чтобы не было как в прошлый раз)
|
|
||||||
let key_len = server_public_key.len() as u16;
|
let key_len = server_public_key.len() as u16;
|
||||||
extensions.put_u16(0x0033);
|
extensions.put_u16(0x0033);
|
||||||
extensions.put_u16(key_len + 4); // Группа(2) + Длина(2) + Ключ
|
extensions.put_u16(key_len + 4);
|
||||||
extensions.put_u16(0x001d); // x25519
|
extensions.put_u16(0x001d);
|
||||||
extensions.put_u16(key_len);
|
extensions.put_u16(key_len);
|
||||||
extensions.put_slice(server_public_key);
|
extensions.put_slice(server_public_key);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
// Это поле будет использоваться как legacy_version (0x0303)
|
|
||||||
version: ProtocolVersion::Tls12,
|
version: ProtocolVersion::Tls12,
|
||||||
random: server_random, // ИСПОЛЬЗУЕМ переменную
|
random: server_random,
|
||||||
session_id: client_hello.session_id.clone(),
|
session_id: client_hello.session_id.clone(),
|
||||||
cipher_suite: selected_suite,
|
cipher_suite: selected_suite,
|
||||||
extensions,
|
extensions,
|
||||||
@@ -199,26 +183,20 @@ impl ServerHello {
|
|||||||
|
|
||||||
buf.put_u8(HANDSHAKE_TYPE_SERVER_HELLO);
|
buf.put_u8(HANDSHAKE_TYPE_SERVER_HELLO);
|
||||||
|
|
||||||
// Резервируем место под длину Handshake (3 байта)
|
|
||||||
let length_pos = buf.len();
|
let length_pos = buf.len();
|
||||||
buf.put_slice(&[0, 0, 0]);
|
buf.put_slice(&[0, 0, 0]);
|
||||||
|
|
||||||
// Используем версию из структуры (Legacy Version)
|
|
||||||
buf.put_u16(self.version as u16);
|
buf.put_u16(self.version as u16);
|
||||||
|
|
||||||
// Используем рандом из структуры
|
|
||||||
buf.put_slice(&self.random);
|
buf.put_slice(&self.random);
|
||||||
|
|
||||||
// Session ID (Echo)
|
|
||||||
buf.put_u8(self.session_id.len() as u8);
|
buf.put_u8(self.session_id.len() as u8);
|
||||||
buf.put_slice(&self.session_id);
|
buf.put_slice(&self.session_id);
|
||||||
|
|
||||||
buf.put_u16(self.cipher_suite);
|
buf.put_u16(self.cipher_suite);
|
||||||
|
|
||||||
// Compression (0x00)
|
|
||||||
buf.put_u8(0x00);
|
buf.put_u8(0x00);
|
||||||
|
|
||||||
// Extensions
|
|
||||||
if !self.extensions.is_empty() {
|
if !self.extensions.is_empty() {
|
||||||
buf.put_u16(self.extensions.len() as u16);
|
buf.put_u16(self.extensions.len() as u16);
|
||||||
buf.put_slice(&self.extensions);
|
buf.put_slice(&self.extensions);
|
||||||
@@ -226,7 +204,6 @@ impl ServerHello {
|
|||||||
buf.put_u16(0);
|
buf.put_u16(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Заполняем длину handshake-сообщения
|
|
||||||
let total_handshake_body_len = (buf.len() - length_pos - 3) as u32;
|
let total_handshake_body_len = (buf.len() - length_pos - 3) as u32;
|
||||||
let len_bytes = total_handshake_body_len.to_be_bytes();
|
let len_bytes = total_handshake_body_len.to_be_bytes();
|
||||||
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
|
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
|
||||||
|
|||||||
@@ -25,49 +25,41 @@ impl BrowserProfile {
|
|||||||
delegated_signatures: TlsSignatures::BROWSER_STANDARD,
|
delegated_signatures: TlsSignatures::BROWSER_STANDARD,
|
||||||
versions: TlsVersions::TLS_13_ONLY,
|
versions: TlsVersions::TLS_13_ONLY,
|
||||||
|
|
||||||
// 0x0301 (TLS 1.0) — классика для ClientHello в исполнении Chrome
|
|
||||||
record_layer_version: ProtocolVersion::Tls10,
|
record_layer_version: ProtocolVersion::Tls10,
|
||||||
|
|
||||||
cipher_suites: &[
|
cipher_suites: &[
|
||||||
0x1301, 0x1302, 0x1303, // TLS 1.3 Suites
|
0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, 0xc02c, 0xc030, 0xcca9, 0xcca8,
|
||||||
0xc02b, 0xc02f, 0xc02c, 0xc030, // ECDHE-RSA/ECDSA
|
|
||||||
0xcca9, 0xcca8, // ChaCha20
|
|
||||||
],
|
],
|
||||||
|
|
||||||
alpn: &["h2", "http/1.1"],
|
alpn: &["h2", "http/1.1"],
|
||||||
extension_order: ExtensionOrder::CHROMIUM_131,
|
extension_order: ExtensionOrder::CHROMIUM_131,
|
||||||
|
|
||||||
// --- НОВЫЕ ПОЛЯ ДЛЯ ГИБКОСТИ ---
|
|
||||||
is_chromium: true,
|
is_chromium: true,
|
||||||
has_grease: true,
|
has_grease: true,
|
||||||
|
|
||||||
// Chrome активно использует ALPS для ускорения HTTP/2 настроек
|
|
||||||
alps_protocols: &["h2"],
|
alps_protocols: &["h2"],
|
||||||
|
|
||||||
// Chrome старается сделать ClientHello "пухлым",
|
|
||||||
// чтобы избежать проблем с некоторыми старыми Middleboxes
|
|
||||||
target_padding_len: 512,
|
target_padding_len: 512,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const FIREFOX_130: Self = Self {
|
pub const FIREFOX_130: Self = Self {
|
||||||
name: "Firefox 130 (Windows)",
|
name: "Firefox 130 (Windows)",
|
||||||
groups: TlsGroups::MODERN, // У FF свой набор предпочтений
|
groups: TlsGroups::MODERN,
|
||||||
signatures: TlsSignatures::BROWSER_STANDARD,
|
signatures: TlsSignatures::BROWSER_STANDARD,
|
||||||
delegated_signatures: TlsSignatures::BROWSER_STANDARD,
|
delegated_signatures: TlsSignatures::BROWSER_STANDARD,
|
||||||
versions: TlsVersions::MODERN, // FF часто шлет 1.3 + 1.2
|
versions: TlsVersions::MODERN,
|
||||||
|
|
||||||
// Firefox обычно пишет 0x0303 (TLS 1.2) в Record Layer
|
|
||||||
record_layer_version: ProtocolVersion::Tls12,
|
record_layer_version: ProtocolVersion::Tls12,
|
||||||
|
|
||||||
cipher_suites: &[0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, 0xc02c, 0xc030],
|
cipher_suites: &[0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, 0xc02c, 0xc030],
|
||||||
|
|
||||||
alpn: &["h2", "http/1.1"],
|
alpn: &["h2", "http/1.1"],
|
||||||
extension_order: ExtensionOrder::EDGE_130, // Тут стоит подставить личный порядок FF
|
extension_order: ExtensionOrder::EDGE_130,
|
||||||
|
|
||||||
is_chromium: false,
|
is_chromium: false,
|
||||||
has_grease: false, // Firefox НЕ использует GREASE
|
has_grease: false,
|
||||||
alps_protocols: &[], // Firefox пока не так активно внедряет ALPS
|
alps_protocols: &[],
|
||||||
target_padding_len: 0, // Firefox обычно не делает принудительный Padding до 512
|
target_padding_len: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,8 +67,7 @@ pub struct ServerProfile {
|
|||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
pub versions: TlsVersions,
|
pub versions: TlsVersions,
|
||||||
|
|
||||||
// ДОБАВЛЯЕМ:
|
pub record_layer_version: ProtocolVersion,
|
||||||
pub record_layer_version: ProtocolVersion, // Что сервер пишет в ответном Record
|
|
||||||
|
|
||||||
pub cipher_suites: &'static [u16],
|
pub cipher_suites: &'static [u16],
|
||||||
pub groups: TlsGroups,
|
pub groups: TlsGroups,
|
||||||
@@ -91,7 +82,6 @@ impl ServerProfile {
|
|||||||
name: "Modern-Secure",
|
name: "Modern-Secure",
|
||||||
versions: TlsVersions::MODERN,
|
versions: TlsVersions::MODERN,
|
||||||
|
|
||||||
// Сервера обычно отвечают 0x0303 (TLS 1.2) в Record Layer
|
|
||||||
record_layer_version: ProtocolVersion::Tls12,
|
record_layer_version: ProtocolVersion::Tls12,
|
||||||
|
|
||||||
cipher_suites: &[0x1301, 0x1302, 0x1303],
|
cipher_suites: &[0x1301, 0x1302, 0x1303],
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ impl TlsExtensions {
|
|||||||
if (id & 0x0f0f) != 0x0a0a {
|
if (id & 0x0f0f) != 0x0a0a {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Убеждаемся, что оба байта идентичны (0x1A1A, а не 0x1A2A)
|
|
||||||
(id & 0xff) == (id >> 8)
|
(id & 0xff) == (id >> 8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ impl Logger {
|
|||||||
registry.with(fmt_layer)
|
registry.with(fmt_layer)
|
||||||
};
|
};
|
||||||
|
|
||||||
// 4. Инициализируем подписчик
|
|
||||||
registry.init();
|
registry.init();
|
||||||
|
|
||||||
let _ = LOGGER.set(Logger {
|
let _ = LOGGER.set(Logger {
|
||||||
|
|||||||
Reference in New Issue
Block a user