Compare commits

...

10 commits

Author SHA1 Message Date
Σrebe - Romain GERARD
fab129b516
chore: add tests for command line parsing
Some checks are pending
/ Build - Windows x86 (push) Waiting to run
/ Build - Windows x86_64 (push) Waiting to run
/ Build - Android aarch64 (push) Waiting to run
/ Build - Android armv7 (push) Waiting to run
/ Build - Freebsd x86 (push) Waiting to run
/ Build - Freebsd x86_64 (push) Waiting to run
/ Build - Linux aarch64 (push) Waiting to run
/ Build - Linux armv7hf (push) Waiting to run
/ Build - Linux x86 (push) Waiting to run
/ Build - Linux x86_64 (push) Waiting to run
/ Build - MacOS aarch64 (push) Waiting to run
/ Build - MacOS x86_64 (push) Waiting to run
/ Release (push) Blocked by required conditions
2024-12-15 22:52:14 +01:00
Σrebe - Romain GERARD
314c619b8b
Bump version v10.1.8 2024-12-15 09:34:40 +01:00
Σrebe - Romain GERARD
4c4d3022e9
Revert "Bump version v10.1.8"
This reverts commit 36dc795d84.
2024-12-15 09:33:57 +01:00
Σrebe - Romain GERARD
ee4d087577
bump rust version to 1.83 2024-12-15 09:29:40 +01:00
Σrebe - Romain GERARD
38180e70f6
fix(cmd_line): correctly parse port when it is 443 2024-12-15 09:27:47 +01:00
Σrebe - Romain GERARD
36dc795d84
Bump version v10.1.8 2024-12-15 08:50:28 +01:00
Σrebe - Romain GERARD
d15d8e36f0
fix(cmd_line): correctly parse port when it is 443 2024-12-15 08:49:03 +01:00
Σrebe - Romain GERARD
30cd4a72b9
fix panic on bad http path prefix 2024-12-14 21:52:15 +01:00
Erèbe - Romain Gerard
07d38cae6c
Update using_mtls.md 2024-12-14 21:24:55 +01:00
Σrebe - Romain GERARD
ef5b1b4f73
Bump version v10.1.7 2024-12-11 16:05:45 +01:00
9 changed files with 520 additions and 26 deletions

View file

@ -7,7 +7,7 @@ on:
branches: [ "main" ]
env:
RUST_VERSION: 1.80.1
RUST_VERSION: 1.83.0
BIN_NAME: "wstunnel"
jobs:

203
Cargo.lock generated
View file

@ -320,7 +320,7 @@ dependencies = [
"hyperlocal",
"log",
"pin-project-lite",
"rustls 0.23.19",
"rustls 0.23.20",
"rustls-native-certs 0.7.3",
"rustls-pemfile 2.2.0",
"rustls-pki-types",
@ -368,9 +368,9 @@ checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
[[package]]
name = "cc"
version = "1.2.3"
version = "1.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d"
checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf"
dependencies = [
"jobserver",
"libc",
@ -471,6 +471,12 @@ dependencies = [
"cc",
]
[[package]]
name = "collection_macros"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50b180e6a75e306052a61658f832b4fc565a6e5a204da05f0fe7f50a31fb827a"
[[package]]
name = "colorchoice"
version = "1.0.3"
@ -887,6 +893,12 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-timer"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
[[package]]
name = "futures-util"
version = "0.3.31"
@ -1180,7 +1192,7 @@ dependencies = [
"http 1.2.0",
"hyper",
"hyper-util",
"rustls 0.23.19",
"rustls 0.23.20",
"rustls-pki-types",
"tokio",
"tokio-rustls 0.26.1",
@ -1570,7 +1582,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.6.0",
"libc",
"redox_syscall 0.5.7",
"redox_syscall 0.5.8",
]
[[package]]
@ -1837,7 +1849,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.5.7",
"redox_syscall 0.5.8",
"smallvec",
"windows-targets 0.52.6",
]
@ -1945,6 +1957,15 @@ dependencies = [
"syn",
]
[[package]]
name = "proc-macro-crate"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.92"
@ -2023,9 +2044,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.7"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [
"bitflags 2.6.0",
]
@ -2074,6 +2095,12 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "relative-path"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
[[package]]
name = "resolv-conf"
version = "0.7.0"
@ -2099,6 +2126,36 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "rstest"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2c585be59b6b5dd66a9d2084aa1d8bd52fbdb806eafdeffb52791147862035"
dependencies = [
"futures",
"futures-timer",
"rstest_macros",
"rustc_version",
]
[[package]]
name = "rstest_macros"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "825ea780781b15345a146be27eaefb05085e337e869bff01b4306a4fd4a9ad5a"
dependencies = [
"cfg-if",
"glob",
"proc-macro-crate",
"proc-macro2",
"quote",
"regex",
"relative-path",
"rustc_version",
"syn",
"unicode-ident",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
@ -2111,6 +2168,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "rusticata-macros"
version = "4.1.0"
@ -2147,9 +2213,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.19"
version = "0.23.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1"
checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
dependencies = [
"aws-lc-rs",
"log",
@ -2218,9 +2284,9 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
version = "1.10.0"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37"
[[package]]
name = "rustls-webpki"
@ -2259,6 +2325,15 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "scc"
version = "2.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66b202022bb57c049555430e11fc22fea12909276a80a4c3d368da36ac1d88ed"
dependencies = [
"sdd",
]
[[package]]
name = "schannel"
version = "0.1.27"
@ -2284,6 +2359,12 @@ dependencies = [
"untrusted 0.9.0",
]
[[package]]
name = "sdd"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49c1eeaf4b6a87c7479688c6d52b9f1153cedd3c489300564f932b065c6eab95"
[[package]]
name = "security-framework"
version = "2.11.1"
@ -2320,6 +2401,12 @@ dependencies = [
"libc",
]
[[package]]
name = "semver"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
[[package]]
name = "serde"
version = "1.0.216"
@ -2428,6 +2515,31 @@ dependencies = [
"unsafe-libyaml",
]
[[package]]
name = "serial_test"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9"
dependencies = [
"futures",
"log",
"once_cell",
"parking_lot",
"scc",
"serial_test_derive",
]
[[package]]
name = "serial_test_derive"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "sha1"
version = "0.10.6"
@ -2584,6 +2696,39 @@ dependencies = [
"syn",
]
[[package]]
name = "test-case"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8"
dependencies = [
"test-case-macros",
]
[[package]]
name = "test-case-core"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f"
dependencies = [
"cfg-if",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "test-case-macros"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
dependencies = [
"proc-macro2",
"quote",
"syn",
"test-case-core",
]
[[package]]
name = "testcontainers"
version = "0.23.1"
@ -2756,7 +2901,7 @@ version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
dependencies = [
"rustls 0.23.19",
"rustls 0.23.20",
"tokio",
]
@ -2799,6 +2944,23 @@ dependencies = [
"tokio",
]
[[package]]
name = "toml_datetime"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
[[package]]
name = "toml_edit"
version = "0.22.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [
"indexmap 2.7.0",
"toml_datetime",
"winnow",
]
[[package]]
name = "tower-service"
version = "0.3.3"
@ -3253,6 +3415,15 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.50.0"
@ -3277,7 +3448,7 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
[[package]]
name = "wstunnel"
version = "10.1.6"
version = "10.1.8"
dependencies = [
"ahash",
"anyhow",
@ -3287,6 +3458,7 @@ dependencies = [
"bb8",
"bytes",
"clap",
"collection_macros",
"crossterm",
"fast-socks5",
"fastwebsockets",
@ -3306,13 +3478,16 @@ dependencies = [
"ppp",
"rcgen",
"regex",
"rstest",
"rustls-native-certs 0.8.1",
"rustls-pemfile 2.2.0",
"scopeguard",
"serde",
"serde_regex",
"serde_yaml",
"serial_test",
"socket2",
"test-case",
"testcontainers",
"tokio",
"tokio-fd",

View file

@ -1,6 +1,6 @@
[package]
name = "wstunnel"
version = "10.1.6"
version = "10.1.8"
edition = "2021"
repository = "https://github.com/erebe/wstunnel.git"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -71,6 +71,10 @@ rcgen = { version = "0.13.1", default-features = false, features = ["ring"] }
[dev-dependencies]
testcontainers = "0.23.1"
test-case = "3.3.1"
collection_macros = "0.2.0"
rstest = "0.23.0"
serial_test = "3.2.0"
[profile.release]
lto = "fat"

View file

@ -2,7 +2,7 @@ ARG BUILDER_IMAGE=builder_cache
############################################################
# Cache image with all the deps
FROM rust:1.80-bookworm AS builder_cache
FROM rust:1.83-bookworm AS builder_cache
RUN rustup component add rustfmt clippy

View file

@ -191,7 +191,7 @@ State or Province Name [England]:
Locality Name []:
Organization Name [Alice Ltd]:
Organizational Unit Name []:
Common Name []:wstunnel Development Client 1
Common Name []:wstunnel_client_1 # must contains only url valid characters
Email Address []:
$ openssl ca -config openssl.cnf \

View file

@ -1,6 +1,8 @@
mod embedded_certificate;
mod protocols;
mod restrictions;
#[cfg(test)]
mod test_integrations;
mod tunnel;
use crate::protocols::dns::DnsResolver;
@ -379,7 +381,7 @@ struct Server {
http_proxy_password: Option<String>,
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub struct LocalToRemote {
local_protocol: LocalProtocol,
local: SocketAddr,
@ -459,11 +461,16 @@ fn parse_tunnel_dest(remaining: &str) -> Result<(Host<String>, u16, BTreeMap<Str
));
};
let Some(remote_port) = remote.port() else {
return Err(Error::new(
ErrorKind::InvalidInput,
format!("cannot parse remote port from {}", remaining),
));
let remote_port = match remote.port() {
Some(remote_port) => remote_port,
// the url lib does not parse the port if it is the default one
None if remaining.ends_with(":443") => 443,
_ => {
return Err(Error::new(
ErrorKind::InvalidInput,
format!("cannot parse remote port from {}", remaining),
))
}
};
let options: BTreeMap<String, String> = remote.query_pairs().into_owned().collect();
@ -1195,3 +1202,62 @@ fn mk_http_proxy(
Ok(Some(proxy))
}
#[cfg(test)]
mod test {
use crate::tunnel::LocalProtocol;
use crate::{parse_local_bind, parse_tunnel_arg, parse_tunnel_dest, LocalToRemote};
use collection_macros::btreemap;
use std::collections::BTreeMap;
use std::io;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use test_case::test_case;
use url::Host;
#[test_case("localhost:443" => (Host::Domain("localhost".to_string()), 443, BTreeMap::new()) ; "with domain")]
#[test_case("127.0.0.1:443" => (Host::Ipv4(Ipv4Addr::new(127, 0, 0, 1)), 443, BTreeMap::new()) ; "with IPv4")]
#[test_case("[::1]:8080" => (Host::Ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080, BTreeMap::new()) ; "with IpV6")]
#[test_case("a:1?timeout_sec=30&b=5" => (Host::Domain("a".to_string()), 1, btreemap! { "b".to_string() => "5".to_string(), "timeout_sec".to_string() => "30".to_string() }) ; "with options")]
fn test_parse_tunnel_dest(input: &str) -> (Host<String>, u16, BTreeMap<String, String>) {
parse_tunnel_dest(input).unwrap()
}
const LOCALHOST_IP4: SocketAddrV4 = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 443);
const LOCALHOST_IP6: SocketAddrV6 = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 443, 0, 0);
#[test_case("domain.com:443" => matches Err(_) ; "with domain")]
#[test_case("127.0.0.1" => matches Err(_) ; "with no port")]
#[test_case("127.0.0.1:444444443" => matches Err(_) ; "with too long port")]
#[test_case("127.0.0.1:443" => matches Ok((SocketAddr::V4(LOCALHOST_IP4), _)) ; "with ipv4")]
#[test_case("[::1]:443" => matches Ok((SocketAddr::V6(LOCALHOST_IP6), _)) ; "with ipv6")]
fn test_parse_local_bind(input: &str) -> Result<(SocketAddr, &str), io::Error> {
parse_local_bind(input)
}
#[test_case("domain.com:443" => panics ""; "with no protocol")]
#[test_case("sdsf://443:domain.com:443" => panics ""; "with invalid protocol")]
#[test_case("tcp://443:domain.com:4443" =>
LocalToRemote {
local_protocol: LocalProtocol::Tcp { proxy_protocol: false },
local: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 443)),
remote: (Host::Domain("domain.com".to_string()), 4443),
}
; "with no local bind")]
#[test_case("udp://[::1]:443:toto.com:4443?timeout_sec=30" =>
LocalToRemote {
local_protocol: LocalProtocol::Udp { timeout: Some(std::time::Duration::from_secs(30)) },
local: SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 443, 0, 0)),
remote: (Host::Domain("toto.com".to_string()), 4443),
}
; "with fully defined tunnel")]
#[test_case("udp://[::1]:443:[::1]:4443?timeout_sec=30" =>
LocalToRemote {
local_protocol: LocalProtocol::Udp { timeout: Some(std::time::Duration::from_secs(30)) },
local: SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 443, 0, 0)),
remote: (Host::Ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 4443),
}
; "with full ipv6 tunnel")]
fn test_parse_tunnel_arg(input: &str) -> LocalToRemote {
parse_tunnel_arg(input).unwrap()
}
}

233
src/test_integrations.rs Normal file
View file

@ -0,0 +1,233 @@
use crate::protocols;
use crate::protocols::dns::DnsResolver;
use crate::restrictions::types;
use crate::restrictions::types::{AllowConfig, MatchConfig, RestrictionConfig, RestrictionsRules};
use crate::tunnel::client::{WsClient, WsClientConfig};
use crate::tunnel::listeners::{TcpTunnelListener, UdpTunnelListener};
use crate::tunnel::server::{WsServer, WsServerConfig};
use crate::tunnel::transport::{TransportAddr, TransportScheme};
use bytes::BytesMut;
use futures_util::StreamExt;
use hyper::http::HeaderValue;
use ipnet::{IpNet, Ipv4Net, Ipv6Net};
use regex::Regex;
use rstest::{fixture, rstest};
use scopeguard::defer;
use serial_test::serial;
use std::collections::HashMap;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::time::Duration;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::pin;
use url::Host;
#[fixture]
fn dns_resolver() -> DnsResolver {
DnsResolver::new_from_urls(&[], None, None, true).expect("Cannot create DNS resolver")
}
#[fixture]
fn server_no_tls(dns_resolver: DnsResolver) -> WsServer {
let server_config = WsServerConfig {
socket_so_mark: None,
bind: "127.0.0.1:8080".parse().unwrap(),
websocket_ping_frequency: Some(Duration::from_secs(10)),
timeout_connect: Duration::from_secs(10),
websocket_mask_frame: false,
tls: None,
dns_resolver,
restriction_config: None,
http_proxy: None,
};
WsServer::new(server_config)
}
#[fixture]
async fn client_ws(dns_resolver: DnsResolver) -> WsClient {
let client_config = WsClientConfig {
remote_addr: TransportAddr::new(TransportScheme::Ws, Host::Ipv4("127.0.0.1".parse().unwrap()), 8080, None)
.unwrap(),
socket_so_mark: None,
http_upgrade_path_prefix: "wstunnel".to_string(),
http_upgrade_credentials: None,
http_headers: HashMap::new(),
http_headers_file: None,
http_header_host: HeaderValue::from_static("127.0.0.1:8080"),
timeout_connect: Duration::from_secs(10),
websocket_ping_frequency: Some(Duration::from_secs(10)),
websocket_mask_frame: false,
dns_resolver,
http_proxy: None,
};
WsClient::new(client_config, 1, Duration::from_secs(1)).await.unwrap()
}
#[fixture]
fn no_restrictions() -> RestrictionsRules {
pub fn default_host() -> Regex {
Regex::new("^.*$").unwrap()
}
pub fn default_cidr() -> Vec<IpNet> {
vec![IpNet::V4(Ipv4Net::default()), IpNet::V6(Ipv6Net::default())]
}
let tunnels = types::AllowConfig::Tunnel(types::AllowTunnelConfig {
protocol: vec![],
port: vec![],
host: default_host(),
cidr: default_cidr(),
});
let reverse_tunnel = AllowConfig::ReverseTunnel(types::AllowReverseTunnelConfig {
protocol: vec![],
port: vec![],
port_mapping: Default::default(),
cidr: default_cidr(),
});
RestrictionsRules {
restrictions: vec![RestrictionConfig {
name: "".to_string(),
r#match: vec![MatchConfig::Any],
allow: vec![tunnels, reverse_tunnel],
}],
}
}
const TUNNEL_LISTEN: (SocketAddr, Host) = (
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 9998)),
Host::Ipv4(Ipv4Addr::new(127, 0, 0, 1)),
);
const ENDPOINT_LISTEN: (SocketAddr, Host) = (
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 9999)),
Host::Ipv4(Ipv4Addr::new(127, 0, 0, 1)),
);
#[rstest]
#[timeout(Duration::from_secs(10))]
#[tokio::test]
#[serial]
async fn test_tcp_tunnel(
#[future] client_ws: WsClient,
server_no_tls: WsServer,
no_restrictions: RestrictionsRules,
dns_resolver: DnsResolver,
) {
let server_h = tokio::spawn(server_no_tls.serve(no_restrictions));
defer! { server_h.abort(); };
let client_ws = client_ws.await;
let server = TcpTunnelListener::new(TUNNEL_LISTEN.0, (ENDPOINT_LISTEN.1, ENDPOINT_LISTEN.0.port()), false)
.await
.unwrap();
tokio::spawn(async move {
client_ws.run_tunnel(server).await.unwrap();
});
let mut tcp_listener = protocols::tcp::run_server(ENDPOINT_LISTEN.0, false).await.unwrap();
let mut client = protocols::tcp::connect(
&TUNNEL_LISTEN.1,
TUNNEL_LISTEN.0.port(),
None,
Duration::from_secs(10),
&dns_resolver,
)
.await
.unwrap();
client.write_all(b"Hello").await.unwrap();
let mut dd = tcp_listener.next().await.unwrap().unwrap();
let mut buf = BytesMut::new();
dd.read_buf(&mut buf).await.unwrap();
assert_eq!(&buf[..5], b"Hello");
buf.clear();
dd.write_all(b"world!").await.unwrap();
client.read_buf(&mut buf).await.unwrap();
assert_eq!(&buf[..6], b"world!");
}
#[rstest]
#[timeout(Duration::from_secs(10))]
#[tokio::test]
#[serial]
async fn test_udp_tunnel(
#[future] client_ws: WsClient,
server_no_tls: WsServer,
no_restrictions: RestrictionsRules,
dns_resolver: DnsResolver,
) {
let server_h = tokio::spawn(server_no_tls.serve(no_restrictions));
defer! { server_h.abort(); };
let client_ws = client_ws.await;
let server = UdpTunnelListener::new(TUNNEL_LISTEN.0, (ENDPOINT_LISTEN.1, ENDPOINT_LISTEN.0.port()), None)
.await
.unwrap();
tokio::spawn(async move {
client_ws.run_tunnel(server).await.unwrap();
});
let udp_listener = protocols::udp::run_server(ENDPOINT_LISTEN.0, None, |_| Ok(()), |s| Ok(s.clone()))
.await
.unwrap();
let mut client = protocols::udp::connect(
&TUNNEL_LISTEN.1,
TUNNEL_LISTEN.0.port(),
Duration::from_secs(10),
None,
&dns_resolver,
)
.await
.unwrap();
client.write_all(b"Hello").await.unwrap();
pin!(udp_listener);
let dd = udp_listener.next().await.unwrap().unwrap();
pin!(dd);
let mut buf = BytesMut::new();
dd.read_buf(&mut buf).await.unwrap();
assert_eq!(&buf[..5], b"Hello");
buf.clear();
dd.writer().write_all(b"world!").await.unwrap();
client.read_buf(&mut buf).await.unwrap();
assert_eq!(&buf[..6], b"world!");
}
//#[rstest]
//#[timeout(Duration::from_secs(10))]
//#[tokio::test]
//async fn test_socks5_tunnel(
// #[future] client_ws: WsClient,
// server_no_tls: WsServer,
// no_restrictions: RestrictionsRules,
// dns_resolver: DnsResolver,
//) {
// let server_h = tokio::spawn(server_no_tls.serve(no_restrictions));
// defer! { server_h.abort(); };
//
// let client_ws = client_ws.await;
//
// let server = Socks5TunnelListener::new(TUNNEL_LISTEN.0, None, None).await.unwrap();
// tokio::spawn(async move { client_ws.run_tunnel(server).await.unwrap(); });
//
// let socks5_listener = protocols::socks5::run_server(ENDPOINT_LISTEN.0, None, None).await.unwrap();
// let mut client = protocols::tcp::connect(&TUNNEL_LISTEN.1, TUNNEL_LISTEN.0.port(), None, Duration::from_secs(10), &dns_resolver).await.unwrap();
//
// client.write_all(b"Hello").await.unwrap();
// pin!(socks5_listener);
// let (dd, _) = socks5_listener.next().await.unwrap().unwrap();
// let (mut read, mut write) = dd.into_split();
// let mut buf = BytesMut::new();
// read.read_buf(&mut buf).await.unwrap();
// assert_eq!(&buf[..5], b"Hello");
// buf.clear();
//
// write.write_all(b"world!").await.unwrap();
// client.read_buf(&mut buf).await.unwrap();
// assert_eq!(&buf[..6], b"world!");
//}

View file

@ -156,7 +156,15 @@ pub async fn connect(
.header(CONTENT_TYPE, "application/json")
.version(hyper::Version::HTTP_2);
let headers = req.headers_mut().unwrap();
let headers = match req.headers_mut() {
Some(h) => h,
None => {
return Err(anyhow!(
"failed to build HTTP request to contact the server {:?}. Most likely path_prefix `{}` or http headers is not valid",
req, client.config.http_upgrade_path_prefix
))
}
};
for (k, v) in &client.config.http_headers {
let _ = headers.remove(k);
headers.append(k, v.clone());

View file

@ -257,7 +257,15 @@ pub async fn connect(
)
.version(hyper::Version::HTTP_11);
let headers = req.headers_mut().unwrap();
let headers = match req.headers_mut() {
Some(h) => h,
None => {
return Err(anyhow!(
"failed to build HTTP request to contact the server {:?}. Most likely path_prefix `{}` or http headers is not valid",
req.body(Empty::<Bytes>::new()), client_cfg.http_upgrade_path_prefix
))
}
};
for (k, v) in &client_cfg.http_headers {
let _ = headers.remove(k);
headers.append(k, v.clone());