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 pub listen_addr: SocketAddr,
14
15 pub public_addr: Option<std::net::IpAddr>,
17
18 pub auth: AuthMode,
20
21 pub skip_auth: bool,
23
24 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 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}