diff --git a/src/network.rs b/src/network.rs index c1b22ba..bc10e44 100644 --- a/src/network.rs +++ b/src/network.rs @@ -6,7 +6,7 @@ pub mod standard; pub mod stream; use crate::messages::ServerResponseStatus; -pub use crate::network::address::{SOCKSv5Address, ToSOCKSAddress}; +pub use crate::network::address::SOCKSv5Address; pub use crate::network::standard::Builtin; use async_trait::async_trait; use futures::{AsyncRead, AsyncWrite}; @@ -19,17 +19,17 @@ pub trait Network { type UdpSocket; type Error: fmt::Debug + fmt::Display + Into; - async fn connect( + async fn connect>( &mut self, addr: A, port: u16, ) -> Result; - async fn udp_socket( + async fn udp_socket>( &mut self, addr: A, port: Option, ) -> Result; - async fn listen( + async fn listen>( &mut self, addr: A, port: Option, diff --git a/src/network/address.rs b/src/network/address.rs index f30db52..153cfb0 100644 --- a/src/network/address.rs +++ b/src/network/address.rs @@ -10,13 +10,11 @@ use futures::io::Cursor; use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; #[cfg(test)] use quickcheck::{quickcheck, Arbitrary, Gen}; +use std::convert::TryFrom; use std::fmt; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::pin::Pin; - -pub trait ToSOCKSAddress: Send { - fn to_socks_address(&self) -> SOCKSv5Address; -} +use thiserror::Error; #[derive(Clone, Debug, Eq, PartialEq)] pub enum SOCKSv5Address { @@ -25,42 +23,83 @@ pub enum SOCKSv5Address { Name(String), } -impl ToSOCKSAddress for SOCKSv5Address { - fn to_socks_address(&self) -> SOCKSv5Address { - self.clone() - } +#[derive(Error, Debug, PartialEq)] +pub enum AddressConversionError { + #[error("Couldn't convert IPv4 address into destination type")] + CouldntConvertIP4, + #[error("Couldn't convert IPv6 address into destination type")] + CouldntConvertIP6, + #[error("Couldn't convert name into destination type")] + CouldntConvertName, } -impl ToSOCKSAddress for IpAddr { - fn to_socks_address(&self) -> SOCKSv5Address { - match self { - IpAddr::V4(a) => SOCKSv5Address::IP4(*a), - IpAddr::V6(a) => SOCKSv5Address::IP6(*a), + +impl From for SOCKSv5Address { + fn from(x: IpAddr) -> SOCKSv5Address { + match x { + IpAddr::V4(a) => SOCKSv5Address::IP4(a), + IpAddr::V6(a) => SOCKSv5Address::IP6(a), } } } -impl ToSOCKSAddress for Ipv4Addr { - fn to_socks_address(&self) -> SOCKSv5Address { - SOCKSv5Address::IP4(*self) +impl TryFrom for IpAddr { + type Error = AddressConversionError; + + fn try_from(value: SOCKSv5Address) -> Result { + match value { + SOCKSv5Address::IP4(a) => Ok(IpAddr::V4(a)), + SOCKSv5Address::IP6(a) => Ok(IpAddr::V6(a)), + SOCKSv5Address::Name(_) => Err(AddressConversionError::CouldntConvertName), + } } } -impl ToSOCKSAddress for Ipv6Addr { - fn to_socks_address(&self) -> SOCKSv5Address { - SOCKSv5Address::IP6(*self) +impl From for SOCKSv5Address { + fn from(x: Ipv4Addr) -> Self { + SOCKSv5Address::IP4(x) } } -impl ToSOCKSAddress for String { - fn to_socks_address(&self) -> SOCKSv5Address { - SOCKSv5Address::Name(self.clone()) +impl TryFrom for Ipv4Addr { + type Error = AddressConversionError; + + fn try_from(value: SOCKSv5Address) -> Result { + match value { + SOCKSv5Address::IP4(a) => Ok(a), + SOCKSv5Address::IP6(_) => Err(AddressConversionError::CouldntConvertIP6), + SOCKSv5Address::Name(_) => Err(AddressConversionError::CouldntConvertName), + } } } -impl<'a> ToSOCKSAddress for &'a str { - fn to_socks_address(&self) -> SOCKSv5Address { - SOCKSv5Address::Name(self.to_string()) +impl From for SOCKSv5Address { + fn from(x: Ipv6Addr) -> Self { + SOCKSv5Address::IP6(x) + } +} + +impl TryFrom for Ipv6Addr { + type Error = AddressConversionError; + + fn try_from(value: SOCKSv5Address) -> Result { + match value { + SOCKSv5Address::IP4(_) => Err(AddressConversionError::CouldntConvertIP4), + SOCKSv5Address::IP6(a) => Ok(a), + SOCKSv5Address::Name(_) => Err(AddressConversionError::CouldntConvertName), + } + } +} + +impl From for SOCKSv5Address { + fn from(x: String) -> Self { + SOCKSv5Address::Name(x) + } +} + +impl<'a> From<&'a str> for SOCKSv5Address { + fn from(x: &str) -> SOCKSv5Address { + SOCKSv5Address::Name(x.to_string()) } } @@ -74,15 +113,6 @@ impl fmt::Display for SOCKSv5Address { } } -impl From for SOCKSv5Address { - fn from(addr: IpAddr) -> SOCKSv5Address { - match addr { - IpAddr::V4(a) => SOCKSv5Address::IP4(a), - IpAddr::V6(a) => SOCKSv5Address::IP6(a), - } - } -} - impl SOCKSv5Address { pub async fn read( mut r: Pin<&mut R>, @@ -156,3 +186,65 @@ impl Arbitrary for SOCKSv5Address { } standard_roundtrip!(address_roundtrips, SOCKSv5Address); + +#[cfg(test)] +quickcheck! { + fn ip_conversion(x: IpAddr) -> bool { + match x { + IpAddr::V4(ref a) => + assert_eq!(Err(AddressConversionError::CouldntConvertIP4), + Ipv6Addr::try_from(SOCKSv5Address::from(a.clone()))), + IpAddr::V6(ref a) => + assert_eq!(Err(AddressConversionError::CouldntConvertIP6), + Ipv4Addr::try_from(SOCKSv5Address::from(a.clone()))), + } + x == IpAddr::try_from(SOCKSv5Address::from(x.clone())).unwrap() + } + + fn ip4_conversion(x: Ipv4Addr) -> bool { + x == Ipv4Addr::try_from(SOCKSv5Address::from(x.clone())).unwrap() + } + + fn ip6_conversion(x: Ipv6Addr) -> bool { + x == Ipv6Addr::try_from(SOCKSv5Address::from(x.clone())).unwrap() + } + + fn display_matches(x: SOCKSv5Address) -> bool { + match x { + SOCKSv5Address::IP4(a) => format!("{}", a) == format!("{}", x), + SOCKSv5Address::IP6(a) => format!("{}", a) == format!("{}", x), + SOCKSv5Address::Name(ref a) => format!("{}", a) == format!("{}", x), + } + } + + fn bad_read_key(x: u8) -> bool { + match x { + 1 => true, + 3 => true, + 4 => true, + _ => { + let buffer = [x, 0, 1, 2, 9, 10]; + let mut cursor = Cursor::new(buffer); + let meh = SOCKSv5Address::read(Pin::new(&mut cursor)); + Err(DeserializationError::InvalidAddressType(x)) == task::block_on(meh) + } + } + } +} + +#[test] +fn domain_name_sanity() { + let name = "uhsure.com"; + let strname = name.to_string(); + + let addr1 = SOCKSv5Address::from(name); + let addr2 = SOCKSv5Address::from(strname); + + assert_eq!(addr1, addr2); + assert_eq!(Err(AddressConversionError::CouldntConvertName), + IpAddr::try_from(addr1.clone())); + assert_eq!(Err(AddressConversionError::CouldntConvertName), + Ipv4Addr::try_from(addr1.clone())); + assert_eq!(Err(AddressConversionError::CouldntConvertName), + Ipv6Addr::try_from(addr1.clone())); +} \ No newline at end of file diff --git a/src/network/generic.rs b/src/network/generic.rs index 00e2a64..8d87e84 100644 --- a/src/network/generic.rs +++ b/src/network/generic.rs @@ -1,11 +1,12 @@ use crate::messages::ServerResponseStatus; -use crate::network::address::ToSOCKSAddress; +use crate::network::address::SOCKSv5Address; use crate::network::datagram::GenericDatagramSocket; use crate::network::listener::GenericListener; use crate::network::stream::GenericStream; use async_trait::async_trait; use std::fmt::Display; + #[async_trait] pub trait Networklike { /// The error type for things that fail on this network. Apologies in advance @@ -19,7 +20,7 @@ pub trait Networklike { /// may be exactly what you're using. However, in order to support tunnelling /// scenarios (i.e., using another proxy, going through Tor or SSH, etc.) we /// work generically over any stream-like object. - async fn connect( + async fn connect>( &mut self, addr: A, port: u16, @@ -27,7 +28,7 @@ pub trait Networklike { /// Listen for connections on the given address and port, returning a generic /// listener socket to use in the future. - async fn listen( + async fn listen>( &mut self, addr: A, port: u16, @@ -40,7 +41,7 @@ pub trait Networklike { /// /// Recall when using these functions that datagram protocols allow for packet /// loss and out-of-order delivery. So ... be warned. - async fn bind( + async fn bind>( &mut self, addr: A, port: u16, diff --git a/src/network/standard.rs b/src/network/standard.rs index 8eae7de..2fc9628 100644 --- a/src/network/standard.rs +++ b/src/network/standard.rs @@ -1,5 +1,5 @@ use crate::messages::ServerResponseStatus; -use crate::network::address::{SOCKSv5Address, ToSOCKSAddress}; +use crate::network::address::SOCKSv5Address; use crate::network::datagram::{Datagramlike, GenericDatagramSocket}; use crate::network::generic::Networklike; use crate::network::listener::{GenericListener, Listenerlike}; @@ -75,12 +75,12 @@ impl Datagramlike for UdpSocket { impl Networklike for Builtin { type Error = io::Error; - async fn connect( + async fn connect>( &mut self, addr: A, port: u16, ) -> Result { - let target = addr.to_socks_address(); + let target = addr.into(); let base_stream = match target { SOCKSv5Address::IP4(a) => TcpStream::connect((a, port)).await?, @@ -91,12 +91,12 @@ impl Networklike for Builtin { Ok(GenericStream::from(base_stream)) } - async fn listen( + async fn listen>( &mut self, addr: A, port: u16, ) -> Result, Self::Error> { - let target = addr.to_socks_address(); + let target = addr.into(); let base_stream = match target { SOCKSv5Address::IP4(a) => TcpListener::bind((a, port)).await?, @@ -109,12 +109,12 @@ impl Networklike for Builtin { }) } - async fn bind( + async fn bind>( &mut self, addr: A, port: u16, ) -> Result, Self::Error> { - let target = addr.to_socks_address(); + let target = addr.into(); let base_socket = match target { SOCKSv5Address::IP4(a) => UdpSocket::bind((a, port)).await?, @@ -158,7 +158,7 @@ impl From for ServerResponseStatus { // addr: A, // port: u16, // ) -> Result { -// let target = addr.to_socks_address(); +// let target = addr.into(); // // match target { // SOCKSv5Address::IP4(a) => TcpStream::connect((a, port)).await, @@ -172,7 +172,7 @@ impl From for ServerResponseStatus { // addr: A, // port: Option, // ) -> Result { -// let me = addr.to_socks_address(); +// let me = addr.into(); // let real_port = port.unwrap_or(0); // // match me { @@ -187,7 +187,7 @@ impl From for ServerResponseStatus { // addr: A, // port: Option, // ) -> Result { -// let me = addr.to_socks_address(); +// let me = addr.into(); // let real_port = port.unwrap_or(0); // // match me {