Skip to content

Commit

Permalink
feat(sock5): Add login/passzord support
Browse files Browse the repository at this point in the history
  • Loading branch information
erebe committed Jul 9, 2024
1 parent ba57b76 commit d797fa1
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 29 deletions.
58 changes: 41 additions & 17 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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>,
Expand Down Expand Up @@ -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 { .. })
}
}

Expand Down Expand Up @@ -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),
})
Expand Down Expand Up @@ -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,
};
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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))| {
Expand Down Expand Up @@ -1228,7 +1252,7 @@ async fn main() {
}
LocalProtocol::ReverseTcp => {}
LocalProtocol::ReverseUdp { .. } => {}
LocalProtocol::ReverseSocks5 => {}
LocalProtocol::ReverseSocks5 { .. } => {}
LocalProtocol::ReverseUnix { .. } => {}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/restrictions/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
Expand All @@ -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 { .. }
Expand Down
24 changes: 19 additions & 5 deletions src/socks5.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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();
cfg.set_allow_no_auth(true);
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);
Expand Down
2 changes: 1 addition & 1 deletion src/tunnel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
8 changes: 4 additions & 4 deletions src/tunnel/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,15 @@ 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)));

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);
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit d797fa1

Please sign in to comment.