#![allow(dead_code)]
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Once, OnceLock};
use std::time::Duration;
use rusnel::common::remote::RemoteRequest;
use rusnel::common::tls::{ClientTlsConfig, ServerTlsConfig};
use rusnel::{ClientConfig, ReconnectConfig, ServerConfig, ServerEndpoint};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
pub const TEST_TIMEOUT: Duration = Duration::from_secs(20);
pub const STARTUP_DELAY: Duration = Duration::from_millis(500);
static INIT: Once = Once::new();
pub fn init_crypto() {
INIT.call_once(|| {
rustls::crypto::ring::default_provider()
.install_default()
.expect("Failed to install rustls crypto provider");
});
}
const PORT_RANGE_START: u16 = 40_000;
const PORT_RANGE_SPAN: u16 = 20_000;
static PORT_OFFSET: AtomicU32 = AtomicU32::new(0);
static PORT_BASE: OnceLock<u32> = OnceLock::new();
fn next_port_candidate() -> u16 {
let base = *PORT_BASE.get_or_init(|| (std::process::id() % PORT_RANGE_SPAN as u32));
let off = PORT_OFFSET.fetch_add(1, Ordering::Relaxed);
PORT_RANGE_START + ((base + off) % PORT_RANGE_SPAN as u32) as u16
}
pub fn get_available_port() -> u16 {
loop {
let port = next_port_candidate();
if std::net::TcpListener::bind(("127.0.0.1", port)).is_ok() {
return port;
}
}
}
pub fn get_available_udp_port() -> u16 {
loop {
let port = next_port_candidate();
if std::net::UdpSocket::bind(("127.0.0.1", port)).is_ok() {
return port;
}
}
}
pub fn server_config(port: u16, allow_reverse: bool) -> ServerConfig {
server_config_with_tls(port, allow_reverse, ServerTlsConfig::Insecure)
}
pub fn server_config_with_tls(
port: u16,
allow_reverse: bool,
tls: ServerTlsConfig,
) -> ServerConfig {
ServerConfig {
host: IpAddr::V4(Ipv4Addr::LOCALHOST),
port,
allow_reverse,
tls,
congestion: Default::default(),
max_connections: None,
}
}
pub fn client_config(server_port: u16, remotes: Vec<RemoteRequest>) -> ClientConfig {
client_config_with_tls(server_port, remotes, ClientTlsConfig::Insecure)
}
pub fn client_config_with_tls(
server_port: u16,
remotes: Vec<RemoteRequest>,
tls: ClientTlsConfig,
) -> ClientConfig {
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), server_port);
ClientConfig {
server: ServerEndpoint {
addrs: vec![addr],
host: addr.ip().to_string(),
},
remotes,
tls,
congestion: Default::default(),
reconnect: ReconnectConfig::default(),
}
}
pub struct TestEnv {
pub server_handle: tokio::task::JoinHandle<()>,
pub client_handle: tokio::task::JoinHandle<()>,
}
impl Drop for TestEnv {
fn drop(&mut self) {
self.server_handle.abort();
self.client_handle.abort();
}
}
pub async fn start_tunnel(
server_port: u16,
allow_reverse: bool,
remotes: Vec<RemoteRequest>,
) -> TestEnv {
init_crypto();
let sc = server_config(server_port, allow_reverse);
let server_handle = tokio::spawn(async move {
let _ = rusnel::server::run_async(sc).await;
});
tokio::time::sleep(STARTUP_DELAY).await;
let cc = client_config(server_port, remotes);
let client_handle = tokio::spawn(async move {
let _ = rusnel::client::run_async(cc).await;
});
tokio::time::sleep(STARTUP_DELAY).await;
TestEnv {
server_handle,
client_handle,
}
}
pub async fn socks5_connect_ipv4(
socks_addr: &str,
target_ip: [u8; 4],
target_port: u16,
) -> TcpStream {
let mut conn = TcpStream::connect(socks_addr).await.unwrap();
conn.write_all(&[0x05, 0x01, 0x00]).await.unwrap();
let mut greet_resp = [0u8; 2];
conn.read_exact(&mut greet_resp).await.unwrap();
assert_eq!(greet_resp, [0x05, 0x00]);
let mut req = vec![
0x05, 0x01, 0x00, 0x01, target_ip[0],
target_ip[1],
target_ip[2],
target_ip[3],
];
req.extend_from_slice(&target_port.to_be_bytes());
conn.write_all(&req).await.unwrap();
let mut reply = [0u8; 10];
conn.read_exact(&mut reply).await.unwrap();
assert_eq!(reply[0], 0x05, "SOCKS reply version");
assert_eq!(reply[1], 0x00, "SOCKS reply status (0x00 = success)");
conn
}
pub async fn socks5_connect_domain(
socks_addr: &str,
target_domain: &str,
target_port: u16,
) -> TcpStream {
let mut conn = TcpStream::connect(socks_addr).await.unwrap();
conn.write_all(&[0x05, 0x01, 0x00]).await.unwrap();
let mut greet_resp = [0u8; 2];
conn.read_exact(&mut greet_resp).await.unwrap();
assert_eq!(greet_resp, [0x05, 0x00]);
let domain_bytes = target_domain.as_bytes();
assert!(domain_bytes.len() <= u8::MAX as usize);
let mut req = vec![
0x05,
0x01,
0x00,
0x03, domain_bytes.len() as u8,
];
req.extend_from_slice(domain_bytes);
req.extend_from_slice(&target_port.to_be_bytes());
conn.write_all(&req).await.unwrap();
let mut reply = [0u8; 10];
conn.read_exact(&mut reply).await.unwrap();
assert_eq!(reply[0], 0x05);
assert_eq!(reply[1], 0x00);
conn
}