Wire up, and add a test for, connecting via a client through a proxy server.
This commit is contained in:
@@ -1,10 +1,15 @@
|
|||||||
use crate::errors::{DeserializationError, SerializationError};
|
use crate::errors::{DeserializationError, SerializationError};
|
||||||
use crate::messages::{
|
use crate::messages::{
|
||||||
AuthenticationMethod, ClientGreeting, ClientUsernamePassword, ServerAuthResponse, ServerChoice,
|
AuthenticationMethod, ClientConnectionCommand, ClientConnectionRequest, ClientGreeting,
|
||||||
ServerResponseStatus,
|
ClientUsernamePassword, ServerAuthResponse, ServerChoice, ServerResponse, ServerResponseStatus,
|
||||||
};
|
};
|
||||||
|
use crate::network::datagram::GenericDatagramSocket;
|
||||||
use crate::network::generic::Networklike;
|
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_std::io;
|
||||||
|
use async_trait::async_trait;
|
||||||
use futures::io::{AsyncRead, AsyncWrite};
|
use futures::io::{AsyncRead, AsyncWrite};
|
||||||
use log::{trace, warn};
|
use log::{trace, warn};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@@ -27,13 +32,22 @@ pub enum SOCKSv5Error {
|
|||||||
ConnectionError(#[from] io::Error),
|
ConnectionError(#[from] io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<SOCKSv5Error> for ServerResponseStatus {
|
||||||
|
fn from(x: SOCKSv5Error) -> Self {
|
||||||
|
match x {
|
||||||
|
SOCKSv5Error::ServerFailure(v) => v,
|
||||||
|
_ => ServerResponseStatus::GeneralFailure,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SOCKSv5Client<S, N>
|
pub struct SOCKSv5Client<S, N>
|
||||||
where
|
where
|
||||||
S: AsyncRead + AsyncWrite,
|
S: AsyncRead + AsyncWrite + Sync,
|
||||||
N: Networklike,
|
N: Networklike + Sync,
|
||||||
{
|
{
|
||||||
_network: N,
|
network: N,
|
||||||
_stream: S,
|
stream: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LoginInfo {
|
pub struct LoginInfo {
|
||||||
@@ -62,12 +76,12 @@ pub struct UsernamePassword {
|
|||||||
|
|
||||||
impl<S, N> SOCKSv5Client<S, N>
|
impl<S, N> SOCKSv5Client<S, N>
|
||||||
where
|
where
|
||||||
S: AsyncRead + AsyncWrite + Send + Unpin,
|
S: AsyncRead + AsyncWrite + Send + Unpin + Sync,
|
||||||
N: Networklike,
|
N: Networklike + Sync,
|
||||||
{
|
{
|
||||||
/// Create a new SOCKSv5 client connection over the given steam, using the given
|
/// Create a new SOCKSv5 client connection over the given steam, using the given
|
||||||
/// authentication information.
|
/// authentication information.
|
||||||
pub async fn new(_network: N, mut stream: S, login: &LoginInfo) -> Result<Self, SOCKSv5Error> {
|
pub async fn new(network: N, mut stream: S, login: &LoginInfo) -> Result<Self, SOCKSv5Error> {
|
||||||
let acceptable_methods = login.acceptable_methods();
|
let acceptable_methods = login.acceptable_methods();
|
||||||
trace!(
|
trace!(
|
||||||
"Computed acceptable methods -- {:?} -- sending client greeting.",
|
"Computed acceptable methods -- {:?} -- sending client greeting.",
|
||||||
@@ -113,8 +127,63 @@ where
|
|||||||
|
|
||||||
trace!("Returning new SOCKSv5Client object!");
|
trace!("Returning new SOCKSv5Client object!");
|
||||||
Ok(SOCKSv5Client {
|
Ok(SOCKSv5Client {
|
||||||
_network,
|
network,
|
||||||
_stream: stream,
|
stream,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<S, N> Networklike for SOCKSv5Client<S, N>
|
||||||
|
where
|
||||||
|
S: AsyncRead + AsyncWrite + Send + Unpin + Sync,
|
||||||
|
N: Networklike + Sync + Send,
|
||||||
|
{
|
||||||
|
type Error = SOCKSv5Error;
|
||||||
|
|
||||||
|
async fn connect<A: Send + Into<SOCKSv5Address>>(
|
||||||
|
&mut self,
|
||||||
|
addr: A,
|
||||||
|
port: u16,
|
||||||
|
) -> Result<GenericStream, Self::Error> {
|
||||||
|
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<A: Send + Into<SOCKSv5Address>>(
|
||||||
|
&mut self,
|
||||||
|
_addr: A,
|
||||||
|
_port: u16,
|
||||||
|
) -> Result<GenericListener<Self::Error>, Self::Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn bind<A: Send + Into<SOCKSv5Address>>(
|
||||||
|
&mut self,
|
||||||
|
_addr: A,
|
||||||
|
_port: u16,
|
||||||
|
) -> Result<GenericDatagramSocket<Self::Error>, Self::Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
42
src/lib.rs
42
src/lib.rs
@@ -9,9 +9,12 @@ pub mod server;
|
|||||||
mod test {
|
mod test {
|
||||||
use crate::client::{LoginInfo, SOCKSv5Client, UsernamePassword};
|
use crate::client::{LoginInfo, SOCKSv5Client, UsernamePassword};
|
||||||
use crate::network::generic::Networklike;
|
use crate::network::generic::Networklike;
|
||||||
|
use crate::network::listener::Listenerlike;
|
||||||
use crate::network::testing::TestingStack;
|
use crate::network::testing::TestingStack;
|
||||||
use crate::server::{SOCKSv5Server, SecurityParameters};
|
use crate::server::{SOCKSv5Server, SecurityParameters};
|
||||||
|
use async_std::io::prelude::WriteExt;
|
||||||
use async_std::task;
|
use async_std::task;
|
||||||
|
use futures::AsyncReadExt;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unrestricted_login() {
|
fn unrestricted_login() {
|
||||||
@@ -127,4 +130,43 @@ mod test {
|
|||||||
assert!(client.is_err());
|
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]);
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user