done CONNECT command

This commit is contained in:
Y. T. Chung
2014-10-20 01:33:30 +08:00
parent f1a395e7dc
commit 2ae41ebee7
7 changed files with 302 additions and 109 deletions

View File

@@ -6,6 +6,7 @@ extern crate shadowsocks;
extern crate log;
use getopts::{optopt, optflag, getopts, usage};
use std::os;
use shadowsocks::config::Config;

View File

@@ -2,10 +2,11 @@ extern crate serialize;
use serialize::Encodable;
use serialize::json;
use serialize::json::PrettyEncoder;
use std::io::{File, Read, Open};
use std::to_string::ToString;
use std::fmt::{Show, Formatter, mod};
use std::fmt::{Show, Formatter, WriteError, mod};
use std::option::Option;
@@ -129,6 +130,10 @@ impl Config {
impl Show for Config {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", json::encode(self))
let mut encoder = PrettyEncoder::new(f);
match self.encode(&mut encoder) {
Ok(..) => Ok(()),
Err(..) => Err(WriteError),
}
}
}

View File

@@ -6,6 +6,8 @@ extern crate serialize;
#[phase(plugin, link)]
extern crate log;
extern crate native;
pub const VERSION: &'static str = "0.0.1";
pub mod config;

View File

@@ -2,6 +2,9 @@ pub use self::tcprelay::local::TcpRelayLocal;
pub use self::tcprelay::server::TcpRelayServer;
use std::fmt::{Show, Formatter, FormatError};
use std::io::net::ip::{SocketAddr, Port};
use std::io::TcpStream;
use std::io::net::ip::{Ipv4Addr, Ipv6Addr};
pub mod tcprelay;
@@ -11,10 +14,10 @@ pub trait Relay {
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_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;
@@ -24,29 +27,100 @@ 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 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,
#[deriving(Show)]
pub enum Sock5CmdType {
Sock5CmdTcpConnect,
Sock5CmdTcpBind,
Sock5CmdUdpAssociate,
}
impl Show for Sock5AddrType {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
match *self {
Sock5AddrTypeIpv4 => formatter.write("Sock5AddrTypeIpv4".as_bytes()),
Sock5AddrTypeIpv6 => formatter.write("Sock5AddrTypeIpv6".as_bytes()),
Sock5AddrTypeDomainName => formatter.write("Sock5AddrTypeDomainName".as_bytes())
pub struct DomainNameAddr {
pub domain_name: String,
pub port: Port,
}
impl Show for DomainNameAddr {
fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
f.write(format!("{}:{}", self.domain_name, self.port).as_slice().as_bytes())
}
}
#[deriving(Show)]
pub enum Sock5AddrType {
Sock5SocketAddr(SocketAddr),
Sock5DomainNameAddr(DomainNameAddr),
}
pub fn parse_request_header(stream: &mut TcpStream, buf: &[u8]) -> (uint, Sock5AddrType) {
let atyp = buf[0];
match atyp {
SOCK5_ADDR_TYPE_IPV4 => {
if buf.len() < 7 {
fail!("Invalid header");
}
let raw_addr = buf.slice(1, 5);
let v4addr = Ipv4Addr(raw_addr[0], raw_addr[1], raw_addr[2], raw_addr[3]);
let raw_port = buf.slice(5, 7);
let port = (raw_port[0] as u16 << 8) | raw_port[1] as u16;
(7u, Sock5SocketAddr(SocketAddr{ip: v4addr, port: port}))
},
SOCK5_ADDR_TYPE_IPV6 => {
if buf.len() < 19 {
fail!("Invalid header");
}
let raw_addr = buf.slice(1, 17);
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 = buf.slice(17, 19);
// Big Endian
let port = (raw_port[0] as u16 << 8) | raw_port[1] as u16;
(19u, Sock5SocketAddr(SocketAddr{ip: v6addr, port: port}))
},
SOCK5_ADDR_TYPE_DOMAIN_NAME => {
let addr_len = buf[1] as uint;
if buf.len() < 4 + addr_len {
fail!("Invalid header");
}
let raw_addr = buf.slice(2, 2 + addr_len);
let raw_port = buf.slice(2 + addr_len, 4 + addr_len);
let port = (raw_port[0] as u16 << 8) | raw_port[1] as u16;
(4 + addr_len, Sock5DomainNameAddr(DomainNameAddr{
domain_name: String::from_utf8(raw_addr.to_vec()).unwrap(),
port: port,
}))
},
_ => {
// Address type not supported
send_error_reply(stream, SOCK5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED);
fail!("Unsupported address type: {}", atyp);
}
}
}
pub 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");
}

View File

@@ -1,22 +1,19 @@
#[phase(plugin, link)]
extern crate log;
// extern crate native;
use std::sync::Arc;
use std::io::{Listener, TcpListener, Acceptor, TcpStream};
use std::io::{EndOfFile};
use std::io::net::ip::{Ipv4Addr, Ipv6Addr};
use std::vec::Vec;
use std::string::String;
use std::io::{EndOfFile, TimedOut};
use config::Config;
use relay::Relay;
use relay::Relay;
use relay::{parse_request_header, send_error_reply};
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_REPLY_COMMAND_NOT_SUPPORTED};
use relay::SOCK5_REPLY_SUCCEEDED;
use relay::{SOCK5_ADDR_TYPE_IPV4, SOCK5_ADDR_TYPE_IPV6, SOCK5_ADDR_TYPE_DOMAIN_NAME};
use relay::{Sock5AddrType, Sock5AddrTypeIpv4, Sock5AddrTypeIpv6, Sock5AddrTypeDomainName};
use crypto::cipher;
use crypto::cipher::CipherVariant;
@@ -60,66 +57,6 @@ impl TcpRelayLocal {
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);
}
}
}
fn handle_connect_local_stream(local_stream: &mut TcpStream, remote_stream: &mut TcpStream,
cipher: &mut CipherVariant) {
let mut buf = [0u8, .. 0xffff];
@@ -133,17 +70,25 @@ impl TcpRelayLocal {
match remote_stream.write(encrypted_msg.as_slice()) {
Ok(..) => {},
Err(err) => {
if err.kind != EndOfFile {
error!("Error occurs in handle_local_stream while writing to remote stream: {}", err);
match err.kind {
EndOfFile | TimedOut => {},
_ => {
error!("Error occurs while writing to remote stream: {}", err);
}
}
local_stream.close_read().unwrap();
break
}
}
},
Err(err) => {
if err.kind != EndOfFile {
debug!("Error occurs in handle_local_stream while reading from local stream: {}", err);
match err.kind {
EndOfFile | TimedOut => {},
_ => {
error!("Error occurs while reading from local stream: {}", err);
}
}
remote_stream.close_write().unwrap();
break
}
}
@@ -168,17 +113,25 @@ impl TcpRelayLocal {
match local_stream.write(decrypted_msg.as_slice()) {
Ok(..) => {},
Err(err) => {
if err.kind != EndOfFile {
debug!("Error occurs in handle_remote_stream while writing to local stream: {}", err);
match err.kind {
EndOfFile | TimedOut => {},
_ => {
error!("Error occurs while writing to local stream: {}", err);
}
}
remote_stream.close_read().unwrap();
break
}
}
},
Err(err) => {
if err.kind != EndOfFile {
error!("Error occurs in handle_remote_stream while reading from remote stream: {}", err);
match err.kind {
EndOfFile | TimedOut => {},
_ => {
error!("Error occurs while reading from remote stream: {}", err);
}
}
local_stream.close_write().unwrap();
break
}
}
@@ -234,11 +187,12 @@ impl Relay for TcpRelayLocal {
fail!("Invalid sock version {}", sock_ver);
}
let (raw_header, atyp, bind_addr, bind_port)
= TcpRelayLocal::parse_request_header(&mut stream);
let mut header_buf = [0u8, .. 512];
stream.read_at_least(1, header_buf)
.ok().expect("Error occurs while reading header");
debug!("SockVer {}, CMD {}, atyp {}, bind_addr {}, bind_port {}",
sock_ver, cmd, atyp, bind_addr, bind_port);
let (header_len, addr)
= parse_request_header(&mut stream, header_buf);
let mut remote_stream = TcpStream::connect(server_addr.as_slice(),
*server_port.deref())
@@ -250,11 +204,13 @@ impl Relay for TcpRelayLocal {
match cmd {
SOCK5_CMD_TCP_CONNECT => {
info!("CONNECT {}", addr);
let reply = [SOCK5_VERSION, SOCK5_REPLY_SUCCEEDED,
0x00, SOCK5_CMD_TCP_CONNECT, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10];
stream.write(reply).unwrap();
let encrypted_header = cipher.encrypt(raw_header.as_slice());
let encrypted_header = cipher.encrypt(header_buf.slice_to(header_len));
remote_stream.write(encrypted_header.as_slice()).unwrap();
TcpRelayLocal::async_handle_connect_remote_stream(stream.clone(),
@@ -266,14 +222,14 @@ impl Relay for TcpRelayLocal {
&mut cipher);
},
SOCK5_CMD_TCP_BIND => {
unimplemented!();
},
SOCK5_CMD_UDP_ASSOCIATE => {
unimplemented!();
},
_ => {
// unsupported CMD
TcpRelayLocal::send_error_reply(&mut stream, SOCK5_REPLY_COMMAND_NOT_SUPPORTED);
send_error_reply(&mut stream, SOCK5_REPLY_COMMAND_NOT_SUPPORTED);
fail!("Unsupported command");
}
}

View File

@@ -1,5 +1,7 @@
#[phase(plugin, link)]
extern crate log;
extern crate native;
pub mod local;
pub mod server;

View File

@@ -1,6 +1,20 @@
#[phase(plugin, link)]
extern crate log;
use std::sync::Arc;
use std::io::{Listener, TcpListener, Acceptor, TcpStream};
use std::io::net::ip::{Port, IpAddr};
use std::io::{EndOfFile, TimedOut};
use config::Config;
use relay::Relay;
use relay::{parse_request_header, Sock5SocketAddr, Sock5DomainNameAddr};
use crypto::cipher;
use crypto::cipher::Cipher;
use crypto::cipher::CipherVariant;
use std::io::net::addrinfo::get_host_addresses;
pub struct TcpRelayServer {
config: Config,
@@ -12,10 +26,149 @@ impl TcpRelayServer {
config: c,
}
}
fn connect_remote(addrs: Vec<IpAddr>, port: Port) -> Option<TcpStream> {
for addr in addrs.iter() {
match TcpStream::connect(addr.to_string().as_slice(), port) {
Ok(s) => { return Some(s) },
Err(..) => {}
}
}
None
}
fn async_handle_connect_remote_stream(mut local_stream: TcpStream, mut remote_stream: TcpStream,
mut cipher: CipherVariant) {
spawn(proc() {
let mut buf = [0u8, .. 0xffff];
loop {
match remote_stream.read_at_least(1, buf) {
Ok(len) => {
let real_buf = buf.slice_to(len);
let encrypted_msg = cipher.encrypt(real_buf);
match local_stream.write(encrypted_msg.as_slice()) {
Ok(..) => {},
Err(err) => {
match err.kind {
EndOfFile | TimedOut => {},
_ => {
error!("Error occurs while writing to local stream: {}", err);
}
}
remote_stream.close_read().unwrap();
break
}
}
},
Err(err) => {
match err.kind {
EndOfFile | TimedOut => {},
_ => {
error!("Error occurs while reading from remote stream: {}", err);
}
}
local_stream.close_write().unwrap();
break
}
}
}
});
}
}
impl Relay for TcpRelayServer {
fn run(&self) {
let server_addr = self.config.server.as_slice();
let server_port = self.config.server_port;
let password = Arc::new(self.config.password.clone());
let encrypt_method = Arc::new(self.config.method.clone());
let timeout = match self.config.timeout {
Some(timeout) => Some(timeout * 1000),
None => None
};
let mut acceptor = match TcpListener::bind(server_addr, server_port).listen() {
Ok(acpt) => acpt,
Err(e) => {
fail!("Error occurs while listening server address: {}", e.to_string());
}
};
info!("Shadowsocks listening on {}:{}", server_addr, server_port);
loop {
match acceptor.accept() {
Ok(mut stream) => {
stream.set_timeout(timeout);
let password = password.clone();
let encrypt_method = encrypt_method.clone();
spawn(proc() {
let mut cipher = cipher::with_name(encrypt_method.as_slice(),
password.as_slice().as_bytes())
.expect("Unsupported cipher");
let mut buf = [0u8, .. 0xffff];
let header = {
let header_len = stream.read(buf).ok()
.expect("Error occurs while reading header");
let encrypted_header = buf.slice_to(header_len);
cipher.decrypt(encrypted_header)
};
let (_, addr) = parse_request_header(&mut stream, header.as_slice());
info!("Connecting to {}", addr);
let mut remote_stream = match addr {
Sock5SocketAddr(sockaddr) => {
TcpStream::connect(sockaddr.ip.to_string().as_slice(), sockaddr.port)
.ok().expect("Unable to connect to remote")
},
Sock5DomainNameAddr(domainaddr) => {
let addrs = match get_host_addresses(domainaddr.domain_name.as_slice()) {
Ok(ipaddrs) => ipaddrs,
Err(e) => {
fail!("Error occurs while get_host_addresses: {}", e);
}
};
if addrs.len() == 0 {
fail!("Cannot resolve host {}, empty host list", domainaddr.domain_name);
}
TcpRelayServer::connect_remote(addrs, domainaddr.port)
.expect(format!("Unable to resolve {}", domainaddr.domain_name).as_slice())
}
};
TcpRelayServer::async_handle_connect_remote_stream(stream.clone(),
remote_stream.clone(),
cipher.clone());
loop {
match stream.read(buf) {
Ok(len) => {
let real_buf = buf.slice_to(len);
let decrypted_msg = cipher.decrypt(real_buf);
remote_stream.write(decrypted_msg.as_slice());
},
Err(e) => {
error!("Error occurs while reading from client: {}", e);
break
}
}
}
});
},
Err(e) => {
fail!("Error occurs while accepting: {}", e.to_string());
}
}
}
}
}