diff --git a/src/client.rs b/src/client.rs index 4245297..76c718c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,10 +1,15 @@ use crate::errors::{DeserializationError, SerializationError}; use crate::messages::{ - AuthenticationMethod, ClientGreeting, ClientUsernamePassword, ServerAuthResponse, ServerChoice, - ServerResponseStatus, + AuthenticationMethod, ClientConnectionCommand, ClientConnectionRequest, ClientGreeting, + ClientUsernamePassword, ServerAuthResponse, ServerChoice, ServerResponse, ServerResponseStatus, }; +use crate::network::datagram::GenericDatagramSocket; use crate::network::generic::Networklike; +use crate::network::listener::GenericListener; +use crate::network::stream::GenericStream; +use crate::network::SOCKSv5Address; use async_std::io; +use async_trait::async_trait; use futures::io::{AsyncRead, AsyncWrite}; use log::{trace, warn}; use thiserror::Error; @@ -27,13 +32,22 @@ pub enum SOCKSv5Error { ConnectionError(#[from] io::Error), } +impl From for ServerResponseStatus { + fn from(x: SOCKSv5Error) -> Self { + match x { + SOCKSv5Error::ServerFailure(v) => v, + _ => ServerResponseStatus::GeneralFailure, + } + } +} + pub struct SOCKSv5Client where - S: AsyncRead + AsyncWrite, - N: Networklike, + S: AsyncRead + AsyncWrite + Sync, + N: Networklike + Sync, { - _network: N, - _stream: S, + network: N, + stream: S, } pub struct LoginInfo { @@ -62,12 +76,12 @@ pub struct UsernamePassword { impl SOCKSv5Client where - S: AsyncRead + AsyncWrite + Send + Unpin, - N: Networklike, + S: AsyncRead + AsyncWrite + Send + Unpin + Sync, + N: Networklike + Sync, { /// Create a new SOCKSv5 client connection over the given steam, using the given /// authentication information. - pub async fn new(_network: N, mut stream: S, login: &LoginInfo) -> Result { + pub async fn new(network: N, mut stream: S, login: &LoginInfo) -> Result { let acceptable_methods = login.acceptable_methods(); trace!( "Computed acceptable methods -- {:?} -- sending client greeting.", @@ -113,8 +127,63 @@ where trace!("Returning new SOCKSv5Client object!"); Ok(SOCKSv5Client { - _network, - _stream: stream, + network, + stream, }) } } + +#[async_trait] +impl Networklike for SOCKSv5Client +where + S: AsyncRead + AsyncWrite + Send + Unpin + Sync, + N: Networklike + Sync + Send, +{ + type Error = SOCKSv5Error; + + async fn connect>( + &mut self, + addr: A, + port: u16, + ) -> Result { + let request = ClientConnectionRequest { + command_code: ClientConnectionCommand::EstablishTCPStream, + destination_address: addr.into(), + destination_port: port, + }; + + request.write(&mut self.stream).await?; + + let response = ServerResponse::read(&mut self.stream).await?; + + if response.status == ServerResponseStatus::RequestGranted { + self.network + .connect(response.bound_address, response.bound_port) + .await + .map_err(|e| { + SOCKSv5Error::ConnectionError(io::Error::new( + io::ErrorKind::Other, + format!("{}", e), + )) + }) + } else { + Err(SOCKSv5Error::ServerFailure(response.status)) + } + } + + async fn listen>( + &mut self, + _addr: A, + _port: u16, + ) -> Result, Self::Error> { + unimplemented!() + } + + async fn bind>( + &mut self, + _addr: A, + _port: u16, + ) -> Result, Self::Error> { + unimplemented!() + } +} diff --git a/src/lib.rs b/src/lib.rs index 83db050..fdf8c80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,9 +9,12 @@ pub mod server; mod test { use crate::client::{LoginInfo, SOCKSv5Client, UsernamePassword}; use crate::network::generic::Networklike; + use crate::network::listener::Listenerlike; use crate::network::testing::TestingStack; use crate::server::{SOCKSv5Server, SecurityParameters}; + use async_std::io::prelude::WriteExt; use async_std::task; + use futures::AsyncReadExt; #[test] fn unrestricted_login() { @@ -127,4 +130,43 @@ mod test { assert!(client.is_err()); }) } + + #[test] + fn establish_stream() { + task::block_on(async { + let mut network_stack = TestingStack::default(); + + let target_port = network_stack.listen("localhost", 1337).await.unwrap(); + + // generate the server + let security_parameters = SecurityParameters::unrestricted(); + let default_port = network_stack.listen("localhost", 9999).await.unwrap(); + let server = + SOCKSv5Server::new(network_stack.clone(), security_parameters, default_port); + + let _server_task = task::spawn(async move { server.run().await }); + + let stream = network_stack.connect("localhost", 9999).await.unwrap(); + let login_info = LoginInfo { + username_password: None, + }; + + let mut client = SOCKSv5Client::new(network_stack, stream, &login_info) + .await + .unwrap(); + + task::spawn(async move { + let mut conn = client.connect("localhost", 1337).await.unwrap(); + conn.write_all(&[1, 3, 3, 7, 9]).await.unwrap(); + }); + + let (mut target_connection, _, _) = target_port.accept().await.unwrap(); + let mut read_buffer = [0; 4]; + target_connection + .read_exact(&mut read_buffer) + .await + .unwrap(); + assert_eq!(read_buffer, [1, 3, 3, 7]); + }) + } }