Skip to content

Commit d797fa1

Browse files
committed
feat(sock5): Add login/passzord support
1 parent ba57b76 commit d797fa1

File tree

5 files changed

+67
-29
lines changed

5 files changed

+67
-29
lines changed

src/main.rs

+41-17
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ struct Client {
106106
/// '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]
107107
///
108108
/// 'socks5://[::1]:1212' => listen locally with socks5 on port 1212 and forward dynamically requested tunnel
109+
/// 'socks5://[::1]:1212?login=admin&password=admin' => listen locally with socks5 on port 1212 and only accept connection with login=admin and password=admin
109110
///
110111
/// 'tproxy+tcp://[::1]:1212' => listen locally on tcp on port 1212 as a *transparent proxy* and forward dynamically requested tunnel
111112
/// '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 {
121122
/// examples:
122123
/// '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
123124
/// '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
124-
/// 'socks5://[::1]:1212' => listen on server for incoming socks5 request on port 1212 and forward dynamically request from local machine
125+
/// 'socks5://[::1]:1212' => listen on server for incoming socks5 request on port 1212 and forward dynamically request from local machine (login/password is supported)
125126
/// '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
126127
#[arg(short='R', long, value_name = "{tcp,udp,socks5,unix}://[BIND:]PORT:HOST:PORT", value_parser = parse_tunnel_arg, verbatim_doc_comment)]
127128
remote_to_local: Vec<LocalToRemote>,
@@ -342,22 +343,40 @@ struct Server {
342343

343344
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
344345
enum LocalProtocol {
345-
Tcp { proxy_protocol: bool },
346-
Udp { timeout: Option<Duration> },
346+
Tcp {
347+
proxy_protocol: bool,
348+
},
349+
Udp {
350+
timeout: Option<Duration>,
351+
},
347352
Stdio,
348-
Socks5 { timeout: Option<Duration> },
353+
Socks5 {
354+
timeout: Option<Duration>,
355+
credentials: Option<(String, String)>,
356+
},
349357
TProxyTcp,
350-
TProxyUdp { timeout: Option<Duration> },
358+
TProxyUdp {
359+
timeout: Option<Duration>,
360+
},
351361
ReverseTcp,
352-
ReverseUdp { timeout: Option<Duration> },
353-
ReverseSocks5,
354-
ReverseUnix { path: PathBuf },
355-
Unix { path: PathBuf },
362+
ReverseUdp {
363+
timeout: Option<Duration>,
364+
},
365+
ReverseSocks5 {
366+
timeout: Option<Duration>,
367+
credentials: Option<(String, String)>,
368+
},
369+
ReverseUnix {
370+
path: PathBuf,
371+
},
372+
Unix {
373+
path: PathBuf,
374+
},
356375
}
357376

358377
impl LocalProtocol {
359378
pub const fn is_reverse_tunnel(&self) -> bool {
360-
matches!(self, Self::ReverseTcp | Self::ReverseUdp { .. } | Self::ReverseSocks5)
379+
matches!(self, Self::ReverseTcp | Self::ReverseUdp { .. } | Self::ReverseSocks5 { .. })
361380
}
362381
}
363382

@@ -506,8 +525,11 @@ fn parse_tunnel_arg(arg: &str) -> Result<LocalToRemote, io::Error> {
506525
.and_then(|x| x.parse::<u64>().ok())
507526
.map(|d| if d == 0 { None } else { Some(Duration::from_secs(d)) })
508527
.unwrap_or(Some(Duration::from_secs(30)));
528+
let credentials = options
529+
.get("login")
530+
.and_then(|login| options.get("password").map(|p| (login.to_string(), p.to_string())));
509531
Ok(LocalToRemote {
510-
local_protocol: LocalProtocol::Socks5 { timeout },
532+
local_protocol: LocalProtocol::Socks5 { timeout, credentials },
511533
local: local_bind,
512534
remote: (dest_host, dest_port),
513535
})
@@ -953,16 +975,18 @@ async fn main() {
953975
}
954976
});
955977
}
956-
LocalProtocol::Socks5 { .. } => {
978+
LocalProtocol::Socks5 { timeout, credentials } => {
957979
trait T: AsyncWrite + AsyncRead + Unpin + Send {}
958980
impl T for TcpStream {}
959981
impl T for MyUdpSocket {}
960982

983+
let credentials = credentials.clone();
984+
let timeout = *timeout;
961985
tokio::spawn(async move {
962986
let cfg = client_config.clone();
963987
let (host, port) = to_host_port(tunnel.local);
964988
let remote = RemoteAddr {
965-
protocol: LocalProtocol::ReverseSocks5,
989+
protocol: LocalProtocol::ReverseSocks5 { timeout, credentials },
966990
host,
967991
port,
968992
};
@@ -1037,7 +1061,7 @@ async fn main() {
10371061
| LocalProtocol::TProxyUdp { .. }
10381062
| LocalProtocol::ReverseTcp
10391063
| LocalProtocol::ReverseUdp { .. }
1040-
| LocalProtocol::ReverseSocks5
1064+
| LocalProtocol::ReverseSocks5 { .. }
10411065
| LocalProtocol::ReverseUnix { .. } => {
10421066
panic!("Invalid protocol for reverse tunnel");
10431067
}
@@ -1175,8 +1199,8 @@ async fn main() {
11751199
}
11761200
});
11771201
}
1178-
LocalProtocol::Socks5 { timeout } => {
1179-
let server = socks5::run_server(tunnel.local, *timeout)
1202+
LocalProtocol::Socks5 { timeout, credentials } => {
1203+
let server = socks5::run_server(tunnel.local, *timeout, credentials.clone())
11801204
.await
11811205
.unwrap_or_else(|err| panic!("Cannot start Socks5 server on {}: {}", tunnel.local, err))
11821206
.map_ok(|(stream, (host, port))| {
@@ -1228,7 +1252,7 @@ async fn main() {
12281252
}
12291253
LocalProtocol::ReverseTcp => {}
12301254
LocalProtocol::ReverseUdp { .. } => {}
1231-
LocalProtocol::ReverseSocks5 => {}
1255+
LocalProtocol::ReverseSocks5 { .. } => {}
12321256
LocalProtocol::ReverseUnix { .. } => {}
12331257
}
12341258
}

src/restrictions/types.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ impl From<&LocalProtocol> for ReverseTunnelConfigProtocol {
163163
| LocalProtocol::Unix { .. } => Self::Unknown,
164164
LocalProtocol::ReverseTcp => Self::Tcp,
165165
LocalProtocol::ReverseUdp { .. } => Self::Udp,
166-
LocalProtocol::ReverseSocks5 => Self::Socks5,
166+
LocalProtocol::ReverseSocks5 { .. } => Self::Socks5,
167167
LocalProtocol::ReverseUnix { .. } => Self::Unix,
168168
}
169169
}
@@ -173,7 +173,7 @@ impl From<&LocalProtocol> for TunnelConfigProtocol {
173173
match value {
174174
LocalProtocol::ReverseTcp
175175
| LocalProtocol::ReverseUdp { .. }
176-
| LocalProtocol::ReverseSocks5
176+
| LocalProtocol::ReverseSocks5 { .. }
177177
| LocalProtocol::ReverseUnix { .. }
178178
| LocalProtocol::Stdio
179179
| LocalProtocol::Socks5 { .. }

src/socks5.rs

+19-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::socks5_udp::Socks5UdpStream;
22
use crate::{socks5_udp, LocalProtocol};
33
use anyhow::Context;
4-
use fast_socks5::server::{Config, DenyAuthentication, Socks5Server};
4+
use fast_socks5::server::{Config, DenyAuthentication, SimpleUserPassword, Socks5Server};
55
use fast_socks5::util::target_addr::TargetAddr;
66
use fast_socks5::{consts, ReplyError};
77
use futures_util::{stream, Stream, StreamExt};
@@ -45,15 +45,29 @@ impl Stream for Socks5Listener {
4545
}
4646
}
4747

48-
pub async fn run_server(bind: SocketAddr, timeout: Option<Duration>) -> Result<Socks5Listener, anyhow::Error> {
49-
info!("Starting SOCKS5 server listening cnx on {}", bind);
48+
pub async fn run_server(
49+
bind: SocketAddr,
50+
timeout: Option<Duration>,
51+
credentials: Option<(String, String)>,
52+
) -> Result<Socks5Listener, anyhow::Error> {
53+
info!(
54+
"Starting SOCKS5 server listening cnx on {} with credentials {:?}",
55+
bind, credentials
56+
);
5057

5158
let server = Socks5Server::<DenyAuthentication>::bind(bind)
5259
.await
5360
.with_context(|| format!("Cannot create socks5 server {:?}", bind))?;
5461

55-
let mut cfg = Config::<DenyAuthentication>::default();
56-
cfg.set_allow_no_auth(true);
62+
let mut cfg = Config::default();
63+
cfg = if let Some((username, password)) = credentials {
64+
cfg.set_allow_no_auth(false);
65+
cfg.with_authentication(SimpleUserPassword { username, password })
66+
} else {
67+
cfg.set_allow_no_auth(true);
68+
cfg
69+
};
70+
5771
cfg.set_dns_resolve(false);
5872
cfg.set_execute_command(false);
5973
cfg.set_udp_support(true);

src/tunnel/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl JwtTunnelConfig {
4343
LocalProtocol::Socks5 { .. } => LocalProtocol::Tcp { proxy_protocol: false },
4444
LocalProtocol::ReverseTcp => LocalProtocol::ReverseTcp,
4545
LocalProtocol::ReverseUdp { .. } => dest.protocol.clone(),
46-
LocalProtocol::ReverseSocks5 => LocalProtocol::ReverseSocks5,
46+
LocalProtocol::ReverseSocks5 { .. } => dest.protocol.clone(),
4747
LocalProtocol::TProxyTcp => LocalProtocol::Tcp { proxy_protocol: false },
4848
LocalProtocol::TProxyUdp { timeout } => LocalProtocol::Udp { timeout },
4949
LocalProtocol::Unix { .. } => LocalProtocol::Tcp { proxy_protocol: false },

src/tunnel/server.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,15 @@ async fn run_tunnel(
129129
};
130130
Ok((remote, Box::pin(local_rx), Box::pin(local_tx)))
131131
}
132-
LocalProtocol::ReverseSocks5 => {
132+
LocalProtocol::ReverseSocks5 { timeout, credentials } => {
133133
#[allow(clippy::type_complexity)]
134134
static SERVERS: Lazy<Mutex<HashMap<(Host<String>, u16), mpsc::Receiver<(Socks5Stream, (Host, u16))>>>> =
135135
Lazy::new(|| Mutex::new(HashMap::with_capacity(0)));
136136

137137
let remote_port = find_mapped_port(remote.port, restriction);
138138
let local_srv = (remote.host, remote_port);
139139
let bind = format!("{}:{}", local_srv.0, local_srv.1);
140-
let listening_server = socks5::run_server(bind.parse()?, None);
140+
let listening_server = socks5::run_server(bind.parse()?, timeout, credentials);
141141
let (stream, local_srv) = run_listening_server(&local_srv, SERVERS.deref(), listening_server).await?;
142142
let protocol = stream.local_protocol();
143143
let (local_rx, local_tx) = tokio::io::split(stream);
@@ -571,7 +571,7 @@ async fn ws_server_upgrade(
571571
.instrument(Span::current()),
572572
);
573573

574-
if req_protocol == LocalProtocol::ReverseSocks5 {
574+
if matches!(req_protocol, LocalProtocol::ReverseSocks5 { .. }) {
575575
let Ok(header_val) = HeaderValue::from_str(&tunnel_to_jwt_token(Uuid::from_u128(0), &remote_addr)) else {
576576
error!("Bad headervalue for reverse socks5: {} {}", remote_addr.host, remote_addr.port);
577577
return http::Response::builder()
@@ -691,7 +691,7 @@ async fn http_server_upgrade(
691691
.instrument(Span::current()),
692692
);
693693

694-
if req_protocol == LocalProtocol::ReverseSocks5 {
694+
if matches!(req_protocol, LocalProtocol::ReverseSocks5 { .. }) {
695695
let Ok(header_val) = HeaderValue::from_str(&tunnel_to_jwt_token(Uuid::from_u128(0), &remote_addr)) else {
696696
error!("Bad header value for reverse socks5: {} {}", remote_addr.host, remote_addr.port);
697697
return http::Response::builder()

0 commit comments

Comments
 (0)