#![cfg(all(feature = "local", feature = "server"))] use std::{ net::{SocketAddr, ToSocketAddrs}, str, }; use tokio::{ io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, time::{self, Duration}, }; use shadowsocks_service::{ config::{Config, ConfigType, LocalConfig, LocalInstanceConfig, ProtocolType, ServerInstanceConfig}, local::socks::client::socks5::Socks5TcpClient, run_local, run_server, shadowsocks::{ config::{Mode, ServerAddr, ServerConfig}, crypto::CipherKind, relay::socks5::Address, }, }; pub struct Socks5TestServer { local_addr: SocketAddr, svr_config: Config, cli_config: Config, } impl Socks5TestServer { pub fn new(svr_addr: S, local_addr: L, pwd: &str, method: CipherKind, enable_udp: bool) -> Self where S: ToSocketAddrs, L: ToSocketAddrs, { let svr_addr = svr_addr.to_socket_addrs().unwrap().next().unwrap(); let local_addr = local_addr.to_socket_addrs().unwrap().next().unwrap(); Self { local_addr, svr_config: { let mut cfg = Config::new(ConfigType::Server); cfg.server = vec![ServerInstanceConfig::with_server_config( ServerConfig::new(svr_addr, pwd.to_owned(), method).unwrap(), )]; cfg.server[0] .config .set_mode(if enable_udp { Mode::TcpAndUdp } else { Mode::TcpOnly }); cfg }, cli_config: { let mut cfg = Config::new(ConfigType::Local); cfg.local = vec![LocalInstanceConfig::with_local_config(LocalConfig::new_with_addr( ServerAddr::from(local_addr), ProtocolType::Socks, ))]; cfg.local[0].config.mode = if enable_udp { Mode::TcpAndUdp } else { Mode::TcpOnly }; cfg.server = vec![ServerInstanceConfig::with_server_config( ServerConfig::new(svr_addr, pwd.to_owned(), method).unwrap(), )]; cfg }, } } pub fn client_addr(&self) -> &SocketAddr { &self.local_addr } pub async fn run(&self) { let svr_cfg = self.svr_config.clone(); tokio::spawn(run_server(svr_cfg)); let client_cfg = self.cli_config.clone(); tokio::spawn(run_local(client_cfg)); time::sleep(Duration::from_secs(1)).await; } } #[cfg(feature = "stream-cipher")] #[tokio::test] async fn socks5_relay_stream() { let _ = env_logger::try_init(); const SERVER_ADDR: &str = "127.0.0.1:8100"; const LOCAL_ADDR: &str = "127.0.0.1:8200"; const PASSWORD: &str = "test-password"; const METHOD: CipherKind = CipherKind::AES_128_CFB128; let svr = Socks5TestServer::new(SERVER_ADDR, LOCAL_ADDR, PASSWORD, METHOD, false); svr.run().await; let mut c = Socks5TcpClient::connect( Address::DomainNameAddress("www.example.com".to_owned(), 80), svr.client_addr(), ) .await .unwrap(); let req = b"GET / HTTP/1.1\r\nHost: www.example.com\r\nAccept: */*\r\nConnection: close\r\n\r\n"; c.write_all(req).await.unwrap(); c.flush().await.unwrap(); let mut r = BufReader::new(c); let mut buf = Vec::new(); r.read_until(b'\n', &mut buf).await.unwrap(); let http_status = b"HTTP/1.1 200 OK\r\n"; assert!(buf.starts_with(http_status), "buf: {:?}", str::from_utf8(&buf)); } #[tokio::test] async fn socks5_relay_aead() { let _ = env_logger::try_init(); const SERVER_ADDR: &str = "127.0.0.1:8110"; const LOCAL_ADDR: &str = "127.0.0.1:8210"; const PASSWORD: &str = "test-password"; const METHOD: CipherKind = CipherKind::AES_256_GCM; let svr = Socks5TestServer::new(SERVER_ADDR, LOCAL_ADDR, PASSWORD, METHOD, false); svr.run().await; let mut c = Socks5TcpClient::connect( Address::DomainNameAddress("detectportal.firefox.com".to_owned(), 80), svr.client_addr(), ) .await .unwrap(); let req = b"GET /success.txt HTTP/1.0\r\nHost: detectportal.firefox.com\r\nAccept: */*\r\n\r\n"; c.write_all(req).await.unwrap(); c.flush().await.unwrap(); let mut r = BufReader::new(c); let mut buf = Vec::new(); r.read_until(b'\n', &mut buf).await.unwrap(); let http_status = b"HTTP/1.0 200 OK\r\n"; assert!(buf.starts_with(http_status)); }