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
extern crate byteorder;
use byteorder::{WriteBytesExt, BigEndian};
use std::net::{TcpStream, IpAddr, ToSocketAddrs};
use std::io::{self, Read, Write};
pub struct SocksStream {
stream: TcpStream
}
enum Version {
Five = 5,
}
enum AuthType {
None = 0x00,
Invalid = 0xff
}
enum Command {
Connect = 1,
}
enum AddrType {
Ipv4 = 1,
Ipv6 = 4,
}
enum ReplyStatus {
Success = 0,
}
impl SocksStream {
pub fn connect<T: ToSocketAddrs>(proxy: T, target: T) -> io::Result<SocksStream> {
let target_addr = target.to_socket_addrs()?.next().unwrap();
let mut stream = TcpStream::connect(proxy)?;
let mut buf = Vec::new();
let _ = buf.write_u8(Version::Five as u8);
let _ = buf.write_u8(1);
let _ = buf.write_u8(AuthType::None as u8);
let _ = stream.write_all(&buf);
let mut reply = [0; 2];
let _ = stream.read(&mut reply);
if reply[1] == AuthType::Invalid as u8 {
return Err(io::Error::new(io::ErrorKind::Other, "No acceptable methods"));
}
buf.clear();
let _ = buf.write_u8(Version::Five as u8);
let _ = buf.write_u8(Command::Connect as u8);
let _ = buf.write_u8(0);
match target_addr.ip() {
IpAddr::V4(addr) => {
let _ = buf.write_u8(AddrType::Ipv4 as u8);
let _ = buf.write_u32::<BigEndian>(addr.into());
},
IpAddr::V6(addr) => {
let _ = buf.write_u8(AddrType::Ipv6 as u8);
for &segment in addr.segments().iter() {
let _ = buf.write_u16::<BigEndian>(segment);
}
},
};
let _ = buf.write_u16::<BigEndian>(target_addr.port());
let _ = stream.write_all(&buf);
let mut reply = [0; 128];
let _ = stream.read(&mut reply);
if reply[1] != ReplyStatus::Success as u8 {
return Err(io::Error::new(io::ErrorKind::Other, format!("Failure: {}", reply[1])));
}
Ok(SocksStream {
stream: stream
})
}
}
impl Read for SocksStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.stream.read(buf)
}
}
impl Write for SocksStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.stream.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.stream.flush()
}
}
#[cfg(test)]
mod tests {
use super::SocksStream;
use std::io::{Read, Write};
#[test]
fn ipv4() {
let mut proxy = SocksStream::connect("127.0.0.1:9050", "216.58.216.238:80").unwrap();
let _ = proxy.write_all(b"GET / HTTP/1.0\n\n");
let mut response = Vec::new();
let _ = proxy.read_to_end(&mut response);
assert!(String::from_utf8_lossy(&response).starts_with("HTTP/1.0"));
}
}