chrootable_https/
socks5.rs

1use byteorder::{NetworkEndian, WriteBytesExt};
2use hyper::client::connect;
3use std::io;
4use std::net::SocketAddr;
5use std::net::{Ipv4Addr, Ipv6Addr};
6use tokio::net::tcp::TcpStream;
7use tokio::prelude::*;
8
9
10pub enum ProxyDest {
11    Ipv4Addr(Ipv4Addr),
12    Ipv6Addr(Ipv6Addr),
13    Domain(String),
14}
15
16impl ProxyDest {
17    fn apply_ipv4(buf: &mut Vec<u8>, addr: Ipv4Addr) {
18        info!("Setting socks5 destination as ipv4: {:?}", addr);
19        buf.push(0x01); // ipv4
20        buf.extend(&addr.octets());
21    }
22
23    fn apply_ipv6(buf: &mut Vec<u8>, addr: Ipv6Addr) {
24        info!("Setting socks5 destination as ipv6: {:?}", addr);
25        buf.push(0x04); // ipv6
26        buf.extend(&addr.octets());
27    }
28
29    fn apply_domain(buf: &mut Vec<u8>, domain: &str) {
30        info!("Setting socks5 destination as domain: {:?}", domain);
31        let domain = domain.bytes();
32        buf.push(0x03); // domain
33        buf.push(domain.len() as u8);
34        buf.extend(domain);
35    }
36
37    fn apply(&self, buf: &mut Vec<u8>) {
38        match self {
39            ProxyDest::Ipv4Addr(addr) => Self::apply_ipv4(buf, *addr),
40            ProxyDest::Ipv6Addr(addr) => Self::apply_ipv6(buf, *addr),
41            ProxyDest::Domain(domain) => Self::apply_domain(buf, domain),
42        }
43    }
44
45    pub fn from_hyper(dest: connect::Destination) -> (ProxyDest, u16) {
46        let port = match (dest.scheme(), dest.port()) {
47            (_, Some(port)) => port,
48            ("https", None) => 443,
49            ("http", None) => 80,
50            (_, None) => 443, // TODO: raise error
51        };
52
53        let host = dest.host();
54        let host = match host.parse::<Ipv4Addr>() {
55            Ok(ipaddr) => ProxyDest::Ipv4Addr(ipaddr),
56            _ => ProxyDest::Domain(host.to_string()),
57        };
58
59        (host, port)
60    }
61}
62
63/// A `Future` that will resolve to an tcp connection.
64#[must_use = "futures do nothing unless polled"]
65pub struct ConnectionFuture(Box<dyn Future<Item = TcpStream, Error = io::Error> + Send>);
66
67impl Future for ConnectionFuture {
68    type Item = TcpStream;
69    type Error = io::Error;
70
71    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
72        self.0.poll()
73    }
74}
75
76/// A `Future` that will resolve to an tcp connection.
77#[must_use = "futures do nothing unless polled"]
78pub struct SkipFuture(Box<dyn Future<Item = (TcpStream, usize), Error = io::Error> + Send>);
79
80impl Future for SkipFuture {
81    type Item = (TcpStream, usize);
82    type Error = io::Error;
83
84    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
85        self.0.poll()
86    }
87}
88
89fn err<T: 'static + Send>(msg: &str) -> Box<dyn Future<Item = T, Error = io::Error> + Send> {
90    Box::new(future::err(io::Error::new(io::ErrorKind::InvalidData, msg)))
91}
92
93fn socks5_request_connect(stream: TcpStream, buf: Vec<u8>, dest: &ProxyDest, port: u16) -> ConnectionFuture {
94    info!("Reading socks5 server hello");
95
96    // version
97    if buf[0] != 0x05 {
98        return ConnectionFuture(err("wrong version"));
99    }
100
101    // unauthenticated
102    if buf[1] != 0x00 {
103        return ConnectionFuture(err("auth failed"));
104    }
105
106    info!("Socks5 authentication successful");
107
108    let mut buf = vec![
109        0x05, // version
110        0x01, // tcp connect
111        0x00, // reserved
112    ];
113
114    dest.apply(&mut buf);
115    buf.write_u16::<NetworkEndian>(port).unwrap();
116    info!("Sending connect request");
117    let fut = tokio::io::write_all(stream, buf)
118        .and_then(|(stream, _)| future::ok(stream));
119    ConnectionFuture(Box::new(fut))
120}
121
122pub fn connect(addr: &SocketAddr, dest: ProxyDest, port: u16) -> ConnectionFuture {
123    let fut = TcpStream::connect(&addr)
124        .and_then(|stream| {
125            info!("Sending socks5 hello");
126            tokio::io::write_all(stream, &[
127                0x05, // version
128                0x01, // number of supported auths
129                0x00, // unauthenticated
130            ])
131        })
132        .and_then(|(stream, _)| {
133            let buf = vec![0; 2];
134            tokio::io::read_exact(stream, buf)
135        })
136        .and_then(move |(stream, buf)| socks5_request_connect(stream, buf, &dest, port))
137        .and_then(|stream| {
138            let buf = vec![0; 4];
139            tokio::io::read_exact(stream, buf)
140        })
141        .and_then(|(stream, buf)| {
142            info!("Reading connect response");
143
144            // version
145            if buf[0] != 0x05 {
146                return SkipFuture(err("wrong version"));
147            }
148            // status
149            match buf[1] {
150                0x00 => (),
151                0x01 => return SkipFuture(err("general failure")),
152                0x02 => return SkipFuture(err("connection not allowed by ruleset")),
153                0x03 => return SkipFuture(err("network unreachable")),
154                0x04 => return SkipFuture(err("host unreachable")),
155                0x05 => return SkipFuture(err("connection refused by destination host")),
156                0x06 => return SkipFuture(err("TTL expired")),
157                0x07 => return SkipFuture(err("command not supported / protocol error")),
158                0x08 => return SkipFuture(err("address type not supported")),
159                _    => return SkipFuture(err("unknown connection error")),
160            }
161            // reserved
162            if buf[2] != 0x00 {
163                return SkipFuture(err("wrong reserved bytes"));
164            }
165            info!("Connection successful");
166
167            match buf[3] {
168                0x01 => SkipFuture(Box::new(future::ok((stream, 4)))), // ipv4
169                0x03 => {
170                    let buf = vec![0; 1];
171                    let fut = tokio::io::read_exact(stream, buf)
172                        .and_then(|(stream, buf)| {
173                            Ok((stream, buf[0] as usize))
174                        });
175                    SkipFuture(Box::new(fut))
176                },
177                0x04 => SkipFuture(Box::new(future::ok((stream, 16)))), // ipv6
178                _ => SkipFuture(err("wrong address type")),
179            }
180        })
181        .and_then(|(stream, n)| {
182            let buf = vec![0; n + 2];
183            tokio::io::read_exact(stream, buf)
184        })
185        .and_then(|(stream, _)| {
186            info!("Socks5 tunnel established");
187            future::ok(stream)
188        });
189    ConnectionFuture(Box::new(fut))
190}