feat(sock5): Add login/passzord support
This commit is contained in:
parent
ba57b76a73
commit
d797fa135c
5 changed files with 67 additions and 29 deletions
58
src/main.rs
58
src/main.rs
|
@ -106,6 +106,7 @@ struct Client {
|
||||||
/// 'udp://1212:1.1.1.1:53?timeout_sec=10' timeout_sec on udp force close the tunnel after 10sec. Set it to 0 to disable the timeout [default: 30]
|
/// 'udp://1212:1.1.1.1:53?timeout_sec=10' timeout_sec on udp force close the tunnel after 10sec. Set it to 0 to disable the timeout [default: 30]
|
||||||
///
|
///
|
||||||
/// 'socks5://[::1]:1212' => listen locally with socks5 on port 1212 and forward dynamically requested tunnel
|
/// 'socks5://[::1]:1212' => listen locally with socks5 on port 1212 and forward dynamically requested tunnel
|
||||||
|
/// 'socks5://[::1]:1212?login=admin&password=admin' => listen locally with socks5 on port 1212 and only accept connection with login=admin and password=admin
|
||||||
///
|
///
|
||||||
/// 'tproxy+tcp://[::1]:1212' => listen locally on tcp on port 1212 as a *transparent proxy* and forward dynamically requested tunnel
|
/// 'tproxy+tcp://[::1]:1212' => listen locally on tcp on port 1212 as a *transparent proxy* and forward dynamically requested tunnel
|
||||||
/// 'tproxy+udp://[::1]:1212?timeout_sec=10' listen locally on udp on port 1212 as a *transparent proxy* and forward dynamically requested tunnel
|
/// 'tproxy+udp://[::1]:1212?timeout_sec=10' listen locally on udp on port 1212 as a *transparent proxy* and forward dynamically requested tunnel
|
||||||
|
@ -121,7 +122,7 @@ struct Client {
|
||||||
/// examples:
|
/// examples:
|
||||||
/// 'tcp://1212:google.com:443' => listen on server for incoming tcp cnx on port 1212 and forward to google.com on port 443 from local machine
|
/// 'tcp://1212:google.com:443' => listen on server for incoming tcp cnx on port 1212 and forward to google.com on port 443 from local machine
|
||||||
/// 'udp://1212:1.1.1.1:53' => listen on server for incoming udp on port 1212 and forward to cloudflare dns 1.1.1.1 on port 53 from local machine
|
/// 'udp://1212:1.1.1.1:53' => listen on server for incoming udp on port 1212 and forward to cloudflare dns 1.1.1.1 on port 53 from local machine
|
||||||
/// 'socks5://[::1]:1212' => listen on server for incoming socks5 request on port 1212 and forward dynamically request from local machine
|
/// 'socks5://[::1]:1212' => listen on server for incoming socks5 request on port 1212 and forward dynamically request from local machine (login/password is supported)
|
||||||
/// 'unix://wstunnel.sock:g.com:443' => listen on server for incoming data from unix socket of path wstunnel.sock and forward to g.com:443 from local machine
|
/// 'unix://wstunnel.sock:g.com:443' => listen on server for incoming data from unix socket of path wstunnel.sock and forward to g.com:443 from local machine
|
||||||
#[arg(short='R', long, value_name = "{tcp,udp,socks5,unix}://[BIND:]PORT:HOST:PORT", value_parser = parse_tunnel_arg, verbatim_doc_comment)]
|
#[arg(short='R', long, value_name = "{tcp,udp,socks5,unix}://[BIND:]PORT:HOST:PORT", value_parser = parse_tunnel_arg, verbatim_doc_comment)]
|
||||||
remote_to_local: Vec<LocalToRemote>,
|
remote_to_local: Vec<LocalToRemote>,
|
||||||
|
@ -342,22 +343,40 @@ struct Server {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
enum LocalProtocol {
|
enum LocalProtocol {
|
||||||
Tcp { proxy_protocol: bool },
|
Tcp {
|
||||||
Udp { timeout: Option<Duration> },
|
proxy_protocol: bool,
|
||||||
|
},
|
||||||
|
Udp {
|
||||||
|
timeout: Option<Duration>,
|
||||||
|
},
|
||||||
Stdio,
|
Stdio,
|
||||||
Socks5 { timeout: Option<Duration> },
|
Socks5 {
|
||||||
|
timeout: Option<Duration>,
|
||||||
|
credentials: Option<(String, String)>,
|
||||||
|
},
|
||||||
TProxyTcp,
|
TProxyTcp,
|
||||||
TProxyUdp { timeout: Option<Duration> },
|
TProxyUdp {
|
||||||
|
timeout: Option<Duration>,
|
||||||
|
},
|
||||||
ReverseTcp,
|
ReverseTcp,
|
||||||
ReverseUdp { timeout: Option<Duration> },
|
ReverseUdp {
|
||||||
ReverseSocks5,
|
timeout: Option<Duration>,
|
||||||
ReverseUnix { path: PathBuf },
|
},
|
||||||
Unix { path: PathBuf },
|
ReverseSocks5 {
|
||||||
|
timeout: Option<Duration>,
|
||||||
|
credentials: Option<(String, String)>,
|
||||||
|
},
|
||||||
|
ReverseUnix {
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
|
Unix {
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocalProtocol {
|
impl LocalProtocol {
|
||||||
pub const fn is_reverse_tunnel(&self) -> bool {
|
pub const fn is_reverse_tunnel(&self) -> bool {
|
||||||
matches!(self, Self::ReverseTcp | Self::ReverseUdp { .. } | Self::ReverseSocks5)
|
matches!(self, Self::ReverseTcp | Self::ReverseUdp { .. } | Self::ReverseSocks5 { .. })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,8 +525,11 @@ fn parse_tunnel_arg(arg: &str) -> Result<LocalToRemote, io::Error> {
|
||||||
.and_then(|x| x.parse::<u64>().ok())
|
.and_then(|x| x.parse::<u64>().ok())
|
||||||
.map(|d| if d == 0 { None } else { Some(Duration::from_secs(d)) })
|
.map(|d| if d == 0 { None } else { Some(Duration::from_secs(d)) })
|
||||||
.unwrap_or(Some(Duration::from_secs(30)));
|
.unwrap_or(Some(Duration::from_secs(30)));
|
||||||
|
let credentials = options
|
||||||
|
.get("login")
|
||||||
|
.and_then(|login| options.get("password").map(|p| (login.to_string(), p.to_string())));
|
||||||
Ok(LocalToRemote {
|
Ok(LocalToRemote {
|
||||||
local_protocol: LocalProtocol::Socks5 { timeout },
|
local_protocol: LocalProtocol::Socks5 { timeout, credentials },
|
||||||
local: local_bind,
|
local: local_bind,
|
||||||
remote: (dest_host, dest_port),
|
remote: (dest_host, dest_port),
|
||||||
})
|
})
|
||||||
|
@ -953,16 +975,18 @@ async fn main() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
LocalProtocol::Socks5 { .. } => {
|
LocalProtocol::Socks5 { timeout, credentials } => {
|
||||||
trait T: AsyncWrite + AsyncRead + Unpin + Send {}
|
trait T: AsyncWrite + AsyncRead + Unpin + Send {}
|
||||||
impl T for TcpStream {}
|
impl T for TcpStream {}
|
||||||
impl T for MyUdpSocket {}
|
impl T for MyUdpSocket {}
|
||||||
|
|
||||||
|
let credentials = credentials.clone();
|
||||||
|
let timeout = *timeout;
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let cfg = client_config.clone();
|
let cfg = client_config.clone();
|
||||||
let (host, port) = to_host_port(tunnel.local);
|
let (host, port) = to_host_port(tunnel.local);
|
||||||
let remote = RemoteAddr {
|
let remote = RemoteAddr {
|
||||||
protocol: LocalProtocol::ReverseSocks5,
|
protocol: LocalProtocol::ReverseSocks5 { timeout, credentials },
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
};
|
};
|
||||||
|
@ -1037,7 +1061,7 @@ async fn main() {
|
||||||
| LocalProtocol::TProxyUdp { .. }
|
| LocalProtocol::TProxyUdp { .. }
|
||||||
| LocalProtocol::ReverseTcp
|
| LocalProtocol::ReverseTcp
|
||||||
| LocalProtocol::ReverseUdp { .. }
|
| LocalProtocol::ReverseUdp { .. }
|
||||||
| LocalProtocol::ReverseSocks5
|
| LocalProtocol::ReverseSocks5 { .. }
|
||||||
| LocalProtocol::ReverseUnix { .. } => {
|
| LocalProtocol::ReverseUnix { .. } => {
|
||||||
panic!("Invalid protocol for reverse tunnel");
|
panic!("Invalid protocol for reverse tunnel");
|
||||||
}
|
}
|
||||||
|
@ -1175,8 +1199,8 @@ async fn main() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
LocalProtocol::Socks5 { timeout } => {
|
LocalProtocol::Socks5 { timeout, credentials } => {
|
||||||
let server = socks5::run_server(tunnel.local, *timeout)
|
let server = socks5::run_server(tunnel.local, *timeout, credentials.clone())
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|err| panic!("Cannot start Socks5 server on {}: {}", tunnel.local, err))
|
.unwrap_or_else(|err| panic!("Cannot start Socks5 server on {}: {}", tunnel.local, err))
|
||||||
.map_ok(|(stream, (host, port))| {
|
.map_ok(|(stream, (host, port))| {
|
||||||
|
@ -1228,7 +1252,7 @@ async fn main() {
|
||||||
}
|
}
|
||||||
LocalProtocol::ReverseTcp => {}
|
LocalProtocol::ReverseTcp => {}
|
||||||
LocalProtocol::ReverseUdp { .. } => {}
|
LocalProtocol::ReverseUdp { .. } => {}
|
||||||
LocalProtocol::ReverseSocks5 => {}
|
LocalProtocol::ReverseSocks5 { .. } => {}
|
||||||
LocalProtocol::ReverseUnix { .. } => {}
|
LocalProtocol::ReverseUnix { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,7 +163,7 @@ impl From<&LocalProtocol> for ReverseTunnelConfigProtocol {
|
||||||
| LocalProtocol::Unix { .. } => Self::Unknown,
|
| LocalProtocol::Unix { .. } => Self::Unknown,
|
||||||
LocalProtocol::ReverseTcp => Self::Tcp,
|
LocalProtocol::ReverseTcp => Self::Tcp,
|
||||||
LocalProtocol::ReverseUdp { .. } => Self::Udp,
|
LocalProtocol::ReverseUdp { .. } => Self::Udp,
|
||||||
LocalProtocol::ReverseSocks5 => Self::Socks5,
|
LocalProtocol::ReverseSocks5 { .. } => Self::Socks5,
|
||||||
LocalProtocol::ReverseUnix { .. } => Self::Unix,
|
LocalProtocol::ReverseUnix { .. } => Self::Unix,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,7 @@ impl From<&LocalProtocol> for TunnelConfigProtocol {
|
||||||
match value {
|
match value {
|
||||||
LocalProtocol::ReverseTcp
|
LocalProtocol::ReverseTcp
|
||||||
| LocalProtocol::ReverseUdp { .. }
|
| LocalProtocol::ReverseUdp { .. }
|
||||||
| LocalProtocol::ReverseSocks5
|
| LocalProtocol::ReverseSocks5 { .. }
|
||||||
| LocalProtocol::ReverseUnix { .. }
|
| LocalProtocol::ReverseUnix { .. }
|
||||||
| LocalProtocol::Stdio
|
| LocalProtocol::Stdio
|
||||||
| LocalProtocol::Socks5 { .. }
|
| LocalProtocol::Socks5 { .. }
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::socks5_udp::Socks5UdpStream;
|
use crate::socks5_udp::Socks5UdpStream;
|
||||||
use crate::{socks5_udp, LocalProtocol};
|
use crate::{socks5_udp, LocalProtocol};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use fast_socks5::server::{Config, DenyAuthentication, Socks5Server};
|
use fast_socks5::server::{Config, DenyAuthentication, SimpleUserPassword, Socks5Server};
|
||||||
use fast_socks5::util::target_addr::TargetAddr;
|
use fast_socks5::util::target_addr::TargetAddr;
|
||||||
use fast_socks5::{consts, ReplyError};
|
use fast_socks5::{consts, ReplyError};
|
||||||
use futures_util::{stream, Stream, StreamExt};
|
use futures_util::{stream, Stream, StreamExt};
|
||||||
|
@ -45,15 +45,29 @@ impl Stream for Socks5Listener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run_server(bind: SocketAddr, timeout: Option<Duration>) -> Result<Socks5Listener, anyhow::Error> {
|
pub async fn run_server(
|
||||||
info!("Starting SOCKS5 server listening cnx on {}", bind);
|
bind: SocketAddr,
|
||||||
|
timeout: Option<Duration>,
|
||||||
|
credentials: Option<(String, String)>,
|
||||||
|
) -> Result<Socks5Listener, anyhow::Error> {
|
||||||
|
info!(
|
||||||
|
"Starting SOCKS5 server listening cnx on {} with credentials {:?}",
|
||||||
|
bind, credentials
|
||||||
|
);
|
||||||
|
|
||||||
let server = Socks5Server::<DenyAuthentication>::bind(bind)
|
let server = Socks5Server::<DenyAuthentication>::bind(bind)
|
||||||
.await
|
.await
|
||||||
.with_context(|| format!("Cannot create socks5 server {:?}", bind))?;
|
.with_context(|| format!("Cannot create socks5 server {:?}", bind))?;
|
||||||
|
|
||||||
let mut cfg = Config::<DenyAuthentication>::default();
|
let mut cfg = Config::default();
|
||||||
cfg.set_allow_no_auth(true);
|
cfg = if let Some((username, password)) = credentials {
|
||||||
|
cfg.set_allow_no_auth(false);
|
||||||
|
cfg.with_authentication(SimpleUserPassword { username, password })
|
||||||
|
} else {
|
||||||
|
cfg.set_allow_no_auth(true);
|
||||||
|
cfg
|
||||||
|
};
|
||||||
|
|
||||||
cfg.set_dns_resolve(false);
|
cfg.set_dns_resolve(false);
|
||||||
cfg.set_execute_command(false);
|
cfg.set_execute_command(false);
|
||||||
cfg.set_udp_support(true);
|
cfg.set_udp_support(true);
|
||||||
|
|
|
@ -43,7 +43,7 @@ impl JwtTunnelConfig {
|
||||||
LocalProtocol::Socks5 { .. } => LocalProtocol::Tcp { proxy_protocol: false },
|
LocalProtocol::Socks5 { .. } => LocalProtocol::Tcp { proxy_protocol: false },
|
||||||
LocalProtocol::ReverseTcp => LocalProtocol::ReverseTcp,
|
LocalProtocol::ReverseTcp => LocalProtocol::ReverseTcp,
|
||||||
LocalProtocol::ReverseUdp { .. } => dest.protocol.clone(),
|
LocalProtocol::ReverseUdp { .. } => dest.protocol.clone(),
|
||||||
LocalProtocol::ReverseSocks5 => LocalProtocol::ReverseSocks5,
|
LocalProtocol::ReverseSocks5 { .. } => dest.protocol.clone(),
|
||||||
LocalProtocol::TProxyTcp => LocalProtocol::Tcp { proxy_protocol: false },
|
LocalProtocol::TProxyTcp => LocalProtocol::Tcp { proxy_protocol: false },
|
||||||
LocalProtocol::TProxyUdp { timeout } => LocalProtocol::Udp { timeout },
|
LocalProtocol::TProxyUdp { timeout } => LocalProtocol::Udp { timeout },
|
||||||
LocalProtocol::Unix { .. } => LocalProtocol::Tcp { proxy_protocol: false },
|
LocalProtocol::Unix { .. } => LocalProtocol::Tcp { proxy_protocol: false },
|
||||||
|
|
|
@ -129,7 +129,7 @@ async fn run_tunnel(
|
||||||
};
|
};
|
||||||
Ok((remote, Box::pin(local_rx), Box::pin(local_tx)))
|
Ok((remote, Box::pin(local_rx), Box::pin(local_tx)))
|
||||||
}
|
}
|
||||||
LocalProtocol::ReverseSocks5 => {
|
LocalProtocol::ReverseSocks5 { timeout, credentials } => {
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
static SERVERS: Lazy<Mutex<HashMap<(Host<String>, u16), mpsc::Receiver<(Socks5Stream, (Host, u16))>>>> =
|
static SERVERS: Lazy<Mutex<HashMap<(Host<String>, u16), mpsc::Receiver<(Socks5Stream, (Host, u16))>>>> =
|
||||||
Lazy::new(|| Mutex::new(HashMap::with_capacity(0)));
|
Lazy::new(|| Mutex::new(HashMap::with_capacity(0)));
|
||||||
|
@ -137,7 +137,7 @@ async fn run_tunnel(
|
||||||
let remote_port = find_mapped_port(remote.port, restriction);
|
let remote_port = find_mapped_port(remote.port, restriction);
|
||||||
let local_srv = (remote.host, remote_port);
|
let local_srv = (remote.host, remote_port);
|
||||||
let bind = format!("{}:{}", local_srv.0, local_srv.1);
|
let bind = format!("{}:{}", local_srv.0, local_srv.1);
|
||||||
let listening_server = socks5::run_server(bind.parse()?, None);
|
let listening_server = socks5::run_server(bind.parse()?, timeout, credentials);
|
||||||
let (stream, local_srv) = run_listening_server(&local_srv, SERVERS.deref(), listening_server).await?;
|
let (stream, local_srv) = run_listening_server(&local_srv, SERVERS.deref(), listening_server).await?;
|
||||||
let protocol = stream.local_protocol();
|
let protocol = stream.local_protocol();
|
||||||
let (local_rx, local_tx) = tokio::io::split(stream);
|
let (local_rx, local_tx) = tokio::io::split(stream);
|
||||||
|
@ -571,7 +571,7 @@ async fn ws_server_upgrade(
|
||||||
.instrument(Span::current()),
|
.instrument(Span::current()),
|
||||||
);
|
);
|
||||||
|
|
||||||
if req_protocol == LocalProtocol::ReverseSocks5 {
|
if matches!(req_protocol, LocalProtocol::ReverseSocks5 { .. }) {
|
||||||
let Ok(header_val) = HeaderValue::from_str(&tunnel_to_jwt_token(Uuid::from_u128(0), &remote_addr)) else {
|
let Ok(header_val) = HeaderValue::from_str(&tunnel_to_jwt_token(Uuid::from_u128(0), &remote_addr)) else {
|
||||||
error!("Bad headervalue for reverse socks5: {} {}", remote_addr.host, remote_addr.port);
|
error!("Bad headervalue for reverse socks5: {} {}", remote_addr.host, remote_addr.port);
|
||||||
return http::Response::builder()
|
return http::Response::builder()
|
||||||
|
@ -691,7 +691,7 @@ async fn http_server_upgrade(
|
||||||
.instrument(Span::current()),
|
.instrument(Span::current()),
|
||||||
);
|
);
|
||||||
|
|
||||||
if req_protocol == LocalProtocol::ReverseSocks5 {
|
if matches!(req_protocol, LocalProtocol::ReverseSocks5 { .. }) {
|
||||||
let Ok(header_val) = HeaderValue::from_str(&tunnel_to_jwt_token(Uuid::from_u128(0), &remote_addr)) else {
|
let Ok(header_val) = HeaderValue::from_str(&tunnel_to_jwt_token(Uuid::from_u128(0), &remote_addr)) else {
|
||||||
error!("Bad header value for reverse socks5: {} {}", remote_addr.host, remote_addr.port);
|
error!("Bad header value for reverse socks5: {} {}", remote_addr.host, remote_addr.port);
|
||||||
return http::Response::builder()
|
return http::Response::builder()
|
||||||
|
|
Loading…
Reference in a new issue