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]
|
||||
///
|
||||
/// '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+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:
|
||||
/// '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
|
||||
/// '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
|
||||
#[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>,
|
||||
|
@ -342,22 +343,40 @@ struct Server {
|
|||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
enum LocalProtocol {
|
||||
Tcp { proxy_protocol: bool },
|
||||
Udp { timeout: Option<Duration> },
|
||||
Tcp {
|
||||
proxy_protocol: bool,
|
||||
},
|
||||
Udp {
|
||||
timeout: Option<Duration>,
|
||||
},
|
||||
Stdio,
|
||||
Socks5 { timeout: Option<Duration> },
|
||||
Socks5 {
|
||||
timeout: Option<Duration>,
|
||||
credentials: Option<(String, String)>,
|
||||
},
|
||||
TProxyTcp,
|
||||
TProxyUdp { timeout: Option<Duration> },
|
||||
TProxyUdp {
|
||||
timeout: Option<Duration>,
|
||||
},
|
||||
ReverseTcp,
|
||||
ReverseUdp { timeout: Option<Duration> },
|
||||
ReverseSocks5,
|
||||
ReverseUnix { path: PathBuf },
|
||||
Unix { path: PathBuf },
|
||||
ReverseUdp {
|
||||
timeout: Option<Duration>,
|
||||
},
|
||||
ReverseSocks5 {
|
||||
timeout: Option<Duration>,
|
||||
credentials: Option<(String, String)>,
|
||||
},
|
||||
ReverseUnix {
|
||||
path: PathBuf,
|
||||
},
|
||||
Unix {
|
||||
path: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
impl LocalProtocol {
|
||||
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())
|
||||
.map(|d| if d == 0 { None } else { Some(Duration::from_secs(d)) })
|
||||
.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 {
|
||||
local_protocol: LocalProtocol::Socks5 { timeout },
|
||||
local_protocol: LocalProtocol::Socks5 { timeout, credentials },
|
||||
local: local_bind,
|
||||
remote: (dest_host, dest_port),
|
||||
})
|
||||
|
@ -953,16 +975,18 @@ async fn main() {
|
|||
}
|
||||
});
|
||||
}
|
||||
LocalProtocol::Socks5 { .. } => {
|
||||
LocalProtocol::Socks5 { timeout, credentials } => {
|
||||
trait T: AsyncWrite + AsyncRead + Unpin + Send {}
|
||||
impl T for TcpStream {}
|
||||
impl T for MyUdpSocket {}
|
||||
|
||||
let credentials = credentials.clone();
|
||||
let timeout = *timeout;
|
||||
tokio::spawn(async move {
|
||||
let cfg = client_config.clone();
|
||||
let (host, port) = to_host_port(tunnel.local);
|
||||
let remote = RemoteAddr {
|
||||
protocol: LocalProtocol::ReverseSocks5,
|
||||
protocol: LocalProtocol::ReverseSocks5 { timeout, credentials },
|
||||
host,
|
||||
port,
|
||||
};
|
||||
|
@ -1037,7 +1061,7 @@ async fn main() {
|
|||
| LocalProtocol::TProxyUdp { .. }
|
||||
| LocalProtocol::ReverseTcp
|
||||
| LocalProtocol::ReverseUdp { .. }
|
||||
| LocalProtocol::ReverseSocks5
|
||||
| LocalProtocol::ReverseSocks5 { .. }
|
||||
| LocalProtocol::ReverseUnix { .. } => {
|
||||
panic!("Invalid protocol for reverse tunnel");
|
||||
}
|
||||
|
@ -1175,8 +1199,8 @@ async fn main() {
|
|||
}
|
||||
});
|
||||
}
|
||||
LocalProtocol::Socks5 { timeout } => {
|
||||
let server = socks5::run_server(tunnel.local, *timeout)
|
||||
LocalProtocol::Socks5 { timeout, credentials } => {
|
||||
let server = socks5::run_server(tunnel.local, *timeout, credentials.clone())
|
||||
.await
|
||||
.unwrap_or_else(|err| panic!("Cannot start Socks5 server on {}: {}", tunnel.local, err))
|
||||
.map_ok(|(stream, (host, port))| {
|
||||
|
@ -1228,7 +1252,7 @@ async fn main() {
|
|||
}
|
||||
LocalProtocol::ReverseTcp => {}
|
||||
LocalProtocol::ReverseUdp { .. } => {}
|
||||
LocalProtocol::ReverseSocks5 => {}
|
||||
LocalProtocol::ReverseSocks5 { .. } => {}
|
||||
LocalProtocol::ReverseUnix { .. } => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ impl From<&LocalProtocol> for ReverseTunnelConfigProtocol {
|
|||
| LocalProtocol::Unix { .. } => Self::Unknown,
|
||||
LocalProtocol::ReverseTcp => Self::Tcp,
|
||||
LocalProtocol::ReverseUdp { .. } => Self::Udp,
|
||||
LocalProtocol::ReverseSocks5 => Self::Socks5,
|
||||
LocalProtocol::ReverseSocks5 { .. } => Self::Socks5,
|
||||
LocalProtocol::ReverseUnix { .. } => Self::Unix,
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ impl From<&LocalProtocol> for TunnelConfigProtocol {
|
|||
match value {
|
||||
LocalProtocol::ReverseTcp
|
||||
| LocalProtocol::ReverseUdp { .. }
|
||||
| LocalProtocol::ReverseSocks5
|
||||
| LocalProtocol::ReverseSocks5 { .. }
|
||||
| LocalProtocol::ReverseUnix { .. }
|
||||
| LocalProtocol::Stdio
|
||||
| LocalProtocol::Socks5 { .. }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::socks5_udp::Socks5UdpStream;
|
||||
use crate::{socks5_udp, LocalProtocol};
|
||||
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::{consts, ReplyError};
|
||||
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> {
|
||||
info!("Starting SOCKS5 server listening cnx on {}", bind);
|
||||
pub async fn run_server(
|
||||
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)
|
||||
.await
|
||||
.with_context(|| format!("Cannot create socks5 server {:?}", bind))?;
|
||||
|
||||
let mut cfg = Config::<DenyAuthentication>::default();
|
||||
let mut cfg = Config::default();
|
||||
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_execute_command(false);
|
||||
cfg.set_udp_support(true);
|
||||
|
|
|
@ -43,7 +43,7 @@ impl JwtTunnelConfig {
|
|||
LocalProtocol::Socks5 { .. } => LocalProtocol::Tcp { proxy_protocol: false },
|
||||
LocalProtocol::ReverseTcp => LocalProtocol::ReverseTcp,
|
||||
LocalProtocol::ReverseUdp { .. } => dest.protocol.clone(),
|
||||
LocalProtocol::ReverseSocks5 => LocalProtocol::ReverseSocks5,
|
||||
LocalProtocol::ReverseSocks5 { .. } => dest.protocol.clone(),
|
||||
LocalProtocol::TProxyTcp => LocalProtocol::Tcp { proxy_protocol: false },
|
||||
LocalProtocol::TProxyUdp { timeout } => LocalProtocol::Udp { timeout },
|
||||
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)))
|
||||
}
|
||||
LocalProtocol::ReverseSocks5 => {
|
||||
LocalProtocol::ReverseSocks5 { timeout, credentials } => {
|
||||
#[allow(clippy::type_complexity)]
|
||||
static SERVERS: Lazy<Mutex<HashMap<(Host<String>, u16), mpsc::Receiver<(Socks5Stream, (Host, u16))>>>> =
|
||||
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 local_srv = (remote.host, remote_port);
|
||||
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 protocol = stream.local_protocol();
|
||||
let (local_rx, local_tx) = tokio::io::split(stream);
|
||||
|
@ -571,7 +571,7 @@ async fn ws_server_upgrade(
|
|||
.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 {
|
||||
error!("Bad headervalue for reverse socks5: {} {}", remote_addr.host, remote_addr.port);
|
||||
return http::Response::builder()
|
||||
|
@ -691,7 +691,7 @@ async fn http_server_upgrade(
|
|||
.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 {
|
||||
error!("Bad header value for reverse socks5: {} {}", remote_addr.host, remote_addr.port);
|
||||
return http::Response::builder()
|
||||
|
|
Loading…
Reference in a new issue