1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use futures::{Async, Future, Poll};
use hyper::client::connect::{Connect, Connected, Destination};
#[cfg(feature = "tls")]
use native_tls::TlsConnector;
use socks::{Socks4Stream, Socks5Stream};
use std::{io, net::ToSocketAddrs};
use tokio::{net::TcpStream, reactor::Handle};
#[cfg(feature = "tls")]
pub use {hyper_tls::HttpsConnector, native_tls::Error};
pub struct Connection {
inner: Option<Poll<(TcpStream, Connected), io::Error>>,
}
impl Connection {
fn result(result: Result<(TcpStream, Connected), io::Error>) -> Self {
let inner = Some(result.map(Async::Ready));
Connection { inner }
}
fn error(error: io::Error) -> Self {
Connection::result(Err(error))
}
}
impl Future for Connection {
type Item = (TcpStream, Connected);
type Error = io::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.inner.take().expect("cannot take Poll twice")
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Proxy<T: ToSocketAddrs> {
Socks4 { addrs: T, user_id: String },
Socks5 { addrs: T, auth: Option<Auth> },
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Auth {
pub user: String,
pub pass: String,
}
impl<T> Proxy<T>
where
T: ToSocketAddrs,
{
#[cfg(feature = "tls")]
pub fn with_tls(self) -> Result<HttpsConnector<Self>, Error> {
let args = (self, TlsConnector::new()?);
Ok(HttpsConnector::from(args))
}
}
impl<T> Connect for Proxy<T>
where
T: ToSocketAddrs + Send + Sync,
{
type Transport = TcpStream;
type Error = io::Error;
type Future = Connection;
fn connect(&self, dst: Destination) -> Self::Future {
let scheme = dst.scheme();
let port = if let Some(p) = dst.port() {
p
} else if scheme == "http" {
80
} else if scheme == "https" {
443
} else {
return Connection::error(io::Error::new(io::ErrorKind::InvalidInput, "missing port"));
};
let target = (dst.host(), port);
let res = match self {
Proxy::Socks4 {
ref addrs,
ref user_id,
} => Socks4Stream::connect(addrs, target, &user_id).map(Socks4Stream::into_inner),
Proxy::Socks5 {
ref addrs,
ref auth,
} => {
let res = match auth {
Some(auth) => {
Socks5Stream::connect_with_password(addrs, target, &auth.user, &auth.pass)
}
None => Socks5Stream::connect(addrs, target),
};
res.map(Socks5Stream::into_inner)
}
};
let res = res
.and_then(|stream| TcpStream::from_std(stream, &Handle::default()))
.map(|stream| (stream, Connected::new()));
Connection::result(res)
}
}