mirror of
https://github.com/shadowsocks/shadowsocks-rust.git
synced 2026-02-09 01:59:16 +08:00
re-constructed
This commit is contained in:
@@ -9,8 +9,9 @@ use getopts::{optopt, optflag, getopts, usage};
|
||||
use std::os;
|
||||
|
||||
use shadowsocks::config::Config;
|
||||
use shadowsocks::tcprelay::TcpRelayLocal;
|
||||
use shadowsocks::relay::TcpRelayLocal;
|
||||
use shadowsocks::relay::Relay;
|
||||
use shadowsocks::crypto::cipher::CIPHER_AES_128_CFB;
|
||||
|
||||
fn main() {
|
||||
let opts = [
|
||||
@@ -22,7 +23,7 @@ fn main() {
|
||||
optopt("k", "password", "password", ""),
|
||||
optopt("p", "server-port", "server port", ""),
|
||||
optopt("l", "local-port", "local socks5 proxy port", ""),
|
||||
optopt("m", "encrypt-method", "entryption method", "aes-256-cfb"),
|
||||
optopt("m", "encrypt-method", "entryption method", CIPHER_AES_128_CFB),
|
||||
];
|
||||
|
||||
let matches = getopts(os::args().tail(), opts).unwrap();
|
||||
@@ -38,20 +39,21 @@ fn main() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut config = if matches.opt_present("c") {
|
||||
Config::load_from_file(matches.opt_str("c")
|
||||
.unwrap().as_slice()).unwrap()
|
||||
} else {
|
||||
match Config::load_from_file("config.json") {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
error!("Cannot find any `config.json` under current directory");
|
||||
println!("{}", usage(format!("Usage: {} [options]", os::args()[0]).as_slice(),
|
||||
opts));
|
||||
return;
|
||||
let mut config =
|
||||
if matches.opt_present("c") {
|
||||
Config::load_from_file(matches.opt_str("c")
|
||||
.unwrap().as_slice()).unwrap()
|
||||
} else {
|
||||
match Config::load_from_file("config.json") {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
error!("Cannot find any `config.json` under current directory");
|
||||
println!("{}", usage(format!("Usage: {} [options]", os::args()[0]).as_slice(),
|
||||
opts));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
if matches.opt_present("s") {
|
||||
let server_ip = matches.opt_str("s").unwrap();
|
||||
@@ -76,7 +78,7 @@ fn main() {
|
||||
if matches.opt_present("m") {
|
||||
let mut encrypt_meth = matches.opt_str("m").unwrap();
|
||||
if encrypt_meth.as_slice() == "" {
|
||||
encrypt_meth = "aes-256-cfb".to_string();
|
||||
encrypt_meth = CIPHER_AES_128_CFB.to_string();
|
||||
}
|
||||
|
||||
config.method = encrypt_meth;
|
||||
@@ -86,5 +88,5 @@ fn main() {
|
||||
|
||||
debug!("Config: {}", config)
|
||||
|
||||
TcpRelayLocal::new(&config).run();
|
||||
TcpRelayLocal::new(config).run();
|
||||
}
|
||||
|
||||
@@ -9,8 +9,9 @@ use getopts::{optopt, optflag, getopts, usage};
|
||||
use std::os;
|
||||
|
||||
use shadowsocks::config::Config;
|
||||
use shadowsocks::tcprelay::TcpRelayServer;
|
||||
use shadowsocks::relay::TcpRelayServer;
|
||||
use shadowsocks::relay::Relay;
|
||||
use shadowsocks::crypto::cipher::CIPHER_AES_128_CFB;
|
||||
|
||||
fn main() {
|
||||
let opts = [
|
||||
@@ -22,7 +23,7 @@ fn main() {
|
||||
optopt("k", "password", "password", ""),
|
||||
optopt("p", "server-port", "server port", ""),
|
||||
optopt("l", "local-port", "local socks5 proxy port", ""),
|
||||
optopt("m", "encrypt-method", "entryption method", "aes-256-cfb"),
|
||||
optopt("m", "encrypt-method", "entryption method", CIPHER_AES_128_CFB),
|
||||
];
|
||||
|
||||
let matches = getopts(os::args().tail(), opts).unwrap();
|
||||
@@ -38,18 +39,19 @@ fn main() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut config = if matches.opt_present("c") {
|
||||
Config::load_from_file(matches.opt_str("c")
|
||||
.unwrap().as_slice()).unwrap()
|
||||
} else {
|
||||
match Config::load_from_file("config.json") {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
error!("Cannot find any `config.json` under current directory");
|
||||
return;
|
||||
let mut config =
|
||||
if matches.opt_present("c") {
|
||||
Config::load_from_file(matches.opt_str("c")
|
||||
.unwrap().as_slice()).unwrap()
|
||||
} else {
|
||||
match Config::load_from_file("config.json") {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
error!("Cannot find any `config.json` under current directory");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
if matches.opt_present("s") {
|
||||
let server_ip = matches.opt_str("s").unwrap();
|
||||
@@ -74,7 +76,7 @@ fn main() {
|
||||
if matches.opt_present("m") {
|
||||
let mut encrypt_meth = matches.opt_str("m").unwrap();
|
||||
if encrypt_meth.as_slice() == "" {
|
||||
encrypt_meth = "aes-256-cfb".to_string();
|
||||
encrypt_meth = CIPHER_AES_128_CFB.to_string();
|
||||
}
|
||||
|
||||
config.method = encrypt_meth;
|
||||
@@ -84,5 +86,5 @@ fn main() {
|
||||
|
||||
debug!("Config: {}", config)
|
||||
|
||||
TcpRelayServer::new(&config).run();
|
||||
TcpRelayServer::new(config).run();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ use std::fmt::{Show, Formatter, mod};
|
||||
|
||||
use std::option::Option;
|
||||
|
||||
use crypto::cipher::CIPHER_AES_128_CFB;
|
||||
|
||||
#[deriving(Encodable, Clone)]
|
||||
pub struct Config {
|
||||
pub server: String,
|
||||
@@ -29,7 +31,7 @@ impl Config {
|
||||
server_port: 8000,
|
||||
local_port: 8000,
|
||||
password: "".to_string(),
|
||||
method: "aes-128-cfb".to_string(),
|
||||
method: CIPHER_AES_128_CFB.to_string(),
|
||||
timeout: None,
|
||||
fast_open: false,
|
||||
}
|
||||
@@ -92,7 +94,7 @@ impl Config {
|
||||
pub fn load_from_str(s: &str) -> Option<Config> {
|
||||
let object = match json::from_str(s) {
|
||||
Ok(obj) => { obj },
|
||||
Err(e) => return None,
|
||||
Err(..) => return None,
|
||||
};
|
||||
|
||||
let json_object = match object.as_object() {
|
||||
@@ -106,7 +108,7 @@ impl Config {
|
||||
pub fn load_from_file(filename: &str) -> Option<Config> {
|
||||
let mut readeropt = File::open_mode(&Path::new(filename), Open, Read);
|
||||
|
||||
let mut reader = match readeropt {
|
||||
let reader = match readeropt {
|
||||
Ok(ref mut r) => r,
|
||||
Err(..) => return None,
|
||||
};
|
||||
|
||||
@@ -6,23 +6,83 @@ pub trait Cipher {
|
||||
fn decrypt(&self, data: &[u8]) -> Vec<u8>;
|
||||
}
|
||||
|
||||
pub const CIPHER_AES_128_CFB: &'static str = "aes-128-cfb";
|
||||
pub const CIPHER_AES_128_CFB_1: &'static str = "aes-128-cfb1";
|
||||
pub const CIPHER_AES_128_CFB_8: &'static str = "aes-128-cfb8";
|
||||
pub const CIPHER_AES_128_CFB_128: &'static str = "aes-128-cfb128";
|
||||
|
||||
pub const CIPHER_AES_192_CFB: &'static str = "aes-192-cfb";
|
||||
pub const CIPHER_AES_192_CFB_1: &'static str = "aes-192-cfb1";
|
||||
pub const CIPHER_AES_192_CFB_8: &'static str = "aes-192-cfb8";
|
||||
pub const CIPHER_AES_192_CFB_128: &'static str = "aes-192-cfb128";
|
||||
|
||||
pub const CIPHER_AES_256_CFB: &'static str = "aes-256-cfb";
|
||||
pub const CIPHER_AES_256_CFB_1: &'static str = "aes-256-cfb1";
|
||||
pub const CIPHER_AES_256_CFB_8: &'static str = "aes-256-cfb8";
|
||||
pub const CIPHER_AES_256_CFB_128: &'static str = "aes-256-cfb128";
|
||||
|
||||
pub enum CipherType {
|
||||
CipherTypeAes128Cfb,
|
||||
CipherTypeAes128Cfb1,
|
||||
CipherTypeAes128Cfb8,
|
||||
CipherTypeAes128Cfb128,
|
||||
CipherTypeAes192Cfb,
|
||||
CipherTypeAes192Cfb1,
|
||||
CipherTypeAes192Cfb8,
|
||||
CipherTypeAes192Cfb128,
|
||||
CipherTypeAes256Cfb,
|
||||
CipherTypeAes256Cfb1,
|
||||
CipherTypeAes256Cfb8,
|
||||
CipherTypeAes256Cfb128,
|
||||
}
|
||||
|
||||
// pub fn get_cipher_by_name(method: &str, key: &[u8]) -> Cipher {
|
||||
// let c = match method {
|
||||
// CIPHER_AES_128_CFB =>
|
||||
// Some(openssl::OpenSSLCipher::new(CipherTypeAes128Cfb, key)),
|
||||
// CIPHER_AES_192_CFB =>
|
||||
// Some(openssl::OpenSSLCipher::new(CipherTypeAes192Cfb, key)),
|
||||
// CIPHER_AES_256_CFB =>
|
||||
// Some(openssl::OpenSSLCipher::new(CipherTypeAes256Cfb, key)),
|
||||
pub enum CipherVariant {
|
||||
OpenSSLCrypto(openssl::OpenSSLCipher),
|
||||
}
|
||||
|
||||
// _ => None
|
||||
// };
|
||||
impl Cipher for CipherVariant {
|
||||
fn encrypt(&self, data: &[u8]) -> Vec<u8> {
|
||||
match *self {
|
||||
OpenSSLCrypto(ref c) => c.encrypt(data),
|
||||
}
|
||||
}
|
||||
|
||||
// c.unwrap()
|
||||
// }
|
||||
fn decrypt(&self, data: &[u8]) -> Vec<u8> {
|
||||
match *self {
|
||||
OpenSSLCrypto(ref c) => c.decrypt(data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_name(method: &str, key: &[u8]) -> Option<CipherVariant> {
|
||||
match method {
|
||||
CIPHER_AES_128_CFB => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeAes128Cfb, key))),
|
||||
CIPHER_AES_128_CFB_1 => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeAes128Cfb1, key))),
|
||||
CIPHER_AES_128_CFB_8 => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeAes128Cfb8, key))),
|
||||
CIPHER_AES_128_CFB_128 => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeAes128Cfb128, key))),
|
||||
|
||||
CIPHER_AES_192_CFB => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeAes192Cfb, key))),
|
||||
CIPHER_AES_192_CFB_1 => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeAes192Cfb1, key))),
|
||||
CIPHER_AES_192_CFB_8 => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeAes192Cfb8, key))),
|
||||
CIPHER_AES_192_CFB_128 => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeAes192Cfb128, key))),
|
||||
|
||||
CIPHER_AES_256_CFB => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeAes256Cfb, key))),
|
||||
CIPHER_AES_256_CFB_1 => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeAes256Cfb1, key))),
|
||||
CIPHER_AES_256_CFB_8 => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeAes256Cfb8, key))),
|
||||
CIPHER_AES_256_CFB_128 => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeAes256Cfb128, key))),
|
||||
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_cipher() {
|
||||
let key = "PASSWORD";
|
||||
let c = with_name(CIPHER_AES_128_CFB, key.as_bytes()).unwrap();
|
||||
let message = "HELLO WORLD";
|
||||
|
||||
let encrypted_msg = c.encrypt(message.as_bytes());
|
||||
let decrypted_msg = c.decrypt(encrypted_msg.as_slice());
|
||||
|
||||
assert!(message.as_bytes() == decrypted_msg.as_slice());
|
||||
}
|
||||
|
||||
@@ -2,7 +2,3 @@ extern crate libc;
|
||||
|
||||
pub mod cipher;
|
||||
pub mod openssl;
|
||||
|
||||
pub const CIPHER_AES_128_CFB: &'static str = "aes-128-cfb";
|
||||
pub const CIPHER_AES_192_CFB: &'static str = "aes-192-cfb";
|
||||
pub const CIPHER_AES_256_CFB: &'static str = "aes-256-cfb";
|
||||
|
||||
@@ -4,13 +4,12 @@ extern crate log;
|
||||
use crypto::cipher::Cipher;
|
||||
use crypto::cipher;
|
||||
|
||||
use std::str;
|
||||
use std::ptr;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
type EVP_CIPHER_CTX = *const libc::c_void;
|
||||
#[allow(non_camel_case_types)]
|
||||
type EVP_CIPHER = *mut libc::c_void;
|
||||
type EVP_CIPHER = *const libc::c_void;
|
||||
#[allow(non_camel_case_types)]
|
||||
type EVP_MD = *const libc::c_void;
|
||||
|
||||
@@ -20,7 +19,6 @@ const CIPHER_MODE_DECRYPT: libc::c_int = 0;
|
||||
#[allow(dead_code)]
|
||||
#[link(name="crypto")]
|
||||
extern {
|
||||
fn EVP_get_cipherbyname(name: *const libc::c_char) -> EVP_CIPHER;
|
||||
fn EVP_CIPHER_CTX_new() -> EVP_CIPHER_CTX;
|
||||
fn EVP_CIPHER_CTX_cleanup(ctx: EVP_CIPHER_CTX);
|
||||
fn EVP_CIPHER_CTX_free(ctx: EVP_CIPHER_CTX);
|
||||
@@ -37,17 +35,23 @@ extern {
|
||||
count: libc::c_int, key: *mut libc::c_uchar, iv: *mut libc::c_uchar) -> libc::c_int;
|
||||
|
||||
// Ciphers
|
||||
fn EVP_aes_128_cfb() -> EVP_CIPHER;
|
||||
fn EVP_aes_128_cfb1() -> EVP_CIPHER;
|
||||
fn EVP_aes_128_cfb8() -> EVP_CIPHER;
|
||||
fn EVP_aes_128_cfb128() -> EVP_CIPHER;
|
||||
fn EVP_aes_192_cfb() -> EVP_CIPHER;
|
||||
fn EVP_aes_192_cfb1() -> EVP_CIPHER;
|
||||
fn EVP_aes_192_cfb8() -> EVP_CIPHER;
|
||||
fn EVP_aes_192_cfb128() -> EVP_CIPHER;
|
||||
fn EVP_aes_256_cfb() -> EVP_CIPHER;
|
||||
fn EVP_aes_256_cfb1() -> EVP_CIPHER;
|
||||
fn EVP_aes_256_cfb8() -> EVP_CIPHER;
|
||||
fn EVP_aes_256_cfb128() -> EVP_CIPHER;
|
||||
|
||||
// MD
|
||||
fn EVP_md5() -> EVP_MD;
|
||||
fn EVP_sha() -> EVP_MD;
|
||||
fn EVP_sha1() -> EVP_MD;
|
||||
|
||||
// Errors
|
||||
fn ERR_print_errors_fp(fp: libc::c_int);
|
||||
}
|
||||
|
||||
enum CryptoMode {
|
||||
@@ -65,9 +69,20 @@ impl OpenSSLCrypto {
|
||||
pub fn new(cipher_type: cipher::CipherType, key: &[u8], mode: CryptoMode) -> OpenSSLCrypto {
|
||||
let (ctx, _, block_size) = unsafe {
|
||||
let (cipher, key_size, block_size) = match cipher_type {
|
||||
cipher::CipherTypeAes128Cfb => { (EVP_aes_128_cfb128(), 16, 16) },
|
||||
cipher::CipherTypeAes192Cfb => { (EVP_aes_192_cfb() , 24, 16) },
|
||||
cipher::CipherTypeAes256Cfb => { (EVP_aes_256_cfb() , 32, 16) },
|
||||
cipher::CipherTypeAes128Cfb => { (EVP_aes_128_cfb(), 16, 16) },
|
||||
cipher::CipherTypeAes128Cfb1 => { (EVP_aes_128_cfb1(), 16, 16) },
|
||||
cipher::CipherTypeAes128Cfb8 => { (EVP_aes_128_cfb8(), 16, 16) },
|
||||
cipher::CipherTypeAes128Cfb128 => { (EVP_aes_128_cfb128(), 16, 16) },
|
||||
|
||||
cipher::CipherTypeAes192Cfb => { (EVP_aes_192_cfb(), 24, 16) },
|
||||
cipher::CipherTypeAes192Cfb1 => { (EVP_aes_192_cfb1(), 24, 16) },
|
||||
cipher::CipherTypeAes192Cfb8 => { (EVP_aes_192_cfb8(), 24, 16) },
|
||||
cipher::CipherTypeAes192Cfb128 => { (EVP_aes_192_cfb128(), 24, 16) },
|
||||
|
||||
cipher::CipherTypeAes256Cfb => { (EVP_aes_256_cfb(), 32, 16) },
|
||||
cipher::CipherTypeAes256Cfb1 => { (EVP_aes_256_cfb1(), 32, 16) },
|
||||
cipher::CipherTypeAes256Cfb8 => { (EVP_aes_256_cfb8(), 32, 16) },
|
||||
cipher::CipherTypeAes256Cfb128 => { (EVP_aes_256_cfb128(), 32, 16) },
|
||||
};
|
||||
|
||||
let evp_ctx = EVP_CIPHER_CTX_new();
|
||||
@@ -173,10 +188,27 @@ impl Cipher for OpenSSLCipher {
|
||||
|
||||
#[test]
|
||||
fn test_aes() {
|
||||
use std::str;
|
||||
|
||||
let message = "hello world";
|
||||
let key = "passwordhaha";
|
||||
|
||||
let types = [cipher::CipherTypeAes128Cfb, cipher::CipherTypeAes192Cfb, cipher::CipherTypeAes256Cfb];
|
||||
let types = [
|
||||
cipher::CipherTypeAes128Cfb,
|
||||
cipher::CipherTypeAes128Cfb1,
|
||||
cipher::CipherTypeAes128Cfb8,
|
||||
cipher::CipherTypeAes128Cfb128,
|
||||
|
||||
cipher::CipherTypeAes192Cfb,
|
||||
cipher::CipherTypeAes192Cfb1,
|
||||
cipher::CipherTypeAes192Cfb8,
|
||||
cipher::CipherTypeAes192Cfb128,
|
||||
|
||||
cipher::CipherTypeAes256Cfb,
|
||||
cipher::CipherTypeAes256Cfb1,
|
||||
cipher::CipherTypeAes256Cfb8,
|
||||
cipher::CipherTypeAes256Cfb128,
|
||||
];
|
||||
|
||||
for t in types.iter() {
|
||||
let cipher = OpenSSLCipher::new(*t, key.as_bytes());
|
||||
@@ -187,6 +219,6 @@ fn test_aes() {
|
||||
let decrypted_msg = cipher.decrypt(encrypted_msg.as_slice());
|
||||
debug!("DEC {}", str::from_utf8(decrypted_msg.as_slice()).unwrap());
|
||||
|
||||
assert!(message == str::from_utf8(decrypted_msg.as_slice()).unwrap());
|
||||
assert!(message.as_bytes() == decrypted_msg.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,4 @@ pub const VERSION: &'static str = "0.0.1";
|
||||
|
||||
pub mod config;
|
||||
pub mod relay;
|
||||
pub mod tcprelay;
|
||||
pub mod udprelay;
|
||||
mod crypto;
|
||||
pub mod crypto;
|
||||
|
||||
23
src/relay.rs
23
src/relay.rs
@@ -1,23 +0,0 @@
|
||||
|
||||
pub trait Relay {
|
||||
fn run(&mut self);
|
||||
}
|
||||
|
||||
pub enum Stage {
|
||||
StageInit,
|
||||
StageHello,
|
||||
StageUdpAssoc,
|
||||
StageDns,
|
||||
StageReply,
|
||||
StageStream,
|
||||
}
|
||||
|
||||
pub const SOCK5_VERSION : u8 = 5;
|
||||
|
||||
pub const SOCK5_CMD_TCP_CONNECT : u8 = 1;
|
||||
pub const SOCK5_CMD_TCP_BIND : u8 = 2;
|
||||
pub const SOCK5_CMD_UDP_ASSOCIATE : u8 = 3;
|
||||
|
||||
pub const SOCK5_ADDR_MODE_IPV4 : u8 = 0x01;
|
||||
pub const SOCK5_ADDR_MODE_DOMAIN_NAME : u8 = 0x03;
|
||||
pub const SOCK5_ADDR_MODE_IPV6 : u8 = 0x04;
|
||||
40
src/relay/mod.rs
Normal file
40
src/relay/mod.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
pub use self::tcprelay::local::TcpRelayLocal;
|
||||
pub use self::tcprelay::server::TcpRelayServer;
|
||||
|
||||
pub mod tcprelay;
|
||||
|
||||
pub trait Relay {
|
||||
fn run(&self);
|
||||
}
|
||||
|
||||
pub const SOCK5_VERSION : u8 = 0x05;
|
||||
|
||||
pub const SOCK5_AUTH_METHOD_NONE : u8 = 0x00;
|
||||
pub const SOCK5_AUTH_METHOD_GSSAPI : u8 = 0x01;
|
||||
pub const SOCK5_AUTH_METHOD_PASSWORD : u8 = 0x02;
|
||||
pub const SOCK5_AUTH_METHOD_NOT_ACCEPTABLE : u8 = 0xff;
|
||||
|
||||
pub const SOCK5_CMD_TCP_CONNECT : u8 = 0x01;
|
||||
pub const SOCK5_CMD_TCP_BIND : u8 = 0x02;
|
||||
pub const SOCK5_CMD_UDP_ASSOCIATE : u8 = 0x03;
|
||||
|
||||
pub const SOCK5_ADDR_TYPE_IPV4 : u8 = 0x01;
|
||||
pub const SOCK5_ADDR_TYPE_DOMAIN_NAME : u8 = 0x03;
|
||||
pub const SOCK5_ADDR_TYPE_IPV6 : u8 = 0x04;
|
||||
|
||||
pub const SOCK5_REPLY_SUCCEEDED : u8 = 0x00;
|
||||
pub const SOCK5_REPLY_GENERAL_FAILURE : u8 = 0x01;
|
||||
pub const SOCK5_REPLY_CONNECTION_NOT_ALLOWED: u8 = 0x02;
|
||||
pub const SOCK5_REPLY_NETWORK_UNREACHABLE : u8 = 0x03;
|
||||
pub const SOCK5_REPLY_HOST_UNREACHABLE : u8 = 0x04;
|
||||
pub const SOCK5_REPLY_CONNECTION_REFUSED : u8 = 0x05;
|
||||
pub const SOCK5_REPLY_TTL_EXPIRED : u8 = 0x06;
|
||||
pub const SOCK5_REPLY_COMMAND_NOT_SUPPORTED : u8 = 0x07;
|
||||
pub const SOCK5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED : u8 = 0x08;
|
||||
|
||||
pub enum Sock5AddrType {
|
||||
Sock5AddrTypeIpv4,
|
||||
Sock5AddrTypeIpv6,
|
||||
Sock5AddrTypeDomainName,
|
||||
}
|
||||
|
||||
197
src/relay/tcprelay/local.rs
Normal file
197
src/relay/tcprelay/local.rs
Normal file
@@ -0,0 +1,197 @@
|
||||
#[phase(plugin, link)]
|
||||
extern crate log;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::io::{Listener, TcpListener, Acceptor, TcpStream};
|
||||
use std::io::net::ip::{Ipv4Addr, Ipv6Addr};
|
||||
use std::vec::Vec;
|
||||
use std::string::String;
|
||||
|
||||
use config::Config;
|
||||
use relay::Relay;
|
||||
|
||||
use relay::{SOCK5_VERSION, SOCK5_AUTH_METHOD_NONE};
|
||||
use relay::{SOCK5_CMD_TCP_CONNECT, SOCK5_CMD_TCP_BIND, SOCK5_CMD_UDP_ASSOCIATE};
|
||||
use relay::{SOCK5_REPLY_COMMAND_NOT_SUPPORTED, SOCK5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED};
|
||||
use relay::{SOCK5_ADDR_TYPE_IPV4, SOCK5_ADDR_TYPE_IPV6, SOCK5_ADDR_TYPE_DOMAIN_NAME};
|
||||
use relay::{Sock5AddrType, Sock5AddrTypeIpv4, Sock5AddrTypeIpv6, Sock5AddrTypeDomainName};
|
||||
|
||||
use crypto::cipher;
|
||||
|
||||
pub struct TcpRelayLocal {
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl TcpRelayLocal {
|
||||
pub fn new(c: Config) -> TcpRelayLocal {
|
||||
TcpRelayLocal {
|
||||
config: c,
|
||||
}
|
||||
}
|
||||
|
||||
fn do_handshake(stream: &mut TcpStream) {
|
||||
// Read the handshake header
|
||||
// +----+----------+----------+
|
||||
// |VER | NMETHODS | METHODS |
|
||||
// +----+----------+----------+
|
||||
// | 5 | 1 | 1 to 255 |
|
||||
// +----+----------+----------+
|
||||
let handshake_header = stream.read_exact(2).ok().expect("Error occurs while receiving handshake header");
|
||||
let (sock_ver, nmethods) = (handshake_header[0], handshake_header[1]);
|
||||
|
||||
if sock_ver != SOCK5_VERSION {
|
||||
fail!("Invalid sock version {}", sock_ver);
|
||||
}
|
||||
|
||||
let _ = stream.read_exact(nmethods as uint).ok().expect("Error occurs while receiving methods");
|
||||
// TODO: validating methods
|
||||
|
||||
// Reply to client
|
||||
// +----+--------+
|
||||
// |VER | METHOD |
|
||||
// +----+--------+
|
||||
// | 1 | 1 |
|
||||
// +----+--------+
|
||||
let data_to_send: &[u8] = [SOCK5_VERSION, SOCK5_AUTH_METHOD_NONE];
|
||||
stream.write(data_to_send).ok().expect("Error occurs while sending handshake reply");
|
||||
}
|
||||
|
||||
fn send_error_reply(stream: &mut TcpStream, err_code: u8) {
|
||||
let reply = [SOCK5_VERSION, err_code, 0x00];
|
||||
stream.write(reply).ok().expect("Error occurs while sending errors");
|
||||
}
|
||||
|
||||
fn parse_request_header(stream: &mut TcpStream) -> (Vec<u8>, Sock5AddrType, String, u16) {
|
||||
let mut raw_header = Vec::new();
|
||||
|
||||
let atyp = stream.read_exact(1).unwrap()[0];
|
||||
raw_header.push(atyp);
|
||||
match atyp {
|
||||
SOCK5_ADDR_TYPE_IPV4 => {
|
||||
let raw_addr = stream.read_exact(4).unwrap();
|
||||
raw_header.push_all(raw_addr.as_slice());
|
||||
let v4addr = Ipv4Addr(raw_addr[0], raw_addr[1], raw_addr[2], raw_addr[3]);
|
||||
|
||||
let raw_port = stream.read_exact(2).unwrap();
|
||||
raw_header.push_all(raw_port.as_slice());
|
||||
let port = (raw_port[0] as u16 << 8) | raw_port[1] as u16;
|
||||
|
||||
(raw_header, Sock5AddrTypeIpv4, v4addr.to_string(), port)
|
||||
},
|
||||
SOCK5_ADDR_TYPE_IPV6 => {
|
||||
let raw_addr = stream.read_exact(16).unwrap();
|
||||
raw_header.push_all(raw_addr.as_slice());
|
||||
let v6addr = Ipv6Addr((raw_addr[0] as u16 << 8) | raw_addr[1] as u16,
|
||||
(raw_addr[2] as u16 << 8) | raw_addr[3] as u16,
|
||||
(raw_addr[4] as u16 << 8) | raw_addr[5] as u16,
|
||||
(raw_addr[6] as u16 << 8) | raw_addr[7] as u16,
|
||||
(raw_addr[8] as u16 << 8) | raw_addr[9] as u16,
|
||||
(raw_addr[10] as u16 << 8) | raw_addr[11] as u16,
|
||||
(raw_addr[12] as u16 << 8) | raw_addr[13] as u16,
|
||||
(raw_addr[14] as u16 << 8) | raw_addr[15] as u16);
|
||||
|
||||
let raw_port = stream.read_exact(2).unwrap();
|
||||
raw_header.push_all(raw_port.as_slice());
|
||||
let port = (raw_port[0] as u16 << 8) | raw_port[1] as u16;
|
||||
|
||||
(raw_header, Sock5AddrTypeIpv6, v6addr.to_string(), port)
|
||||
},
|
||||
SOCK5_ADDR_TYPE_DOMAIN_NAME => {
|
||||
let addr_len = stream.read_exact(1).unwrap()[0];
|
||||
raw_header.push(addr_len);
|
||||
let raw_addr = stream.read_exact(addr_len as uint).unwrap();
|
||||
raw_header.push_all(raw_addr.as_slice());
|
||||
|
||||
let raw_port = stream.read_exact(2).unwrap();
|
||||
raw_header.push_all(raw_port.as_slice());
|
||||
let port = (raw_port[0] as u16 << 8) | raw_port[1] as u16;
|
||||
|
||||
(raw_header, Sock5AddrTypeDomainName, String::from_utf8(raw_addr).unwrap(), port)
|
||||
},
|
||||
_ => {
|
||||
// Address type not supported
|
||||
TcpRelayLocal::send_error_reply(stream, SOCK5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED);
|
||||
fail!("Unsupported address type: {}", atyp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Relay for TcpRelayLocal {
|
||||
fn run(&self) {
|
||||
let local_addr = self.config.local.as_slice();
|
||||
let local_port = self.config.local_port;
|
||||
|
||||
let server_addr = Arc::new(self.config.server.clone());
|
||||
let server_port = Arc::new(self.config.server_port);
|
||||
|
||||
let password = Arc::new(self.config.password.clone());
|
||||
let encrypt_method = Arc::new(self.config.method.clone());
|
||||
|
||||
let mut acceptor = match TcpListener::bind(local_addr, local_port).listen() {
|
||||
Ok(acpt) => acpt,
|
||||
Err(e) => {
|
||||
fail!("Error occurs while listening local address: {}", e.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
match acceptor.accept() {
|
||||
Ok(mut stream) => {
|
||||
stream.set_timeout(self.config.timeout);
|
||||
|
||||
let server_addr = server_addr.clone();
|
||||
let server_port = server_port.clone();
|
||||
|
||||
let password = password.clone();
|
||||
let encrypt_method = encrypt_method.clone();
|
||||
|
||||
spawn(proc() {
|
||||
TcpRelayLocal::do_handshake(&mut stream);
|
||||
|
||||
let raw_header_part1 = stream.read_exact(3)
|
||||
.ok().expect("Error occurs while reading request header");
|
||||
let (sock_ver, cmd) = (raw_header_part1[0], raw_header_part1[1]);
|
||||
|
||||
if sock_ver != SOCK5_VERSION {
|
||||
fail!("Invalid sock version {}", sock_ver);
|
||||
}
|
||||
|
||||
let (raw_header, atyp, bind_addr, bind_port)
|
||||
= TcpRelayLocal::parse_request_header(&mut stream);
|
||||
|
||||
let mut remote_stream = TcpStream::connect(server_addr.as_slice(),
|
||||
*server_port.deref())
|
||||
.ok().expect("Error occurs while connecting to remote server");
|
||||
|
||||
let cipher = cipher::with_name(encrypt_method.as_slice(),
|
||||
password.as_slice().as_bytes())
|
||||
.expect("Unsupported cipher");
|
||||
|
||||
match cmd {
|
||||
SOCK5_CMD_TCP_CONNECT => {
|
||||
|
||||
},
|
||||
SOCK5_CMD_TCP_BIND => {
|
||||
|
||||
},
|
||||
SOCK5_CMD_UDP_ASSOCIATE => {
|
||||
|
||||
},
|
||||
_ => {
|
||||
// unsupported CMD
|
||||
TcpRelayLocal::send_error_reply(&mut stream, SOCK5_REPLY_COMMAND_NOT_SUPPORTED);
|
||||
fail!("Unsupported command");
|
||||
}
|
||||
}
|
||||
|
||||
drop(stream);
|
||||
})
|
||||
},
|
||||
Err(e) => {
|
||||
fail!("Error occurs while accepting: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
5
src/relay/tcprelay/mod.rs
Normal file
5
src/relay/tcprelay/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
#[phase(plugin, link)]
|
||||
extern crate log;
|
||||
|
||||
pub mod local;
|
||||
pub mod server;
|
||||
21
src/relay/tcprelay/server.rs
Normal file
21
src/relay/tcprelay/server.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
use config::Config;
|
||||
use relay::Relay;
|
||||
|
||||
pub struct TcpRelayServer {
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl TcpRelayServer {
|
||||
pub fn new(c: Config) -> TcpRelayServer {
|
||||
TcpRelayServer {
|
||||
config: c,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Relay for TcpRelayServer {
|
||||
fn run(&self) {
|
||||
|
||||
}
|
||||
}
|
||||
217
src/tcprelay.rs
217
src/tcprelay.rs
@@ -1,217 +0,0 @@
|
||||
extern crate log;
|
||||
|
||||
// mod relay;
|
||||
use relay::Relay;
|
||||
use relay::Stage;
|
||||
use relay::{StageInit, StageHello, StageUdpAssoc, StageDns, StageReply, StageStream};
|
||||
use relay;
|
||||
|
||||
// mod config;
|
||||
use config::Config;
|
||||
|
||||
use std::io::{TcpListener};
|
||||
use std::io::{Acceptor, Listener};
|
||||
use std::io::net::tcp::{TcpAcceptor, TcpStream};
|
||||
use std::io::{TimedOut, EndOfFile};
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crypto::cipher::Cipher;
|
||||
use crypto::openssl;
|
||||
use crypto::cipher;
|
||||
|
||||
pub struct TcpRelayLocal {
|
||||
acceptor: TcpAcceptor,
|
||||
stage: Stage,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl TcpRelayLocal {
|
||||
pub fn new(c: &Config) -> TcpRelayLocal {
|
||||
let acceptor = TcpListener::bind(c.local.as_slice(), c.local_port).unwrap().listen().unwrap();
|
||||
|
||||
TcpRelayLocal {
|
||||
acceptor: acceptor,
|
||||
stage: StageInit,
|
||||
config: c.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_hello(remote_stream: &mut TcpStream) {
|
||||
let buf = [relay::SOCK5_VERSION, 1, 1];
|
||||
debug!("Sent {} to server", buf.to_vec());
|
||||
remote_stream.write(buf);
|
||||
|
||||
let reply = remote_stream.read_exact(2).unwrap();
|
||||
|
||||
debug!("Recv {} from server", reply);
|
||||
|
||||
if reply[0] != relay::SOCK5_VERSION {
|
||||
fail!("Invalid sock5 version")
|
||||
}
|
||||
|
||||
let method_num = reply[1];
|
||||
|
||||
if method_num == 0xff {
|
||||
fail!("Server does not support the encrypt method");
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_auth(remote_stream: &mut TcpStream) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl Relay for TcpRelayLocal {
|
||||
fn run(&mut self) {
|
||||
let server_str_arc = Arc::new(self.config.server.clone());
|
||||
let server_port_arc = Arc::new(self.config.server_port.clone());
|
||||
let encrypt_password = Arc::new(self.config.password.clone());
|
||||
let encrypt_method = Arc::new(self.config.method.clone());
|
||||
|
||||
loop {
|
||||
let server_str = server_str_arc.clone();
|
||||
let server_port = server_port_arc.clone();
|
||||
let encrypt_password_clone = encrypt_password.clone();
|
||||
|
||||
let cipher = openssl::OpenSSLCipher::new(cipher::CipherTypeAes128Cfb,
|
||||
encrypt_password_clone.as_slice().as_bytes());
|
||||
|
||||
match self.acceptor.accept() {
|
||||
Ok(mut stream) => spawn(proc() {
|
||||
info!("Client {} connected", stream.peer_name().unwrap());
|
||||
|
||||
let server = server_str.as_slice();
|
||||
|
||||
let mut remote_stream = TcpStream::connect(server, *server_port.deref()).unwrap();
|
||||
TcpRelayLocal::handle_hello(&mut remote_stream);
|
||||
TcpRelayLocal::handle_auth(&mut remote_stream);
|
||||
|
||||
loop {
|
||||
let mut buf = [0u8, .. 10240];
|
||||
match stream.read(buf) {
|
||||
Ok(len) => {
|
||||
let s = buf.slice_to(len);
|
||||
|
||||
let encrypted_data = cipher.encrypt(s);
|
||||
|
||||
debug!("Encrypted data {}", encrypted_data);
|
||||
remote_stream.write(encrypted_data.as_slice());
|
||||
},
|
||||
Err(err) => {
|
||||
if err.kind == EndOfFile {
|
||||
break
|
||||
}
|
||||
error!("Err: {}", err);
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!("Client {} disconnected", stream.peer_name().unwrap());
|
||||
|
||||
drop(stream)
|
||||
}),
|
||||
Err(e) => {
|
||||
fail!(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TcpRelayServer {
|
||||
acceptor: TcpAcceptor,
|
||||
stage: Stage,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl TcpRelayServer {
|
||||
pub fn new(c: &Config) -> TcpRelayServer {
|
||||
let acceptor = TcpListener::bind(c.server.as_slice(), c.server_port).unwrap().listen().unwrap();
|
||||
|
||||
TcpRelayServer {
|
||||
acceptor: acceptor,
|
||||
stage: StageInit,
|
||||
config: c.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn accept_loop(&mut self) {
|
||||
let encrypt_password = Arc::new(self.config.password.clone());
|
||||
let encrypt_method = Arc::new(self.config.method.clone());
|
||||
|
||||
loop {
|
||||
let encrypt_password_clone = encrypt_password.clone();
|
||||
let cipher = openssl::OpenSSLCipher::new(cipher::CipherTypeAes128Cfb,
|
||||
encrypt_password_clone.as_slice().as_bytes());
|
||||
|
||||
match self.acceptor.accept() {
|
||||
Ok(mut stream) => spawn(proc() {
|
||||
info!("Client {} connected", stream.peer_name().unwrap());
|
||||
|
||||
TcpRelayServer::handle_hello(&mut stream);
|
||||
|
||||
loop {
|
||||
let mut buf = [0u8, .. 10240];
|
||||
match stream.read(buf) {
|
||||
Ok(len) => {
|
||||
let s = buf.slice_to(len);
|
||||
|
||||
debug!("Password {}", encrypt_password_clone.as_slice());
|
||||
|
||||
let decrypted_msg = cipher.decrypt(s);
|
||||
|
||||
debug!("Decrypted Msg: {}", decrypted_msg);
|
||||
|
||||
debug!("{} Received: {}", stream.peer_name().unwrap(),
|
||||
str::from_utf8(decrypted_msg.as_slice()).unwrap());
|
||||
},
|
||||
Err(err) => {
|
||||
if err.kind == EndOfFile {
|
||||
break
|
||||
}
|
||||
error!("Err: {}", err);
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!("Client {} disconnected", stream.peer_name().unwrap());
|
||||
|
||||
drop(stream)
|
||||
}),
|
||||
Err(e) => {
|
||||
fail!(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_hello(stream: &mut TcpStream) {
|
||||
let first_two_bytes = stream.read_exact(2).unwrap();
|
||||
|
||||
if first_two_bytes[0] != relay::SOCK5_VERSION {
|
||||
fail!("Invalid sock5 version");
|
||||
} else if first_two_bytes[1] == 0 {
|
||||
fail!("Invalid sock5 method number");
|
||||
}
|
||||
|
||||
let methods = stream.read_exact(first_two_bytes[1] as uint);
|
||||
|
||||
for m in methods.iter() {
|
||||
// Choose
|
||||
}
|
||||
|
||||
let chosen_method = 1u8;
|
||||
|
||||
let buf = [relay::SOCK5_VERSION, chosen_method];
|
||||
stream.write(buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl Relay for TcpRelayServer {
|
||||
fn run(&mut self) {
|
||||
self.accept_loop()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user