From ca2eddf515e760bc24670751f997c86cb03e03a8 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Mon, 5 Jul 2021 20:35:56 -0700 Subject: [PATCH] Ditch `ToSocksAddress` for the standard `From`/`TryFrom`. This induces an added `Send` in the Network trait and its implementations, but provides us the ability to use standard functions with obvious extensions. So that's nice. I've also added some additional testing to sanity check the conversions. --- src/network.rs | 8 +- src/network/address.rs | 160 +++++++++++++++++++++++++++++++--------- src/network/generic.rs | 9 ++- src/network/standard.rs | 20 ++--- 4 files changed, 145 insertions(+), 52 deletions(-) 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 {