Skip to main content

wind_socks/
udp.rs

1use std::{
2	io::IoSliceMut,
3	net::{Ipv4Addr, SocketAddr},
4	pin::Pin,
5	sync::Arc,
6	task::{Context, Poll, ready},
7};
8
9use arc_swap::ArcSwap;
10use fast_socks5::{new_udp_header, util::target_addr::TargetAddr as SocksTargetAddr};
11use tokio::io::Interest;
12use wind_core::{
13	types::TargetAddr,
14	udp::{AbstractUdpSocket, QuinnRecvMeta, RecvMeta, Transmit, UdpPollHelper, UdpPoller, UdpSocketState},
15};
16
17/// A virtual UDP socket that handles SOCKS5 UDP headers
18/// It parses incoming SOCKS5 UDP packets and strips the headers,
19/// and adds SOCKS5 headers to outgoing packets
20#[derive(Debug)]
21pub struct Socks5UdpSocket {
22	io:          tokio::net::UdpSocket,
23	inner:       UdpSocketState,
24	source_addr: ArcSwap<SocketAddr>,
25}
26
27impl Socks5UdpSocket {
28	pub fn new(sock: std::net::UdpSocket) -> std::io::Result<Self> {
29		Ok(Self {
30			inner:       UdpSocketState::new((&sock).into())?,
31			io:          tokio::net::UdpSocket::from_std(sock)?,
32			source_addr: ArcSwap::new(Arc::new(SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0))),
33		})
34	}
35
36	/// Convert SOCKS target address to our TargetAddr
37	fn convert_target_addr(socks_addr: &SocksTargetAddr) -> TargetAddr {
38		match socks_addr {
39			SocksTargetAddr::Ip(socket_addr) => match socket_addr {
40				SocketAddr::V4(addr) => TargetAddr::IPv4(*addr.ip(), addr.port()),
41				SocketAddr::V6(addr) => TargetAddr::IPv6(*addr.ip(), addr.port()),
42			},
43			SocksTargetAddr::Domain(domain, port) => TargetAddr::Domain(domain.clone(), *port),
44		}
45	}
46
47	/// Get the currently stored source address
48	pub fn source_addr(&self) -> SocketAddr {
49		**self.source_addr.load()
50	}
51
52	/// Synchronously parse SOCKS5 UDP request header
53	/// This is a simplified version that doesn't require async/await
54	fn parse_udp_request_sync(data: &[u8]) -> Result<(u8, SocksTargetAddr, &[u8]), Box<dyn std::error::Error>> {
55		if data.len() < 4 {
56			return Err("Packet too short for SOCKS5 UDP header".into());
57		}
58
59		// Check reserved bytes (should be 0x00 0x00)
60		if data[0] != 0x00 || data[1] != 0x00 {
61			return Err("Invalid reserved bytes in SOCKS5 UDP header".into());
62		}
63
64		let frag = data[2];
65		let atyp = data[3];
66
67		let mut offset = 4;
68
69		// Parse target address based on address type
70		let target_addr = match atyp {
71			0x01 => {
72				// IPv4
73				if data.len() < offset + 6 {
74					return Err("Incomplete IPv4 address in SOCKS5 UDP header".into());
75				}
76				let ip = std::net::Ipv4Addr::new(data[offset], data[offset + 1], data[offset + 2], data[offset + 3]);
77				let port = u16::from_be_bytes([data[offset + 4], data[offset + 5]]);
78				offset += 6;
79				SocksTargetAddr::Ip(SocketAddr::V4(std::net::SocketAddrV4::new(ip, port)))
80			}
81			0x03 => {
82				// Domain name
83				if data.len() < offset + 1 {
84					return Err("Incomplete domain length in SOCKS5 UDP header".into());
85				}
86				let domain_len = data[offset] as usize;
87				offset += 1;
88
89				if data.len() < offset + domain_len + 2 {
90					return Err("Incomplete domain name in SOCKS5 UDP header".into());
91				}
92
93				let domain = String::from_utf8_lossy(&data[offset..offset + domain_len]).to_string();
94				offset += domain_len;
95				let port = u16::from_be_bytes([data[offset], data[offset + 1]]);
96				offset += 2;
97				SocksTargetAddr::Domain(domain, port)
98			}
99			0x04 => {
100				// IPv6
101				if data.len() < offset + 18 {
102					return Err("Incomplete IPv6 address in SOCKS5 UDP header".into());
103				}
104				let mut ip_bytes = [0u8; 16];
105				ip_bytes.copy_from_slice(&data[offset..offset + 16]);
106				let ip = std::net::Ipv6Addr::from(ip_bytes);
107				let port = u16::from_be_bytes([data[offset + 16], data[offset + 17]]);
108				offset += 18;
109				SocksTargetAddr::Ip(SocketAddr::V6(std::net::SocketAddrV6::new(ip, port, 0, 0)))
110			}
111			_ => {
112				return Err(format!("Unsupported address type: {}", atyp).into());
113			}
114		};
115
116		let payload = &data[offset..];
117		Ok((frag, target_addr, payload))
118	}
119}
120
121impl AbstractUdpSocket for Socks5UdpSocket {
122	fn create_io_poller(self: Arc<Self>) -> Pin<Box<dyn UdpPoller>> {
123		Box::pin(UdpPollHelper::new(move || {
124			let socket = self.clone();
125			async move { socket.io.writable().await }
126		}))
127	}
128
129	fn try_send(&self, transmit: &Transmit) -> std::io::Result<()> {
130		// For outgoing packets in SOCKS5 UDP proxy, we need to add SOCKS5 headers
131		// Create SOCKS5 target address from the destination
132		let socks_target = self.source_addr();
133
134		// Add SOCKS5 UDP header to the packet
135		if let Ok(mut packet_with_header) = new_udp_header(socks_target) {
136			packet_with_header.extend_from_slice(transmit.contents);
137
138			// Create new transmit with the header-wrapped packet
139			let new_transmit = Transmit {
140				destination:  socks_target,
141				contents:     &packet_with_header,
142				ecn:          transmit.ecn,
143				segment_size: transmit.segment_size,
144				src_ip:       transmit.src_ip,
145			};
146
147			self.io
148				.try_io(Interest::WRITABLE, || self.inner.send((&self.io).into(), &new_transmit))
149		} else {
150			// Fall back to direct send if header creation fails
151			self.io
152				.try_io(Interest::WRITABLE, || self.inner.send((&self.io).into(), transmit))
153		}
154	}
155
156	fn poll_recv(&self, cx: &mut Context, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> Poll<std::io::Result<usize>> {
157		// For SOCKS5 UDP, we need to parse incoming packets and strip SOCKS5 headers
158		loop {
159			ready!(self.io.poll_recv_ready(cx))?;
160
161			// First, receive data into a temporary buffer
162			let mut temp_bufs: Vec<Vec<u8>> = Vec::new();
163
164			// Create temporary buffers matching the input buffers
165			for buf in bufs.iter() {
166				let temp_buf = vec![0u8; buf.len()];
167				temp_bufs.push(temp_buf);
168			}
169
170			// Convert temp_bufs to IoSliceMut
171			let mut temp_io_bufs: Vec<IoSliceMut<'_>> = temp_bufs.iter_mut().map(|buf| IoSliceMut::new(buf)).collect();
172
173			// Create temporary metadata using RecvMeta
174			let mut temp_meta = vec![QuinnRecvMeta::default(); meta.len()];
175
176			if let Ok(res) = self.io.try_io(Interest::READABLE, || {
177				self.inner.recv((&self.io).into(), &mut temp_io_bufs, &mut temp_meta)
178			}) {
179				// Process each received packet
180				let mut processed_count = 0;
181				for i in 0..res {
182					if temp_meta[i].len > 0 {
183						let packet_data = &temp_bufs[i][..temp_meta[i].len];
184
185						// Record the source address from the received packet
186						self.source_addr.store(Arc::new(temp_meta[i].addr));
187
188						// Try to parse SOCKS5 UDP header synchronously
189						match Self::parse_udp_request_sync(packet_data) {
190							Ok((_frag, target_addr, payload)) => {
191								// Successfully parsed SOCKS5 header, copy payload to output buffer
192								let payload_len = payload.len().min(bufs[processed_count].len());
193								bufs[processed_count][..payload_len].copy_from_slice(&payload[..payload_len]);
194
195								// Update metadata with SOCKS5 destination information
196								meta[processed_count] = RecvMeta::from(temp_meta[i]);
197								meta[processed_count].len = payload_len;
198								meta[processed_count].destination = Some(Self::convert_target_addr(&target_addr));
199								processed_count += 1;
200							}
201							Err(_) => {
202								// Failed to parse SOCKS5 header, treat as raw UDP packet
203								let data_len = temp_meta[i].len.min(bufs[processed_count].len());
204								bufs[processed_count][..data_len].copy_from_slice(&packet_data[..data_len]);
205
206								// Update metadata without destination info
207								meta[processed_count] = RecvMeta::from(temp_meta[i]);
208								meta[processed_count].len = data_len;
209								processed_count += 1;
210							}
211						}
212					}
213				}
214				return Poll::Ready(Ok(processed_count));
215			}
216		}
217	}
218
219	fn local_addr(&self) -> std::io::Result<SocketAddr> {
220		self.io.local_addr()
221	}
222
223	fn may_fragment(&self) -> bool {
224		self.inner.may_fragment()
225	}
226
227	fn max_transmit_segments(&self) -> usize {
228		self.inner.max_gso_segments()
229	}
230
231	fn max_receive_segments(&self) -> usize {
232		self.inner.gro_segments()
233	}
234}