1#![doc = include_str!("../SOCKS4.md")]
2use rustls::ClientConfig;
3use rustls_pki_types::ServerName;
4use std::{net::SocketAddrV4, sync::Arc};
5use tokio::{
6 io::{AsyncReadExt, AsyncWriteExt},
7 net::TcpStream,
8};
9use tokio_rustls::{TlsConnector, client::TlsStream};
10
11use crate::errors;
12static VERSION_4: u8 = 0x04;
13static CONNECT_REQUEST: u8 = 0x01;
14static NULL: u8 = 0x00;
15
16#[derive(Debug)]
17pub enum Response {
18 Granted = 90,
19 Failure = 91,
20 IdentFailure = 92,
21 IdentInvalid = 93,
22}
23
24impl TryFrom<u8> for Response {
25 type Error = errors::Error;
26
27 fn try_from(value: u8) -> Result<Self, Self::Error> {
28 match value {
29 90 => Ok(Response::Granted),
30 91 => Err(errors::Error::ProxyReply0x91),
31 92 => Err(errors::Error::ProxyReply0x92),
32 93 => Err(errors::Error::ProxyReply0x93),
33 _ => Err(errors::Error::ProxyResponseNotOk(
34 "unknown status reply from proxy server".into(),
35 )),
36 }
37 }
38}
39
40#[derive(Default)]
41pub struct Socks4 {
42 proxy: Option<SocketAddrV4>,
43 to: Option<SocketAddrV4>,
44}
45
46impl Socks4 {
47 pub fn new() -> Self {
48 Default::default()
49 }
50
51 pub fn proxy(mut self, ipv4: SocketAddrV4) -> Self {
52 self.proxy = Some(ipv4);
53 self
54 }
55
56 pub fn to(mut self, ipv4: SocketAddrV4) -> Self {
57 self.to = Some(ipv4);
58 self
59 }
60
61 pub async fn connect(&self) -> crate::Result<TcpStream> {
62 let proxy = self.proxy.ok_or(errors::Error::SocksProxyAddrNotSet)?;
63 let target = self.to.ok_or(errors::Error::SocksTargetAddrNotSet)?;
64
65 let mut conn = TcpStream::connect(proxy).await?;
66 let mut packet = [0u8; 9];
67
68 packet[0] = VERSION_4;
69 packet[1] = CONNECT_REQUEST;
70
71 packet[2..4].copy_from_slice(&target.port().to_be_bytes());
72 packet[4..8].copy_from_slice(&target.ip().octets());
73 packet[8] = NULL;
74
75 conn.write_all(&packet).await?;
76
77 let mut reply = [0u8; 8];
78 conn.read(&mut reply[..]).await?;
79
80 Response::try_from(reply[1])?;
81 Ok(conn)
82 }
83
84 pub async fn connect_tls(
85 &self,
86 config: Arc<ClientConfig>,
87 sni: ServerName<'static>,
88 ) -> crate::Result<TlsStream<TcpStream>> {
89 let conn = self.connect().await?;
90
91 let connector = TlsConnector::from(config);
92 let conn = connector.connect(sni, conn).await?;
93 Ok(conn)
94 }
95}