1use std::{
2 io,
3 net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
4};
5
6use fast_socks5::{
7 server::{ErrorContext as _, Socks5ServerProtocol, SocksServerError, states, wait_on_tcp},
8 util::target_addr::TargetAddr,
9};
10use futures_util::TryFutureExt;
11use socket2::{Domain, Socket, Type};
12use tokio::{
13 io::{AsyncRead, AsyncWrite},
14 try_join,
15};
16use tracing::debug;
17use wind_core::{error, warn};
18
19use crate::Error;
20
21macro_rules! try_notify {
22 ($proto:expr, $e:expr) => {
23 match $e {
24 Ok(res) => res,
25 Err(err) => {
26 if let Err(rep_err) = $proto.reply_error(&err.to_reply_error()).await {
27 error!(
28 name: "socks",
29 "extra error while reporting an error to the client: {}",
30 rep_err
31 );
32 }
33 return Err(err.into());
34 }
35 }
36 };
37}
38
39fn udp_bind_random_port(addr: Option<IpAddr>) -> io::Result<Socket> {
40 if let Some(addr) = addr {
41 let sock_addr = SocketAddr::new(addr, 0);
42 let socket = Socket::new(Domain::for_address(sock_addr), Type::DGRAM, None)?;
43 socket.bind(&sock_addr.into())?;
44 Ok(socket)
45 } else {
46 const V4_UNSPEC: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0);
47 const V6_UNSPEC: SocketAddr = SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0);
48 Socket::new(Domain::IPV6, Type::DGRAM, None)
49 .and_then(|socket| socket.set_only_v6(false).map(|_| socket))
50 .and_then(|socket| socket.bind(&V6_UNSPEC.into()).map(|_| socket))
51 .or_else(|_| {
52 Socket::new(Domain::IPV4, Type::DGRAM, None).and_then(|socket| socket.bind(&V4_UNSPEC.into()).map(|_| socket))
53 })
54 }
55 .and_then(|socket| socket.set_nonblocking(true).map(|_| socket))
56}
57
58pub async fn run_udp_proxy<T, F, R>(
59 proto: Socks5ServerProtocol<T, states::CommandRead>,
60 _addr: &TargetAddr,
61 peer_bind_ip: Option<IpAddr>,
62 reply_ip: IpAddr,
63 transfer: F,
64) -> Result<T, Error>
65where
66 T: AsyncRead + AsyncWrite + Unpin,
67 F: FnOnce(Socket) -> R,
68 R: Future<Output = Result<(), Error>>,
69{
70 let peer_sock = try_notify!(
71 proto,
72 udp_bind_random_port(peer_bind_ip).err_when("binding client udp socket")
73 );
74
75 let peer_addr = try_notify!(proto, peer_sock.local_addr().err_when("getting peer's local addr"));
76
77 let reply_port = peer_addr.as_socket().ok_or(SocksServerError::Bug("addr not IP"))?.port();
78
79 let mut inner = proto.reply_success(SocketAddr::new(reply_ip, reply_port)).await?;
81
82 let udp_fut = transfer(peer_sock);
83 let tcp_fut = wait_on_tcp(&mut inner).map_err(Error::from);
84
85 match try_join!(udp_fut, tcp_fut) {
86 Ok(_) => warn!("unreachable"),
87 Err(Error::Socks {
88 source: SocksServerError::EOF,
89 backtrace: _,
90 }) => {
91 debug!("EOF on controlling TCP stream, closed UDP proxy")
92 }
93 Err(err) => warn!("while UDP proxying: {err}"),
94 }
95 Ok(inner)
96}