Just to have a chance to try it out: Switch to proptest.
This commit is contained in:
@@ -12,6 +12,10 @@ async-std = { version = "1.9.0", features = ["attributes"] }
|
|||||||
async-trait = "0.1.50"
|
async-trait = "0.1.50"
|
||||||
futures = "0.3.15"
|
futures = "0.3.15"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
quickcheck = "1.0.3"
|
proptest = "1.0.0"
|
||||||
simplelog = "0.10.0"
|
simplelog = "0.10.0"
|
||||||
thiserror = "1.0.24"
|
thiserror = "1.0.24"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
proptest = "1.0.0"
|
||||||
|
proptest-derive = "0.3.0"
|
||||||
@@ -5,8 +5,11 @@ use async_std::task;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use futures::io::Cursor;
|
use futures::io::Cursor;
|
||||||
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||||
|
use proptest::proptest;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use quickcheck::{quickcheck, Arbitrary, Gen};
|
use proptest::prelude::{Arbitrary, Just, Strategy, prop_oneof};
|
||||||
|
#[cfg(test)]
|
||||||
|
use proptest::strategy::BoxedStrategy;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
@@ -45,6 +48,31 @@ impl fmt::Display for AuthenticationMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl Arbitrary for AuthenticationMethod {
|
||||||
|
type Parameters = ();
|
||||||
|
type Strategy = BoxedStrategy<Self>;
|
||||||
|
|
||||||
|
fn arbitrary_with(_args: Self::Parameters) -> BoxedStrategy<Self> {
|
||||||
|
prop_oneof![
|
||||||
|
Just(AuthenticationMethod::None),
|
||||||
|
Just(AuthenticationMethod::GSSAPI),
|
||||||
|
Just(AuthenticationMethod::UsernameAndPassword),
|
||||||
|
Just(AuthenticationMethod::ChallengeHandshake),
|
||||||
|
Just(AuthenticationMethod::ChallengeResponse),
|
||||||
|
Just(AuthenticationMethod::SSL),
|
||||||
|
Just(AuthenticationMethod::NDS),
|
||||||
|
Just(AuthenticationMethod::MultiAuthenticationFramework),
|
||||||
|
Just(AuthenticationMethod::JSONPropertyBlock),
|
||||||
|
Just(AuthenticationMethod::NoAcceptableMethods),
|
||||||
|
|
||||||
|
(0x80u8..=0xfe).prop_map(AuthenticationMethod::PrivateMethod),
|
||||||
|
].boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl AuthenticationMethod {
|
impl AuthenticationMethod {
|
||||||
pub async fn read<R: AsyncRead + Send + Unpin>(
|
pub async fn read<R: AsyncRead + Send + Unpin>(
|
||||||
r: &mut R,
|
r: &mut R,
|
||||||
@@ -94,28 +122,6 @@ impl AuthenticationMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl Arbitrary for AuthenticationMethod {
|
|
||||||
fn arbitrary(g: &mut Gen) -> AuthenticationMethod {
|
|
||||||
let mut vals = vec![
|
|
||||||
AuthenticationMethod::None,
|
|
||||||
AuthenticationMethod::GSSAPI,
|
|
||||||
AuthenticationMethod::UsernameAndPassword,
|
|
||||||
AuthenticationMethod::ChallengeHandshake,
|
|
||||||
AuthenticationMethod::ChallengeResponse,
|
|
||||||
AuthenticationMethod::SSL,
|
|
||||||
AuthenticationMethod::NDS,
|
|
||||||
AuthenticationMethod::MultiAuthenticationFramework,
|
|
||||||
AuthenticationMethod::JSONPropertyBlock,
|
|
||||||
AuthenticationMethod::NoAcceptableMethods,
|
|
||||||
];
|
|
||||||
for x in 0x80..0xffu8 {
|
|
||||||
vals.push(AuthenticationMethod::PrivateMethod(x));
|
|
||||||
}
|
|
||||||
g.choose(&vals).unwrap().clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
standard_roundtrip!(auth_byte_roundtrips, AuthenticationMethod);
|
standard_roundtrip!(auth_byte_roundtrips, AuthenticationMethod);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -10,12 +10,14 @@ use async_std::task;
|
|||||||
use futures::io::Cursor;
|
use futures::io::Cursor;
|
||||||
use futures::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
|
use futures::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
use proptest::proptest;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use quickcheck::{quickcheck, Arbitrary, Gen};
|
use proptest_derive::Arbitrary;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
pub enum ClientConnectionCommand {
|
pub enum ClientConnectionCommand {
|
||||||
EstablishTCPStream,
|
EstablishTCPStream,
|
||||||
EstablishTCPPortBinding,
|
EstablishTCPPortBinding,
|
||||||
@@ -23,6 +25,7 @@ pub enum ClientConnectionCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
pub struct ClientConnectionRequest {
|
pub struct ClientConnectionRequest {
|
||||||
pub command_code: ClientConnectionCommand,
|
pub command_code: ClientConnectionCommand,
|
||||||
pub destination_address: SOCKSv5Address,
|
pub destination_address: SOCKSv5Address,
|
||||||
@@ -89,33 +92,6 @@ impl ClientConnectionRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl Arbitrary for ClientConnectionCommand {
|
|
||||||
fn arbitrary(g: &mut Gen) -> ClientConnectionCommand {
|
|
||||||
let options = [
|
|
||||||
ClientConnectionCommand::EstablishTCPStream,
|
|
||||||
ClientConnectionCommand::EstablishTCPPortBinding,
|
|
||||||
ClientConnectionCommand::AssociateUDPPort,
|
|
||||||
];
|
|
||||||
*g.choose(&options).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl Arbitrary for ClientConnectionRequest {
|
|
||||||
fn arbitrary(g: &mut Gen) -> Self {
|
|
||||||
let command_code = ClientConnectionCommand::arbitrary(g);
|
|
||||||
let destination_address = SOCKSv5Address::arbitrary(g);
|
|
||||||
let destination_port = u16::arbitrary(g);
|
|
||||||
|
|
||||||
ClientConnectionRequest {
|
|
||||||
command_code,
|
|
||||||
destination_address,
|
|
||||||
destination_port,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
standard_roundtrip!(client_request_roundtrips, ClientConnectionRequest);
|
standard_roundtrip!(client_request_roundtrips, ClientConnectionRequest);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ use async_std::task;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use futures::io::Cursor;
|
use futures::io::Cursor;
|
||||||
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||||
|
use proptest::proptest;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use quickcheck::{quickcheck, Arbitrary, Gen};
|
use proptest_derive::Arbitrary;
|
||||||
|
|
||||||
/// Client greetings are the first message sent in a SOCKSv5 session. They
|
/// Client greetings are the first message sent in a SOCKSv5 session. They
|
||||||
/// identify that there's a client that wants to talk to a server, and that
|
/// identify that there's a client that wants to talk to a server, and that
|
||||||
@@ -17,6 +18,7 @@ use quickcheck::{quickcheck, Arbitrary, Gen};
|
|||||||
/// said server. (It feels weird that the offer/choice goes this way instead
|
/// said server. (It feels weird that the offer/choice goes this way instead
|
||||||
/// of the reverse, but whatever.)
|
/// of the reverse, but whatever.)
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
pub struct ClientGreeting {
|
pub struct ClientGreeting {
|
||||||
pub acceptable_methods: Vec<AuthenticationMethod>,
|
pub acceptable_methods: Vec<AuthenticationMethod>,
|
||||||
}
|
}
|
||||||
@@ -68,20 +70,6 @@ impl ClientGreeting {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl Arbitrary for ClientGreeting {
|
|
||||||
fn arbitrary(g: &mut Gen) -> ClientGreeting {
|
|
||||||
let amt = u8::arbitrary(g);
|
|
||||||
let mut acceptable_methods = Vec::with_capacity(amt as usize);
|
|
||||||
|
|
||||||
for _ in 0..amt {
|
|
||||||
acceptable_methods.push(AuthenticationMethod::arbitrary(g));
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientGreeting { acceptable_methods }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
standard_roundtrip!(client_greeting_roundtrips, ClientGreeting);
|
standard_roundtrip!(client_greeting_roundtrips, ClientGreeting);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
use crate::errors::{DeserializationError, SerializationError};
|
use crate::errors::{DeserializationError, SerializationError};
|
||||||
#[cfg(test)]
|
|
||||||
use crate::messages::utils::arbitrary_socks_string;
|
|
||||||
use crate::serialize::{read_string, write_string};
|
use crate::serialize::{read_string, write_string};
|
||||||
use crate::standard_roundtrip;
|
use crate::standard_roundtrip;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -9,7 +7,10 @@ use async_std::task;
|
|||||||
use futures::io::Cursor;
|
use futures::io::Cursor;
|
||||||
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use quickcheck::{quickcheck, Arbitrary, Gen};
|
use proptest::prelude::{Arbitrary, BoxedStrategy};
|
||||||
|
use proptest::proptest;
|
||||||
|
#[cfg(test)]
|
||||||
|
use proptest::strategy::Strategy;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct ClientUsernamePassword {
|
pub struct ClientUsernamePassword {
|
||||||
@@ -17,6 +18,26 @@ pub struct ClientUsernamePassword {
|
|||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
const USERNAME_REGEX: &str = "[a-zA-Z0-9~!@#$%^&*_\\-+=:;?<>]+";
|
||||||
|
#[cfg(test)]
|
||||||
|
const PASSWORD_REGEX: &str = "[a-zA-Z0-9~!@#$%^&*_\\-+=:;?<>]+";
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl Arbitrary for ClientUsernamePassword {
|
||||||
|
type Parameters = Option<u8>;
|
||||||
|
type Strategy = BoxedStrategy<Self>;
|
||||||
|
|
||||||
|
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
||||||
|
let max_len = args.unwrap_or(12) as usize;
|
||||||
|
(USERNAME_REGEX, PASSWORD_REGEX).prop_map(move |(mut username, mut password)| {
|
||||||
|
username.shrink_to(max_len);
|
||||||
|
password.shrink_to(max_len);
|
||||||
|
ClientUsernamePassword { username, password }
|
||||||
|
}).boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ClientUsernamePassword {
|
impl ClientUsernamePassword {
|
||||||
pub async fn read<R: AsyncRead + Send + Unpin>(
|
pub async fn read<R: AsyncRead + Send + Unpin>(
|
||||||
r: &mut R,
|
r: &mut R,
|
||||||
@@ -47,16 +68,6 @@ impl ClientUsernamePassword {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl Arbitrary for ClientUsernamePassword {
|
|
||||||
fn arbitrary(g: &mut Gen) -> Self {
|
|
||||||
let username = arbitrary_socks_string(g);
|
|
||||||
let password = arbitrary_socks_string(g);
|
|
||||||
|
|
||||||
ClientUsernamePassword { username, password }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
standard_roundtrip!(username_password_roundtrips, ClientUsernamePassword);
|
standard_roundtrip!(username_password_roundtrips, ClientUsernamePassword);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ use async_std::task;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use futures::io::Cursor;
|
use futures::io::Cursor;
|
||||||
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||||
|
use proptest::proptest;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use quickcheck::{quickcheck, Arbitrary, Gen};
|
use proptest_derive::Arbitrary;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
pub struct ServerAuthResponse {
|
pub struct ServerAuthResponse {
|
||||||
pub success: bool,
|
pub success: bool,
|
||||||
}
|
}
|
||||||
@@ -55,14 +57,6 @@ impl ServerAuthResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl Arbitrary for ServerAuthResponse {
|
|
||||||
fn arbitrary(g: &mut Gen) -> ServerAuthResponse {
|
|
||||||
let success = bool::arbitrary(g);
|
|
||||||
ServerAuthResponse { success }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
standard_roundtrip!(server_auth_response, ServerAuthResponse);
|
standard_roundtrip!(server_auth_response, ServerAuthResponse);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ use async_std::task;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use futures::io::Cursor;
|
use futures::io::Cursor;
|
||||||
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||||
|
use proptest::proptest;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use quickcheck::{quickcheck, Arbitrary, Gen};
|
use proptest_derive::Arbitrary;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
pub struct ServerChoice {
|
pub struct ServerChoice {
|
||||||
pub chosen_method: AuthenticationMethod,
|
pub chosen_method: AuthenticationMethod,
|
||||||
}
|
}
|
||||||
@@ -56,15 +58,6 @@ impl ServerChoice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl Arbitrary for ServerChoice {
|
|
||||||
fn arbitrary(g: &mut Gen) -> ServerChoice {
|
|
||||||
ServerChoice {
|
|
||||||
chosen_method: AuthenticationMethod::arbitrary(g),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
standard_roundtrip!(server_choice_roundtrips, ServerChoice);
|
standard_roundtrip!(server_choice_roundtrips, ServerChoice);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -11,12 +11,14 @@ use async_std::task;
|
|||||||
use futures::io::Cursor;
|
use futures::io::Cursor;
|
||||||
use futures::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
|
use futures::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
use proptest::proptest;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use quickcheck::{quickcheck, Arbitrary, Gen};
|
use proptest_derive::Arbitrary;
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Error, PartialEq)]
|
#[derive(Clone, Debug, Eq, Error, PartialEq)]
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
pub enum ServerResponseStatus {
|
pub enum ServerResponseStatus {
|
||||||
#[error("Actually, everything's fine (weird to see this in an error)")]
|
#[error("Actually, everything's fine (weird to see this in an error)")]
|
||||||
RequestGranted,
|
RequestGranted,
|
||||||
@@ -45,6 +47,7 @@ impl IntoErrorResponse for ServerResponseStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
pub struct ServerResponse {
|
pub struct ServerResponse {
|
||||||
pub status: ServerResponseStatus,
|
pub status: ServerResponseStatus,
|
||||||
pub bound_address: SOCKSv5Address,
|
pub bound_address: SOCKSv5Address,
|
||||||
@@ -128,39 +131,6 @@ impl ServerResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl Arbitrary for ServerResponseStatus {
|
|
||||||
fn arbitrary(g: &mut Gen) -> ServerResponseStatus {
|
|
||||||
let options = [
|
|
||||||
ServerResponseStatus::RequestGranted,
|
|
||||||
ServerResponseStatus::GeneralFailure,
|
|
||||||
ServerResponseStatus::ConnectionNotAllowedByRule,
|
|
||||||
ServerResponseStatus::NetworkUnreachable,
|
|
||||||
ServerResponseStatus::HostUnreachable,
|
|
||||||
ServerResponseStatus::ConnectionRefused,
|
|
||||||
ServerResponseStatus::TTLExpired,
|
|
||||||
ServerResponseStatus::CommandNotSupported,
|
|
||||||
ServerResponseStatus::AddressTypeNotSupported,
|
|
||||||
];
|
|
||||||
g.choose(&options).unwrap().clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl Arbitrary for ServerResponse {
|
|
||||||
fn arbitrary(g: &mut Gen) -> Self {
|
|
||||||
let status = ServerResponseStatus::arbitrary(g);
|
|
||||||
let bound_address = SOCKSv5Address::arbitrary(g);
|
|
||||||
let bound_port = u16::arbitrary(g);
|
|
||||||
|
|
||||||
ServerResponse {
|
|
||||||
status,
|
|
||||||
bound_address,
|
|
||||||
bound_port,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
standard_roundtrip!(server_response_roundtrips, ServerResponse);
|
standard_roundtrip!(server_response_roundtrips, ServerResponse);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1,32 +1,15 @@
|
|||||||
#[cfg(test)]
|
|
||||||
use quickcheck::{Arbitrary, Gen};
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub fn arbitrary_socks_string(g: &mut Gen) -> String {
|
|
||||||
loop {
|
|
||||||
let mut potential = String::arbitrary(g);
|
|
||||||
|
|
||||||
potential.truncate(255);
|
|
||||||
let bytestring = potential.as_bytes();
|
|
||||||
|
|
||||||
if !bytestring.is_empty() && bytestring.len() < 256 {
|
|
||||||
return potential;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! standard_roundtrip {
|
macro_rules! standard_roundtrip {
|
||||||
($name: ident, $t: ty) => {
|
($name: ident, $t: ty) => {
|
||||||
#[cfg(test)]
|
proptest! {
|
||||||
quickcheck! {
|
#[test]
|
||||||
fn $name(xs: $t) -> bool {
|
fn $name(xs: $t) {
|
||||||
let mut buffer = vec![];
|
let mut buffer = vec![];
|
||||||
task::block_on(xs.write(&mut buffer)).unwrap();
|
task::block_on(xs.write(&mut buffer)).unwrap();
|
||||||
let mut cursor = Cursor::new(buffer);
|
let mut cursor = Cursor::new(buffer);
|
||||||
let ys = <$t>::read(&mut cursor);
|
let ys = <$t>::read(&mut cursor);
|
||||||
xs == task::block_on(ys).unwrap()
|
assert_eq!(xs, task::block_on(ys).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
use crate::errors::{DeserializationError, SerializationError};
|
use crate::errors::{DeserializationError, SerializationError};
|
||||||
#[cfg(test)]
|
|
||||||
use crate::messages::utils::arbitrary_socks_string;
|
|
||||||
use crate::serialize::{read_amt, read_string, write_string};
|
use crate::serialize::{read_amt, read_string, write_string};
|
||||||
use crate::standard_roundtrip;
|
use crate::standard_roundtrip;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -8,8 +6,9 @@ use async_std::task;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use futures::io::Cursor;
|
use futures::io::Cursor;
|
||||||
use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||||
|
use proptest::prelude::proptest;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use quickcheck::{quickcheck, Arbitrary, Gen};
|
use proptest::prelude::{Arbitrary, BoxedStrategy, Strategy, any, prop_oneof};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||||
@@ -22,6 +21,28 @@ pub enum SOCKSv5Address {
|
|||||||
Name(String),
|
Name(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
const HOSTNAME_REGEX: &str = "[a-zA-Z0-9_.]+";
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl Arbitrary for SOCKSv5Address {
|
||||||
|
type Parameters = Option<u16>;
|
||||||
|
type Strategy = BoxedStrategy<Self>;
|
||||||
|
|
||||||
|
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
||||||
|
let max_len = args.unwrap_or(32) as usize;
|
||||||
|
|
||||||
|
prop_oneof![
|
||||||
|
any::<Ipv4Addr>().prop_map(SOCKSv5Address::IP4),
|
||||||
|
any::<Ipv6Addr>().prop_map(SOCKSv5Address::IP6),
|
||||||
|
HOSTNAME_REGEX.prop_map(move |mut hostname| {
|
||||||
|
hostname.shrink_to(max_len);
|
||||||
|
SOCKSv5Address::Name(hostname)
|
||||||
|
}),
|
||||||
|
].boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq)]
|
||||||
pub enum AddressConversionError {
|
pub enum AddressConversionError {
|
||||||
#[error("Couldn't convert IPv4 address into destination type")]
|
#[error("Couldn't convert IPv4 address into destination type")]
|
||||||
@@ -170,28 +191,11 @@ pub trait HasLocalAddress {
|
|||||||
fn local_addr(&self) -> (SOCKSv5Address, u16);
|
fn local_addr(&self) -> (SOCKSv5Address, u16);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl Arbitrary for SOCKSv5Address {
|
|
||||||
fn arbitrary(g: &mut Gen) -> Self {
|
|
||||||
let ip4 = Ipv4Addr::arbitrary(g);
|
|
||||||
let ip6 = Ipv6Addr::arbitrary(g);
|
|
||||||
let nm = arbitrary_socks_string(g);
|
|
||||||
|
|
||||||
g.choose(&[
|
|
||||||
SOCKSv5Address::IP4(ip4),
|
|
||||||
SOCKSv5Address::IP6(ip6),
|
|
||||||
SOCKSv5Address::Name(nm),
|
|
||||||
])
|
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
standard_roundtrip!(address_roundtrips, SOCKSv5Address);
|
standard_roundtrip!(address_roundtrips, SOCKSv5Address);
|
||||||
|
|
||||||
#[cfg(test)]
|
proptest! {
|
||||||
quickcheck! {
|
#[test]
|
||||||
fn ip_conversion(x: IpAddr) -> bool {
|
fn ip_conversion(x: IpAddr) {
|
||||||
match x {
|
match x {
|
||||||
IpAddr::V4(ref a) =>
|
IpAddr::V4(ref a) =>
|
||||||
assert_eq!(Err(AddressConversionError::CouldntConvertIP4),
|
assert_eq!(Err(AddressConversionError::CouldntConvertIP4),
|
||||||
@@ -200,35 +204,37 @@ quickcheck! {
|
|||||||
assert_eq!(Err(AddressConversionError::CouldntConvertIP6),
|
assert_eq!(Err(AddressConversionError::CouldntConvertIP6),
|
||||||
Ipv4Addr::try_from(SOCKSv5Address::from(*a))),
|
Ipv4Addr::try_from(SOCKSv5Address::from(*a))),
|
||||||
}
|
}
|
||||||
x == IpAddr::try_from(SOCKSv5Address::from(x)).unwrap()
|
assert_eq!(x, IpAddr::try_from(SOCKSv5Address::from(x)).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ip4_conversion(x: Ipv4Addr) -> bool {
|
#[test]
|
||||||
x == Ipv4Addr::try_from(SOCKSv5Address::from(x)).unwrap()
|
fn ip4_conversion(x: Ipv4Addr) {
|
||||||
|
assert_eq!(x, Ipv4Addr::try_from(SOCKSv5Address::from(x)).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ip6_conversion(x: Ipv6Addr) -> bool {
|
#[test]
|
||||||
x == Ipv6Addr::try_from(SOCKSv5Address::from(x)).unwrap()
|
fn ip6_conversion(x: Ipv6Addr) {
|
||||||
|
assert_eq!(x, Ipv6Addr::try_from(SOCKSv5Address::from(x)).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_matches(x: SOCKSv5Address) -> bool {
|
#[test]
|
||||||
|
fn display_matches(x: SOCKSv5Address) {
|
||||||
match x {
|
match x {
|
||||||
SOCKSv5Address::IP4(a) => format!("{}", a) == format!("{}", x),
|
SOCKSv5Address::IP4(a) => assert_eq!(format!("{}", a), format!("{}", x)),
|
||||||
SOCKSv5Address::IP6(a) => format!("{}", a) == format!("{}", x),
|
SOCKSv5Address::IP6(a) => assert_eq!(format!("{}", a), format!("{}", x)),
|
||||||
SOCKSv5Address::Name(ref a) => *a == x.to_string(),
|
SOCKSv5Address::Name(ref a) => assert_eq!(*a, x.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bad_read_key(x: u8) -> bool {
|
#[test]
|
||||||
|
fn bad_read_key(x: u8) {
|
||||||
match x {
|
match x {
|
||||||
1 => true,
|
1 | 3 | 4 => {}
|
||||||
3 => true,
|
|
||||||
4 => true,
|
|
||||||
_ => {
|
_ => {
|
||||||
let buffer = [x, 0, 1, 2, 9, 10];
|
let buffer = [x, 0, 1, 2, 9, 10];
|
||||||
let mut cursor = Cursor::new(buffer);
|
let mut cursor = Cursor::new(buffer);
|
||||||
let meh = SOCKSv5Address::read(&mut cursor);
|
let meh = SOCKSv5Address::read(&mut cursor);
|
||||||
Err(DeserializationError::InvalidAddressType(x)) == task::block_on(meh)
|
assert_eq!(Err(DeserializationError::InvalidAddressType(x)), task::block_on(meh));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user