Skip to main content

wind_socks/
inbound.rs

1use std::net::{IpAddr, Ipv4Addr, SocketAddr};
2
3use fast_socks5::{ReplyError, Socks5Command, server::Socks5ServerProtocol, util::target_addr::TargetAddr as SocksTargetAddr};
4use snafu::ResultExt;
5use tokio::net::{TcpListener, TcpStream};
6use tokio_util::sync::CancellationToken;
7use wind_core::{AbstractInbound, InboundCallback, error, info, types::TargetAddr};
8
9use crate::{CallbackSnafu, Error, IoSnafu, SocksSnafu};
10
11pub struct SocksInboundOpt {
12	/// Bind on address address. eg. `127.0.0.1:1080`
13	pub listen_addr: SocketAddr,
14
15	/// Our external IP address to be sent in reply packets (required for UDP)
16	pub public_addr: Option<std::net::IpAddr>,
17
18	/// Choose authentication type
19	pub auth: AuthMode,
20
21	/// Don't perform the auth handshake, send directly the command request
22	pub skip_auth: bool,
23
24	/// Allow UDP proxying, requires public-addr to be set
25	pub allow_udp: bool,
26}
27
28pub enum AuthMode {
29	NoAuth,
30	Password { username: String, password: String },
31}
32
33pub struct SocksInbound {
34	opts:   SocksInboundOpt,
35	cancel: CancellationToken,
36}
37
38impl AbstractInbound for SocksInbound {
39	async fn listen(&self, cb: &impl InboundCallback) -> eyre::Result<()> {
40		let listener = TcpListener::bind(self.opts.listen_addr).await?;
41		loop {
42			tokio::select! {
43				_ = self.cancel.cancelled() => {
44					info!(target: "[IN] REACTOR", "Cancellation received, shutting down");
45					break;
46				}
47				res = listener.accept() => {
48					match res {
49						Err(err) => error!(target:"[IN] REACTOR", "{:}", err),
50						Ok((stream, client_addr)) => {
51							match self.handle_income(stream, client_addr, cb).await {
52								Ok(_) => {}
53								Err(err) => error!(target: "[IN] HANDLER" , "{:}", err),
54							}
55						}
56					}
57				}
58			};
59		}
60		Ok(())
61	}
62}
63
64impl SocksInbound {
65	pub async fn new(opts: SocksInboundOpt, cancel: CancellationToken) -> Self {
66		Self { opts, cancel }
67	}
68
69	async fn handle_income(&self, stream: TcpStream, _client_addr: SocketAddr, cb: &impl InboundCallback) -> Result<(), Error> {
70		let proto = match &self.opts.auth {
71			AuthMode::NoAuth => Socks5ServerProtocol::accept_no_auth(stream).await.context(SocksSnafu)?,
72			AuthMode::Password { username, password } => {
73				Socks5ServerProtocol::accept_password_auth(stream, |user, pass| user == *username && pass == *password)
74					.await
75					.context(SocksSnafu)?
76					.0
77			}
78		};
79		let (proto, cmd, target_addr) = proto.read_command().await?;
80
81		match cmd {
82			Socks5Command::TCPConnect => {
83				let inner = proto
84					.reply_success(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0))
85					.await?;
86				let target_addr = match target_addr {
87					SocksTargetAddr::Ip(socket_addr) => match socket_addr {
88						SocketAddr::V4(socket_addr) => TargetAddr::IPv4(*socket_addr.ip(), socket_addr.port()),
89						SocketAddr::V6(socket_addr) => TargetAddr::IPv6(*socket_addr.ip(), socket_addr.port()),
90					},
91					SocksTargetAddr::Domain(domain, port) => TargetAddr::Domain(domain, port),
92				};
93				cb.handle_tcpstream(target_addr, inner).await.context(CallbackSnafu)?;
94			}
95			Socks5Command::UDPAssociate if self.opts.allow_udp => {
96				let reply_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
97				crate::ext::run_udp_proxy(proto, &target_addr, None, reply_ip, move |inbound| async move {
98					// Create a virtual UDP socket that handles SOCKS5 UDP headers
99					let virtual_socket = crate::udp::Socks5UdpSocket::new(inbound.into()).context(IoSnafu)?;
100					cb.handle_udpsocket(virtual_socket).await.context(CallbackSnafu)
101				})
102				.await?;
103			}
104			_ => {
105				proto.reply_error(&ReplyError::CommandNotSupported).await?;
106				return Err(ReplyError::CommandNotSupported.into());
107			}
108		};
109		Ok(())
110	}
111}