From a00f4b1a8b2e6c883d29dadb342600c718764e11 Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Mon, 24 Jul 2023 20:35:29 +0800 Subject: [PATCH] socks5 stuff --- src/socks.rs | 108 +++++++++++++++++---------------------------------- 1 file changed, 36 insertions(+), 72 deletions(-) diff --git a/src/socks.rs b/src/socks.rs index 47e755e..3c84b20 100644 --- a/src/socks.rs +++ b/src/socks.rs @@ -6,7 +6,7 @@ use crate::{ }, }; use smoltcp::wire::IpProtocol; -use socks5_impl::protocol::{self, Address, UserKey}; +use socks5_impl::protocol::{self, handshake, password_method, Address, AuthMethod, UserKey}; use std::{collections::VecDeque, net::SocketAddr, rc::Rc}; #[derive(Eq, PartialEq, Debug)] @@ -28,36 +28,6 @@ pub enum SocksVersion { V5 = 5, } -#[allow(dead_code)] -enum SocksAuthentication { - None = 0, - GssApi = 1, - Password = 2, - ChallengeHandshake = 3, - Unassigned = 4, - Unassigned100 = 100, -} - -#[allow(dead_code)] -#[repr(u8)] -#[derive(Debug, Eq, PartialEq)] -enum SocksReplies { - Succeeded, - GeneralFailure, - ConnectionDisallowed, - NetworkUnreachable, - ConnectionRefused, - TtlExpired, - CommandUnsupported, - AddressUnsupported, -} - -impl std::fmt::Display for SocksReplies { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{:?}", self) - } -} - pub(crate) struct SocksConnection { connection: Connection, state: SocksState, @@ -129,24 +99,15 @@ impl SocksConnection { SocksVersion::V5 => { // 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() { - self.server_outbuf.extend(&[ - self.version as u8, - 4u8, - SocksAuthentication::None as u8, - SocksAuthentication::Password as u8, - SocksAuthentication::Unassigned as u8, - SocksAuthentication::Unassigned100 as u8, - ]); - } else { - self.server_outbuf.extend(&[ - self.version as u8, - 3u8, - SocksAuthentication::None as u8, - SocksAuthentication::Unassigned as u8, - SocksAuthentication::Unassigned100 as u8, - ]); + methods.push(AuthMethod::UserPass); } + handshake::Request::new(methods).write_to_stream(&mut self.server_outbuf)?; } } self.state = SocksState::ServerHello; @@ -171,26 +132,26 @@ impl SocksConnection { } fn receive_server_hello_socks5(&mut self) -> Result<(), Error> { - if self.server_inbuf.len() < 2 { - return Ok(()); - } - if self.server_inbuf[0] != 5 { - return Err("SOCKS5 server replied with an unexpected version.".into()); + let response = handshake::Response::rebuild_from_stream(&mut self.server_inbuf); + if let Err(e) = &response { + if e.kind() == std::io::ErrorKind::UnexpectedEof { + log::trace!("receive_server_hello_socks5 need more data \"{}\"...", e); + return Ok(()); + } else { + return Err(e.to_string().into()); + } } + let respones = response?; + let auth_method = respones.method; - let auth_method = self.server_inbuf[1]; - - if auth_method != SocksAuthentication::None as u8 && self.credentials.is_none() - || (auth_method != SocksAuthentication::None as u8 - && auth_method != SocksAuthentication::Password as u8) + 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()); } - self.server_inbuf.drain(0..2); - - if auth_method == SocksAuthentication::Password as u8 { + if auth_method == AuthMethod::UserPass { self.state = SocksState::SendAuthData; } else { self.state = SocksState::SendRequest; @@ -208,24 +169,27 @@ impl SocksConnection { fn send_auth_data(&mut self) -> Result<(), Error> { let tmp = UserKey::default(); let credentials = self.credentials.as_ref().unwrap_or(&tmp); - self.server_outbuf - .extend(&[1u8, credentials.username.len() as u8]); - self.server_outbuf.extend(credentials.username.as_bytes()); - self.server_outbuf - .extend(&[credentials.password.len() as u8]); - self.server_outbuf.extend(credentials.password.as_bytes()); + 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> { - if self.server_inbuf.len() < 2 { - return Ok(()); + let response = password_method::Response::rebuild_from_stream(&mut self.server_inbuf); + if let Err(e) = &response { + if e.kind() == std::io::ErrorKind::UnexpectedEof { + log::trace!("receive_auth_data need more data \"{}\"...", e); + return Ok(()); + } else { + return Err(e.to_string().into()); + } } - if self.server_inbuf[0] != 1 || self.server_inbuf[1] != 0 { - return Err("SOCKS authentication failed.".into()); + assert!(self.server_inbuf.is_empty()); + let response = response?; + if response.status != password_method::Status::Succeeded { + return Err(format!("SOCKS authentication failed: {:?}", response.status).into()); } - self.server_inbuf.drain(0..2); self.state = SocksState::SendRequest; self.state_change() } @@ -234,7 +198,7 @@ impl SocksConnection { let response = protocol::Response::rebuild_from_stream(&mut self.server_inbuf); if let Err(e) = &response { if e.kind() == std::io::ErrorKind::UnexpectedEof { - log::trace!("Waiting for more data \"{}\"...", e); + log::trace!("receive_connection_status need more data \"{}\"...", e); return Ok(()); } else { return Err(e.to_string().into());