#![cfg(all(feature = "local-socks4", 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::Socks4TcpClient, run_local, run_server, shadowsocks::{ config::{ServerAddr, ServerConfig}, crypto::CipherKind, }, }; pub struct Socks4TestServer { local_addr: SocketAddr, svr_config: Config, cli_config: Config, } impl Socks4TestServer { pub fn new(svr_addr: S, local_addr: L, pwd: &str, method: CipherKind) -> 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 }, 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.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; } } #[tokio::test] async fn socks4_relay_connect() { let _ = env_logger::try_init(); const SERVER_ADDR: &str = "127.0.0.1:7100"; const LOCAL_ADDR: &str = "127.0.0.1:7200"; const PASSWORD: &str = "test-password"; const METHOD: CipherKind = CipherKind::AES_128_GCM; let svr = Socks4TestServer::new(SERVER_ADDR, LOCAL_ADDR, PASSWORD, METHOD); svr.run().await; const HTTP_REQUEST: &[u8] = b"GET /success.txt HTTP/1.0\r\nHost: detectportal.firefox.com\r\nAccept: */*\r\n\r\n"; let mut c = Socks4TcpClient::connect(("detectportal.firefox.com", 80), LOCAL_ADDR, Vec::new()) .await .unwrap(); c.write_all(HTTP_REQUEST).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)); }