add a crypto mod, fix bugs

This commit is contained in:
Y. T. Chung
2014-10-16 23:37:49 +08:00
parent 17c72b9175
commit ed4c826207
12 changed files with 560 additions and 68 deletions

View File

@@ -18,6 +18,6 @@ path = "src/bin/local.rs"
name = "ssserver"
path = "src/bin/server.rs"
[dependencies.rust-crypto]
# [dependencies.rust-crypto]
git = "https://github.com/DaGenix/rust-crypto.git"
# git = "https://github.com/DaGenix/rust-crypto.git"

View File

@@ -23,7 +23,6 @@ fn main() {
optopt("p", "server-port", "server port", ""),
optopt("l", "local-port", "local socks5 proxy port", ""),
optopt("m", "encrypt-method", "entryption method", "aes-256-cfb"),
optflag("d", "debug", "print debug message"),
];
let matches = getopts(os::args().tail(), opts).unwrap();
@@ -39,14 +38,20 @@ fn main() {
return;
}
let mut config: Config;
if matches.opt_present("c") {
config = Config::load_from_file(matches.opt_str("c")
.unwrap().as_slice())
let mut config = if matches.opt_present("c") {
Config::load_from_file(matches.opt_str("c")
.unwrap().as_slice()).unwrap()
} else {
config = Config::new()
}
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();
@@ -79,8 +84,7 @@ fn main() {
info!("ShadowSocks {}", shadowsocks::VERSION);
println!("{}", config)
debug!("Config: {}", config)
let mut tcp_server = TcpRelayLocal::new(&config);
tcp_server.run();
TcpRelayLocal::new(&config).run();
}

View File

@@ -9,6 +9,8 @@ use getopts::{optopt, optflag, getopts, usage};
use std::os;
use shadowsocks::config::Config;
use shadowsocks::tcprelay::TcpRelayServer;
use shadowsocks::relay::Relay;
fn main() {
let opts = [
@@ -21,7 +23,6 @@ fn main() {
optopt("p", "server-port", "server port", ""),
optopt("l", "local-port", "local socks5 proxy port", ""),
optopt("m", "encrypt-method", "entryption method", "aes-256-cfb"),
optflag("d", "debug", "print debug message"),
];
let matches = getopts(os::args().tail(), opts).unwrap();
@@ -37,14 +38,18 @@ fn main() {
return;
}
let mut config: Config;
if matches.opt_present("c") {
config = Config::load_from_file(matches.opt_str("c")
.unwrap().as_slice())
let mut config = if matches.opt_present("c") {
Config::load_from_file(matches.opt_str("c")
.unwrap().as_slice()).unwrap()
} else {
config = Config::new()
}
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();
@@ -77,5 +82,7 @@ fn main() {
info!("ShadowSocks {}", shadowsocks::VERSION);
println!("{}", config)
debug!("Config: {}", config)
TcpRelayServer::new(&config).run();
}

View File

@@ -1,6 +1,6 @@
extern crate serialize;
use serialize::{Decodable, Encodable};
use serialize::Encodable;
use serialize::json;
use std::io::{File, Read, Open};
@@ -29,56 +29,98 @@ impl Config {
server_port: 8000,
local_port: 8000,
password: "".to_string(),
method: "aes-256-cfb".to_string(),
method: "aes-128-cfb".to_string(),
timeout: None,
fast_open: false,
}
}
fn parse_json_object(o: &json::JsonObject) -> Config {
fn parse_json_object(o: &json::JsonObject) -> Option<Config> {
let mut config = Config::new();
for (key, value) in o.iter() {
match key.as_slice() {
"server" => {
config.server = value.as_string().unwrap().to_string();
config.server = match value.as_string() {
Some(v) => v.to_string(),
None => return None,
};
},
"server_port" => {
config.server_port = value.as_i64().unwrap() as u16;
config.server_port = match value.as_i64() {
Some(v) => v as u16,
None => return None,
};
},
"local_port" => {
config.local_port = value.as_i64().unwrap() as u16;
config.local_port = match value.as_i64() {
Some(v) => v as u16,
None => return None,
};
},
"password" => {
config.password = value.as_string().unwrap().to_string();
config.password = match value.as_string() {
Some(v) => v.to_string(),
None => return None,
};
},
"method" => {
config.method = value.as_string().unwrap().to_string();
config.method = match value.as_string() {
Some(v) => v.to_string(),
None => return None,
};
},
"timeout" => {
config.timeout = Some(value.as_i64().unwrap() as u64);
config.timeout = match value.as_i64() {
Some(v) => Some(v as u64),
None => return None,
};
},
"fast_open" => {
config.fast_open = value.as_boolean().unwrap();
config.fast_open = match value.as_boolean() {
Some(v) => v,
None => return None,
}
},
_ => (),
}
}
config
Some(config)
}
pub fn load_from_str(s: &str) -> Config {
let object = json::from_str(s).unwrap();
let json_object = object.as_object().unwrap();
pub fn load_from_str(s: &str) -> Option<Config> {
let object = match json::from_str(s) {
Ok(obj) => { obj },
Err(e) => return None,
};
let json_object = match object.as_object() {
Some(obj) => { obj },
None => return None,
};
Config::parse_json_object(json_object)
}
pub fn load_from_file(filename: &str) -> Config {
let reader = &mut File::open_mode(&Path::new(filename), Open, Read).unwrap();
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 {
Ok(ref mut r) => r,
Err(..) => return None,
};
let object = match json::from_reader(reader) {
Ok(obj) => { obj },
Err(..) => return None,
};
let json_object = match object.as_object() {
Some(obj) => obj,
None => return None,
};
let object = json::from_reader(reader).unwrap();
let json_object = object.as_object().unwrap();
Config::parse_json_object(json_object)
}
}

View File

@@ -1,4 +0,0 @@
extern crate "rust-crypto" as crypto;
pub const AES_128_CFB: &'static str = "aes-128-cfb";
pub const AES_256_CFB: &'static str = "aes-256-cfb";

5
src/crypto/cipher.rs Normal file
View File

@@ -0,0 +1,5 @@
pub trait Cipher {
fn encrypt(&self, data: &[u8]) -> Vec<u8>;
fn decrypt(&self, data: &[u8]) -> Vec<u8>;
}

120
src/crypto/crypto.rs Normal file
View File

@@ -0,0 +1,120 @@
extern crate libc;
use std::vec::Vec;
pub const CIPHER_AES_128_CFB: &'static str = "aes-128-cfb1";
pub const CIPHER_AES_256_CFB: &'static str = "aes-256-cfb";
struct CipherDef(&'static str, int, int);
const Ciphers: [CipherDef, .. 2] = [
CipherDef(CIPHER_AES_128_CFB, 16, 16),
CipherDef(CIPHER_AES_256_CFB, 32, 16),
];
pub enum CipherType {
CipherAes128Cfb,
CipherAes256Cfb,
}
#[allow(non_camel_case_types)]
type EVP_CIPHER_CTX = *mut libc::c_void;
#[allow(non_camel_case_types)]
type EVP_CIPHER = *mut libc::c_void;
pub const CIPHER_MODE_ENCRYPT: libc::c_int = 1;
pub const CIPHER_MODE_DECRYPT: libc::c_int = 0;
pub enum CipherMode {
CipherModeEncrypt,
CipherModeDecrypt,
}
pub fn cipher_type_from_str(type_string: &str) -> CipherType {
match type_string {
CIPHER_AES_128_CFB => CipherAes128Cfb,
CIPHER_AES_256_CFB => CipherAes256Cfb,
_ => fail!("Unknown cipher type"),
}
}
pub struct Crypto {
cipher: EVP_CIPHER,
cipher_ctx: EVP_CIPHER_CTX,
}
impl Crypto {
pub fn new(method: CipherType, key: &str, iv: &str, mode: CipherMode) -> Crypto {
let cipher = unsafe {
match method {
CipherAes128Cfb => EVP_aes_128_cfb128(),
CipherAes256Cfb => EVP_aes_256_cfb(),
}
};
assert!(!cipher.is_null());
let cipher_ctx = unsafe { EVP_CIPHER_CTX_new() };
let op = match mode {
CipherModeEncrypt => 1,
CipherModeDecrypt => 0
};
unsafe {
EVP_CipherInit(cipher_ctx, cipher,
key.to_c_str().as_ptr(), iv.to_c_str().as_ptr(), op);
}
Crypto {
cipher: cipher,
cipher_ctx: cipher_ctx,
}
}
pub fn update(&self, data: &[u8]) -> Vec<u8> {
let pdata: *const u8 = data.as_ptr();
let datalen: u32 = data.len() as u32;
let mut reslen: u32 = datalen + (16 as u32);
let preslen: *mut u32 = &mut reslen;
let mut res = Vec::from_elem(reslen as uint, 0u8);
let pres: *mut libc::c_uchar = res.as_mut_ptr();
unsafe {
EVP_CipherUpdate(self.cipher_ctx, pres, preslen, pdata, datalen);
}
res
}
pub fn drop(&mut self) {
unsafe {
EVP_CIPHER_CTX_cleanup(self.cipher_ctx);
EVP_CIPHER_CTX_free(self.cipher_ctx);
}
}
}
pub struct Cipher {
encryptor: Crypto,
decryptor: Crypto,
}
impl Cipher {
fn new(method: CipherType, key: &str) -> Cipher {
}
pub fn encrypt(&self, data: &[u8]) -> Vec<u8> {
}
pub fn decrypt(&self, data: &[u8]) -> Vec<u8> {
}
}
#[test]
fn test_encrypt_decrypt() {
}

5
src/crypto/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
extern crate libc;
pub mod cipher;
pub mod openssl;

198
src/crypto/openssl.rs Normal file
View File

@@ -0,0 +1,198 @@
extern crate libc;
extern crate log;
use crypto::cipher::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;
#[allow(non_camel_case_types)]
type EVP_MD = *const libc::c_void;
const CIPHER_MODE_ENCRYPT: libc::c_int = 1;
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);
fn EVP_CipherInit(ctx: EVP_CIPHER_CTX, evp: EVP_CIPHER,
key: *const libc::c_uchar, iv: *const libc::c_uchar, mode: libc::c_int) -> libc::c_int;
fn EVP_CipherUpdate(ctx: EVP_CIPHER_CTX,
outbuf: *mut libc::c_uchar, outlen: *mut libc::c_int,
inbuf: *const libc::c_uchar, inlen: libc::c_int) -> libc::c_int;
fn EVP_CipherFinal(ctx: EVP_CIPHER_CTX, res: *mut libc::c_uchar, len: *mut libc::c_int) -> libc::c_int;
fn EVP_BytesToKey(cipher: EVP_CIPHER, md: EVP_MD,
salt: *const libc::c_uchar, data: *const libc::c_uchar, datal: libc::c_int,
count: libc::c_int, key: *mut libc::c_uchar, iv: *mut libc::c_uchar) -> libc::c_int;
// Ciphers
fn EVP_aes_128_cfb128() -> EVP_CIPHER;
fn EVP_aes_192_cfb() -> EVP_CIPHER;
fn EVP_aes_256_cfb() -> 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 {
CryptoModeDecrypt,
CryptoModeEncrypt,
}
enum CipherType {
CipherTypeAes128Cfb,
CipherTypeAes192Cfb,
CipherTypeAes256Cfb,
}
struct OpenSSLCrypto {
evp_ctx: EVP_CIPHER_CTX,
block_size: uint,
// key_size: uint,
}
impl OpenSSLCrypto {
pub fn new(cipher_type: CipherType, key: &[u8], mode: CryptoMode) -> OpenSSLCrypto {
let (ctx, _, block_size) = unsafe {
let (cipher, key_size, block_size) = match cipher_type {
CipherTypeAes128Cfb => (EVP_aes_128_cfb128(), 16, 16),
CipherTypeAes192Cfb => (EVP_aes_192_cfb() , 24, 16),
CipherTypeAes256Cfb => (EVP_aes_256_cfb() , 32, 16),
};
let evp_ctx = EVP_CIPHER_CTX_new();
let mut pad_key: Vec<u8> = Vec::with_capacity(key_size);
let mut pad_iv: Vec<u8> = Vec::with_capacity(block_size);
EVP_BytesToKey(cipher, EVP_md5(), ptr::null(), key.as_ptr(), key.len() as libc::c_int,
1, pad_key.as_mut_ptr(), pad_iv.as_mut_ptr());
let op = match mode {
CryptoModeEncrypt => 1 as libc::c_int,
CryptoModeDecrypt => 0 as libc::c_int,
};
if EVP_CipherInit(evp_ctx, cipher, pad_key.as_slice().as_ptr(),
pad_iv.as_slice().as_ptr(), op) != 1 as libc::c_int {
EVP_CIPHER_CTX_free(evp_ctx);
fail!("EVP_CipherInit error");
}
(evp_ctx, key_size, block_size)
};
OpenSSLCrypto {
evp_ctx: ctx,
block_size: block_size,
// key_size: key_size,
}
}
pub fn cipher(&self, data: &[u8]) -> Vec<u8> {
unsafe {
let pdata: *const u8 = data.as_ptr();
let datalen: libc::c_int = data.len() as libc::c_int;
let reslen: uint = datalen as uint + self.block_size;
let mut res = Vec::from_elem(reslen, 0u8);
let mut len: libc::c_int = 0;
let pres: *mut u8 = res.as_mut_ptr();
if EVP_CipherUpdate(self.evp_ctx,
pres, &mut len,
pdata, datalen) != 1 {
drop(self);
fail!("Failed on EVP_CipherUpdate");
}
let mut total_length = len;
if EVP_CipherFinal(self.evp_ctx, pres.offset(len as int), &mut len) != 1 {
drop(self);
fail!("Failed on EVP_CipherFinal");
}
total_length += len;
res.truncate(total_length as uint);
res
}
}
}
#[unsafe_destructor]
impl Drop for OpenSSLCrypto {
fn drop(&mut self) {
unsafe {
EVP_CIPHER_CTX_cleanup(self.evp_ctx);
EVP_CIPHER_CTX_free(self.evp_ctx);
}
}
}
struct OpenSSLCipher {
encryptor: OpenSSLCrypto,
decryptor: OpenSSLCrypto,
}
impl OpenSSLCipher {
pub fn new(cipher_type: CipherType, key: &[u8]) -> OpenSSLCipher {
let enc = OpenSSLCrypto::new(cipher_type, key, CryptoModeEncrypt);
let dec = OpenSSLCrypto::new(cipher_type, key, CryptoModeDecrypt);
OpenSSLCipher {
encryptor: enc,
decryptor: dec,
}
}
}
impl Cipher for OpenSSLCipher {
fn encrypt(&self, data: &[u8]) -> Vec<u8> {
self.encryptor.cipher(data)
}
fn decrypt(&self, data: &[u8]) -> Vec<u8> {
self.decryptor.cipher(data)
}
}
#[test]
fn test_aes() {
let message = "hello world";
let key = "passwordhaha";
let types = [CipherTypeAes128Cfb, CipherTypeAes192Cfb, CipherTypeAes256Cfb];
for t in types.iter() {
let cipher = OpenSSLCipher::new(*t, key.as_bytes());
let encrypted_msg = cipher.encrypt(message.as_bytes());
debug!("ENC {}", encrypted_msg);
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());
}
}

View File

@@ -1,6 +1,6 @@
#![crate_type="lib"]
#![crate_name="shadowsocks"]
#![feature(phase)]
#![feature(phase, unsafe_destructor)]
extern crate serialize;
#[phase(plugin, link)]
@@ -12,4 +12,4 @@ pub mod config;
pub mod relay;
pub mod tcprelay;
pub mod udprelay;
pub mod crypto;
mod crypto;

View File

@@ -11,3 +11,13 @@ pub enum Stage {
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;

View File

@@ -4,28 +4,121 @@ extern crate log;
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::{TcpListener};
use std::io::{Acceptor, Listener};
use std::io::net::tcp::TcpAcceptor;
use std::io::net::tcp::{TcpAcceptor, TcpStream};
use std::io::{TimedOut, EndOfFile};
use std::str;
use std::sync::Arc;
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());
loop {
let server_str = server_str_arc.clone();
let server_port = server_port_arc.clone();
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);
remote_stream.write(s);
},
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,
timeout: Option<u64>,
}
impl TcpRelayLocal {
pub fn new(c: &Config) -> TcpRelayLocal {
let mut acceptor = TcpListener::bind(c.local.as_slice(), c.local_port).unwrap().listen().unwrap();
impl TcpRelayServer {
pub fn new(c: &Config) -> TcpRelayServer {
let acceptor = TcpListener::bind(c.server.as_slice(), c.server_port).unwrap().listen().unwrap();
TcpRelayLocal {
TcpRelayServer {
acceptor: acceptor,
stage: StageInit,
timeout: c.timeout,
@@ -36,16 +129,16 @@ impl TcpRelayLocal {
loop {
match self.acceptor.accept() {
Ok(mut stream) => spawn(proc() {
info!("Client {} connected", stream.socket_name().unwrap());
info!("Client {} connected", stream.peer_name().unwrap());
TcpRelayServer::handle_hello(&mut stream);
loop {
let mut buf = [0u8, .. 10240];
match stream.read(buf) {
Ok(len) => {
println!("Len: {}", len)
let s = buf.slice_to(len);
println!("Received: {}", str::from_utf8(s).unwrap());
debug!("{} Received: {}", stream.peer_name().unwrap(), s);
stream.write(s).unwrap()
},
Err(err) => {
@@ -58,7 +151,7 @@ impl TcpRelayLocal {
}
}
info!("Client {} disconnected", stream.socket_name().unwrap());
info!("Client {} disconnected", stream.peer_name().unwrap());
drop(stream)
}),
@@ -68,19 +161,31 @@ impl TcpRelayLocal {
}
}
}
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 TcpRelayLocal {
impl Relay for TcpRelayServer {
fn run(&mut self) {
self.accept_loop()
}
}
pub struct TcpRelayServer;
impl TcpRelayServer {
pub fn new() -> TcpRelayServer {
TcpRelayServer
}
}