use crate::{ error::Error, tun2proxy::{ ConnectionInfo, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection, OutgoingDataEvent, OutgoingDirection, TcpProxy, }, }; use smoltcp::wire::IpProtocol; use socks5_impl::protocol::{self, handshake, password_method, Address, AuthMethod, StreamOperation, UserKey, Version}; use std::{collections::VecDeque, net::SocketAddr}; #[derive(Eq, PartialEq, Debug)] #[allow(dead_code)] enum SocksState { ClientHello, ServerHello, SendAuthData, ReceiveAuthResponse, SendRequest, ReceiveResponse, Established, } struct SocksProxyImpl { info: ConnectionInfo, state: SocksState, client_inbuf: VecDeque, server_inbuf: VecDeque, client_outbuf: VecDeque, server_outbuf: VecDeque, data_buf: VecDeque, version: Version, credentials: Option, } impl SocksProxyImpl { fn new(info: &ConnectionInfo, credentials: Option, version: Version) -> Result { let mut result = Self { info: info.clone(), state: SocksState::ServerHello, client_inbuf: VecDeque::default(), server_inbuf: VecDeque::default(), client_outbuf: VecDeque::default(), server_outbuf: VecDeque::default(), data_buf: VecDeque::default(), version, credentials, }; result.send_client_hello()?; Ok(result) } fn send_client_hello_socks4(&mut self) -> Result<(), Error> { let credentials = &self.credentials; self.server_outbuf .extend(&[self.version as u8, protocol::Command::Connect.into()]); self.server_outbuf.extend(self.info.dst.port().to_be_bytes()); let mut ip_vec = Vec::::new(); let mut name_vec = Vec::::new(); match &self.info.dst { Address::SocketAddress(SocketAddr::V4(addr)) => { ip_vec.extend(addr.ip().octets().as_ref()); } Address::SocketAddress(SocketAddr::V6(_)) => { return Err("SOCKS4 does not support IPv6".into()); } Address::DomainAddress(host, _) => { ip_vec.extend(&[0, 0, 0, host.len() as u8]); name_vec.extend(host.as_bytes()); name_vec.push(0); } } self.server_outbuf.extend(ip_vec); if let Some(credentials) = credentials { self.server_outbuf.extend(credentials.username.as_bytes()); if !credentials.password.is_empty() { self.server_outbuf.push_back(b':'); self.server_outbuf.extend(credentials.password.as_bytes()); } } self.server_outbuf.push_back(0); self.server_outbuf.extend(name_vec); Ok(()) } fn send_client_hello_socks5(&mut self) -> Result<(), Error> { let credentials = &self.credentials; // Providing unassigned methods is supposed to bypass China's GFW. // For details, refer to https://github.com/blechschmidt/tun2proxy/issues/35. let mut methods = vec![AuthMethod::NoAuth, AuthMethod::from(4_u8), AuthMethod::from(100_u8)]; if credentials.is_some() { methods.push(AuthMethod::UserPass); } handshake::Request::new(methods).write_to_stream(&mut self.server_outbuf)?; Ok(()) } fn send_client_hello(&mut self) -> Result<(), Error> { match self.version { Version::V4 => { self.send_client_hello_socks4()?; } Version::V5 => { self.send_client_hello_socks5()?; } } self.state = SocksState::ServerHello; Ok(()) } fn receive_server_hello_socks4(&mut self) -> Result<(), Error> { if self.server_inbuf.len() < 8 { return Ok(()); } if self.server_inbuf[1] != 0x5a { return Err("SOCKS4 server replied with an unexpected reply code.".into()); } self.server_inbuf.drain(0..8); self.server_outbuf.append(&mut self.data_buf); self.data_buf.clear(); self.state = SocksState::Established; self.state_change() } fn receive_server_hello_socks5(&mut self) -> Result<(), Error> { let response = handshake::Response::retrieve_from_stream(&mut self.server_inbuf.clone()); if let Err(e) = &response { if e.kind() == std::io::ErrorKind::UnexpectedEof { log::trace!("receive_server_hello_socks5 needs more data \"{}\"...", e); return Ok(()); } else { return Err(e.to_string().into()); } } let respones = response?; self.server_inbuf.drain(0..respones.len()); let auth_method = respones.method; if auth_method != AuthMethod::NoAuth && self.credentials.is_none() || (auth_method != AuthMethod::NoAuth && auth_method != AuthMethod::UserPass) && self.credentials.is_some() { return Err("SOCKS5 server requires an unsupported authentication method.".into()); } if auth_method == AuthMethod::UserPass { self.state = SocksState::SendAuthData; } else { self.state = SocksState::SendRequest; } self.state_change() } fn receive_server_hello(&mut self) -> Result<(), Error> { match self.version { Version::V4 => self.receive_server_hello_socks4(), Version::V5 => self.receive_server_hello_socks5(), } } fn send_auth_data(&mut self) -> Result<(), Error> { let tmp = UserKey::default(); let credentials = self.credentials.as_ref().unwrap_or(&tmp); let request = password_method::Request::new(&credentials.username, &credentials.password); request.write_to_stream(&mut self.server_outbuf)?; self.state = SocksState::ReceiveAuthResponse; self.state_change() } fn receive_auth_data(&mut self) -> Result<(), Error> { use password_method::Response; let response = Response::retrieve_from_stream(&mut self.server_inbuf.clone()); if let Err(e) = &response { if e.kind() == std::io::ErrorKind::UnexpectedEof { log::trace!("receive_auth_data needs more data \"{}\"...", e); return Ok(()); } else { return Err(e.to_string().into()); } } let response = response?; self.server_inbuf.drain(0..response.len()); if response.status != password_method::Status::Succeeded { return Err(format!("SOCKS authentication failed: {:?}", response.status).into()); } self.state = SocksState::SendRequest; self.state_change() } fn receive_connection_status(&mut self) -> Result<(), Error> { let response = protocol::Response::retrieve_from_stream(&mut self.server_inbuf.clone()); if let Err(e) = &response { if e.kind() == std::io::ErrorKind::UnexpectedEof { log::trace!("receive_connection_status needs more data \"{}\"...", e); return Ok(()); } else { return Err(e.to_string().into()); } } let response = response?; self.server_inbuf.drain(0..response.len()); if response.reply != protocol::Reply::Succeeded { return Err(format!("SOCKS connection failed: {}", response.reply).into()); } self.server_outbuf.append(&mut self.data_buf); self.data_buf.clear(); self.state = SocksState::Established; self.state_change() } fn send_request_socks5(&mut self) -> Result<(), Error> { // self.server_outbuf.extend(&[self.version as u8, self.command as u8, 0]); protocol::Request::new(protocol::Command::Connect, self.info.dst.clone()) .write_to_stream(&mut self.server_outbuf)?; self.state = SocksState::ReceiveResponse; self.state_change() } fn relay_traffic(&mut self) -> Result<(), Error> { self.client_outbuf.extend(self.server_inbuf.iter()); self.server_outbuf.extend(self.client_inbuf.iter()); self.server_inbuf.clear(); self.client_inbuf.clear(); Ok(()) } fn state_change(&mut self) -> Result<(), Error> { match self.state { SocksState::ServerHello => self.receive_server_hello(), SocksState::SendAuthData => self.send_auth_data(), SocksState::ReceiveAuthResponse => self.receive_auth_data(), SocksState::SendRequest => self.send_request_socks5(), SocksState::ReceiveResponse => self.receive_connection_status(), SocksState::Established => self.relay_traffic(), _ => Ok(()), } } } impl TcpProxy for SocksProxyImpl { fn push_data(&mut self, event: IncomingDataEvent<'_>) -> Result<(), Error> { let direction = event.direction; let buffer = event.buffer; match direction { IncomingDirection::FromServer => { self.server_inbuf.extend(buffer.iter()); } IncomingDirection::FromClient => { if self.state == SocksState::Established { self.client_inbuf.extend(buffer.iter()); } else { self.data_buf.extend(buffer.iter()); } } } self.state_change() } fn consume_data(&mut self, dir: OutgoingDirection, size: usize) { let buffer = if dir == OutgoingDirection::ToServer { &mut self.server_outbuf } else { &mut self.client_outbuf }; buffer.drain(0..size); } fn peek_data(&mut self, dir: OutgoingDirection) -> OutgoingDataEvent { let buffer = if dir == OutgoingDirection::ToServer { &mut self.server_outbuf } else { &mut self.client_outbuf }; OutgoingDataEvent { direction: dir, buffer: buffer.make_contiguous(), } } fn connection_established(&self) -> bool { self.state == SocksState::Established } fn have_data(&mut self, dir: Direction) -> bool { match dir { Direction::Incoming(incoming) => match incoming { IncomingDirection::FromServer => !self.server_inbuf.is_empty(), IncomingDirection::FromClient => !self.client_inbuf.is_empty() || !self.data_buf.is_empty(), }, Direction::Outgoing(outgoing) => match outgoing { OutgoingDirection::ToServer => !self.server_outbuf.is_empty(), OutgoingDirection::ToClient => !self.client_outbuf.is_empty(), }, } } fn reset_connection(&self) -> bool { false } } pub(crate) struct SocksProxyManager { server: SocketAddr, credentials: Option, version: Version, } impl ConnectionManager for SocksProxyManager { fn handles_connection(&self, info: &ConnectionInfo) -> bool { info.protocol == IpProtocol::Tcp } fn new_tcp_proxy(&self, info: &ConnectionInfo) -> Result, Error> { if info.protocol != IpProtocol::Tcp { return Err("Invalid protocol".into()); } Ok(Box::new(SocksProxyImpl::new( info, self.credentials.clone(), self.version, )?)) } fn close_connection(&self, _: &ConnectionInfo) {} fn get_server_addr(&self) -> SocketAddr { self.server } fn get_credentials(&self) -> &Option { &self.credentials } } impl SocksProxyManager { pub(crate) fn new(server: SocketAddr, version: Version, credentials: Option) -> Self { Self { server, credentials, version, } } }