Skip to content

Instantly share code, notes, and snippets.

@LuoZijun
Created December 22, 2020 08:22
Show Gist options
  • Save LuoZijun/cde4b00ed038159e19642834ee72dea9 to your computer and use it in GitHub Desktop.
Save LuoZijun/cde4b00ed038159e19642834ee72dea9 to your computer and use it in GitHub Desktop.
#![allow(dead_code, unused_imports)]
use std::net::TcpStream;
use std::net::Shutdown;
use std::net::Ipv4Addr;
use std::net::SocketAddr;
use std::net::SocketAddrV4;
use std::net::ToSocketAddrs;
use std::io::{self, Read, Write};
use std::time::Instant;
use std::time::Duration;
pub fn test_socks5(socks5_server_addr: SocketAddr, target_addr: SocketAddr) -> Result<Duration, io::Error> {
pub const SOCKS_V4: u8 = 0x04; // SOCKS-4、SOCKS-4A
pub const SOCKS_V5: u8 = 0x05; // SOCKS-5
pub const SOCKS_CMD_CONNECT: u8 = 0x01; // SOCKS-4、SOCKS-4A、SOCKS-5
pub const SOCKS_CMD_BIND: u8 = 0x02; // SOCKS-4、SOCKS-4A、SOCKS-5
pub const SOCKS_CMD_UDP_ASSOCIATE: u8 = 0x03; // SOCKS-5
pub const SOCKS_REP_SUCCEEDED: u8 = 0x00;
pub const SOCKS_REP_GENERAL_SERVER_FAILURE: u8 = 0x01;
pub const SOCKS_REP_CONNECTION_NOT_ALLOWED_BY_RULESET: u8 = 0x02;
pub const SOCKS_REP_NETWORK_UNREACHABLE: u8 = 0x03;
pub const SOCKS_REP_HOST_UNREACHABLE: u8 = 0x04;
pub const SOCKS_REP_CONNECTION_REFUSED: u8 = 0x05;
pub const SOCKS_REP_TTL_EXPIRED: u8 = 0x06;
pub const SOCKS_REP_COMMAND_NOT_SUPPORTED: u8 = 0x07;
pub const SOCKS_REP_ADDRESS_TYPE_NOT_SUPPORTED: u8 = 0x08;
pub const SOCKS_ATYP_IPV4: u8 = 0x01;
pub const SOCKS_ATYP_DOMAIN_NAME: u8 = 0x03;
pub const SOCKS_ATYP_IPV6: u8 = 0x04;
pub const SOCKS_METHOD_NO_AUTH: u8 = 0x00;
pub const SOCKS_METHOD_GSSAPI: u8 = 0x01;
pub const SOCKS_METHOD_PASSWD_AUTH: u8 = 0x02;
pub const SOCKS_METHOD_NO_ACCEPTABLE: u8 = 0xFF;
let mut buffer = [0u8; 4096];
let now = Instant::now();
let mut stream = TcpStream::connect(socks5_server_addr)?;
// https://tools.ietf.org/html/rfc1928#section-3
// +----+----------+----------+
// |VER | NMETHODS | METHODS |
// +----+----------+----------+
// | 1 | 1 | 1 to 255 |
// +----+----------+----------+
buffer[0] = SOCKS_V5;
buffer[1] = 0x01;
buffer[2] = SOCKS_METHOD_NO_AUTH;
stream.write_all(&buffer[..3])?;
// +----+--------+
// |VER | METHOD |
// +----+--------+
// | 1 | 1 |
// +----+--------+
stream.read_exact(&mut buffer[..2])?;
assert_eq!(buffer[0], SOCKS_V5);
assert_eq!(buffer[1], SOCKS_METHOD_NO_AUTH);
// https://tools.ietf.org/html/rfc1928#section-4
// +----+-----+-------+------+----------+----------+
// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
buffer[0] = SOCKS_V5; // VER
buffer[1] = SOCKS_CMD_CONNECT; // CMD
buffer[2] = 0x00; // RSV
match target_addr {
SocketAddr::V4(v4_addr) => {
let ip_octets = v4_addr.ip().octets();
let port_octets = v4_addr.port().to_be_bytes();
buffer[3] = SOCKS_ATYP_IPV4; // ATYP
buffer[4..8].copy_from_slice(&ip_octets); // BND.ADDR
buffer[8] = port_octets[0]; // BND.PORT
buffer[9] = port_octets[1];
let pkt = &buffer[..10];
stream.write_all(pkt)?;
},
SocketAddr::V6(v6_addr) => {
let ip_octets = v6_addr.ip().octets();
let port_octets = v6_addr.port().to_be_bytes();
buffer[3] = SOCKS_ATYP_IPV6; // ATYP
buffer[4..20].copy_from_slice(&ip_octets); // BND.ADDR
buffer[20] = port_octets[0]; // BND.PORT
buffer[21] = port_octets[1];
let pkt = &buffer[..22];
stream.write_all(pkt)?;
},
}
// https://tools.ietf.org/html/rfc1928#section-6
// +----+-----+-------+------+----------+----------+
// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
stream.read_exact(&mut buffer[..4])?;
assert_eq!(buffer[0], SOCKS_V5);
assert_eq!(buffer[1], SOCKS_REP_SUCCEEDED);
let atype = buffer[3];
match atype {
SOCKS_ATYP_IPV4 => {
stream.read_exact(&mut buffer[..6])?; // IPV4_ADDR + Port
},
SOCKS_ATYP_DOMAIN_NAME => {
stream.read_exact(&mut buffer[..1])?; // DOMAIN-NAME LEN in octets
let nlen = buffer[0] as usize;
stream.read_exact(&mut buffer[..nlen])?; // DOMAIN-NAME
stream.read_exact(&mut buffer[..2])?; // Port
},
SOCKS_ATYP_IPV6 => {
stream.read_exact(&mut buffer[..18])?; // IPV6_ADDR + Port
},
_ => return Err(io::Error::new(io::ErrorKind::ConnectionAborted, "unsupported socks5 address type")),
}
// Relay
stream.write_all(b"GET / HTTP/1.1\r\n\
Host: www.pornhub.com\r\n\
\r\n")?;
let amt = stream.read(&mut buffer)?;
assert!(amt > 0);
let new_now = Instant::now();
let duration = new_now.duration_since(now);
stream.shutdown(Shutdown::Both)?;
Ok(duration)
}
pub fn test_native(target_addr: SocketAddr) -> Result<Duration, io::Error> {
let mut buffer = [0u8; 4096];
let now = Instant::now();
let mut stream = TcpStream::connect(target_addr)?;
stream.write_all(b"GET / HTTP/1.1\r\n\
Host: www.pornhub.com\r\n\
\r\n")?;
let amt = stream.read(&mut buffer)?;
assert!(amt > 0);
let new_now = Instant::now();
let duration = new_now.duration_since(now);
stream.shutdown(Shutdown::Both)?;
Ok(duration)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let socks5_server_addr = SocketAddr::from(([127, 0, 0, 1], 1080));
let target_addr = SocketAddr::from(([127, 0, 0, 1], 80));
println!("TARGET-ADDR: {:?}", target_addr);
for _ in 0..20 {
let t1 = test_native(target_addr)?;
let t2 = test_socks5(socks5_server_addr, target_addr)?;
println!(" Native : {:>13} via SOCKS-5: {:>13}", format!("{:?}", t1), format!("{:?}", t2));
}
let target_addr = SocketAddr::from(([180, 101, 49, 12], 80)); // baidu.com
println!("TARGET-ADDR: {:?}", target_addr);
for _ in 0..20 {
let t1 = test_native(target_addr)?;
let t2 = test_socks5(socks5_server_addr, target_addr)?;
println!(" Native : {:>13} via SOCKS-5: {:>13}", format!("{:?}", t1), format!("{:?}", t2));
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment