1use std::time::Duration;
2
3use bytes::{BufMut, BytesMut};
4use tokio::io::{AsyncReadExt, AsyncWriteExt};
5use tokio::net::TcpStream;
6use tokio::time::timeout;
7
8use crate::ProxyAuth;
9use crate::error::{ErrorName, ProxyError};
10use crate::result::ProxyResult;
11
12#[derive(Debug, Clone)]
31pub struct Proxy {
32 proxy_type: ProxyType,
33 proxy_address: String,
34 timeout: u64,
35 auth: Option<ProxyAuth>,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum ProxyType {
41 Socks5,
42 Socks4,
43}
44
45async fn write_all_to(stream: &mut TcpStream, buffer: Vec<u8>) -> ProxyResult<()> {
47 match timeout(Duration::from_secs(10), stream.write_all(&buffer)).await {
48 Ok(result) => match result {
49 Ok(_) => ProxyResult::Ok(()),
50 Err(e) => ProxyResult::Err(ProxyError::new(ErrorName::StreamError, e.to_string())),
51 },
52 Err(_) => ProxyResult::Err(ProxyError::new(ErrorName::StreamError, "failed to write buffer to stream")),
53 }
54}
55
56async fn read_exact_from<'a>(stream: &mut TcpStream, buffer: &'a mut [u8]) -> ProxyResult<()> {
58 match timeout(Duration::from_secs(10), stream.read_exact(buffer)).await {
59 Ok(result) => match result {
60 Ok(_) => ProxyResult::Ok(()),
61 Err(e) => ProxyResult::Err(ProxyError::new(ErrorName::StreamError, e.to_string())),
62 },
63 Err(_) => ProxyResult::Err(ProxyError::new(ErrorName::StreamError, "failed to read buffer from stream")),
64 }
65}
66
67impl From<String> for Proxy {
68 fn from(value: String) -> Self {
69 let split = value.split("://").collect::<Vec<&str>>();
70 let (protocol, proxy) = (split.get(0).unwrap_or(&"socks5"), split.get(1).unwrap_or(&"127.0.0.1"));
71
72 Self {
73 proxy_address: (*proxy).to_string(),
74 proxy_type: match *protocol {
75 "socks5" => ProxyType::Socks5,
76 "socks4" => ProxyType::Socks4,
77 _ => ProxyType::Socks5,
78 },
79 timeout: 20000,
80 auth: None,
81 }
82 }
83}
84
85impl From<&str> for Proxy {
86 fn from(value: &str) -> Self {
87 let split = value.split("://").collect::<Vec<&str>>();
88 let (protocol, proxy) = (split.get(0).unwrap_or(&"socks5"), split.get(1).unwrap_or(&"127.0.0.1"));
89
90 Self {
91 proxy_address: (*proxy).to_string(),
92 proxy_type: match *protocol {
93 "socks5" => ProxyType::Socks5,
94 "socks4" => ProxyType::Socks4,
95 _ => ProxyType::Socks5,
96 },
97 timeout: 20000,
98 auth: None,
99 }
100 }
101}
102
103impl Proxy {
104 pub fn new(proxy_address: impl Into<String>, proxy_type: ProxyType) -> Self {
106 Self {
107 proxy_address: proxy_address.into(),
108 proxy_type: proxy_type,
109 timeout: 20000,
110 auth: None,
111 }
112 }
113
114 pub fn new_with_auth(proxy_address: impl Into<String>, proxy_type: ProxyType, auth: ProxyAuth) -> Self {
116 Self {
117 proxy_address: proxy_address.into(),
118 proxy_type: proxy_type,
119 timeout: 20000,
120 auth: Some(auth),
121 }
122 }
123
124 pub fn with_timeout(mut self, timeout: u64) -> Self {
126 self.timeout = timeout;
127 self
128 }
129
130 pub fn with_proxy_type(mut self, proxy_type: ProxyType) -> Self {
132 self.proxy_type = proxy_type;
133 self
134 }
135
136 pub fn with_auth(mut self, auth: ProxyAuth) -> Self {
138 self.auth = Some(auth);
139 self
140 }
141
142 pub async fn is_available(&self) -> bool {
144 match timeout(Duration::from_millis(self.timeout), TcpStream::connect(&self.proxy_address)).await {
145 Ok(result) => match result {
146 Ok(_) => return true,
147 Err(_) => return false,
148 },
149 Err(_) => return false,
150 }
151 }
152
153 pub fn get_ip(&self) -> Option<String> {
155 if let Some(ip) = self.proxy_address.split(":").collect::<Vec<&str>>().get(0) {
156 Some(ip.to_string())
157 } else {
158 None
159 }
160 }
161
162 pub async fn connect(&self, target_host: impl Into<String>, target_port: u16) -> ProxyResult<TcpStream> {
164 let mut stream = match timeout(Duration::from_millis(self.timeout), TcpStream::connect(&self.proxy_address)).await {
165 Ok(result) => match result {
166 Ok(s) => s,
167 Err(_) => return ProxyResult::Err(ProxyError::new(ErrorName::NotConnected, "could not connect to specified server")),
168 },
169 Err(_) => return ProxyResult::Err(ProxyError::new(ErrorName::Timeout, "failed to connect to server within specified time")),
170 };
171
172 match self.proxy_type {
173 ProxyType::Socks5 => self.connect_socks5(&mut stream, target_host.into(), target_port).await?,
174 ProxyType::Socks4 => self.connect_socks4(&mut stream, target_host.into(), target_port).await?,
175 }
176
177 ProxyResult::Ok(stream)
178 }
179
180 async fn connect_socks5(&self, stream: &mut TcpStream, target_host: String, target_port: u16) -> ProxyResult<()> {
182 let greet = if self.auth.is_some() { vec![0x05, 0x02, 0x00, 0x02] } else { vec![0x05, 0x01, 0x00] };
183
184 write_all_to(stream, greet).await?;
185
186 let mut response = [0u8; 2];
187
188 read_exact_from(stream, &mut response).await?;
189
190 if response[0] != 0x05 {
191 return ProxyResult::Err(ProxyError::new(ErrorName::InvalidVersion, "invalid response version"));
192 }
193
194 match response[1] {
195 0x00 => {}
196 0x02 => {
197 if let Some(auth) = &self.auth {
198 let username = auth.username();
199 let password = auth.password();
200
201 if username.len() > 255 || password.len() > 255 {
202 return Err(ProxyError::new(ErrorName::InvalidData, "username or password is too long"));
203 }
204
205 let mut buffer = BytesMut::with_capacity(2 + username.len() + password.len());
206 buffer.put_u8(0x01);
207 buffer.put_u8(username.len() as u8);
208 buffer.put_slice(username.as_bytes());
209 buffer.put_u8(password.len() as u8);
210 buffer.put_slice(password.as_bytes());
211
212 write_all_to(stream, buffer.into()).await?;
213
214 let mut resp = [0u8; 2];
215
216 read_exact_from(stream, &mut resp).await?;
217
218 if resp[0] != 0x01 {
219 return Err(ProxyError::new(ErrorName::AuthFailed, "invalid authorization version"));
220 }
221
222 if resp[1] != 0x00 {
223 return Err(ProxyError::new(ErrorName::AuthFailed, "authorization failed (possibly incorrect password or username)"));
224 }
225 } else {
226 return ProxyResult::Err(ProxyError::new(ErrorName::AuthFailed, "proxy requires authorization (username, password)"));
227 }
228 }
229 _ => return ProxyResult::Err(ProxyError::new(ErrorName::Unsupported, "unsupported authorization method")),
230 }
231
232 let mut request = BytesMut::with_capacity(512);
233 request.put_u8(0x05);
234 request.put_u8(0x01);
235 request.put_u8(0x00);
236
237 if let Ok(ipv4) = target_host.parse::<std::net::Ipv4Addr>() {
238 request.put_u8(0x01);
239 request.put_slice(&ipv4.octets());
240 } else if let Ok(ipv6) = target_host.parse::<std::net::Ipv6Addr>() {
241 request.put_u8(0x04);
242 request.put_slice(&ipv6.octets());
243 } else {
244 request.put_u8(0x03);
245 let host_bytes = target_host.as_bytes();
246
247 if host_bytes.len() > 255 {
248 return ProxyResult::Err(ProxyError::new(ErrorName::InvalidData, "target host is too long"));
249 }
250
251 request.put_u8(host_bytes.len() as u8);
252 request.put_slice(host_bytes);
253 }
254
255 request.put_u16(target_port);
256
257 write_all_to(stream, request.into()).await?;
258
259 let mut header = [0u8; 4];
260
261 read_exact_from(stream, &mut header).await?;
262
263 if header[0] != 0x05 {
264 return ProxyResult::Err(ProxyError::new(ErrorName::InvalidVersion, "invalid response version"));
265 }
266
267 let rep = header[1];
268
269 if rep != 0x00 {
270 return ProxyResult::Err(ProxyError::new(ErrorName::NotConnected, format!("proxy connection error (rep: 0x{:02x})", rep)));
271 }
272
273 let atyp = header[3];
274
275 match atyp {
276 0x01 => {
277 let mut addr = [0u8; 4 + 2];
278 read_exact_from(stream, &mut addr).await?;
279 }
280 0x04 => {
281 let mut addr = [0u8; 16 + 2];
282 read_exact_from(stream, &mut addr).await?;
283 }
284 0x03 => {
285 let mut len = [0u8; 1];
286 read_exact_from(stream, &mut len).await?;
287 let mut rest = vec![0u8; len[0] as usize + 2];
288 read_exact_from(stream, &mut rest).await?;
289 }
290 _ => return ProxyResult::Err(ProxyError::new(ErrorName::InvalidData, format!("unknown address type in reply: 0x{:02x}", atyp))),
291 }
292
293 ProxyResult::Ok(())
294 }
295
296 async fn connect_socks4(&self, stream: &mut TcpStream, target_host: String, target_port: u16) -> ProxyResult<()> {
298 let mut request = BytesMut::with_capacity(512);
299 request.put_u8(0x04);
300 request.put_u8(0x01);
301 request.put_u16(target_port);
302
303 if let Ok(ipv4) = target_host.parse::<std::net::Ipv4Addr>() {
304 request.put_slice(&ipv4.octets());
305
306 if let Some(auth) = &self.auth {
307 request.put_slice(auth.username().as_bytes());
308 } else {
309 request.put_u8(0x00);
310 }
311 } else {
312 request.put_slice(&[0x00, 0x00, 0x00, 0x01]);
313
314 if let Some(auth) = &self.auth {
315 request.put_slice(auth.username().as_bytes());
316 } else {
317 request.put_u8(0x00);
318 }
319
320 if target_host.len() > 255 {
321 return Err(ProxyError::new(ErrorName::InvalidData, "target host is too long"));
322 }
323
324 request.put_slice(target_host.as_bytes());
325 request.put_u8(0x00);
326 }
327
328 write_all_to(stream, request.into()).await?;
329
330 let mut response = [0u8; 8];
331 read_exact_from(stream, &mut response).await?;
332
333 if response[0] != 0x00 {
334 return Err(ProxyError::new(ErrorName::InvalidVersion, "invalid response version"));
335 }
336
337 match response[1] {
338 0x5a => Ok(()),
339 0x5b => Err(ProxyError::new(ErrorName::NotConnected, "request rejected or failed")),
340 0x5c => Err(ProxyError::new(ErrorName::AuthFailed, "client not identd-authenticated")),
341 0x5d => Err(ProxyError::new(ErrorName::AuthFailed, "client identd-user mismatch")),
342 _ => Err(ProxyError::new(ErrorName::Unsupported, format!("unknown response code 0x{:02x}", response[1]))),
343 }
344 }
345}
346
347#[cfg(test)]
348mod tests {
349 use std::io::{Error, ErrorKind};
350
351 use tokio::io::{AsyncReadExt, AsyncWriteExt};
352
353 use crate::result::ProxyResult;
354 use crate::{Proxy, ProxyType};
355
356 #[tokio::test]
357 async fn test_socks5_proxy() -> std::io::Result<()> {
358 let proxy = Proxy::new("212.58.132.5:1080", ProxyType::Socks5);
359
360 let mut conn = match proxy.connect("ipinfo.io".to_string(), 80).await {
361 ProxyResult::Ok(s) => s,
362 ProxyResult::Err(e) => return Err(Error::new(ErrorKind::NotConnected, e.text())),
363 };
364
365 conn.write_all(b"GET / HTTP/1.0\r\nHost: ipinfo.io\r\n\r\n").await?;
366
367 let mut buf = Vec::new();
368 conn.read_to_end(&mut buf).await?;
369
370 println!("{}", String::from_utf8_lossy(&buf));
371
372 Ok(())
373 }
374
375 #[tokio::test]
376 async fn test_socks4_proxy() -> std::io::Result<()> {
377 let proxy = Proxy::new("68.71.242.118:4145", ProxyType::Socks4);
378
379 let mut conn = match proxy.connect("ipinfo.io".to_string(), 80).await {
380 ProxyResult::Ok(s) => s,
381 ProxyResult::Err(e) => return Err(Error::new(ErrorKind::NotConnected, e.text())),
382 };
383
384 conn.write_all(b"GET / HTTP/1.0\r\nHost: ipinfo.io\r\n\r\n").await?;
385
386 let mut buf = Vec::new();
387 conn.read_to_end(&mut buf).await?;
388
389 println!("{}", String::from_utf8_lossy(&buf));
390
391 Ok(())
392 }
393}