buffer size changes and dns adds block

This commit is contained in:
2026-03-17 16:21:05 +07:00
parent 25d7e8b290
commit 530568d27a
13 changed files with 89025 additions and 117 deletions
+140 -27
View File
@@ -1,35 +1,148 @@
use crate::connections::ip_store::FakeIpStore;
use hickory_proto::op::{Message, MessageType, Query, ResponseCode};
use hickory_proto::op::{Message, MessageType, ResponseCode};
use hickory_proto::rr::{RData, Record, RecordType};
use netrunner_logger::{debug, error, info, warn};
use std::collections::HashSet;
use std::fs::{self, File};
use std::io::{BufRead, BufReader, Write};
use std::path::Path;
use std::time::{Duration, SystemTime};
pub fn handle_dns_query(data: &[u8], store: &mut FakeIpStore) -> Option<Vec<u8>> {
let request = Message::from_vec(data).ok()?;
let query = request.queries().first()?;
pub struct DnsHandler {
block_list: HashSet<String>,
forbidden_suffixes: Vec<String>,
cache_path: String,
}
let mut response = Message::new();
response
.set_id(request.id())
.set_message_type(MessageType::Response)
.set_recursion_available(true)
.set_response_code(ResponseCode::NoError)
.add_query(query.clone());
match query.query_type() {
RecordType::A => {
let name = query.name().to_string().trim_end_matches('.').to_string();
let fake_ip = store.get_or_assign(&name);
let record = Record::from_rdata(query.name().clone(), 1, RData::A(fake_ip.into()));
response.add_answer(record);
}
RecordType::AAAA => {
response.set_response_code(ResponseCode::NoError);
}
_ => {
response.set_response_code(ResponseCode::NoError);
impl DnsHandler {
pub fn new() -> Self {
Self {
block_list: HashSet::new(),
forbidden_suffixes: vec![
".lan".to_string(),
".local".to_string(),
".home".to_string(),
".arpa".to_string(),
],
cache_path: "hosts_cache.txt".to_string(),
}
}
response.to_vec().ok()
/// Основной метод инициализации: решает, качать или брать из кэша
pub fn init(&mut self) -> anyhow::Result<()> {
let url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts";
let path = Path::new(&self.cache_path);
let should_download = if path.exists() {
let metadata = fs::metadata(path)?;
let last_modified = metadata.modified()?;
let elapsed = SystemTime::now().duration_since(last_modified)?.as_secs();
elapsed > 604800
} else {
true
};
if should_download {
info!("Blocklist is outdated or missing. Trying to download...");
if let Err(e) = self.download_and_cache(url) {
warn!(
"Failed to download blocklist ({}), trying to use cache if available",
e
);
}
}
if path.exists() {
self.load_from_file()?;
} else {
error!("No blocklist available (neither online nor cache)!");
}
Ok(())
}
fn download_and_cache(&self, url: &str) -> anyhow::Result<()> {
let mut response = reqwest::blocking::get(url)?;
if !response.status().is_success() {
return Err(anyhow::anyhow!(
"Server returned status {}",
response.status()
));
}
let mut file = File::create(&self.cache_path)?;
response.copy_to(&mut file)?;
info!("Blocklist downloaded and cached locally.");
Ok(())
}
fn load_from_file(&mut self) -> anyhow::Result<()> {
let file = File::open(&self.cache_path)?;
let reader = BufReader::new(file);
let mut count = 0;
for line in reader.lines() {
let line = line?;
if line.starts_with('#') || line.trim().is_empty() {
continue;
}
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2 {
// Берем домен (второй столбец в StevenBlack hosts)
self.block_list.insert(parts[1].to_lowercase());
count += 1;
}
}
info!("Loaded {} domains from cache.", count);
Ok(())
}
pub fn handle_query(&self, data: &[u8], store: &mut FakeIpStore) -> Option<Vec<u8>> {
let request = Message::from_vec(data).ok()?;
let query = request.queries().first()?;
let mut response = Message::new();
response
.set_id(request.id())
.set_message_type(MessageType::Response)
.set_recursion_available(true)
.add_query(query.clone());
let name = query
.name()
.to_string()
.trim_end_matches('.')
.to_lowercase();
// 1. Спец. суффиксы
if self.forbidden_suffixes.iter().any(|s| name.ends_with(s)) {
info!(domain = %name, "DNS: Blocked (Suffix)");
response.set_response_code(ResponseCode::NXDomain);
return response.to_vec().ok();
}
// 2. Бан-лист (AdBlock)
if self.block_list.contains(&name) {
info!(domain = %name, "DNS: Blocked (AdList)");
response.set_response_code(ResponseCode::NXDomain);
return response.to_vec().ok();
}
// 3. Fake IP Logic
match query.query_type() {
RecordType::A => {
let fake_ip = store.get_or_assign(&name);
let record = Record::from_rdata(query.name().clone(), 60, RData::A(fake_ip.into()));
response.set_response_code(ResponseCode::NoError);
response.add_answer(record);
}
_ => {
response.set_response_code(ResponseCode::NoError);
}
}
response.to_vec().ok()
}
}
+8 -4
View File
@@ -1,11 +1,15 @@
use crate::connections::dns::handle_dns_query;
use crate::connections::dns::DnsHandler; // проверь путь
use crate::connections::ip_store::FakeIpStore;
use smoltcp::socket::udp;
pub struct UdpConnection;
impl UdpConnection {
pub fn process_incoming(socket: &mut udp::Socket, store: &mut FakeIpStore) {
pub fn process_incoming(
socket: &mut udp::Socket,
store: &mut FakeIpStore,
dns_handler: &DnsHandler,
) {
while socket.can_recv() {
let (data, metadata) = match socket.recv() {
Ok(res) => res,
@@ -14,8 +18,8 @@ impl UdpConnection {
let endpoint = metadata.endpoint;
if let Some(response) = handle_dns_query(&data, store) {
netrunner_logger::debug!(to = %endpoint, "Sending DNS response");
if let Some(response) = dns_handler.handle_query(&data, store) {
netrunner_logger::debug!(to = %endpoint, "Sending DNS response (filtered)");
let _ = socket.send_slice(&response, metadata);
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
uniffi::setup_scaffolding!();
mod connections;
pub mod connections;
pub mod tun;
use netrunner_logger::info;
use std::sync::OnceLock;
+81 -57
View File
@@ -1,80 +1,104 @@
use std::net::Ipv4Addr;
use netrunner_client::tun::{
engine::Engine,
routing::{reset_platform_routing, setup_platform_routing},
tun::Tun,
use netrunner_client::{
connections::dns::DnsHandler,
tun::{
engine::Engine,
routing::{reset_platform_routing, setup_platform_routing},
tun::Tun,
},
};
use netrunner_core::proxy::{connection::connection::ConnectionRole, network::Network};
use netrunner_logger::{error, info};
use smoltcp::{iface::Config, phy::DeviceCapabilities};
use tokio_util::sync::CancellationToken;
#[tokio::main]
async fn main() {
// Убираем макрос #[tokio::main]
fn main() -> anyhow::Result<()> {
netrunner_logger::Logger::init();
info!("Initializing NetRunner Stack...");
let tun_device = Tun::create(|config| {
config
.tun_name("netr0")
.address((10, 0, 0, 1))
.netmask((255, 255, 255, 0))
.destination((10, 0, 0, 2))
.up();
})
.expect("Failed to initialize TUN device");
let remote_address: String = "62.60.244.156:443".into();
setup_platform_routing(&remote_address);
info!("TUN interface is UP: 10.0.0.1/24");
// 1. СИНХРОННАЯ ЧАСТЬ
// Здесь reqwest::blocking может спокойно создавать и удалять свои ресурсы
let mut dns_handler = DnsHandler::new();
if let Err(e) = dns_handler.init() {
error!("Failed to initialize DNS blocklist: {}", e);
}
let config = Config::new(smoltcp::wire::HardwareAddress::Ip);
// 2. АСИНХРОННАЯ ЧАСТЬ
// Создаем рантайм вручную
let rt = tokio::runtime::Runtime::new()?;
let mut caps = DeviceCapabilities::default();
// Передаем управление в асинхронный блок
rt.block_on(async move {
let tun_device = Tun::create(|config| {
config
.tun_name("netr0")
.address((10, 0, 0, 1))
.netmask((255, 255, 255, 0))
.destination((10, 0, 0, 2))
.up();
})
.expect("Failed to initialize TUN device");
caps.max_transmission_unit = 1280;
caps.medium = smoltcp::phy::Medium::Ip;
let remote_address: String = "62.60.244.156:443".into();
setup_platform_routing(&remote_address);
let network = Network::new(
"0.0.0.0".into(),
8080,
ConnectionRole::Client,
Some(remote_address.clone()),
);
info!("TUN interface is UP: 10.0.0.1/24");
let proxy_ip = network.get_self_local_address();
let config = Config::new(smoltcp::wire::HardwareAddress::Ip);
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1280;
caps.medium = smoltcp::phy::Medium::Ip;
tokio::spawn(async move {
info!("Network thread started");
network.run(CancellationToken::new()).await;
});
let network = Network::new(
"0.0.0.0".into(),
8080,
ConnectionRole::Client,
Some(remote_address.clone()),
);
let mut engine = Engine::new(config, caps, proxy_ip);
engine.set_any_ip(true);
engine.set_transparent_mode();
engine.set_default_gateway(Ipv4Addr::new(10, 0, 0, 2));
engine.activate();
info!("Stack IP initialized: 10.0.0.2");
let proxy_ip = network.get_self_local_address();
info!("Engine starting process loop...");
// Запускаем сетевой поток
let network_token = CancellationToken::new();
tokio::spawn(async move {
info!("Network thread started");
network.run(network_token).await;
});
let ctrl_c = tokio::signal::ctrl_c();
// Создаем Engine и передаем уже готовый dns_handler
let mut engine = Engine::new(config, caps, proxy_ip, dns_handler);
engine.set_any_ip(true);
engine.set_transparent_mode();
engine.set_default_gateway(Ipv4Addr::new(10, 0, 0, 2));
engine.activate();
tokio::select! {
res = engine.run(tun_device) => {
error!("Engine loop error: {:?}", res);
},
_ = ctrl_c => {
info!("Ctrl+C received, shutting down...");
info!("Stack IP initialized: 10.0.0.2");
info!("Engine starting process loop...");
let ctrl_c = tokio::signal::ctrl_c();
tokio::select! {
res = engine.run(tun_device) => {
error!("Engine loop error: {:?}", res);
},
_ = ctrl_c => {
info!("Ctrl+C received, shutting down...");
}
}
}
info!("Restoring system routing...");
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
let proxy_ip = addr.ip().to_string();
if let Err(e) = reset_platform_routing(Some(&proxy_ip)) {
error!("Failed to reset routing: {}", e);
} else {
info!("System routing restored successfully.");
}
// Очистка при выходе
info!("Restoring system routing...");
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
let p_ip = addr.ip().to_string();
if let Err(e) = reset_platform_routing(Some(&p_ip)) {
error!("Failed to reset routing: {}", e);
} else {
info!("System routing restored successfully.");
}
Ok::<(), anyhow::Error>(())
})?;
Ok(())
}
+7 -1
View File
@@ -6,6 +6,7 @@ pub mod mobile;
use crate::{
RUNTIME, Session,
connections::dns::DnsHandler,
tun::{
engine::Engine,
routing::{reset_platform_routing, setup_platform_routing},
@@ -103,11 +104,16 @@ impl SessionManager {
);
let proxy_ip = network.get_self_local_address();
let mut dns_handler = DnsHandler::new();
if let Err(e) = dns_handler.init() {
error!("Failed to initialize DNS blocklist: {}", e);
}
tokio::spawn(async move {
network.run(net_token).await;
});
let mut engine = Engine::new(config, caps, proxy_ip);
let mut engine = Engine::new(config, caps, proxy_ip, dns_handler);
engine.set_any_ip(true);
engine.set_transparent_mode();
engine.set_default_gateway(Ipv4Addr::new(10, 0, 0, 2));
+13 -12
View File
@@ -1,38 +1,39 @@
use netrunner_core::protocol::codec::socks::TargetAddress;
use netrunner_logger::{debug, info, warn};
use netrunner_logger::{debug, error, info, warn};
use smoltcp::{
iface::{SocketHandle, SocketSet},
socket::{AnySocket, icmp, tcp, udp},
wire::{IpAddress, IpListenEndpoint},
wire::IpListenEndpoint,
};
use std::{
collections::HashMap,
time::{Duration, Instant as StdInstant},
};
use crate::{
connections::{
ip_store::FakeIpStore, tcp_connection::TcpConnection, udp_connection::UdpConnection,
},
tun::engine::START_TIME,
use crate::connections::{
dns::DnsHandler, ip_store::FakeIpStore, tcp_connection::TcpConnection,
udp_connection::UdpConnection,
};
pub struct ConnectionManager {
last_activity: HashMap<SocketHandle, StdInstant>,
active_tcp_sessions: HashMap<SocketHandle, TcpConnection>,
active_udp_sessions: HashMap<SocketHandle, UdpConnection>,
dns_handler: DnsHandler,
fake_ip_store: FakeIpStore,
proxy_ip: String,
failed_until: HashMap<SocketHandle, StdInstant>,
}
impl ConnectionManager {
pub fn new(ip: String) -> Self {
pub fn new(ip: String, dns_handler: DnsHandler) -> Self {
Self {
last_activity: HashMap::new(),
active_tcp_sessions: HashMap::new(),
active_udp_sessions: HashMap::new(),
proxy_ip: ip,
fake_ip_store: FakeIpStore::new(),
failed_until: HashMap::new(),
dns_handler, // Просто сохраняем готовый объект
}
}
pub fn start_listening(&mut self, socket_set: &mut SocketSet) {
@@ -159,7 +160,7 @@ impl ConnectionManager {
fn handle_udp(&mut self, handle: SocketHandle, socket: &mut udp::Socket) {
self.last_activity.insert(handle, StdInstant::now());
UdpConnection::process_incoming(socket, &mut self.fake_ip_store);
UdpConnection::process_incoming(socket, &mut self.fake_ip_store, &self.dns_handler);
}
fn handle_icmp(&mut self, handle: SocketHandle, socket: &mut icmp::Socket) {
@@ -174,7 +175,7 @@ impl ConnectionManager {
}
fn create_tcp_socket<'a>() -> tcp::Socket<'a> {
const BUF_SIZE: usize = 16384;
const BUF_SIZE: usize = 65536;
tcp::Socket::new(
tcp::SocketBuffer::new(vec![0; BUF_SIZE]),
tcp::SocketBuffer::new(vec![0; BUF_SIZE]),
@@ -182,7 +183,7 @@ impl ConnectionManager {
}
fn create_udp_socket<'a>() -> udp::Socket<'a> {
const BUF_SIZE: usize = 16384;
const BUF_SIZE: usize = 32768;
udp::Socket::new(
udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY; 16], vec![0; BUF_SIZE]),
udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY; 16], vec![0; BUF_SIZE]),
+9 -3
View File
@@ -18,6 +18,7 @@ use tun::{DeviceReader, DeviceWriter};
use netrunner_logger::{debug, info, warn};
use crate::connections::dns::{self, DnsHandler};
use crate::tun::connection_manager::ConnectionManager;
use crate::tun::device::{TokenBuffer, VirtTunDevice};
use crate::tun::tun::Tun;
@@ -34,13 +35,18 @@ pub struct Engine {
}
impl Engine {
pub fn new(config: Config, caps: DeviceCapabilities, ip: String) -> Self {
pub fn new(
config: Config,
caps: DeviceCapabilities,
ip: String,
dns_handler: DnsHandler,
) -> Self {
let now = Engine::current_time();
let (mut device, bridge_rx, bridge_tx, avail) = VirtTunDevice::new(caps);
let interface = Interface::new(config, &mut device, now);
let socket_set = ConnectionManager::setup_sockets(128, 8, 4);
let manager = ConnectionManager::new(ip);
let socket_set = ConnectionManager::setup_sockets(128, 128, 4);
let manager = ConnectionManager::new(ip, dns_handler);
Self {
interface,
socket_set,