Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 20 additions & 8 deletions src/args/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::Result;
use anyhow::{anyhow, Result};
use std::collections::HashMap;
use std::env::var;
use std::net::{SocketAddr, ToSocketAddrs};
Expand All @@ -12,11 +12,11 @@ pub fn parse_env_addr() -> Result<(SocketAddr, SocketAddr)> {
let ss_local_addr = format!("{}:{}", ss_local_host, ss_local_port)
.to_socket_addrs()?
.next()
.unwrap();
.ok_or_else(|| anyhow!("resolved no local socket address"))?;
let ss_remote_addr = format!("{}:{}", ss_remote_host, ss_remote_port)
.to_socket_addrs()?
.next()
.unwrap();
.ok_or_else(|| anyhow!("resolved no remote socket address"))?;

Ok((ss_local_addr, ss_remote_addr))
}
Expand All @@ -39,12 +39,15 @@ pub fn parse_env_opts() -> Result<HashMap<String, String>> {
fn parse_plugin_options(options: &str) -> HashMap<String, String> {
let mut plugin_options = HashMap::<String, String>::new();

let opts: Vec<&str> = options.split(';').collect();

// FIXME: backslash is not escaped in this plugin
for opt in opts {
let o: Vec<&str> = opt.splitn(2, '=').collect();
plugin_options.insert(o[0].to_string(), o[1].to_string());
for opt in options.split(';').filter(|s| !s.is_empty()) {
let mut parts = opt.splitn(2, '=');
let Some(key) = parts.next() else { continue };
if key.is_empty() {
continue;
}
let value = parts.next().unwrap_or_default();
plugin_options.insert(key.to_string(), value.to_string());
}

plugin_options
Expand Down Expand Up @@ -84,6 +87,15 @@ mod tests {
assert_eq!(opts.get("secret").unwrap(), "second");
}

#[test]
fn test_parse_plugin_options_malformed_is_ignored_or_empty() {
let opts = parse_plugin_options("host=example.com;flag_only;=no_key;empty=");
assert_eq!(opts.get("host").unwrap(), "example.com");
assert_eq!(opts.get("flag_only").unwrap(), "");
assert_eq!(opts.get("empty").unwrap(), "");
assert!(opts.get("").is_none());
}

#[test]
#[serial]
fn test_parse_env_addr_valid() {
Expand Down
52 changes: 29 additions & 23 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use tokio::net::{TcpListener, TcpStream};
use anyhow::{anyhow, Result};
use clap::Parser;
use futures::future::try_join;
use log::{error, info};
use log::{error, info, warn};
use quinn::ConnectionError;
use quinn::Endpoint;

Expand Down Expand Up @@ -92,8 +92,14 @@ async fn main() -> Result<()> {
roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
};

for certs in rustls_native_certs::load_native_certs().expect("could not load platform certs") {
roots.add(certs).unwrap();
let native_certs = rustls_native_certs::load_native_certs();
for err in native_certs.errors {
warn!("error loading native cert: {:?}", err);
}
for cert in native_certs.certs {
if let Err(err) = roots.add(cert) {
warn!("skipping invalid native cert: {:?}", err);
}
}

let mut client_crypto = rustls::ClientConfig::builder()
Expand Down Expand Up @@ -143,32 +149,32 @@ async fn transfer(
mut inbound: TcpStream,
vpn_mode: bool,
) -> Result<()> {
let new_conn = endpoint
.connect(*remote, &host)?
.await
.map_err(|e| {
if e == ConnectionError::TimedOut {
match create_udp_socket(vpn_mode) {
Ok(socket) => {
let addr = socket.local_addr().unwrap();
let ret = endpoint.rebind(socket);
match ret {
Ok(_) => {
let new_conn = endpoint.connect(*remote, &host)?.await.map_err(|e| {
if e == ConnectionError::TimedOut {
match create_udp_socket(vpn_mode) {
Ok(socket) => {
let addr = socket.local_addr().ok();
let ret = endpoint.rebind(socket);
match ret {
Ok(_) => {
if let Some(addr) = addr {
info!("rebinding to: {}", addr);
}
Err(e) => {
error!("rebind fail: {:?}", e);
} else {
info!("rebinding to new local socket");
}
}
}
Err(e) => {
error!("failed to create socket for rebind: {:?}", e);
Err(e) => {
error!("rebind fail: {:?}", e);
}
}
}
Err(e) => {
error!("failed to create socket for rebind: {:?}", e);
}
}
anyhow!("failed to connect: {:?}", e)
})
.unwrap();
}
anyhow!("failed to connect: {:?}", e)
})?;

let (mut ri, mut wi) = inbound.split();
let (mut wo, mut ro) = new_conn
Expand Down
4 changes: 3 additions & 1 deletion src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ async fn main() -> Result<()> {
conn.refuse();
} else if options.stateless_retry && !conn.remote_address_validated() {
info!("requiring connection to validate its address");
conn.retry().unwrap();
if let Err(e) = conn.retry() {
error!("failed to request stateless retry: {:?}", e);
}
} else {
info!("accepting connection");
let fut = handle_connection(remote.clone(), conn);
Expand Down
Loading