diff --git a/src/bin/local.rs b/src/bin/local.rs index 7cd3b971..5c5e7c60 100644 --- a/src/bin/local.rs +++ b/src/bin/local.rs @@ -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(); } diff --git a/src/bin/server.rs b/src/bin/server.rs index f42a5668..9c83e930 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -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(); } diff --git a/src/config.rs b/src/config.rs index 1d4e5669..a45d3ed3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -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 { 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 { 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, }; diff --git a/src/crypto/cipher.rs b/src/crypto/cipher.rs index c07ece6d..4e51a22e 100644 --- a/src/crypto/cipher.rs +++ b/src/crypto/cipher.rs @@ -6,23 +6,83 @@ pub trait Cipher { fn decrypt(&self, data: &[u8]) -> Vec; } +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 { + match *self { + OpenSSLCrypto(ref c) => c.encrypt(data), + } + } -// c.unwrap() -// } + fn decrypt(&self, data: &[u8]) -> Vec { + match *self { + OpenSSLCrypto(ref c) => c.decrypt(data), + } + } +} + +pub fn with_name(method: &str, key: &[u8]) -> Option { + 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()); +} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 18e5a81e..a0929660 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -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"; diff --git a/src/crypto/openssl.rs b/src/crypto/openssl.rs index 59947de8..24331e1b 100644 --- a/src/crypto/openssl.rs +++ b/src/crypto/openssl.rs @@ -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()); } } diff --git a/src/lib.rs b/src/lib.rs index 7c389754..4f4ad369 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/relay.rs b/src/relay.rs deleted file mode 100644 index 3dbcc4cb..00000000 --- a/src/relay.rs +++ /dev/null @@ -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; diff --git a/src/relay/mod.rs b/src/relay/mod.rs new file mode 100644 index 00000000..4c013850 --- /dev/null +++ b/src/relay/mod.rs @@ -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, +} + diff --git a/src/relay/tcprelay/local.rs b/src/relay/tcprelay/local.rs new file mode 100644 index 00000000..812916a7 --- /dev/null +++ b/src/relay/tcprelay/local.rs @@ -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, 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()); + } + } + } + } +} diff --git a/src/relay/tcprelay/mod.rs b/src/relay/tcprelay/mod.rs new file mode 100644 index 00000000..b8fa1afd --- /dev/null +++ b/src/relay/tcprelay/mod.rs @@ -0,0 +1,5 @@ +#[phase(plugin, link)] +extern crate log; + +pub mod local; +pub mod server; diff --git a/src/relay/tcprelay/server.rs b/src/relay/tcprelay/server.rs new file mode 100644 index 00000000..d0570d1a --- /dev/null +++ b/src/relay/tcprelay/server.rs @@ -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) { + + } +} diff --git a/src/tcprelay.rs b/src/tcprelay.rs deleted file mode 100644 index a2b9040c..00000000 --- a/src/tcprelay.rs +++ /dev/null @@ -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() - } -}