Migrate tokio and uses new features (#108)

* Add #102 link in README

* Migrating to tokio, #100

* Migrated to tokio-signal v0.2, reformatted

* Should not call tokio::spawn before tokio::run

* Build nightly and stable with different image tag

* Removed unnecessary rustup install

* Renamed version to v1.7.0-alpha

* Removed deprecated calls of Buf

* Updated dependencies

* [#106] Fixed bug, should not hold UDP connections forever

* Lower info log to debug

* Bump version to v1.7.0-alpha.2

* Implement a simple DNS relay server, bump version to v1.7.0-alpha.3

* Better logging

* Fixed build on travis

* Add elapsed time in log

* Should not exit if handler return error

* reformatted

* Add test for DNS

* Updated ssdns param

* Use `trust-dns` and impl Trait
1. Replaced `ToSocketAddrs` with `trust-dns`
2. Uses impl Trait for functions
3. Updated dependencies
4. Fixed bugs

* Bug fixed, logging crate in ssdns
This commit is contained in:
Y. T. CHUNG
2018-05-16 00:03:50 +08:00
committed by GitHub
parent b37103deed
commit 8be6da51f3
51 changed files with 2491 additions and 1553 deletions
Generated
+635 -241
View File
File diff suppressed because it is too large Load Diff
+18 -13
View File
@@ -1,6 +1,6 @@
[package]
name = "shadowsocks-rust"
version = "1.6.12"
version = "1.7.0-alpha.4"
authors = ["Y. T. CHUNG <zonyitoo@gmail.com>"]
description = "shadowsocks is a fast tunnel proxy that helps you bypass firewalls."
repository = "https://github.com/zonyitoo/shadowsocks-rust"
@@ -23,6 +23,10 @@ path = "src/bin/server.rs"
name = "ssurl"
path = "src/bin/ssurl.rs"
[[bin]]
name = "ssdns"
path = "src/bin/ssdns.rs"
[profile.release]
lto = true
@@ -32,36 +36,37 @@ sodium = ["libsodium-ffi"]
[dependencies]
log = "0.4"
byteorder = "1.1"
byteorder = "1.2"
rand = "0.4"
time = "0.1"
clap = "2"
env_logger = "0.5.0-rc.1"
openssl = "0.9"
env_logger = "0.5"
openssl = "0.10"
libc = "0.2"
futures = "0.1"
tokio-core = "0.1"
tokio-io = "0.1"
tokio = "0.1"
lazy_static = "1.0"
serde_json = "1.0"
base64 = "0.9"
bytes = "0.4"
scoped-tls = "0.1"
ring = "0.13.0-alpha"
ring = "0.12"
md-5 = "0.7"
digest = "0.7"
typenum = "1.9"
qrcode = { version = "0.5", default-features = false }
typenum = "1.10"
qrcode = { version = "0.7", default-features = false }
subprocess = "0.1"
serde_urlencoded = "0.5"
url = "1.5"
url = "1"
byte_string = "1.0"
libsodium-ffi = { version = "0.1", optional = true }
futures-cpupool = "0.1"
miscreant = { version = "0.3", optional = true }
lru-cache = "0.1"
dns-parser = "0.7"
trust-dns-resolver = { git = "https://github.com/bluejekyll/trust-dns", features = ["dns-over-rustls"] }
[target.'cfg(unix)'.dependencies]
tokio-signal = "0.1"
tokio-signal = "0.2"
[target.'cfg(windows)'.dependencies]
tokio-signal = "0.1"
tokio-signal = "0.2"
+23 -3
View File
@@ -57,6 +57,8 @@ make install TARGET=release
Then `sslocal`, `ssserver` and `ssurl` will be installed in `/usr/local/bin` (variable PREFIX).
For Windows users, if you have encountered any problem in building, check and discuss in [#102](https://github.com/shadowsocks/shadowsocks-rust/issues/102).
### **Build standalone binaries**
Requirements:
@@ -160,6 +162,24 @@ $ ssserver -c /path/to/shadowsocks.json
$ ssserver -s "[::]:8388" -m "aes-256-gcm" -k "hello-kitty" --plugin "obfs-server" --plugin-opts "obfs=tls"
```
DNS Local server
```bash
# Read configuration from file
$ ssdns -c /path/to/shadowsocks.json
# Pass all parameters via command line
$ ssdns -s "[::]:8388" -m "aes-256-gcm" -k "hello-kitty" --dns "8.8.8.8:53"
```
For DNS relay server, you can set remote DNS server in configuration file with `dns` key in root object.
```json
{
"dns": "8.8.8.8:53"
}
```
## Supported Ciphers
### Stream Ciphers
@@ -180,9 +200,9 @@ $ ssserver -s "[::]:8388" -m "aes-256-gcm" -k "hello-kitty" --plugin "obfs-serve
1. `ssurl` is for encoding and decoding ShadowSocks URLs (SIP002). Example:
```plain
ss://YWVzLTI1Ni1jZmI6cGFzc3dvcmQ@127.0.0.1:8388/?plugin=obfs-local%3Bobfs%3Dhttp%3Bobfs-host%3Dwww.baidu.com
```
```plain
ss://YWVzLTI1Ni1jZmI6cGFzc3dvcmQ@127.0.0.1:8388/?plugin=obfs-local%3Bobfs%3Dhttp%3Bobfs-host%3Dwww.baidu.com
```
## Notes
+2 -8
View File
@@ -1,7 +1,5 @@
FROM ekidd/rust-musl-builder
#ENV HTTP_PROXY="http://192.168.0.106:8118"
#ENV HTTPS_PROXY="http://192.168.0.106:8118"
ARG TOOLCHAIN=stable
FROM ekidd/rust-musl-builder:$TOOLCHAIN
ENV SODIUM_VERS="1.0.16"
@@ -13,9 +11,5 @@ RUN cd /home/rust/libs && \
make && sudo make install && \
cd .. && rm -rf libsodium-$SODIUM_VERS.tar.gz libsodium-$SODIUM_VERS
RUN rustup install nightly
RUN rustup default nightly
RUN rustup target add x86_64-unknown-linux-musl
ENV SODIUM_STATIC=yes
ENV SODIUM_LIB_DIR=/usr/local/musl/lib
+14 -4
View File
@@ -40,11 +40,19 @@ fi
echo "* Building ${RELEASE_NAME} package ${VERSION} ..."
IMAGE='shadowsocks-rust:latest'
if $BUILD_NIGHTLY; then
IMAGE='shadowsocks-rust:nightly'
else
IMAGE='shadowsocks-rust:stable'
fi
if $BUILD_DOCKER_IMG; then
echo "* Rebuild docker image ${IMAGE} ...";
docker build -t "${IMAGE}" "$CUR_DIR";
if $BUILD_NIGHTLY; then
docker build -t "${IMAGE}" "$CUR_DIR" --build-arg TOOLCHAIN=nightly;
else
docker build -t "${IMAGE}" "$CUR_DIR" --build-arg TOOLCHAIN=stable;
fi
fi
SRC_PATH="/home/rust/src"
@@ -59,6 +67,7 @@ if $BUILD_NIGHTLY; then
docker run \
-e CARGO_TARGET_DIR="${CARGO_TARGET_DIR}" \
-e CARGO_INSTALL_ROOT="${CARGO_INSTALL_ROOT}" \
-e RUSTFLAGS="-Ctarget-feature=+aes" \
-e HTTP_PROXY="$HTTP_PROXY" \
-e HTTPS_PROXY="$HTTPS_PROXY" \
-v "${CUR_DIR}"/..:"$SRC_PATH" \
@@ -67,7 +76,7 @@ if $BUILD_NIGHTLY; then
/bin/bash -c \
"sudo mkdir -p $CARGO_TARGET_DIR \
&& sudo rm -rf $CARGO_INSTALL_ROOT \
&& rustup run nightly cargo install -f --features miscreant \
&& cargo install -f --features miscreant \
&& sudo chown -R $LOCAL_USER $CARGO_INSTALL_ROOT";
else
docker run \
@@ -117,6 +126,7 @@ echo "* Packaging XZ in ${PKG_PATH} ..."
tar -cJf ${PKG_PATH} \
"sslocal" \
"ssserver" \
"ssurl"
"ssurl" \
"ssdns"
echo "* Done build package ${PKG_NAME}"
+1 -1
View File
@@ -9,4 +9,4 @@ condense_wildcard_suffixes = true
#fn_call_style = "Visual"
#chain_indent = "Visual"
normalize_comments = true
use_try_shorthand = true
use_try_shorthand = true
+14 -17
View File
@@ -3,7 +3,7 @@
//! You have to provide all needed configuration attributes via command line parameters,
//! or you could specify a configuration file. The format of configuration file is defined
//! in mod `config`.
//!
//!
extern crate clap;
extern crate env_logger;
@@ -18,12 +18,12 @@ use std::env;
use std::io::{self, Write};
use std::net::SocketAddr;
use env_logger::Builder;
use env_logger::fmt::Formatter;
use env_logger::Builder;
use log::{LevelFilter, Record};
use shadowsocks::{run_local, Config, ConfigType, ServerAddr, ServerConfig};
use shadowsocks::plugin::PluginConfig;
use shadowsocks::{run_local, Config, ConfigType, ServerAddr, ServerConfig};
fn log_time(fmt: &mut Formatter, without_time: bool, record: &Record) -> io::Result<()> {
if without_time {
@@ -148,18 +148,16 @@ fn main() {
let mut has_provided_config = false;
let mut config = match matches.value_of("CONFIG") {
Some(cpath) => {
match Config::load_from_file(cpath, ConfigType::Local) {
Ok(cfg) => {
has_provided_config = true;
cfg
}
Err(err) => {
error!("{:?}", err);
return;
}
Some(cpath) => match Config::load_from_file(cpath, ConfigType::Local) {
Ok(cfg) => {
has_provided_config = true;
cfg
}
}
Err(err) => {
error!("{:?}", err);
return;
}
},
None => Config::new(),
};
@@ -201,8 +199,7 @@ fn main() {
let has_provided_local_config = match matches.value_of("LOCAL_ADDR") {
Some(local_addr) => {
let local_addr: SocketAddr = local_addr.parse()
.expect("`local-addr` is not a valid IP address");
let local_addr: SocketAddr = local_addr.parse().expect("`local-addr` is not a valid IP address");
config.local = Some(local_addr);
true
@@ -232,5 +229,5 @@ fn main() {
debug!("Config: {:?}", config);
run_local(config).unwrap();
run_local(config);
}
+12 -14
View File
@@ -19,12 +19,12 @@ use std::io::{self, Write};
use clap::{App, Arg};
use env_logger::Builder;
use env_logger::fmt::Formatter;
use env_logger::Builder;
use log::{LevelFilter, Record};
use shadowsocks::{run_server, Config, ConfigType, ServerAddr, ServerConfig};
use shadowsocks::plugin::PluginConfig;
use shadowsocks::{run_server, Config, ConfigType, ServerAddr, ServerConfig};
fn log_time(fmt: &mut Formatter, without_time: bool, record: &Record) -> io::Result<()> {
if without_time {
@@ -129,18 +129,16 @@ fn main() {
let mut has_provided_config = false;
let mut config = match matches.value_of("CONFIG") {
Some(cpath) => {
match Config::load_from_file(cpath, ConfigType::Server) {
Ok(cfg) => {
has_provided_config = true;
cfg
}
Err(err) => {
error!("{:?}", err);
return;
}
Some(cpath) => match Config::load_from_file(cpath, ConfigType::Server) {
Ok(cfg) => {
has_provided_config = true;
cfg
}
}
Err(err) => {
error!("{:?}", err);
return;
}
},
None => Config::new(),
};
@@ -194,5 +192,5 @@ fn main() {
debug!("Config: {:?}", config);
run_server(config).unwrap();
run_server(config);
}
+215
View File
@@ -0,0 +1,215 @@
//! DNS over shadowsocks
extern crate clap;
extern crate shadowsocks;
extern crate tokio;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate time;
use clap::{App, Arg};
use std::env;
use std::io::{self, Write};
use std::net::SocketAddr;
use env_logger::fmt::Formatter;
use env_logger::Builder;
use log::{LevelFilter, Record};
use shadowsocks::{run_dns, Config, ConfigType, ServerAddr, ServerConfig};
fn log_time(fmt: &mut Formatter, without_time: bool, record: &Record) -> io::Result<()> {
if without_time {
writeln!(fmt, "[{}] {}", record.level(), record.args())
} else {
writeln!(fmt,
"[{}][{}] {}",
time::now().strftime("%Y-%m-%d][%H:%M:%S.%f").unwrap(),
record.level(),
record.args())
}
}
fn log_time_module(fmt: &mut Formatter, without_time: bool, record: &Record) -> io::Result<()> {
if without_time {
writeln!(fmt,
"[{}] [{}] {}",
record.level(),
record.module_path().unwrap_or("*"),
record.args())
} else {
writeln!(fmt,
"[{}][{}] [{}] {}",
time::now().strftime("%Y-%m-%d][%H:%M:%S.%f").unwrap(),
record.level(),
record.module_path().unwrap_or("*"),
record.args())
}
}
fn main() {
let matches = App::new("ssdns")
.version(shadowsocks::VERSION)
.about("A DNS proxy that helps you bypass firewalls.")
.arg(Arg::with_name("VERBOSE")
.short("v")
.multiple(true)
.help("Set the level of debug"))
.arg(Arg::with_name("CONFIG")
.short("c")
.long("config")
.takes_value(true)
.help("Specify config file"))
.arg(Arg::with_name("SERVER_ADDR")
.short("s")
.long("server-addr")
.takes_value(true)
.help("Server address"))
.arg(Arg::with_name("LOCAL_ADDR")
.short("b")
.long("local-addr")
.takes_value(true)
.help("Local address, listen only to this address if specified"))
.arg(Arg::with_name("PASSWORD")
.short("k")
.long("password")
.takes_value(true)
.help("Password"))
.arg(Arg::with_name("ENCRYPT_METHOD")
.short("m")
.long("encrypt-method")
.takes_value(true)
.help("Encryption method"))
.arg(Arg::with_name("LOG_WITHOUT_TIME")
.long("log-without-time")
.help("Disable time in log"))
.arg(Arg::with_name("URL")
.long("server-url")
.takes_value(true)
.help("Server address in SIP002 URL"))
.arg(Arg::with_name("DNS")
.long("dns")
.takes_value(true)
.help("Remote DNS server, default is 8.8.8.8:53"))
.get_matches();
let mut log_builder = Builder::new();
log_builder.filter(None, LevelFilter::Info);
let without_time = matches.is_present("LOG_WITHOUT_TIME");
let debug_level = matches.occurrences_of("VERBOSE");
match debug_level {
0 => {
// Default filter
log_builder.format(move |fmt, r| log_time(fmt, without_time, r));
}
1 => {
let log_builder = log_builder.format(move |fmt, r| log_time_module(fmt, without_time, r));
log_builder.filter(Some("ssdns"), LevelFilter::Debug);
}
2 => {
let log_builder = log_builder.format(move |fmt, r| log_time_module(fmt, without_time, r));
log_builder.filter(Some("ssdns"), LevelFilter::Debug)
.filter(Some("shadowsocks"), LevelFilter::Debug);
}
3 => {
let log_builder = log_builder.format(move |fmt, r| log_time_module(fmt, without_time, r));
log_builder.filter(Some("ssdns"), LevelFilter::Trace)
.filter(Some("shadowsocks"), LevelFilter::Trace);
}
_ => {
let log_builder = log_builder.format(move |fmt, r| log_time_module(fmt, without_time, r));
log_builder.filter(None, LevelFilter::Trace);
}
}
if let Ok(env_conf) = env::var("RUST_LOG") {
log_builder.parse(&env_conf);
}
log_builder.init();
let mut has_provided_config = false;
let mut config = match matches.value_of("CONFIG") {
Some(cpath) => {
match Config::load_from_file(cpath, ConfigType::Local) {
Ok(cfg) => {
has_provided_config = true;
cfg
}
Err(err) => {
error!("{:?}", err);
return;
}
}
}
None => Config::new(),
};
let mut has_provided_server_config =
match (matches.value_of("SERVER_ADDR"), matches.value_of("PASSWORD"), matches.value_of("ENCRYPT_METHOD")) {
(Some(svr_addr), Some(password), Some(method)) => {
let method = match method.parse() {
Ok(m) => m,
Err(err) => {
panic!("Does not support {:?} method: {:?}", method, err);
}
};
let sc = ServerConfig::new(svr_addr.parse::<ServerAddr>().expect("Invalid server addr"),
password.to_owned(),
method,
None,
None);
config.server.push(sc);
true
}
(None, None, None) => {
// Does not provide server config
false
}
_ => {
panic!("`server-addr`, `method` and `password` should be provided together");
}
};
if let Some(url) = matches.value_of("URL") {
let svr_addr = url.parse::<ServerConfig>().expect("Failed to parse `url`");
has_provided_server_config = true;
config.server.push(svr_addr);
}
let has_provided_local_config = match matches.value_of("LOCAL_ADDR") {
Some(local_addr) => {
let local_addr: SocketAddr = local_addr.parse().expect("`local-addr` is not a valid IP address");
config.local = Some(local_addr);
true
}
None => false,
};
if !has_provided_config && !(has_provided_server_config && has_provided_local_config) {
println!("You have to specify a configuration file or pass arguments by argument list");
println!("{}", matches.usage());
return;
}
if let Some(dns) = matches.value_of("DNS") {
let dns_addr = dns.parse::<SocketAddr>().expect("`dns` is not a valid SocketAddr, must be IP:Port");
config.dns = dns_addr;
}
info!("ShadowSocks DNS {}", shadowsocks::VERSION);
debug!("Config: {:?}", config);
run_dns(config);
}
+2 -2
View File
@@ -10,11 +10,11 @@ extern crate shadowsocks;
use clap::{App, Arg};
use qrcode::QrCode;
use qrcode::types::Color;
use qrcode::QrCode;
use shadowsocks::VERSION;
use shadowsocks::config::{Config, ConfigType, ServerConfig};
use shadowsocks::VERSION;
const BLACK: &'static str = "\x1b[40m \x1b[0m";
const WHITE: &'static str = "\x1b[47m \x1b[0m";
+173 -219
View File
@@ -40,7 +40,7 @@
//! ```
//!
//! These defined server will be used with a load balancing algorithm.
//!
//!
use std::collections::HashSet;
use std::convert::From;
@@ -48,8 +48,8 @@ use std::default::Default;
use std::error;
use std::fmt::{self, Debug, Display, Formatter};
use std::fs::OpenOptions;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::net::IpAddr;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::option::Option;
use std::path::Path;
use std::str::FromStr;
@@ -177,23 +177,20 @@ pub struct ServerConfig {
impl ServerConfig {
/// Creates a new ServerConfig
pub fn new(
addr: ServerAddr,
pwd: String,
method: CipherType,
timeout: Option<Duration>,
plugin: Option<PluginConfig>,
) -> ServerConfig {
pub fn new(addr: ServerAddr,
pwd: String,
method: CipherType,
timeout: Option<Duration>,
plugin: Option<PluginConfig>)
-> ServerConfig {
let enc_key = method.bytes_to_key(pwd.as_bytes());
trace!("Initialize config with pwd: {:?}, key: {:?}", pwd, enc_key);
ServerConfig {
addr: addr,
password: pwd,
method: method,
timeout: timeout,
enc_key: enc_key,
plugin: plugin,
}
ServerConfig { addr: addr,
password: pwd,
method: method,
timeout: timeout,
enc_key: enc_key,
plugin: plugin, }
}
/// Create a basic config
@@ -341,10 +338,8 @@ impl ServerConfig {
match vsp.next() {
None => {}
Some(p) => {
plugin = Some(PluginConfig {
plugin: p.to_owned(),
plugin_opt: vsp.next().map(ToOwned::to_owned),
})
plugin = Some(PluginConfig { plugin: p.to_owned(),
plugin_opt: vsp.next().map(ToOwned::to_owned), })
}
}
}
@@ -465,6 +460,7 @@ pub struct Config {
pub local: Option<ClientConfig>,
pub enable_udp: bool,
pub forbidden_ip: HashSet<IpAddr>,
pub dns: SocketAddr,
}
impl Default for Config {
@@ -492,47 +488,37 @@ pub struct Error {
impl Error {
pub fn new(kind: ErrorKind, desc: &'static str, detail: Option<String>) -> Error {
Error {
kind: kind,
desc: desc,
detail: detail,
}
Error { kind: kind,
desc: desc,
detail: detail, }
}
}
macro_rules! impl_from {
($error:ty,$kind:expr,$desc:expr) => (
($error:ty, $kind:expr, $desc:expr) => {
impl From<$error> for Error {
fn from(err:$error) -> Self {
Error::new($kind,$desc,Some(format!("{:?}",err)))
fn from(err: $error) -> Self {
Error::new($kind, $desc, Some(format!("{:?}", err)))
}
}
)
};
}
impl_from!(
::std::io::Error,
ErrorKind::IoError,
"error while reading file"
);
impl_from!(
serde_json::Error,
ErrorKind::JsonParsingError,
"Json parse error"
);
impl_from!(::std::io::Error, ErrorKind::IoError, "error while reading file");
impl_from!(serde_json::Error, ErrorKind::JsonParsingError, "Json parse error");
macro_rules! except {
($expr:expr,$kind:expr,$desc:expr) => (except!($expr,$kind,$desc,None));
($expr:expr,$kind:expr,$desc:expr,$detail:expr) => (
($expr:expr, $kind:expr, $desc:expr) => {
except!($expr, $kind, $desc, None)
};
($expr:expr, $kind:expr, $desc:expr, $detail:expr) => {
match $expr {
::std::option::Option::Some(val) => val,
::std::option::Option::None => {
return ::std::result::Result::Err(
$crate::config::Error::new($kind,$desc,$detail)
)
return ::std::result::Result::Err($crate::config::Error::new($kind, $desc, $detail))
}
}
)
};
}
impl Debug for Error {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
@@ -546,94 +532,78 @@ impl Debug for Error {
impl Config {
/// Creates an empty configuration
pub fn new() -> Config {
Config {
server: Vec::new(),
local: None,
enable_udp: false,
forbidden_ip: HashSet::new(),
}
Config { server: Vec::new(),
local: None,
enable_udp: false,
forbidden_ip: HashSet::new(),
dns: "8.8.8.8:53".parse::<SocketAddr>().unwrap(), }
}
fn parse_server(server: &Map<String, Value>) -> Result<ServerConfig, Error> {
let method = server
.get("method")
.ok_or_else(|| Error::new(ErrorKind::MissingField, "need to specify a method", None))
.and_then(|method_o| {
method_o
.as_str()
.ok_or_else(|| Error::new(ErrorKind::Malformed, "`method` should be a string", None))
})
.and_then(|method_str| {
method_str.parse::<CipherType>().map_err(|_| {
Error::new(
ErrorKind::Invalid,
"not supported method",
Some(format!("`{}` is not a supported method", method_str)),
)
})
})?;
let method = server.get("method")
.ok_or_else(|| Error::new(ErrorKind::MissingField, "need to specify a method", None))
.and_then(|method_o| {
method_o.as_str().ok_or_else(|| {
Error::new(ErrorKind::Malformed,
"`method` should be a string",
None)
})
})
.and_then(|method_str| {
method_str.parse::<CipherType>().map_err(|_| {
Error::new(ErrorKind::Invalid,
"not supported method",
Some(format!("`{}` is not a supported method", method_str)))
})
})?;
let port = server
.get("port")
.or_else(|| server.get("server_port"))
.ok_or_else(|| {
Error::new(
ErrorKind::MissingField,
"need to specify a server port",
None,
)
})
.and_then(|port_o| {
port_o
.as_u64()
.map(|u| u as u16)
.ok_or_else(|| Error::new(ErrorKind::Malformed, "`port` should be an integer", None))
})?;
let port = server.get("port")
.or_else(|| server.get("server_port"))
.ok_or_else(|| Error::new(ErrorKind::MissingField, "need to specify a server port", None))
.and_then(|port_o| {
port_o.as_u64().map(|u| u as u16).ok_or_else(|| {
Error::new(ErrorKind::Malformed,
"`port` should be an integer",
None)
})
})?;
let addr = server
.get("address")
.or_else(|| server.get("server"))
.ok_or_else(|| {
Error::new(
ErrorKind::MissingField,
"need to specify a server address",
None,
)
})
.and_then(|addr_o| {
addr_o
.as_str()
.ok_or_else(|| Error::new(ErrorKind::Malformed, "`address` should be a string", None))
})
.and_then(|addr_str| {
addr_str
.parse::<Ipv4Addr>()
.map(|v4| ServerAddr::SocketAddr(SocketAddr::V4(SocketAddrV4::new(v4, port))))
.or_else(|_| {
addr_str
let addr = server.get("address")
.or_else(|| server.get("server"))
.ok_or_else(|| Error::new(ErrorKind::MissingField, "need to specify a server address", None))
.and_then(|addr_o| {
addr_o.as_str().ok_or_else(|| {
Error::new(ErrorKind::Malformed,
"`address` should be a string",
None)
})
})
.and_then(|addr_str| {
addr_str.parse::<Ipv4Addr>()
.map(|v4| ServerAddr::SocketAddr(SocketAddr::V4(SocketAddrV4::new(v4, port))))
.or_else(|_| {
addr_str
.parse::<Ipv6Addr>()
.map(|v6| ServerAddr::SocketAddr(SocketAddr::V6(SocketAddrV6::new(v6, port, 0, 0))))
})
.or_else(|_| Ok(ServerAddr::DomainName(addr_str.to_string(), port)))
})?;
})
.or_else(|_| Ok(ServerAddr::DomainName(addr_str.to_string(), port)))
})?;
let password = server
.get("password")
.ok_or_else(|| Error::new(ErrorKind::MissingField, "need to specify a password", None))
.and_then(|pwd_o| {
pwd_o
.as_str()
.ok_or_else(|| Error::new(ErrorKind::Malformed, "`password` should be a string", None))
.map(|s| s.to_string())
})?;
let password = server.get("password")
.ok_or_else(|| Error::new(ErrorKind::MissingField, "need to specify a password", None))
.and_then(|pwd_o| {
pwd_o.as_str()
.ok_or_else(|| {
Error::new(ErrorKind::Malformed,
"`password` should be a string",
None)
})
.map(|s| s.to_string())
})?;
let timeout = match server.get("timeout") {
Some(t) => {
let val = t.as_u64().ok_or(Error::new(
ErrorKind::Malformed,
"`timeout` should be an integer",
None,
))?;
let val = t.as_u64().ok_or(Error::new(ErrorKind::Malformed, "`timeout` should be an integer", None))?;
Some(Duration::from_secs(val))
}
None => None,
@@ -641,27 +611,23 @@ impl Config {
let plugin = match server.get("plugin") {
Some(p) => {
let plugin = p.as_str()
.ok_or_else(|| Error::new(ErrorKind::Malformed, "`plugin` should be a string", None))?;
let plugin =
p.as_str().ok_or_else(|| Error::new(ErrorKind::Malformed, "`plugin` should be a string", None))?;
let opt = match server.get("plugin_opts") {
None => None,
Some(o) => {
let o = o.as_str().ok_or_else(|| {
Error::new(
ErrorKind::Malformed,
"`plugin_opts` should be a string",
None,
)
})?;
Error::new(ErrorKind::Malformed,
"`plugin_opts` should be a string",
None)
})?;
Some(o.to_owned())
}
};
Some(PluginConfig {
plugin: plugin.to_owned(),
plugin_opt: opt,
})
Some(PluginConfig { plugin: plugin.to_owned(),
plugin_opt: opt, })
}
None => None,
};
@@ -673,11 +639,9 @@ impl Config {
let mut config = Config::new();
if o.contains_key("servers") {
let server_list = o.get("servers").unwrap().as_array().ok_or(Error::new(
ErrorKind::Malformed,
"`servers` should be a list",
None,
))?;
let server_list = o.get("servers").unwrap()
.as_array()
.ok_or(Error::new(ErrorKind::Malformed, "`servers` should be a list", None))?;
for server in server_list.iter() {
if let Some(server) = server.as_object() {
@@ -686,7 +650,7 @@ impl Config {
}
}
} else if o.contains_key("server") && o.contains_key("server_port") && o.contains_key("password")
&& o.contains_key("method")
&& o.contains_key("method")
{
// Traditional configuration file
let single_server = Config::parse_server(o)?;
@@ -700,29 +664,24 @@ impl Config {
if has_local_address && has_local_port {
config.local = match o.get("local_address") {
Some(local_addr) => {
let addr_str = local_addr.as_str().ok_or(Error::new(
ErrorKind::Malformed,
"`local_address` should be a string",
None,
))?;
let addr_str = local_addr.as_str().ok_or(Error::new(ErrorKind::Malformed,
"`local_address` should be a string",
None))?;
let port = o.get("local_port").unwrap().as_u64().ok_or(Error::new(
ErrorKind::Malformed,
"`local_port` should be an integer",
None,
))? as u16;
let port = o.get("local_port").unwrap()
.as_u64()
.ok_or(Error::new(ErrorKind::Malformed, "`local_port` should be an integer", None))?
as u16;
match addr_str.parse::<Ipv4Addr>() {
Ok(ip) => Some(SocketAddr::V4(SocketAddrV4::new(ip, port))),
Err(..) => match addr_str.parse::<Ipv6Addr>() {
Ok(ip) => Some(SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0))),
Err(..) => {
return Err(Error::new(
ErrorKind::Malformed,
"`local_address` is not a valid IP \
address",
None,
))
return Err(Error::new(ErrorKind::Malformed,
"`local_address` is not a valid IP \
address",
None))
}
},
}
@@ -735,33 +694,27 @@ impl Config {
}
if let Some(forbidden_ip_conf) = o.get("forbidden_ip") {
let forbidden_ip_arr = forbidden_ip_conf.as_array().ok_or(Error::new(
ErrorKind::Malformed,
"`forbidden_ip` should be a list",
None,
))?;
config
.forbidden_ip
.extend(forbidden_ip_arr.into_iter().filter_map(|x| {
let x = match x.as_str() {
Some(x) => x,
None => {
error!(
"Forbidden IP should be a string, but found {:?}, skipping",
x
);
return None;
}
};
let forbidden_ip_arr = forbidden_ip_conf.as_array().ok_or(Error::new(ErrorKind::Malformed,
"`forbidden_ip` should be a list",
None))?;
config.forbidden_ip.extend(forbidden_ip_arr.into_iter().filter_map(|x| {
let x = match x.as_str() {
Some(x) => x,
None => {
error!("Forbidden IP should be a string, but found {:?}, skipping",
x);
return None;
}
};
match x.parse::<IpAddr>() {
Ok(sock) => Some(sock),
Err(err) => {
error!("Invalid forbidden IP {}, {:?}, skipping", x, err);
None
}
}
}));
match x.parse::<IpAddr>() {
Ok(sock) => Some(sock),
Err(err) => {
error!("Invalid forbidden IP {}, {:?}, skipping", x, err);
None
}
}
}));
}
if let Some(udp_enable) = o.get("enable_udp") {
@@ -774,40 +727,50 @@ impl Config {
}
}
if let Some(dns) = o.get("dns") {
match dns.as_str() {
None => {
let err = Error::new(ErrorKind::Malformed, "`dns` should be string", None);
return Err(err);
}
Some(dns) => {
match dns.parse::<IpAddr>() {
Err(..) => {
let err = Error::new(ErrorKind::Malformed, "`dns` should be IpAddr", None);
return Err(err);
}
Ok(addr) => {
config.dns = SocketAddr::new(addr, 53);
}
}
}
}
}
Ok(config)
}
pub fn load_from_str(s: &str, config_type: ConfigType) -> Result<Config, Error> {
let object = serde_json::from_str::<Value>(s)?;
let json_object = except!(
object.as_object(),
ErrorKind::JsonParsingError,
"root is not a JsonObject"
);
Config::parse_json_object(
json_object,
match config_type {
ConfigType::Local => true,
ConfigType::Server => false,
},
)
let json_object = except!(object.as_object(),
ErrorKind::JsonParsingError,
"root is not a JsonObject");
Config::parse_json_object(json_object, match config_type {
ConfigType::Local => true,
ConfigType::Server => false,
})
}
pub fn load_from_file(filename: &str, config_type: ConfigType) -> Result<Config, Error> {
let reader = &mut OpenOptions::new().read(true).open(&Path::new(filename))?;
let object = serde_json::from_reader::<_, Value>(reader)?;
let json_object = except!(
object.as_object(),
ErrorKind::JsonParsingError,
"root is not a JsonObject"
);
Config::parse_json_object(
json_object,
match config_type {
ConfigType::Local => true,
ConfigType::Server => false,
},
)
let json_object = except!(object.as_object(),
ErrorKind::JsonParsingError,
"root is not a JsonObject");
Config::parse_json_object(json_object, match config_type {
ConfigType::Local => true,
ConfigType::Server => false,
})
}
}
@@ -820,14 +783,8 @@ impl Config {
let server = &self.server[0];
server.addr.to_json_object_old(&mut obj);
obj.insert(
"password".to_owned(),
Value::String(server.password.clone()),
);
obj.insert(
"method".to_owned(),
Value::String(server.method.to_string()),
);
obj.insert("password".to_owned(), Value::String(server.password.clone()));
obj.insert("method".to_owned(), Value::String(server.method.to_string()));
if let Some(t) = server.timeout {
obj.insert("timeout".to_owned(), Value::Number(From::from(t.as_secs())));
}
@@ -850,10 +807,7 @@ impl Config {
};
obj.insert("local_address".to_owned(), Value::String(ip_str));
obj.insert(
"local_port".to_owned(),
Value::Number(From::from(l.port() as u64)),
);
obj.insert("local_port".to_owned(), Value::Number(From::from(l.port() as u64)));
}
obj.insert("enable_udp".to_owned(), Value::Bool(self.enable_udp));
+26 -39
View File
@@ -121,18 +121,24 @@ pub enum CipherType {
Rc4,
Rc4Md5,
#[cfg(feature = "sodium")] ChaCha20,
#[cfg(feature = "sodium")] Salsa20,
#[cfg(feature = "sodium")] XSalsa20,
#[cfg(feature = "sodium")] ChaCha20Ietf,
#[cfg(feature = "sodium")]
ChaCha20,
#[cfg(feature = "sodium")]
Salsa20,
#[cfg(feature = "sodium")]
XSalsa20,
#[cfg(feature = "sodium")]
ChaCha20Ietf,
Aes128Gcm,
Aes256Gcm,
ChaCha20Poly1305,
#[cfg(feature = "miscreant")] Aes128PmacSiv,
#[cfg(feature = "miscreant")] Aes256PmacSiv,
#[cfg(feature = "miscreant")]
Aes128PmacSiv,
#[cfg(feature = "miscreant")]
Aes256PmacSiv,
}
/// Category of ciphers
@@ -215,35 +221,24 @@ impl CipherType {
match *self {
CipherType::Table | CipherType::Plain => 0,
CipherType::Aes128Cfb1 => {
symm::Cipher::aes_128_cfb1().iv_len()
.expect("iv_len should not be None")
}
CipherType::Aes128Cfb8 => {
symm::Cipher::aes_128_cfb8().iv_len()
.expect("iv_len should not be None")
}
CipherType::Aes128Cfb1 => symm::Cipher::aes_128_cfb1().iv_len()
.expect("iv_len should not be None"),
CipherType::Aes128Cfb8 => symm::Cipher::aes_128_cfb8().iv_len()
.expect("iv_len should not be None"),
CipherType::Aes128Cfb | CipherType::Aes128Cfb128 => {
symm::Cipher::aes_128_cfb128().iv_len()
.expect("iv_len should not be None")
}
CipherType::Aes256Cfb1 => {
symm::Cipher::aes_256_cfb1().iv_len()
.expect("iv_len should not be None")
}
CipherType::Aes256Cfb8 => {
symm::Cipher::aes_256_cfb8().iv_len()
.expect("iv_len should not be None")
}
CipherType::Aes256Cfb1 => symm::Cipher::aes_256_cfb1().iv_len()
.expect("iv_len should not be None"),
CipherType::Aes256Cfb8 => symm::Cipher::aes_256_cfb8().iv_len()
.expect("iv_len should not be None"),
CipherType::Aes256Cfb | CipherType::Aes256Cfb128 => {
symm::Cipher::aes_256_cfb128().iv_len()
.expect("iv_len should not be None")
}
CipherType::Rc4 => {
symm::Cipher::rc4().iv_len()
.expect("iv_len should not be None")
}
CipherType::Rc4 => symm::Cipher::rc4().iv_len().expect("iv_len should not be None"),
CipherType::Rc4Md5 => 16,
#[cfg(feature = "sodium")]
@@ -410,29 +405,21 @@ impl Display for CipherType {
#[cfg(test)]
mod test_cipher {
use crypto::{new_stream, CipherType, StreamCipher};
use crypto::CryptoMode;
use crypto::{new_stream, CipherType, StreamCipher};
#[test]
fn test_get_cipher() {
let key = CipherType::Aes128Cfb.bytes_to_key(b"PassWORD");
let iv = CipherType::Aes128Cfb.gen_init_vec();
let mut encryptor = new_stream(CipherType::Aes128Cfb,
&key[0..],
&iv[0..],
CryptoMode::Encrypt);
let mut decryptor = new_stream(CipherType::Aes128Cfb,
&key[0..],
&iv[0..],
CryptoMode::Decrypt);
let mut encryptor = new_stream(CipherType::Aes128Cfb, &key[0..], &iv[0..], CryptoMode::Encrypt);
let mut decryptor = new_stream(CipherType::Aes128Cfb, &key[0..], &iv[0..], CryptoMode::Decrypt);
let message = "HELLO WORLD";
let mut encrypted_msg = Vec::new();
encryptor.update(message.as_bytes(), &mut encrypted_msg)
.unwrap();
encryptor.update(message.as_bytes(), &mut encrypted_msg).unwrap();
let mut decrypted_msg = Vec::new();
decryptor.update(&encrypted_msg[..], &mut decrypted_msg)
.unwrap();
decryptor.update(&encrypted_msg[..], &mut decrypted_msg).unwrap();
assert!(message.as_bytes() == &decrypted_msg[..]);
}
+8 -7
View File
@@ -4,24 +4,25 @@ use std::convert::From;
use openssl::symm;
pub use self::aead::{new_aead_decryptor, new_aead_encryptor, AeadDecryptor, AeadEncryptor, BoxAeadDecryptor,
BoxAeadEncryptor};
pub use self::aead::{
new_aead_decryptor, new_aead_encryptor, AeadDecryptor, AeadEncryptor, BoxAeadDecryptor, BoxAeadEncryptor,
};
pub use self::cipher::{CipherCategory, CipherResult, CipherType};
pub use self::stream::{new_stream, StreamCipher, StreamCipherVariant};
pub mod aead;
pub mod cipher;
pub mod openssl;
pub mod digest;
pub mod table;
pub mod dummy;
pub mod openssl;
pub mod rc4_md5;
pub mod ring;
pub mod dummy;
pub mod aead;
pub mod stream;
#[cfg(feature = "miscreant")]
pub mod siv;
#[cfg(feature = "sodium")]
pub mod sodium;
pub mod stream;
pub mod table;
/// Crypto mode, encrypt or decrypt
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
+3 -4
View File
@@ -2,8 +2,8 @@
use std::convert::From;
use crypto::{CipherResult, CipherType, StreamCipher};
use crypto::cipher;
use crypto::{CipherResult, CipherType, StreamCipher};
use crypto::CryptoMode;
@@ -28,8 +28,7 @@ impl OpenSSLCrypto {
CipherType::Aes256Cfb128 => symm::Cipher::aes_256_cfb128(),
CipherType::Rc4 => symm::Cipher::rc4(),
_ => panic!("Cipher type {:?} does not supported by OpenSSLCrypt yet",
cipher_type),
_ => panic!("Cipher type {:?} does not supported by OpenSSLCrypt yet", cipher_type),
};
// Panic if error occurs
@@ -81,8 +80,8 @@ impl OpenSSLCrypto {
/// *Note: This behavior works just the same as the official version of shadowsocks.*
///
/// ```rust
/// use shadowsocks::crypto::{CipherType, CryptoMode, StreamCipher};
/// use shadowsocks::crypto::openssl::OpenSSLCipher;
/// use shadowsocks::crypto::{CipherType, CryptoMode, StreamCipher};
///
/// let method = CipherType::Aes128Cfb;
///
+3 -3
View File
@@ -1,9 +1,9 @@
//! Rc4Md5 cipher definition
use crypto::{CipherResult, CipherType, StreamCipher};
use crypto::CryptoMode;
use crypto::digest::{self, Digest, DigestType};
use crypto::openssl::OpenSSLCrypto;
use crypto::CryptoMode;
use crypto::{CipherResult, CipherType, StreamCipher};
use bytes::{BufMut, BytesMut};
@@ -43,8 +43,8 @@ unsafe impl Send for Rc4Md5Cipher {}
#[cfg(test)]
mod test {
use super::*;
use crypto::{CipherType, StreamCipher};
use crypto::CryptoMode;
use crypto::{CipherType, StreamCipher};
#[test]
fn test_rc4_md5_cipher() {
+10 -7
View File
@@ -5,10 +5,10 @@ use std::ptr;
use ring::aead::{open_in_place, seal_in_place, AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305, OpeningKey, SealingKey};
use crypto::{AeadDecryptor, AeadEncryptor};
use crypto::{CipherResult, CipherType};
use crypto::aead::{increase_nonce, make_skey};
use crypto::cipher::Error;
use crypto::{AeadDecryptor, AeadEncryptor};
use crypto::{CipherResult, CipherType};
use bytes::{BufMut, Bytes, BytesMut};
@@ -52,13 +52,16 @@ impl RingAeadCipher {
fn new_variant(t: CipherType, key: &[u8], nonce: &[u8], is_seal: bool) -> RingAeadCryptoVariant {
macro_rules! seal_or_open {
( $item:ident, $key:ident, $crypt:ident ) => {
($item:ident, $key:ident, $crypt:ident) => {
RingAeadCryptoVariant::$item($key::new(&$crypt, key).unwrap(), Bytes::from(nonce))
};
( $crypt:ident ) => {
if is_seal { seal_or_open!(Seal, SealingKey, $crypt) }
else { seal_or_open!(Open, OpeningKey, $crypt) }
}
($crypt:ident) => {
if is_seal {
seal_or_open!(Seal, SealingKey, $crypt)
} else {
seal_or_open!(Open, OpeningKey, $crypt)
}
};
}
match t {
+2 -2
View File
@@ -4,10 +4,10 @@ use std::ptr;
use miscreant::aead::{Aes128PmacSiv, Aes256PmacSiv, Algorithm};
use crypto::{AeadDecryptor, AeadEncryptor};
use crypto::{CipherResult, CipherType};
use crypto::aead::{increase_nonce, make_skey};
use crypto::cipher::Error;
use crypto::{AeadDecryptor, AeadEncryptor};
use crypto::{CipherResult, CipherType};
use bytes::{BufMut, BytesMut};
+36 -26
View File
@@ -7,8 +7,10 @@ use bytes::{BufMut, BytesMut};
use crypto::{CipherResult, CipherType, StreamCipher};
use libc::{c_ulonglong, uint32_t};
use libsodium_ffi::{sodium_init, crypto_stream_chacha20_ietf_xor_ic, crypto_stream_chacha20_xor_ic,
crypto_stream_salsa20_xor_ic, crypto_stream_xsalsa20_xor_ic};
use libsodium_ffi::{
crypto_stream_chacha20_ietf_xor_ic, crypto_stream_chacha20_xor_ic, crypto_stream_salsa20_xor_ic,
crypto_stream_xsalsa20_xor_ic, sodium_init,
};
use crypto::cipher::Error;
@@ -79,30 +81,38 @@ fn crypto_stream_xor_ic<B: BufMut>(t: CipherType,
let ret = unsafe {
match t {
CipherType::ChaCha20 => crypto_stream_chacha20_xor_ic(out.bytes_mut().as_mut_ptr(),
data.as_ptr(),
data.len() as c_ulonglong,
iv.as_ptr(),
ic as c_ulonglong,
key.as_ptr()),
CipherType::ChaCha20Ietf => crypto_stream_chacha20_ietf_xor_ic(out.bytes_mut().as_mut_ptr(),
data.as_ptr(),
data.len() as c_ulonglong,
iv.as_ptr(),
ic as uint32_t,
key.as_ptr()),
CipherType::Salsa20 => crypto_stream_salsa20_xor_ic(out.bytes_mut().as_mut_ptr(),
data.as_ptr(),
data.len() as c_ulonglong,
iv.as_ptr(),
ic as c_ulonglong,
key.as_ptr()),
CipherType::XSalsa20 => crypto_stream_xsalsa20_xor_ic(out.bytes_mut().as_mut_ptr(),
data.as_ptr(),
data.len() as c_ulonglong,
iv.as_ptr(),
ic as c_ulonglong,
key.as_ptr()),
CipherType::ChaCha20 => {
crypto_stream_chacha20_xor_ic(out.bytes_mut().as_mut_ptr(),
data.as_ptr(),
data.len() as c_ulonglong,
iv.as_ptr(),
ic as c_ulonglong,
key.as_ptr())
}
CipherType::ChaCha20Ietf => {
crypto_stream_chacha20_ietf_xor_ic(out.bytes_mut().as_mut_ptr(),
data.as_ptr(),
data.len() as c_ulonglong,
iv.as_ptr(),
ic as uint32_t,
key.as_ptr())
}
CipherType::Salsa20 => {
crypto_stream_salsa20_xor_ic(out.bytes_mut().as_mut_ptr(),
data.as_ptr(),
data.len() as c_ulonglong,
iv.as_ptr(),
ic as c_ulonglong,
key.as_ptr())
}
CipherType::XSalsa20 => {
crypto_stream_xsalsa20_xor_ic(out.bytes_mut().as_mut_ptr(),
data.as_ptr(),
data.len() as c_ulonglong,
iv.as_ptr(),
ic as c_ulonglong,
key.as_ptr())
}
_ => unreachable!(),
}
};
+1 -1
View File
@@ -1,6 +1,5 @@
//! Stream ciphers
use crypto::CryptoMode;
use crypto::cipher::{CipherCategory, CipherResult, CipherType};
use crypto::dummy;
use crypto::openssl;
@@ -8,6 +7,7 @@ use crypto::rc4_md5;
#[cfg(feature = "sodium")]
use crypto::sodium;
use crypto::table;
use crypto::CryptoMode;
use bytes::BufMut;
+2 -2
View File
@@ -2,9 +2,9 @@
use std::io::Cursor;
use crypto::{CipherResult, StreamCipher};
use crypto::CryptoMode;
use crypto::digest::{self, Digest, DigestType};
use crypto::CryptoMode;
use crypto::{CipherResult, StreamCipher};
use byteorder::{LittleEndian, ReadBytesExt};
use bytes::{BufMut, BytesMut};
+9 -8
View File
@@ -54,7 +54,7 @@
//! use shadowsocks::{run_local, Config, ConfigType};
//!
//! let config = Config::load_from_file("shadowsocks.json", ConfigType::Local).unwrap();
//! run_local(config).unwrap();
//! run_local(config);
//! ```
//!
//! That's all! And let me show you how to run a proxy server
@@ -63,7 +63,7 @@
//! use shadowsocks::{run_server, Config, ConfigType};
//!
//! let config = Config::load_from_file("shadowsocks.json", ConfigType::Server).unwrap();
//! run_server(config).unwrap();
//! run_server(config);
//! ```
//!
@@ -77,7 +77,6 @@ extern crate bytes;
extern crate digest;
#[macro_use]
extern crate futures;
extern crate futures_cpupool;
#[macro_use]
extern crate lazy_static;
extern crate libc;
@@ -91,16 +90,17 @@ extern crate miscreant;
extern crate openssl;
extern crate rand;
extern crate ring;
#[macro_use]
extern crate scoped_tls;
extern crate serde_json;
extern crate serde_urlencoded;
extern crate subprocess;
extern crate tokio_core;
#[macro_use]
extern crate tokio_io;
extern crate dns_parser;
extern crate lru_cache;
extern crate tokio;
#[cfg(any(unix, windows))]
extern crate tokio_signal;
extern crate trust_dns_resolver;
extern crate typenum;
extern crate url;
@@ -108,12 +108,13 @@ extern crate url;
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub use self::config::{ClientConfig, Config, ConfigType, ServerAddr, ServerConfig};
pub use self::relay::dns::run as run_dns;
pub use self::relay::local::run as run_local;
pub use self::relay::server::run as run_server;
pub use self::relay::tcprelay::client::Socks5Client;
pub mod config;
pub mod relay;
pub mod crypto;
pub mod plugin;
mod monitor;
pub mod plugin;
pub mod relay;
+92 -86
View File
@@ -4,120 +4,126 @@ use std::io;
use std::process;
use libc;
use tokio_core::reactor::Handle;
use tokio;
use tokio_io::IoFuture;
use futures::future::select_all;
use futures::{self, Future, Stream};
use plugin::Plugin;
use relay::boxed_future;
#[cfg(unix)]
pub fn monitor_signal(handle: &Handle, plugins: Vec<Plugin>) {
pub fn monitor_signal(plugins: Vec<Plugin>) -> impl Future<Item = (), Error = io::Error> + Send {
use tokio_signal::unix::Signal;
// Monitor SIGCHLD, triggered if subprocess (plugin) is exited.
let fut1 = Signal::new(libc::SIGCHLD, handle).and_then(|signal| {
signal.take(1)
.for_each(|_| -> Result<(), io::Error> {
error!("Plugin exited unexpectly (SIGCHLD)");
Ok(())
})
.map(|_| libc::SIGCHLD)
})
.map_err(|err| {
error!("Failed to monitor SIGCHLD, err: {:?}", err);
});
let fut1 = Signal::new(libc::SIGCHLD).and_then(|signal| {
signal.take(1)
.for_each(|_| -> Result<(), io::Error> {
error!("Plugin exited unexpectly (SIGCHLD)");
Ok(())
})
.map(|_| libc::SIGCHLD)
})
.map_err(|err| {
error!("Failed to monitor SIGCHLD, err: {:?}", err);
err
});
// Monitor SIGTERM, triggered if shadowsocks is exited gracefully. (Kill by user).
let fut2 = Signal::new(libc::SIGTERM, handle).and_then(|sigterm| {
sigterm.take(1)
.for_each(|_| -> Result<(), io::Error> {
info!("Received SIGTERM, exiting.");
Ok(())
})
.map(|_| libc::SIGTERM)
})
.map_err(|err| {
error!("Failed to monitor SIGTERM, err: {:?}", err);
});
let fut2 = Signal::new(libc::SIGTERM).and_then(|sigterm| {
sigterm.take(1)
.for_each(|_| -> Result<(), io::Error> {
info!("Received SIGTERM, exiting.");
Ok(())
})
.map(|_| libc::SIGTERM)
})
.map_err(|err| {
error!("Failed to monitor SIGTERM, err: {:?}", err);
err
});
// Monitor SIGINT, triggered by CTRL-C
let fut3 = Signal::new(libc::SIGINT, handle).and_then(|sigint| {
sigint.take(1)
.for_each(|_| -> Result<(), io::Error> {
info!("Received SIGINT, exiting.");
Ok(())
})
.map(|_| libc::SIGINT)
})
.map_err(|err| {
error!("Failed to monitor SIGINT, err: {:?}", err);
});
let fut3 = Signal::new(libc::SIGINT).and_then(|sigint| {
sigint.take(1)
.for_each(|_| -> Result<(), io::Error> {
info!("Received SIGINT, exiting.");
Ok(())
})
.map(|_| libc::SIGINT)
})
.map_err(|err| {
error!("Failed to monitor SIGINT, err: {:?}", err);
err
});
// Join them all, if any of them is triggered, kill all subprocesses and exit.
let fut = fut1.select(fut2).map(|(sig, _)| sig)
.map_err(|(e, _)| e)
.select(fut3)
.map(|(sig, _)| sig)
.map_err(|(e, _)| e)
.then(|r| {
// Something happened ... killing all subprocesses
info!("Killing {} plugin(s) and then ... Bye Bye :)",
plugins.len());
drop(plugins);
fut1.select(fut2).then(|r| match r {
Ok((o, _)) => Ok(o),
Err((e, _)) => Err(e),
})
.select(fut3)
.then(|r| match r {
Ok((o, _)) => Ok(o),
Err((e, _)) => Err(e),
})
.then(move |r| {
// Something happened ... killing all subprocesses
info!("Killing {} plugin(s) and then ... Bye Bye :)", plugins.len());
drop(plugins);
match r {
Ok(_signo) => {
process::exit(0);
}
Err(..) => Err(()),
}
});
handle.spawn(fut);
match r {
Ok(..) => {
process::exit(0);
}
Err(err) => Err(err),
}
})
}
#[cfg(windows)]
pub fn monitor_signal(handle: &Handle, plugins: Vec<Plugin>) {
pub fn monitor_signal(plugins: Vec<Plugin>) -> impl Future<Item = (), Error = io::Error> + Send {
// FIXME: How to handle SIGTERM equavalent in Windows?
use tokio_signal::windows::Event;
let fut1 = Event::ctrl_c(handle).and_then(|ev| {
ev.take(1).for_each(|_| -> Result<(), io::Error> {
error!("Received Ctrl-C event");
Ok(())
})
})
.map_err(|err| {
error!("Failed to monitor Ctrl-C event: {:?}", err);
});
let fut1 = Event::ctrl_c().and_then(|ev| {
ev.take(1).for_each(|_| -> Result<(), io::Error> {
error!("Received Ctrl-C event");
Ok(())
})
})
.map_err(|err| {
error!("Failed to monitor Ctrl-C event: {:?}", err);
err
});
let fut2 = Event::ctrl_break(handle).and_then(|ev| {
ev.take(1).for_each(|_| -> Result<(), io::Error> {
error!("Received Ctrl-Break event");
Ok(())
})
})
.map_err(|err| {
error!("Failed to monitor Ctrl-Break event: {:?}", err);
});
let fut2 = Event::ctrl_break().and_then(|ev| {
ev.take(1).for_each(|_| -> Result<(), io::Error> {
error!("Received Ctrl-Break event");
Ok(())
})
})
.map_err(|err| {
error!("Failed to monitor Ctrl-Break event: {:?}", err);
err
});
let fut = fut1.select(fut2).then(|_| -> Result<(), ()> {
// Something happened ... killing all subprocesses
info!("Killing {} plugin(s) and then ... Bye Bye :)",
plugins.len());
drop(plugins);
process::exit(libc::EXIT_FAILURE);
});
handle.spawn(fut);
fut1.select(fut2).then(|_| -> Result<(), ()> {
// Something happened ... killing all subprocesses
info!("Killing {} plugin(s) and then ... Bye Bye :)", plugins.len());
drop(plugins);
process::exit(libc::EXIT_FAILURE);
})
}
#[cfg(not(any(windows, unix)))]
pub fn monitor_signal(handle: &Handle, plugins: Vec<Plugin>) {
pub fn monitor_signal(plugins: Vec<Plugin>) -> impl Future<Item = (), Error = io::Error> + Send {
// FIXME: What can I do ...
// Blocks forever
let fut = futures::empty::<(), ()>().and_then(|_| {
drop(plugins);
});
handle.spawn(fut);
futures::empty::<(), ()>().and_then(|_| {
drop(plugins);
})
}
+3 -9
View File
@@ -20,8 +20,8 @@ use subprocess::Result as PopenResult;
use config::{Config, ServerAddr};
mod ss_plugin;
mod obfs_proxy;
mod ss_plugin;
/// Config for plugin
#[derive(Debug, Clone)]
@@ -84,14 +84,8 @@ pub fn launch_plugin(config: &mut Config, mode: PluginMode) -> io::Result<Vec<Pl
};
match mode {
PluginMode::Client => info!("Started plugin \"{}\" on {} <-> {}",
c.plugin,
local_addr,
svr.addr()),
PluginMode::Server => info!("Started plugin \"{}\" on {} <-> {}",
c.plugin,
svr.addr(),
local_addr),
PluginMode::Client => info!("Started plugin \"{}\" on {} <-> {}", c.plugin, local_addr, svr.addr()),
PluginMode::Server => info!("Started plugin \"{}\" on {} <-> {}", c.plugin, svr.addr(), local_addr),
}
svr_addr_opt = Some(svr_addr); // Fuck borrow checker
+7 -11
View File
@@ -1,7 +1,7 @@
use std::net::SocketAddr;
use subprocess::{Exec, Popen};
use subprocess::Result as PopenResult;
use subprocess::{Exec, Popen};
use config::ServerAddr;
@@ -46,16 +46,12 @@ pub fn start_plugin(plugin: &PluginConfig,
}
let cmd = match mode {
PluginMode::Client => {
cmd.arg("--dest").arg(remote.to_string())
.arg("client")
.arg(local.to_string())
}
PluginMode::Server => {
cmd.arg("--dest").arg(local.to_string())
.arg("server")
.arg(remote.to_string())
}
PluginMode::Client => cmd.arg("--dest").arg(remote.to_string())
.arg("client")
.arg(local.to_string()),
PluginMode::Server => cmd.arg("--dest").arg(local.to_string())
.arg("server")
.arg(remote.to_string()),
};
cmd.popen()
+2 -5
View File
@@ -1,7 +1,7 @@
use std::net::SocketAddr;
use subprocess::{Exec, NullFile, Popen};
use subprocess::Result as PopenResult;
use subprocess::{Exec, NullFile, Popen};
use config::ServerAddr;
@@ -12,10 +12,7 @@ pub fn start_plugin(plugin: &PluginConfig,
local: &SocketAddr,
_mode: PluginMode)
-> PopenResult<Popen> {
trace!("Start plugin \"{:?}\" remote: {}, local: {}",
plugin,
remote,
local);
trace!("Start plugin \"{:?}\" remote: {}, local: {}", plugin, remote, local);
let mut exec = Exec::cmd(&plugin.plugin).env("SS_REMOTE_HOST", remote.host())
.env("SS_REMOTE_PORT", remote.port().to_string())
+18
View File
@@ -0,0 +1,18 @@
//! DNS relay
use std::sync::Arc;
use futures::Future;
use tokio;
use config::Config;
use relay::udprelay::dns::run as run_udp;
/// DNS Relay server running under local environment.
pub fn run(config: Config) {
let config = Arc::new(config);
tokio::run(run_udp(config).then(|res| match res {
Ok(..) => Ok(()),
Err(err) => panic!("Failed to run server, err: {}", err),
}));
}
+132 -64
View File
@@ -1,80 +1,148 @@
//! Asynchronous DNS resolver
use std::io::{self, ErrorKind};
use std::net::{IpAddr, SocketAddr, ToSocketAddrs};
use std::mem;
use std::net::SocketAddr;
use std::sync::Arc;
use futures::{future, Future};
use futures::Future;
use tokio::runtime::current_thread::Runtime;
use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};
use trust_dns_resolver::ResolverFuture;
use futures_cpupool::CpuPool;
use relay::{boxed_future, BoxIoFuture};
use relay::Context;
use config::Config;
// Taken from
// bluejekyll/trust-dns/resolver/examples/global_resolver.rs
lazy_static! {
static ref GLOBAL_DNS_CPU_POOL: CpuPool = CpuPool::new_num_cpus();
// First we need to setup the global Resolver
static ref GLOBAL_DNS_RESOLVER: ResolverFuture = init_resolver(false);
static ref GLOBAL_GOOGLE_DNS_RESOLVER: ResolverFuture = init_resolver(true);
}
pub fn resolve(addr: &str, port: u16, check_forbidden: bool) -> BoxIoFuture<Vec<SocketAddr>> {
// FIXME: Sometimes addr is actually an IpAddr!
if let Ok(addr) = addr.parse::<IpAddr>() {
if !check_forbidden {
return boxed_future(future::finished(vec![SocketAddr::new(addr, port)]));
}
fn init_resolver(force_google: bool) -> ResolverFuture {
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
let result = Context::with(move |ctx| {
let forbidden_ip = &ctx.forbidden_ip();
// We'll be using this condvar to get the Resolver from the thread...
let pair = Arc::new((Mutex::new(None::<ResolverFuture>), Condvar::new()));
let pair2 = pair.clone();
if forbidden_ip.contains(&addr) {
let err = io::Error::new(ErrorKind::Other,
format!("{} is forbidden, all IPs are filtered",
addr));
Err(err)
} else {
Ok(vec![SocketAddr::new(addr, port)])
}
});
// Spawn the runtime to a new thread...
//
// This thread will manage the actual resolution runtime
thread::spawn(move || {
// A runtime for this new thread
let mut runtime = Runtime::new().expect("failed to launch Runtime");
return boxed_future(future::done(result));
// our platform independent future, result, see next blocks
let future;
// To make this independent, if targeting macOS, BSD, Linux, or Windows, we can use the system's configuration:
#[cfg(any(unix, windows))]
{
if force_google {
future = ResolverFuture::new(ResolverConfig::google(), ResolverOpts::default());
} else {
// use the system resolver configuration
future = ResolverFuture::from_system_conf().expect("Failed to create ResolverFuture");
}
}
// For other operating systems, we can use one of the preconfigured definitions
#[cfg(not(any(unix, windows)))]
{
// Directly reference the config types
use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};
// Get a new resolver with the google nameservers as the upstream recursive resolvers
future = ResolverFuture::new(ResolverConfig::google(), ResolverOpts::default());
}
// this will block the thread until the Resolver is constructed with the above configuration
let resolver = runtime.block_on(future).expect("Failed to create DNS resolver");
let &(ref lock, ref cvar) = &*pair2;
let mut started = lock.lock().unwrap();
*started = Some(resolver);
cvar.notify_one();
drop(started);
runtime.run().expect("Resolver Thread shutdown!");
});
// Wait for the thread to start up.
let &(ref lock, ref cvar) = &*pair;
let mut resolver = lock.lock().unwrap();
while resolver.is_none() {
resolver = cvar.wait(resolver).unwrap();
}
trace!("Going to resolve \"{}:{}\"", addr, port);
let owned_addr = addr.to_owned();
let fut = GLOBAL_DNS_CPU_POOL.spawn_fn(move || match (owned_addr.as_str(), port).to_socket_addrs() {
Ok(a) => Ok((owned_addr, a)),
Err(err) => {
error!("Failed to resolve {}, {}", owned_addr, err);
Err(err)
}
})
.and_then(move |(owned_addr, addr_iter)| {
let v =
if !check_forbidden {
addr_iter.collect::<Vec<SocketAddr>>()
} else {
Context::with(move |ctx| {
let forbidden_ip = ctx.forbidden_ip();
addr_iter.filter(|addr| {
let filtered = forbidden_ip.contains(&addr.ip());
if filtered {
error!("{} is forbidden and ignored", addr.ip());
}
!filtered
})
.collect::<Vec<SocketAddr>>()
})
};
// take the started resolver
let resolver = mem::replace(&mut *resolver, None);
if v.is_empty() {
let err =
io::Error::new(io::ErrorKind::Other,
format!("resolved \"{}:{}\" to empty address",
owned_addr, port));
Err(err)
} else {
debug!("Resolved \"{}\" => {:?}", owned_addr, v);
Ok(v)
}
});
boxed_future(fut)
// set the global resolver
resolver.expect("resolver should not be none")
}
fn inner_resolve(config: Arc<Config>,
addr: &str,
port: u16,
check_forbidden: bool,
force_google: bool)
-> impl Future<Item = Vec<SocketAddr>, Error = io::Error> + Send {
let owned_addr = addr.to_owned();
let owned_addr2 = owned_addr.clone();
let resolver = if force_google {
&*GLOBAL_GOOGLE_DNS_RESOLVER
} else {
&*GLOBAL_DNS_RESOLVER
};
resolver.lookup_ip(addr)
.map_err(move |err| {
error!("Failed to resolve {}, err: {}", owned_addr2, err);
io::Error::new(io::ErrorKind::Other, "dns resolve error")
})
.and_then(move |lookup_result| {
let mut vaddr = Vec::new();
for ip in lookup_result.iter() {
if check_forbidden {
let forbidden_ip = &config.forbidden_ip;
if forbidden_ip.contains(&ip) {
debug!("Resolved {} => {}, which is skipped by forbidden_ip", owned_addr, ip);
continue;
}
vaddr.push(SocketAddr::new(ip, port));
}
}
if vaddr.is_empty() {
let err = io::Error::new(ErrorKind::Other,
format!("resolved {} to empty address, all IPs are filtered",
owned_addr));
Err(err)
} else {
debug!("Resolved {} => {:?}", owned_addr, vaddr);
Ok(vaddr)
}
})
}
pub fn resolve(config: Arc<Config>,
addr: &str,
port: u16,
check_forbidden: bool)
-> impl Future<Item = Vec<SocketAddr>, Error = io::Error> + Send {
inner_resolve(config, addr, port, check_forbidden, false)
}
pub fn resolve_by_google(config: Arc<Config>,
addr: &str,
port: u16,
check_forbidden: bool)
-> impl Future<Item = Vec<SocketAddr>, Error = io::Error> + Send {
inner_resolve(config, addr, port, check_forbidden, true)
}
+2 -2
View File
@@ -1,6 +1,6 @@
//! Load balancer for picking servers
use std::rc::Rc;
use std::sync::Arc;
pub use self::roundrobin::RoundRobin;
@@ -9,6 +9,6 @@ use config::ServerConfig;
pub mod roundrobin;
pub trait LoadBalancer {
fn pick_server(&mut self) -> Rc<ServerConfig>;
fn pick_server(&mut self) -> Arc<ServerConfig>;
fn total(&self) -> usize;
}
+4 -4
View File
@@ -1,25 +1,25 @@
//! Load balancer using round robin strategy
use std::rc::Rc;
use std::sync::Arc;
use config::{Config, ServerConfig};
use relay::loadbalancing::server::LoadBalancer;
#[derive(Clone)]
pub struct RoundRobin {
servers: Vec<Rc<ServerConfig>>,
servers: Vec<Arc<ServerConfig>>,
index: usize,
}
impl RoundRobin {
pub fn new(config: &Config) -> RoundRobin {
RoundRobin { servers: config.server.iter().map(|s| Rc::new(s.clone())).collect(),
RoundRobin { servers: config.server.iter().map(|s| Arc::new(s.clone())).collect(),
index: 0usize, }
}
}
impl LoadBalancer for RoundRobin {
fn pick_server(&mut self) -> Rc<ServerConfig> {
fn pick_server(&mut self) -> Arc<ServerConfig> {
let server = &self.servers;
if server.is_empty() {
+21 -21
View File
@@ -1,14 +1,15 @@
//! Local side
use std::io;
use std::sync::Arc;
use tokio_core::reactor::Core;
use tokio;
use futures::future::join_all;
use futures::Future;
use config::Config;
use plugin::{launch_plugin, PluginMode};
use relay::Context;
use relay::boxed_future;
use relay::tcprelay::local::run as run_tcp;
use relay::udprelay::local::run as run_udp;
@@ -24,27 +25,26 @@ use relay::udprelay::local::run as run_udp;
/// config.server = vec![ServerConfig::basic("127.0.0.1:8388".parse().unwrap(),
/// "server-password".to_string(),
/// CipherType::Aes256Cfb)];
/// run(config).unwrap();
/// run(config);
/// ```
pub fn run(mut config: Config) -> io::Result<()> {
let mut lp = Core::new()?;
let handle = lp.handle();
pub fn run(mut config: Config) {
// Hold it here, kill all plugins when Core is finished
let plugins = launch_plugin(&mut config, PluginMode::Client).expect("Failed to launch plugins");
let mon = ::monitor::monitor_signal(plugins);
let config = Arc::new(config);
let enable_udp = config.enable_udp;
// Hold it here, kill all plugins when Core is finished
let plugins = launch_plugin(&mut config, PluginMode::Client)?;
::monitor::monitor_signal(&handle, plugins);
let tcp_fut = run_tcp(config.clone());
let mut vf = vec![boxed_future(mon), boxed_future(tcp_fut)];
if enable_udp {
let udp_fut = run_udp(config);
vf.push(boxed_future(udp_fut));
}
let context = Context::new(handle, config);
Context::set(&context, move || {
if enable_udp {
let tcp_fut = run_tcp();
let udp_fut = run_udp();
lp.run(tcp_fut.join(udp_fut).map(|_| ()))
} else {
let tcp_fut = run_tcp();
lp.run(tcp_fut)
}
})
tokio::run(join_all(vf).then(|res| match res {
Ok(..) => Ok(()),
Err(err) => panic!("Failed to run server, err: {}", err),
}));
}
+7 -61
View File
@@ -1,73 +1,19 @@
//! Relay server in local and server side implementations.
use std::collections::HashSet;
use std::io;
use std::net::IpAddr;
use config::Config;
use futures::Future;
use tokio_core::reactor::Handle;
pub mod tcprelay;
pub mod udprelay;
pub mod dns;
mod dns_resolver;
mod loadbalancing;
pub mod local;
pub mod server;
mod loadbalancing;
mod dns_resolver;
pub mod socks5;
pub mod tcprelay;
pub mod udprelay;
mod utils;
/// Alias for Boxed Future without Send
pub type BoxIoFuture<T> = Box<Future<Item = T, Error = io::Error>>;
fn boxed_future<T, E, F>(f: F) -> Box<Future<Item = T, Error = E>>
where F: Future<Item = T, Error = E> + 'static
pub fn boxed_future<T, E, F>(f: F) -> Box<Future<Item = T, Error = E> + Send + 'static>
where F: Future<Item = T, Error = E> + Send + 'static
{
Box::new(f)
}
scoped_thread_local!(static CONTEXT: Context);
/// Local server running context
pub struct Context {
handle: Handle,
config: Config,
}
impl Context {
#[doc(hidden)]
/// Creates a new Context
pub fn new(handle: Handle, config: Config) -> Context {
Context { handle: handle,
config: config, }
}
/// Get the value in this context
pub fn with<F, R>(f: F) -> R
where F: FnOnce(&Context) -> R
{
CONTEXT.with(f)
}
#[doc(hidden)]
pub fn set<F, R>(ctx: &Context, f: F) -> R
where F: FnOnce() -> R
{
CONTEXT.set(ctx, f)
}
/// Get Core handle
pub fn handle(&self) -> &Handle {
&self.handle
}
/// Get config
pub fn config(&self) -> &Config {
&self.config
}
/// Get forbidden IPs
pub fn forbidden_ip(&self) -> &HashSet<IpAddr> {
&self.config.forbidden_ip
}
}
+21 -32
View File
@@ -1,14 +1,15 @@
//! Server side
use std::io;
use std::sync::Arc;
use tokio_core::reactor::Core;
use tokio;
use futures::future::join_all;
use futures::Future;
use config::Config;
use plugin::{launch_plugin, PluginMode};
use relay::Context;
use relay::boxed_future;
use relay::tcprelay::server::run as run_tcp;
use relay::udprelay::server::run as run_udp;
@@ -23,38 +24,26 @@ use relay::udprelay::server::run as run_udp;
/// config.server = vec![ServerConfig::basic("127.0.0.1:8388".parse().unwrap(),
/// "server-password".to_string(),
/// CipherType::Aes256Cfb)];
/// run(config).unwrap();
/// run(config);
/// ```
///
pub fn run(mut config: Config) -> io::Result<()> {
let mut lp = Core::new()?;
let handle = lp.handle();
// let config = Rc::new(config);
// let tcp_fut = run_tcp(config.clone(), handle.clone());
// if config.enable_udp {
// lp.run(tcp_fut.join(run_udp(config, handle)).map(|_| ()))
// } else {
// lp.run(tcp_fut)
// }
///
pub fn run(mut config: Config) {
// Hold it here, kill all plugins when Core is finished
let plugins = launch_plugin(&mut config, PluginMode::Server).expect("Failed to launch plugins");
let mon = ::monitor::monitor_signal(plugins);
let config = Arc::new(config);
let enable_udp = config.enable_udp;
// Hold it here, kill all plugins when Core is finished
let plugins = launch_plugin(&mut config, PluginMode::Server)?;
::monitor::monitor_signal(&handle, plugins);
let tcp_fut = run_tcp(config.clone());
let mut vf = vec![boxed_future(mon), boxed_future(tcp_fut)];
if enable_udp {
let udp_fut = run_udp(config);
vf.push(boxed_future(udp_fut));
}
let context = Context::new(handle, config);
Context::set(&context, move || {
if enable_udp {
let tcp_fut = run_tcp();
let udp_fut = run_udp();
lp.run(tcp_fut.join(udp_fut).map(|_| ()))
} else {
let tcp_fut = run_tcp();
lp.run(tcp_fut)
}
})
tokio::run(join_all(vf).then(|res| match res {
Ok(..) => Ok(()),
Err(err) => panic!("Failed to run server, err: {}", err),
}));
}
+92 -101
View File
@@ -15,13 +15,12 @@ use bytes::{BufMut, Bytes, BytesMut, IntoBuf};
use futures::{Async, Future, Poll};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_io::io::read_exact;
use tokio_io::{AsyncRead, AsyncWrite};
use relay::BoxIoFuture;
pub use self::consts::{SOCKS5_AUTH_METHOD_GSSAPI, SOCKS5_AUTH_METHOD_NONE, SOCKS5_AUTH_METHOD_NOT_ACCEPTABLE,
SOCKS5_AUTH_METHOD_PASSWORD};
pub use self::consts::{
SOCKS5_AUTH_METHOD_GSSAPI, SOCKS5_AUTH_METHOD_NONE, SOCKS5_AUTH_METHOD_NOT_ACCEPTABLE, SOCKS5_AUTH_METHOD_PASSWORD,
};
use super::utils::{write_bytes, WriteBytes};
@@ -199,8 +198,7 @@ impl error::Error for Error {
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::new(Reply::GeneralFailure,
<io::Error as error::Error>::description(&err))
Error::new(Reply::GeneralFailure, <io::Error as error::Error>::description(&err))
}
}
@@ -444,10 +442,7 @@ impl<R> ReadAddress<R>
let buf = self.buf.as_mut().unwrap();
while self.already_read < buf.len() {
match self.reader.as_mut()
.unwrap()
.read(&mut buf[self.already_read..])
{
match self.reader.as_mut().unwrap().read(&mut buf[self.already_read..]) {
Ok(0) => {
let err = io::Error::new(io::ErrorKind::Other, "Unexpected EOF");
return Err(err);
@@ -493,14 +488,12 @@ fn write_ipv6_address<B: BufMut>(addr: &SocketAddrV6, buf: &mut B) {
}
fn write_domain_name_address<B: BufMut>(dnaddr: &str, port: u16, buf: &mut B) {
use bytes::BigEndian;
assert!(dnaddr.len() <= u8::max_value() as usize);
buf.put_u8(consts::SOCKS5_ADDR_TYPE_DOMAIN_NAME);
buf.put_u8(dnaddr.len() as u8);
buf.put_slice(dnaddr[..].as_bytes());
buf.put_u16::<BigEndian>(port);
buf.put_u16_be(port);
}
fn write_socket_address<B: BufMut>(addr: &SocketAddr, buf: &mut B) {
@@ -551,36 +544,37 @@ impl TcpRequestHeader {
}
/// Read from a reader
pub fn read_from<R: AsyncRead + 'static>(r: R) -> Box<Future<Item = (R, TcpRequestHeader), Error = Error>> {
let fut = read_exact(r, [0u8; 3]).map_err(From::from)
.and_then(|(r, buf)| {
let ver = buf[0];
if ver != consts::SOCKS5_VERSION {
return Err(Error::new(Reply::ConnectionRefused,
"Unsupported Socks version"));
}
pub fn read_from<R>(r: R) -> impl Future<Item = (R, TcpRequestHeader), Error = Error> + Send
where R: AsyncRead + Send + 'static
{
read_exact(r, [0u8; 3]).map_err(From::from)
.and_then(|(r, buf)| {
let ver = buf[0];
if ver != consts::SOCKS5_VERSION {
return Err(Error::new(Reply::ConnectionRefused,
"Unsupported Socks version"));
}
let cmd = buf[1];
let command = match Command::from_u8(cmd) {
Some(c) => c,
None => {
return Err(Error::new(Reply::CommandNotSupported,
"Unsupported command"))
}
};
let cmd = buf[1];
let command = match Command::from_u8(cmd) {
Some(c) => c,
None => {
return Err(Error::new(Reply::CommandNotSupported,
"Unsupported command"))
}
};
Ok((r, command))
})
.and_then(|(r, command)| {
Address::read_from(r).map(move |(conn, address)| {
let header = TcpRequestHeader { command: command,
address: address, };
Ok((r, command))
})
.and_then(|(r, command)| {
Address::read_from(r).map(move |(conn, address)| {
let header =
TcpRequestHeader { command: command,
address: address, };
(conn, header)
})
});
Box::new(fut)
(conn, header)
})
})
}
/// Write data into a writer
@@ -631,30 +625,30 @@ impl TcpResponseHeader {
}
/// Read from a reader
pub fn read_from<R: AsyncRead + 'static>(r: R) -> Box<Future<Item = (R, TcpResponseHeader), Error = Error>> {
let fut = read_exact(r, [0u8; 3]).map_err(From::from)
.and_then(|(r, buf)| {
let ver = buf[0];
let reply_code = buf[1];
pub fn read_from<R>(r: R) -> impl Future<Item = (R, TcpResponseHeader), Error = Error> + Send
where R: AsyncRead + Send + 'static
{
read_exact(r, [0u8; 3]).map_err(From::from)
.and_then(|(r, buf)| {
let ver = buf[0];
let reply_code = buf[1];
if ver != consts::SOCKS5_VERSION {
return Err(Error::new(Reply::ConnectionRefused,
"Unsupported Socks version"));
}
if ver != consts::SOCKS5_VERSION {
return Err(Error::new(Reply::ConnectionRefused,
"Unsupported Socks version"));
}
Ok((r, reply_code))
})
.and_then(|(r, reply_code)| {
Address::read_from(r).map(move |(r, address)| {
let rep = TcpResponseHeader { reply:
Ok((r, reply_code))
})
.and_then(|(r, reply_code)| {
Address::read_from(r).map(move |(r, address)| {
let rep = TcpResponseHeader { reply:
Reply::from_u8(reply_code),
address: address, };
(r, rep)
})
});
Box::new(fut)
(r, rep)
})
})
}
/// Write to a writer
@@ -666,8 +660,7 @@ impl TcpResponseHeader {
/// Writes to buffer
pub fn write_to_buf<B: BufMut>(&self, buf: &mut B) {
let TcpResponseHeader { ref reply,
ref address, } = *self;
let TcpResponseHeader { ref reply, ref address } = *self;
buf.put_slice(&[consts::SOCKS5_VERSION, reply.as_u8(), 0x00]);
address.write_to_buf(buf);
}
@@ -700,23 +693,22 @@ impl HandshakeRequest {
}
/// Read from a reader
pub fn read_from<R: AsyncRead + 'static>(r: R) -> BoxIoFuture<(R, HandshakeRequest)> {
let fut = read_exact(r, [0u8, 0u8]).and_then(|(r, buf)| {
let ver = buf[0];
let nmet = buf[1];
pub fn read_from<R>(r: R) -> impl Future<Item = (R, HandshakeRequest), Error = io::Error>
where R: AsyncRead + Send + 'static
{
read_exact(r, [0u8, 0u8]).and_then(|(r, buf)| {
let ver = buf[0];
let nmet = buf[1];
if ver != consts::SOCKS5_VERSION {
return Err(io::Error::new(io::ErrorKind::Other,
"Invalid Socks5 version"));
}
if ver != consts::SOCKS5_VERSION {
return Err(io::Error::new(io::ErrorKind::Other,
"Invalid Socks5 version"));
}
Ok((r, nmet))
})
.and_then(|(r, nmet)| {
read_exact(r, vec![0u8; nmet as usize])
.and_then(|(r, methods)| Ok((r, HandshakeRequest { methods: methods })))
});
Box::new(fut)
Ok((r, nmet))
})
.and_then(|(r, nmet)| read_exact(r, vec![0u8; nmet as usize]))
.and_then(|(r, methods)| Ok((r, HandshakeRequest { methods: methods })))
}
/// Write to a writer
@@ -760,19 +752,19 @@ impl HandshakeResponse {
}
/// Read from a reader
pub fn read_from<R: AsyncRead + 'static>(r: R) -> BoxIoFuture<(R, HandshakeResponse)> {
let fut = read_exact(r, [0u8, 0u8]).and_then(|(r, buf)| {
let ver = buf[0];
let met = buf[1];
pub fn read_from<R>(r: R) -> impl Future<Item = (R, HandshakeResponse), Error = io::Error>
where R: AsyncRead + Send + 'static
{
read_exact(r, [0u8, 0u8]).and_then(|(r, buf)| {
let ver = buf[0];
let met = buf[1];
if ver != consts::SOCKS5_VERSION {
Err(io::Error::new(io::ErrorKind::Other,
"Invalid Socks5 version"))
} else {
Ok((r, HandshakeResponse { chosen_method: met }))
}
});
Box::new(fut)
if ver != consts::SOCKS5_VERSION {
Err(io::Error::new(io::ErrorKind::Other, "Invalid Socks5 version"))
} else {
Ok((r, HandshakeResponse { chosen_method: met }))
}
})
}
/// Write to a writer
@@ -820,16 +812,16 @@ impl UdpAssociateHeader {
}
/// Read from a reader
pub fn read_from<R: AsyncRead + 'static>(r: R) -> Box<Future<Item = (R, UdpAssociateHeader), Error = Error>> {
let fut = read_exact(r, [0u8; 3]).map_err(From::from)
.and_then(|(r, buf)| {
let frag = buf[2];
Address::read_from(r).map(move |(r, address)| {
let h = UdpAssociateHeader::new(frag, address);
(r, h)
})
});
Box::new(fut)
pub fn read_from<R>(r: R) -> impl Future<Item = (R, UdpAssociateHeader), Error = Error> + Send
where R: AsyncRead + Send + 'static
{
read_exact(r, [0u8; 3]).map_err(From::from).and_then(|(r, buf)| {
let frag = buf[2];
Address::read_from(r).map(move |(r, address)| {
let h = UdpAssociateHeader::new(frag, address);
(r, h)
})
})
}
/// Write to a writer
@@ -841,8 +833,7 @@ impl UdpAssociateHeader {
/// Write to buffer
pub fn write_to_buf<B: BufMut>(&self, buf: &mut B) {
let UdpAssociateHeader { ref frag,
ref address, } = *self;
let UdpAssociateHeader { ref frag, ref address } = *self;
buf.put_slice(&[0x00, 0x00, *frag]);
address.write_to_buf(buf);
}
+3 -3
View File
@@ -36,7 +36,7 @@ use std::cmp;
use std::io::{self, BufRead, Cursor, Read};
use std::u16;
use bytes::{BigEndian, Buf, BufMut, BytesMut};
use bytes::{Buf, BufMut, BytesMut};
use tokio_io::{AsyncRead, AsyncWrite};
use crypto::{self, BoxAeadDecryptor, BoxAeadEncryptor, CipherType};
@@ -138,7 +138,7 @@ impl<R> DecryptedReader<R>
let len = {
let mut len_buf = [0u8; 2];
self.cipher.decrypt(data, &mut len_buf, tag)?;
Cursor::new(len_buf).get_u16::<BigEndian>() as usize
Cursor::new(len_buf).get_u16_be() as usize
};
if len > MAX_PACKET_SIZE {
@@ -288,7 +288,7 @@ impl<W> EncryptedWrite for EncryptedWriter<W>
let data_length = data.len() as u16;
let mut data_len_buf = BytesMut::with_capacity(2);
data_len_buf.put_u16::<BigEndian>(data_length);
data_len_buf.put_u16_be(data_length);
let mut tag_buf = BytesMut::with_capacity(self.tag_size);
unsafe {
+15 -18
View File
@@ -3,16 +3,15 @@
use std::io::{self, Read, Write};
use std::net::SocketAddr;
use tokio_core::net::TcpStream;
use tokio_core::reactor::Handle;
use tokio_io::{AsyncRead, AsyncWrite};
use tokio::net::TcpStream;
use tokio_io::io::flush;
use tokio_io::{AsyncRead, AsyncWrite};
use futures::{self, Async, Future, Poll};
use relay::{boxed_future, BoxIoFuture};
use relay::socks5::{self, Address, Command, HandshakeRequest, HandshakeResponse, Reply, TcpRequestHeader,
TcpResponseHeader};
use relay::socks5::{
self, Address, Command, HandshakeRequest, HandshakeResponse, Reply, TcpRequestHeader, TcpResponseHeader,
};
/// Socks5 proxy client
pub struct Socks5Client {
@@ -21,11 +20,11 @@ pub struct Socks5Client {
impl Socks5Client {
/// Connects to `addr` via `proxy`
pub fn connect<A>(addr: A, proxy: SocketAddr, handle: Handle) -> BoxIoFuture<Socks5Client>
pub fn connect<A>(addr: A, proxy: SocketAddr) -> impl Future<Item = Socks5Client, Error = io::Error> + Send
where Address: From<A>,
A: 'static
A: Send + 'static
{
let fut = futures::lazy(move || TcpStream::connect(&proxy, &handle))
futures::lazy(move || TcpStream::connect(&proxy))
.and_then(move |s| {
// 1. Handshake
let hs = HandshakeRequest::new(vec![socks5::SOCKS5_AUTH_METHOD_NONE]);
@@ -58,17 +57,17 @@ impl Socks5Client {
}
})
})
.map(|s| Socks5Client { stream: s });
boxed_future(fut)
.map(|s| Socks5Client { stream: s })
}
/// UDP Associate `addr` via `proxy`
pub fn udp_associate<A>(addr: A, proxy: SocketAddr, handle: Handle) -> BoxIoFuture<(Socks5Client, Address)>
pub fn udp_associate<A>(addr: A,
proxy: SocketAddr)
-> impl Future<Item = (Socks5Client, Address), Error = io::Error> + Send
where Address: From<A>,
A: 'static
A: Send + 'static
{
let fut = futures::lazy(move || TcpStream::connect(&proxy, &handle))
futures::lazy(move || TcpStream::connect(&proxy))
.and_then(move |s| {
// 1. Handshake
let hs = HandshakeRequest::new(vec![socks5::SOCKS5_AUTH_METHOD_NONE]);
@@ -101,9 +100,7 @@ impl Socks5Client {
}
})
})
.map(|(s, a)| (Socks5Client { stream: s }, a));
boxed_future(fut)
.map(|(s, a)| (Socks5Client { stream: s }, a))
}
}
+10 -16
View File
@@ -2,20 +2,18 @@
use std::io::{self, BufRead, Read};
use std::mem;
use std::time::Duration;
use std::time::{Duration, Instant};
use futures::{Async, Future, Poll};
use tokio_core::reactor::Timeout;
use tokio_io::{AsyncRead, AsyncWrite};
use tokio::timer::Delay;
use tokio_io::io::{copy, Copy};
use tokio_io::{AsyncRead, AsyncWrite};
use bytes::{BufMut, BytesMut};
use super::BUFFER_SIZE;
use super::utils::{copy_timeout, copy_timeout_opt, CopyTimeout, CopyTimeoutOpt};
use relay::Context;
use super::BUFFER_SIZE;
static DUMMY_BUFFER: [u8; BUFFER_SIZE] = [0u8; BUFFER_SIZE];
@@ -231,9 +229,7 @@ impl<R, W> Future for EncryptedCopy<R, W>
// If our buffer has some data, let's write it out!
while self.pos < self.cap {
let i = try_nb!(self.writer.as_mut()
.unwrap()
.write_raw(&self.buf[self.pos..self.cap]));
let i = try_nb!(self.writer.as_mut().unwrap().write_raw(&self.buf[self.pos..self.cap]));
self.pos += i;
self.amt += i as u64;
}
@@ -261,7 +257,7 @@ pub struct EncryptedCopyTimeout<R, W>
pos: usize,
cap: usize,
timeout: Duration,
timer: Option<Timeout>,
timer: Option<Delay>,
read_buf: [u8; BUFFER_SIZE],
write_buf: BytesMut,
}
@@ -288,7 +284,7 @@ impl<R, W> EncryptedCopyTimeout<R, W>
match self.timer.as_mut() {
None => Ok(()),
Some(t) => match t.poll() {
Err(err) => Err(err),
Err(err) => panic!("Failed to poll on timer, err: {}", err),
Ok(Async::Ready(..)) => Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")),
Ok(Async::NotReady) => Ok(()),
},
@@ -319,16 +315,14 @@ impl<R, W> EncryptedCopyTimeout<R, W>
let buffer_len = self.writer.as_mut().unwrap().buffer_size(data);
self.write_buf.reserve(buffer_len);
self.writer.as_mut()
.unwrap()
.encrypt(data, &mut self.write_buf)?;
self.writer.as_mut().unwrap().encrypt(data, &mut self.write_buf)?;
self.cap = self.write_buf.len();
self.pos = 0;
Ok(n)
}
Err(e) => {
if e.kind() == io::ErrorKind::WouldBlock {
Context::with(|ctx| self.timer = Some(Timeout::new(self.timeout, ctx.handle()).unwrap()));
self.timer = Some(Delay::new(Instant::now() + self.timeout));
}
Err(e)
}
@@ -352,7 +346,7 @@ impl<R, W> EncryptedCopyTimeout<R, W>
}
Err(e) => {
if e.kind() == io::ErrorKind::WouldBlock {
self.timer = Context::with(|ctx| Some(Timeout::new(self.timeout, ctx.handle()).unwrap()));
self.timer = Some(Delay::new(Instant::now() + self.timeout));
}
Err(e)
}
+8 -3
View File
@@ -1,9 +1,14 @@
//! Relay for TCP server that running on local environment
use std::io;
use std::sync::Arc;
use futures::Future;
use super::socks5_local;
use relay::BoxIoFuture;
use config::Config;
/// Starts a TCP local server
pub fn run() -> BoxIoFuture<()> {
socks5_local::run()
pub fn run(config: Arc<Config>) -> impl Future<Item = (), Error = io::Error> + Send {
socks5_local::run(config)
}
+108 -127
View File
@@ -4,21 +4,19 @@ use std::io::{self, BufRead, Read};
use std::iter::{IntoIterator, Iterator};
use std::mem;
use std::net::SocketAddr;
use std::rc::Rc;
use std::time::Duration;
use std::sync::Arc;
use std::time::{Duration, Instant};
use config::{ServerAddr, ServerConfig};
use config::{Config, ServerAddr, ServerConfig};
use crypto::CipherCategory;
use relay::{boxed_future, BoxIoFuture};
use relay::Context;
use relay::boxed_future;
use relay::dns_resolver::resolve;
use relay::socks5::Address;
use tokio_core::net::{TcpStream, TcpStreamNew};
use tokio_core::reactor::{Handle, Timeout};
use tokio_io::AsyncRead;
use tokio_io::io::{ReadHalf, WriteHalf};
use tokio::net::{ConnectFuture, TcpStream};
use tokio_io::io::{read_exact, write_all};
use tokio_io::io::{ReadHalf, WriteHalf};
use tokio_io::{AsyncRead, IoFuture};
use futures::{self, Async, Future, Poll};
@@ -31,13 +29,13 @@ pub use self::crypto_io::{DecryptedRead, EncryptedWrite};
use self::aead::{DecryptedReader as AeadDecryptedReader, EncryptedWriter as AeadEncryptedWriter};
use self::stream::{DecryptedReader as StreamDecryptedReader, EncryptedWriter as StreamEncryptedWriter};
pub mod local;
mod socks5_local;
pub mod server;
mod stream;
mod aead;
pub mod client;
mod crypto_io;
mod aead;
pub mod local;
pub mod server;
mod socks5_local;
mod stream;
mod utils;
const BUFFER_SIZE: usize = 8 * 1024; // 8K buffer
@@ -151,9 +149,9 @@ impl From<AeadEncryptedWriter<TcpWriteHalf>> for EncryptedHalf {
}
/// Boxed future of `DecryptedHalf`
pub type DecryptedHalfFut = BoxIoFuture<DecryptedHalf>;
pub type DecryptedHalfFut = IoFuture<DecryptedHalf>;
/// Boxed future of `EncryptedHalf`
pub type EncryptedHalfFut = BoxIoFuture<EncryptedHalf>;
pub type EncryptedHalfFut = IoFuture<EncryptedHalf>;
/// Try to connect every IPs one by one
enum TcpStreamConnect<I: Iterator<Item = SocketAddr>> {
@@ -161,17 +159,15 @@ enum TcpStreamConnect<I: Iterator<Item = SocketAddr>> {
Connect {
last_err: Option<io::Error>,
addr_iter: I,
opt_stream_new: Option<(TcpStreamNew, SocketAddr)>,
handle: Handle,
opt_stream_new: Option<(ConnectFuture, SocketAddr)>,
},
}
impl<I: Iterator<Item = SocketAddr>> TcpStreamConnect<I> {
fn new(iter: I, handle: &Handle) -> TcpStreamConnect<I> {
fn new(iter: I) -> TcpStreamConnect<I> {
TcpStreamConnect::Connect { last_err: None,
addr_iter: iter,
opt_stream_new: None,
handle: handle.clone(), }
opt_stream_new: None, }
}
}
@@ -185,8 +181,7 @@ impl<I: Iterator<Item = SocketAddr>> Future for TcpStreamConnect<I> {
TcpStreamConnect::Empty => unreachable!(),
TcpStreamConnect::Connect { ref mut last_err,
ref mut addr_iter,
ref mut opt_stream_new,
ref handle, } => {
ref mut opt_stream_new, } => {
loop {
// 1. Poll before doing anything else
if let Some((ref mut stream_new, ref addr)) = *opt_stream_new {
@@ -205,7 +200,7 @@ impl<I: Iterator<Item = SocketAddr>> Future for TcpStreamConnect<I> {
match addr_iter.next() {
None => break,
Some(addr) => {
*opt_stream_new = Some((TcpStream::connect(&addr, &handle), addr));
*opt_stream_new = Some((TcpStream::connect(&addr), addr));
}
}
}
@@ -214,37 +209,36 @@ impl<I: Iterator<Item = SocketAddr>> Future for TcpStreamConnect<I> {
match mem::replace(self, TcpStreamConnect::Empty) {
TcpStreamConnect::Empty => unreachable!(),
TcpStreamConnect::Connect { last_err, .. } => match last_err {
None => {
let err = io::Error::new(ErrorKind::Other, "connect TCP without any addresses");
Err(err)
TcpStreamConnect::Connect { last_err, .. } => {
match last_err {
None => {
let err = io::Error::new(ErrorKind::Other, "connect TCP without any addresses");
Err(err)
}
Some(err) => Err(err),
}
Some(err) => Err(err),
},
}
}
}
}
fn connect_proxy_server(svr_cfg: Rc<ServerConfig>) -> BoxIoFuture<TcpStream> {
fn connect_proxy_server(config: Arc<Config>,
svr_cfg: Arc<ServerConfig>)
-> impl Future<Item = TcpStream, Error = io::Error> + Send {
let timeout = *svr_cfg.timeout();
trace!("Connecting to proxy {:?}, timeout: {:?}",
svr_cfg.addr(),
timeout);
trace!("Connecting to proxy {:?}, timeout: {:?}", svr_cfg.addr(), timeout);
match *svr_cfg.addr() {
ServerAddr::SocketAddr(ref addr) => {
Context::with(|ctx| {
let handle = ctx.handle();
try_timeout(TcpStream::connect(addr, handle), timeout, handle)
})
let fut = try_timeout(TcpStream::connect(addr), timeout);
boxed_future(fut)
}
ServerAddr::DomainName(ref domain, port) => {
let fut = Context::with(|ctx| {
let handle = ctx.handle().clone();
try_timeout(resolve(&domain[..], port, false), timeout, &handle).and_then(move |vec_ipaddr| {
let fut = TcpStreamConnect::new(vec_ipaddr.into_iter(), &handle);
try_timeout(fut, timeout, &handle)
let fut = {
try_timeout(resolve(config.clone(), &domain[..], port, false), timeout).and_then(move |vec_ipaddr| {
let fut = TcpStreamConnect::new(vec_ipaddr.into_iter());
try_timeout(fut, timeout)
})
});
};
boxed_future(fut)
}
}
@@ -252,32 +246,33 @@ fn connect_proxy_server(svr_cfg: Rc<ServerConfig>) -> BoxIoFuture<TcpStream> {
/// Handshake logic for ShadowSocks Client
pub fn proxy_server_handshake(remote_stream: TcpStream,
svr_cfg: Rc<ServerConfig>,
svr_cfg: Arc<ServerConfig>,
relay_addr: Address)
-> BoxIoFuture<(DecryptedHalfFut, EncryptedHalfFut)> {
-> impl Future<Item = (DecryptedHalfFut, EncryptedHalfFut), Error = io::Error> + Send {
let timeout = *svr_cfg.timeout();
let fut = proxy_handshake(remote_stream, svr_cfg).and_then(move |(r_fut, w_fut)| {
proxy_handshake(remote_stream, svr_cfg).and_then(move |(r_fut, w_fut)| {
let w_fut = w_fut.and_then(move |enc_w| {
// Send relay address to remote
let mut buf = BytesMut::with_capacity(relay_addr.len());
relay_addr.write_to_buf(&mut buf);
// Send relay address to remote
let mut buf = BytesMut::with_capacity(relay_addr.len());
relay_addr.write_to_buf(&mut buf);
trace!("Got encrypt stream and going to send addr: {:?}, buf: {:?}", relay_addr, buf);
trace!("Got encrypt stream and going to send addr: {:?}, buf: {:?}",
relay_addr,
buf);
Context::with(|ctx| try_timeout(enc_w.write_all(buf), timeout, ctx.handle()).map(|(w, _)| w))
});
try_timeout(enc_w.write_all(buf), timeout).map(|(w, _)| w)
});
Ok((r_fut, boxed_future(w_fut)))
});
boxed_future(fut)
})
}
/// ShadowSocks Client-Server handshake protocol
/// Exchange cipher IV and creates stream wrapper
pub fn proxy_handshake(remote_stream: TcpStream,
svr_cfg: Rc<ServerConfig>)
-> BoxIoFuture<(DecryptedHalfFut, EncryptedHalfFut)> {
let fut = futures::lazy(|| Ok(remote_stream.split())).and_then(move |(r, w)| {
svr_cfg: Arc<ServerConfig>)
-> impl Future<Item = (DecryptedHalfFut, EncryptedHalfFut), Error = io::Error> + Send {
futures::lazy(|| Ok(remote_stream.split())).and_then(move |(r, w)| {
let timeout = svr_cfg.timeout().clone();
let svr_cfg_cloned = svr_cfg.clone();
@@ -301,21 +296,19 @@ pub fn proxy_handshake(remote_stream: TcpStream,
}
};
Context::with(|ctx| {
try_timeout(write_all(w, prev_buf), timeout, ctx.handle()).and_then(
move |(w, prev_buf)| match svr_cfg.method().category() {
CipherCategory::Stream => {
let local_iv = prev_buf;
Ok(From::from(StreamEncryptedWriter::new(w, svr_cfg.method(), svr_cfg.key(), &local_iv)))
}
CipherCategory::Aead => {
let local_salt = prev_buf;
let wtr = AeadEncryptedWriter::new(w, svr_cfg.method(), svr_cfg.key(), &local_salt[..]);
Ok(From::from(wtr))
}
},
)
})
try_timeout(write_all(w, prev_buf), timeout).and_then(
move |(w, prev_buf)| match svr_cfg.method().category() {
CipherCategory::Stream => {
let local_iv = prev_buf;
Ok(From::from(StreamEncryptedWriter::new(w, svr_cfg.method(), svr_cfg.key(), &local_iv)))
}
CipherCategory::Aead => {
let local_salt = prev_buf;
let wtr = AeadEncryptedWriter::new(w, svr_cfg.method(), svr_cfg.key(), &local_salt[..]);
Ok(From::from(wtr))
}
},
)
};
let dec = {
@@ -329,37 +322,32 @@ pub fn proxy_handshake(remote_stream: TcpStream,
CipherCategory::Aead => method.salt_size(),
};
Context::with(|ctx| {
try_timeout(read_exact(r, vec![0u8; prev_len]), timeout, ctx.handle()).and_then(
move |(r, remote_iv)| match svr_cfg.method().category() {
CipherCategory::Stream => {
trace!("Got initialize vector {:?}", ByteStr::new(&remote_iv));
let decrypt_stream =
StreamDecryptedReader::new(r, svr_cfg.method(), svr_cfg.key(), &remote_iv);
Ok(From::from(decrypt_stream))
}
CipherCategory::Aead => {
trace!("Got salt {:?}", ByteStr::new(&remote_iv));
let dr = AeadDecryptedReader::new(r, svr_cfg.method(), svr_cfg.key(), &remote_iv);
Ok(From::from(dr))
}
},
)
try_timeout(read_exact(r, vec![0u8; prev_len]), timeout).and_then(move |(r, remote_iv)| {
match svr_cfg.method().category() {
CipherCategory::Stream => {
trace!("Got initialize vector {:?}", ByteStr::new(&remote_iv));
let decrypt_stream = StreamDecryptedReader::new(r, svr_cfg.method(), svr_cfg.key(), &remote_iv);
Ok(From::from(decrypt_stream))
}
CipherCategory::Aead => {
trace!("Got salt {:?}", ByteStr::new(&remote_iv));
let dr = AeadDecryptedReader::new(r, svr_cfg.method(), svr_cfg.key(), &remote_iv);
Ok(From::from(dr))
}
}
})
};
Ok((boxed_future(dec), boxed_future(enc)))
});
boxed_future(fut)
})
}
/// Establish tunnel between server and client
pub fn tunnel<CF, CFI, SF, SFI>(addr: Address, c2s: CF, s2c: SF) -> BoxIoFuture<()>
where CF: Future<Item = CFI, Error = io::Error> + 'static,
SF: Future<Item = SFI, Error = io::Error> + 'static
pub fn tunnel<CF, CFI, SF, SFI>(addr: Address, c2s: CF, s2c: SF) -> impl Future<Item = (), Error = io::Error> + Send
where CF: Future<Item = CFI, Error = io::Error> + Send + 'static,
SF: Future<Item = SFI, Error = io::Error> + Send + 'static
{
let addr = Rc::new(addr);
let addr = Arc::new(addr);
let cloned_addr = addr.clone();
let c2s = c2s.then(move |res| {
@@ -390,23 +378,19 @@ pub fn tunnel<CF, CFI, SF, SFI>(addr: Address, c2s: CF, s2c: SF) -> BoxIoFuture<
}
});
let fut = c2s.select(s2c).and_then(move |(dir, _)| {
match dir {
TunnelDirection::Server2Client => {
trace!("Relay {} client <- server is closed, abort connection",
addr)
}
TunnelDirection::Client2Server => {
trace!("Relay {} server -> client is closed, abort connection",
addr)
}
}
c2s.select(s2c).and_then(move |(dir, _)| {
match dir {
TunnelDirection::Server2Client => {
trace!("Relay {} client <- server is closed, abort connection", addr)
}
TunnelDirection::Client2Server => {
trace!("Relay {} server -> client is closed, abort connection", addr)
}
}
Ok(())
})
.map_err(|(err, _)| err);
boxed_future(fut)
Ok(())
})
.map_err(|(err, _)| err)
}
/// Read until EOF, and ignore
@@ -422,8 +406,7 @@ impl<R: Read> Future for IgnoreUntilEnd<R> {
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match *self {
IgnoreUntilEnd::Empty => panic!("poll IgnoreUntilEnd after it is finished"),
IgnoreUntilEnd::Pending { ref mut r,
ref mut amt, } => {
IgnoreUntilEnd::Pending { ref mut r, ref mut amt } => {
let mut buf = [0u8; 4096];
loop {
let n = try_nb!(r.read(&mut buf));
@@ -448,26 +431,24 @@ pub fn ignore_until_end<R: Read>(r: R) -> IgnoreUntilEnd<R> {
IgnoreUntilEnd::Pending { r: r, amt: 0 }
}
fn try_timeout<T, F>(fut: F, dur: Option<Duration>, handle: &Handle) -> BoxIoFuture<T>
where F: Future<Item = T, Error = io::Error> + 'static,
fn try_timeout<T, F>(fut: F, dur: Option<Duration>) -> impl Future<Item = T, Error = io::Error> + Send
where F: Future<Item = T, Error = io::Error> + Send + 'static,
T: 'static
{
match dur {
Some(dur) => io_timeout(fut, dur, handle),
Some(dur) => boxed_future(io_timeout(fut, dur)),
None => boxed_future(fut),
}
}
fn io_timeout<T, F>(fut: F, dur: Duration, handle: &Handle) -> BoxIoFuture<T>
where F: Future<Item = T, Error = io::Error> + 'static,
fn io_timeout<T, F>(fut: F, dur: Duration) -> impl Future<Item = T, Error = io::Error> + Send
where F: Future<Item = T, Error = io::Error> + Send + 'static,
T: 'static
{
let fut = fut.select(Timeout::new(dur, handle)
.unwrap() // It must be succeeded!
.and_then(|_| Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out"))))
.then(|res| match res {
Ok((t, _)) => Ok(t),
Err((err, _)) => Err(err),
});
boxed_future(fut)
use tokio::prelude::*;
fut.deadline(Instant::now() + dur).map_err(|err| match err.into_inner() {
Some(e) => e,
None => io::Error::new(io::ErrorKind::TimedOut, "connection timed out"),
})
}
+96 -108
View File
@@ -1,64 +1,58 @@
//! Relay for TCP server that running on the server side
use std::io::{self, ErrorKind};
use std::rc::Rc;
use std::sync::Arc;
use std::time::Duration;
use config::ServerConfig;
use config::{Config, ServerConfig};
use relay::{boxed_future, BoxIoFuture};
use relay::Context;
use relay::boxed_future;
use relay::dns_resolver::resolve;
use relay::socks5::Address;
use relay::tcprelay::crypto_io::{DecryptedRead, EncryptedWrite};
use futures::{self, Future};
use futures::stream::Stream;
use futures::{self, Future};
use tokio_core::net::{TcpListener, TcpStream};
use tokio_io::AsyncRead;
use tokio;
use tokio::net::{TcpListener, TcpStream};
use tokio_io::io::{ReadHalf, WriteHalf};
use tokio_io::{AsyncRead, IoFuture};
use super::{proxy_handshake, try_timeout, tunnel, DecryptedHalf, EncryptedHalfFut, TcpStreamConnect};
/// Context for doing handshake with client
pub struct TcpRelayClientHandshake {
s: TcpStream,
svr_cfg: Rc<ServerConfig>,
svr_cfg: Arc<ServerConfig>,
config: Arc<Config>,
}
impl TcpRelayClientHandshake {
/// Doing handshake with client
pub fn handshake(self) -> BoxIoFuture<TcpRelayClientPending> {
let TcpRelayClientHandshake { s, svr_cfg } = self;
pub fn handshake(self) -> impl Future<Item = TcpRelayClientPending, Error = io::Error> {
let TcpRelayClientHandshake { s, svr_cfg, config } = self;
let fut = futures::lazy(move || s.peer_addr().map(|p| (s, p))).and_then(|(s, peer_addr)| {
futures::lazy(move || s.peer_addr().map(|p| (s, p))).and_then(|(s, peer_addr)| {
debug!("Handshaking with peer {}", peer_addr);
let timeout = *svr_cfg.timeout();
proxy_handshake(s, svr_cfg).and_then(move |(r_fut, w_fut)| {
r_fut
.and_then(move |r| {
let fut = Address::read_from(r).map_err(move |_| {
io::Error::new(
ErrorKind::Other,
format!(
"failed to decode Address, may be wrong method or key, peer: {}",
peer_addr
),
)
});
Context::with(|ctx| try_timeout(fut, timeout, ctx.handle()))
})
.map(move |(r, addr)| TcpRelayClientPending {
r: r,
addr: addr,
w: w_fut,
timeout: timeout,
})
r_fut.and_then(move |r| {
let fut = Address::read_from(r).map_err(move |_| {
io::Error::new(ErrorKind::Other,
format!("failed to decode Address, may be wrong method or key, peer: {}",
peer_addr))
});
try_timeout(fut, timeout)
})
.map(move |(r, addr)| TcpRelayClientPending { r: r,
addr: addr,
w: w_fut,
timeout: timeout,
config: config, })
})
});
boxed_future(fut)
})
}
}
@@ -68,53 +62,53 @@ pub struct TcpRelayClientPending {
addr: Address,
w: EncryptedHalfFut,
timeout: Option<Duration>,
config: Arc<Config>,
}
impl TcpRelayClientPending {
/// Connect to the remote server
#[inline]
fn connect_remote(addr: Address, timeout: Option<Duration>) -> BoxIoFuture<TcpStream> {
fn connect_remote(config: Arc<Config>,
addr: Address,
timeout: Option<Duration>)
-> impl Future<Item = TcpStream, Error = io::Error> {
debug!("Connecting to remote {}", addr);
match addr {
Address::SocketAddress(saddr) => Context::with(move |ctx| {
if ctx.forbidden_ip().contains(&saddr.ip()) {
let err = io::Error::new(
ErrorKind::Other,
format!("{} is forbidden, failed to connect {}", saddr.ip(), saddr),
);
Address::SocketAddress(saddr) => {
if config.forbidden_ip.contains(&saddr.ip()) {
let err = io::Error::new(ErrorKind::Other,
format!("{} is forbidden, failed to connect {}", saddr.ip(), saddr));
return boxed_future(futures::done(Err(err)));
}
let conn = TcpStream::connect(&saddr, ctx.handle());
try_timeout(conn, timeout, ctx.handle())
}),
let conn = TcpStream::connect(&saddr);
let fut = try_timeout(conn, timeout);
boxed_future(fut)
}
Address::DomainNameAddress(dname, port) => {
let fut = Context::with(move |ctx| {
let handle = ctx.handle().clone();
try_timeout(resolve(dname.as_str(), port, true), timeout, &handle).and_then(move |addrs| {
let conn = TcpStreamConnect::new(addrs.into_iter(), &handle);
try_timeout(conn, timeout, &handle)
let fut = {
try_timeout(resolve(config, dname.as_str(), port, true), timeout).and_then(move |addrs| {
let conn = TcpStreamConnect::new(addrs.into_iter());
try_timeout(conn, timeout)
})
});
};
boxed_future(fut)
}
}
}
/// Connect to the remote server
pub fn connect(self) -> BoxIoFuture<TcpRelayClientConnected> {
pub fn connect(self) -> impl Future<Item = TcpRelayClientConnected, Error = io::Error> + Send {
let addr = self.addr.clone();
let client_pair = (self.r, self.w);
let timeout = self.timeout;
let fut = TcpRelayClientPending::connect_remote(self.addr, self.timeout);
let fut = fut.map(move |stream| TcpRelayClientConnected {
server: stream.split(),
client: client_pair,
addr: addr,
timeout: timeout,
});
Box::new(fut)
TcpRelayClientPending::connect_remote(self.config, self.addr, self.timeout).map(move |stream| {
TcpRelayClientConnected { server: stream.split(),
client: client_pair,
addr: addr,
timeout: timeout, }
})
}
}
@@ -129,74 +123,68 @@ pub struct TcpRelayClientConnected {
impl TcpRelayClientConnected {
/// Establish tunnel
#[inline]
pub fn tunnel(self) -> BoxIoFuture<()> {
pub fn tunnel(self) -> impl Future<Item = (), Error = io::Error> + Send {
let (svr_r, svr_w) = self.server;
let (r, w_fut) = self.client;
let timeout = self.timeout;
tunnel(
self.addr,
r.copy_timeout_opt(svr_w, self.timeout),
w_fut.and_then(move |w| w.copy_timeout_opt(svr_r, timeout)),
)
tunnel(self.addr,
r.copy_timeout_opt(svr_w, self.timeout),
w_fut.and_then(move |w| w.copy_timeout_opt(svr_r, timeout)))
}
}
/// Runs the server
pub fn run() -> Box<Future<Item = (), Error = io::Error>> {
let mut fut: Option<Box<Future<Item = (), Error = io::Error>>> = None;
pub fn run(config: Arc<Config>) -> impl Future<Item = (), Error = io::Error> + Send {
let mut fut: Option<IoFuture<()>> = None;
Context::with(|ctx| {
let config = ctx.config();
for svr_cfg in &config.server {
let listener = {
let addr = svr_cfg.addr();
let addr = addr.listen_addr();
for svr_cfg in &config.server {
let listener = {
let addr = svr_cfg.addr();
let addr = addr.listen_addr();
let listener = TcpListener::bind(&addr).unwrap_or_else(|err| panic!("Failed to listen, {}", err));
let listener =
TcpListener::bind(&addr, ctx.handle()).unwrap_or_else(|err| panic!("Failed to listen, {}", err));
info!("ShadowSocks TCP Listening on {}", addr);
listener
};
info!("ShadowSocks TCP Listening on {}", addr);
listener
};
let svr_cfg = Arc::new(svr_cfg.clone());
let config = config.clone();
let listening = listener.incoming()
.for_each(move |socket| {
let server_cfg = svr_cfg.clone();
let svr_cfg = Rc::new(svr_cfg.clone());
let listening = listener
.incoming()
.for_each(move |(socket, addr)| {
let server_cfg = svr_cfg.clone();
let addr = socket.peer_addr()?;
trace!("Got connection, addr: {}", addr);
trace!("Picked proxy server: {:?}", server_cfg);
trace!("Got connection, addr: {}", addr);
trace!("Picked proxy server: {:?}", server_cfg);
let client = TcpRelayClientHandshake {
s: socket,
svr_cfg: server_cfg,
};
let client = TcpRelayClientHandshake { s: socket,
svr_cfg: server_cfg,
config: config.clone(), };
let fut = client
.handshake()
.and_then(|c| c.connect())
.and_then(|c| c.tunnel())
.map_err(move |err| {
error!("Failed to handle client ({}): {}", addr, err);
});
let fut = client.handshake()
.and_then(|c| c.connect())
.and_then(|c| c.tunnel())
.map_err(move |err| {
error!("Failed to handle client ({}): {}",
addr, err);
});
Context::with(|ctx| ctx.handle().spawn(fut));
Ok(())
})
.map_err(|err| {
error!("Server run failed: {}", err);
err
});
tokio::spawn(fut);
Ok(())
})
.map_err(|err| {
error!("Server run failed: {}", err);
err
});
fut = Some(match fut.take() {
Some(fut) => Box::new(fut.join(listening).map(|_| ())) as Box<Future<Item = (), Error = io::Error>>,
None => Box::new(listening) as Box<Future<Item = (), Error = io::Error>>,
})
}
fut = Some(match fut.take() {
Some(fut) => Box::new(fut.join(listening).map(|_| ())) as IoFuture<()>,
None => Box::new(listening) as IoFuture<()>,
})
}
fut.expect("Must have at least one server")
})
fut.expect("Must have at least one server")
}
+51 -69
View File
@@ -2,20 +2,22 @@
use std::io;
use std::net::SocketAddr;
use std::rc::Rc;
use std::sync::Arc;
use futures::{self, Future};
use futures::stream::Stream;
use futures::{self, Future};
use tokio_core::net::{TcpListener, TcpStream};
use tokio_io::AsyncRead;
use tokio_io::io::{ReadHalf, WriteHalf};
// use tokio_core::net::{TcpListener, TcpStream};
use tokio_io::io::flush;
use tokio_io::io::{ReadHalf, WriteHalf};
use tokio_io::AsyncRead;
use config::ServerConfig;
use tokio;
use tokio::net::{TcpListener, TcpStream};
use relay::{boxed_future, BoxIoFuture};
use relay::Context;
use config::{Config, ServerConfig};
use relay::boxed_future;
use relay::loadbalancing::server::LoadBalancer;
use relay::loadbalancing::server::RoundRobin;
use relay::socks5::{self, Address, HandshakeRequest, HandshakeResponse};
@@ -27,18 +29,19 @@ use super::{ignore_until_end, try_timeout, tunnel};
#[derive(Debug, Clone)]
struct UdpConfig {
enable_udp: bool,
client_addr: Rc<SocketAddr>,
client_addr: SocketAddr,
}
fn handle_socks5_connect((r, w): (ReadHalf<TcpStream>, WriteHalf<TcpStream>),
fn handle_socks5_connect(config: Arc<Config>,
(r, w): (ReadHalf<TcpStream>, WriteHalf<TcpStream>),
client_addr: SocketAddr,
addr: Address,
svr_cfg: Rc<ServerConfig>)
-> BoxIoFuture<()> {
svr_cfg: Arc<ServerConfig>)
-> impl Future<Item = (), Error = io::Error> + Send {
let cloned_addr = addr.clone();
let cloned_svr_cfg = svr_cfg.clone();
let timeout = *svr_cfg.timeout();
let fut = super::connect_proxy_server(svr_cfg)
super::connect_proxy_server(config.clone(), svr_cfg)
.then(move |res| {
match res {
Ok(svr_s) => {
@@ -48,13 +51,9 @@ fn handle_socks5_connect((r, w): (ReadHalf<TcpStream>, WriteHalf<TcpStream>),
let header = TcpResponseHeader::new(socks5::Reply::Succeeded, Address::SocketAddress(client_addr));
trace!("Send header: {:?}", header);
let fut = Context::with(|ctx| {
let handle = ctx.handle();
let fut = try_timeout(header.write_to(w), timeout, &handle);
try_timeout(fut.and_then(flush), timeout, &handle)
});
boxed_future(fut.map(move |w| (svr_s, w)))
boxed_future(
try_timeout(try_timeout(header.write_to(w), timeout).and_then(flush), timeout)
.map(move |w| (svr_s, w)))
}
Err(err) => {
use std::io::ErrorKind;
@@ -71,13 +70,7 @@ fn handle_socks5_connect((r, w): (ReadHalf<TcpStream>, WriteHalf<TcpStream>),
let header = TcpResponseHeader::new(reply, Address::SocketAddress(client_addr));
trace!("Send header: {:?}", header);
let fut = Context::with(|ctx| {
let handle = ctx.handle();
let fut = try_timeout(header.write_to(w), timeout, &handle);
try_timeout(fut.and_then(flush), timeout, &handle)
});
boxed_future(fut.and_then(|_| Err(err)))
boxed_future(try_timeout(try_timeout(header.write_to(w), timeout).and_then(flush), timeout).and_then(|_| Err(err)))
}
}
})
@@ -91,12 +84,14 @@ fn handle_socks5_connect((r, w): (ReadHalf<TcpStream>, WriteHalf<TcpStream>),
tunnel(cloned_addr, whalf, rhalf)
})
});
Box::new(fut)
})
}
fn handle_socks5_client(s: TcpStream, conf: Rc<ServerConfig>, udp_conf: UdpConfig) -> io::Result<()> {
fn handle_socks5_client(config: Arc<Config>,
s: TcpStream,
conf: Arc<ServerConfig>,
udp_conf: UdpConfig)
-> io::Result<()> {
let client_addr = s.peer_addr()?;
let cloned_client_addr = client_addr;
let fut = futures::lazy(|| Ok(s.split()))
@@ -144,7 +139,8 @@ fn handle_socks5_client(s: TcpStream, conf: Rc<ServerConfig>, udp_conf: UdpConfi
match header.command {
socks5::Command::TcpConnect => {
debug!("CONNECT {}", addr);
handle_socks5_connect((r, w), cloned_client_addr, addr, conf)
let fut = handle_socks5_connect(config, (r, w), cloned_client_addr, addr, conf);
boxed_future(fut)
}
socks5::Command::TcpBind => {
warn!("BIND is not supported");
@@ -156,7 +152,7 @@ fn handle_socks5_client(s: TcpStream, conf: Rc<ServerConfig>, udp_conf: UdpConfi
socks5::Command::UdpAssociate => {
if udp_conf.enable_udp {
debug!("UDP ASSOCIATE {}", addr);
let fut = TcpResponseHeader::new(socks5::Reply::Succeeded, From::from(*udp_conf.client_addr))
let fut = TcpResponseHeader::new(socks5::Reply::Succeeded, From::from(udp_conf.client_addr))
.write_to(w)
.and_then(flush)
.and_then(|_| {
@@ -176,51 +172,37 @@ fn handle_socks5_client(s: TcpStream, conf: Rc<ServerConfig>, udp_conf: UdpConfi
}
});
// Runs in Tokio
Context::with(|ctx| {
let handle = &ctx.handle;
handle.spawn(fut.then(|res| match res {
Ok(..) => Ok(()),
Err(err) => {
if err.kind() != io::ErrorKind::BrokenPipe {
error!("Failed to handle client: {}", err);
}
Err(())
}
}));
});
tokio::spawn(fut.then(|res| match res {
Ok(..) => Ok(()),
Err(err) => {
if err.kind() != io::ErrorKind::BrokenPipe {
error!("Failed to handle client: {}", err);
}
Err(())
}
}));
Ok(())
}
/// Starts a TCP local server with Socks5 proxy protocol
pub fn run() -> Box<Future<Item = (), Error = io::Error>> {
let (listener, local_addr) = Context::with(|ctx| {
let config = &ctx.config;
let handle = &ctx.handle;
pub fn run(config: Arc<Config>) -> impl Future<Item = (), Error = io::Error> + Send {
let local_addr = *config.local.as_ref().expect("Missing local config");
let local_addr = config.local.as_ref().unwrap();
let listener = TcpListener::bind(&local_addr).unwrap_or_else(|err| panic!("Failed to listen, {}", err));
let l = TcpListener::bind(&local_addr, &handle)
.unwrap_or_else(|err| panic!("Failed to listen, {}", err));
info!("ShadowSocks TCP Listening on {}", local_addr);
info!("ShadowSocks TCP Listening on {}", local_addr);
(l, *local_addr)
});
let udp_conf = UdpConfig { enable_udp: config.enable_udp,
client_addr: local_addr, };
let udp_conf = UdpConfig { enable_udp: Context::with(|ctx| ctx.config.enable_udp),
client_addr: Rc::new(local_addr), };
let mut servers = RoundRobin::new(&*config);
listener.incoming().for_each(move |socket| {
let server_cfg = servers.pick_server();
let mut servers = Context::with(|ctx| RoundRobin::new(ctx.config()));
let listening = listener.incoming().for_each(move |(socket, addr)| {
let server_cfg = servers.pick_server();
trace!("Got connection, addr: {}", addr);
trace!("Picked proxy server: {:?}", server_cfg);
handle_socks5_client(socket, server_cfg, udp_conf.clone())
});
trace!("Got connection, addr: {}", socket.peer_addr()?);
trace!("Picked proxy server: {:?}", server_cfg);
Box::new(listening.map_err(|err| {
error!("Socks5 server run failed: {}", err);
err
}))
handle_socks5_client(config.clone(), socket, server_cfg, udp_conf.clone())
})
}
+1 -1
View File
@@ -7,8 +7,8 @@ use bytes::{BufMut, BytesMut};
use crypto::{new_stream, CipherType, CryptoMode, StreamCipher, StreamCipherVariant};
use tokio_io::{AsyncRead, AsyncWrite};
use super::{DecryptedRead, EncryptedWrite};
use super::BUFFER_SIZE;
use super::{DecryptedRead, EncryptedWrite};
const DUMMY_BUFFER: [u8; BUFFER_SIZE] = [0u8; BUFFER_SIZE];
+7 -9
View File
@@ -1,16 +1,14 @@
//! Utility functions
use std::io;
use std::time::Duration;
use std::time::{Duration, Instant};
use tokio_core::reactor::Timeout;
use tokio_io::{AsyncRead, AsyncWrite};
use tokio::timer::Delay;
use tokio_io::io::{copy, Copy};
use tokio_io::{AsyncRead, AsyncWrite};
use futures::{Async, Future, Poll};
use relay::Context;
use super::BUFFER_SIZE;
/// Copies all data from `r` to `w`, abort if timeout reaches
@@ -30,7 +28,7 @@ pub struct CopyTimeout<R, W>
w: Option<W>,
timeout: Duration,
amt: u64,
timer: Option<Timeout>,
timer: Option<Delay>,
buf: [u8; BUFFER_SIZE],
pos: usize,
cap: usize,
@@ -56,7 +54,7 @@ impl<R, W> CopyTimeout<R, W>
None => Ok(()),
Some(t) => {
match t.poll() {
Err(err) => Err(err),
Err(err) => panic!("Failed to poll on timer, err: {}", err),
Ok(Async::Ready(..)) => Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")),
Ok(Async::NotReady) => Ok(()),
}
@@ -79,7 +77,7 @@ impl<R, W> CopyTimeout<R, W>
Ok(n) => Ok(n),
Err(e) => {
if e.kind() == io::ErrorKind::WouldBlock {
self.timer = Context::with(|ctx| Some(Timeout::new(self.timeout, ctx.handle()).unwrap()));
self.timer = Some(Delay::new(Instant::now() + self.timeout));
}
Err(e)
}
@@ -97,7 +95,7 @@ impl<R, W> CopyTimeout<R, W>
Ok(n) => Ok(n),
Err(e) => {
if e.kind() == io::ErrorKind::WouldBlock {
self.timer = Context::with(|ctx| Some(Timeout::new(self.timeout, ctx.handle()).unwrap()));
self.timer = Some(Delay::new(Instant::now() + self.timeout));
}
Err(e)
}
+1 -1
View File
@@ -22,8 +22,8 @@
use std::io;
use crypto::{self, CipherCategory, CipherType, CryptoMode};
use crypto::StreamCipher;
use crypto::{self, CipherCategory, CipherType, CryptoMode};
/// Encrypt payload into ShadowSocks UDP encrypted packet
pub fn encrypt_payload(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Vec<u8>> {
+295
View File
@@ -0,0 +1,295 @@
//! UDP DNS relay
use std::fmt;
use std::io::{self, Cursor};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::{Arc, Mutex};
use std::time::Instant;
use dns_parser::{Packet, RRData};
use futures::future::join_all;
use futures::{self, Future, Stream};
use lru_cache::LruCache;
use tokio;
use tokio::net::UdpSocket;
use tokio_io::IoFuture;
use super::crypto_io::{decrypt_payload, encrypt_payload};
use super::{PacketStream, SendDgramRc, SharedUdpSocket};
use config::{Config, ServerAddr, ServerConfig};
use relay::boxed_future;
use relay::dns_resolver::resolve_by_google;
use relay::socks5::Address;
struct PrettyRRData<'a> {
data: &'a RRData<'a>,
}
impl<'a> PrettyRRData<'a> {
pub fn new(d: &'a RRData<'a>) -> PrettyRRData<'a> {
PrettyRRData { data: d }
}
}
impl<'a> fmt::Display for PrettyRRData<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.data {
RRData::CNAME(ref c) => write!(f, "CNAME({})", c.to_string()),
RRData::NS(ref c) => write!(f, "NS({})", c.to_string()),
RRData::A(ref i) => write!(f, "A({})", i),
RRData::AAAA(ref i) => write!(f, "AAAA({})", i),
RRData::SRV { priority, weight, port, target }
=> write!(f,
"SRV({{priority: {}, weight: {}, port: {}, target: {}}})",
priority,
weight,
port,
target.to_string()),
RRData::SOA(ref s)
=> write!(f,
"SOA({{primary_ns: {}, mailbox: {}, serial: {}, refresh: {}, retry: {}, expire: {}, minimum_ttl: {}}})",
s.primary_ns.to_string(),
s.mailbox.to_string(),
s.serial,
s.refresh,
s.retry,
s.expire,
s.minimum_ttl),
RRData::PTR(ref p) => write!(f, "PTR({})", p.to_string()),
RRData::MX { preference, exchange }
=> write!(f, "MX({{preference: {}, exchange: {}}})", preference, exchange.to_string()),
RRData::TXT(t) => write!(f, "TXT({})", t),
RRData::Unknown(u) => write!(f, "Unknown({:?})", u),
}
}
}
struct PrettyPacket<'a> {
pkt: &'a Packet<'a>,
}
impl<'a> PrettyPacket<'a> {
pub fn new(pkt: &'a Packet<'a>) -> PrettyPacket<'a> {
PrettyPacket { pkt: pkt }
}
}
impl<'a> fmt::Display for PrettyPacket<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,
"Packet (id={} opcode={:?})",
self.pkt.header.id, self.pkt.header.opcode)?;
if !self.pkt.questions.is_empty() {
write!(f, " QUESTION[")?;
for q in &self.pkt.questions {
write!(f, "{}, ", q.qname.to_string())?;
}
write!(f, "]")?;
}
if !self.pkt.answers.is_empty() {
write!(f, " ANSWER[")?;
for a in &self.pkt.answers {
write!(f, "{}:{}, ", a.name.to_string(), PrettyRRData::new(&a.data))?;
}
write!(f, "]")?;
}
if !self.pkt.nameservers.is_empty() {
write!(f, " NAMESERVERS[")?;
for n in &self.pkt.nameservers {
write!(f, "{}:{}, ", n.name.to_string(), PrettyRRData::new(&n.data))?;
}
write!(f, "]")?;
}
if !self.pkt.additional.is_empty() {
write!(f, " ADDITIONAL[")?;
for n in &self.pkt.additional {
write!(f, "{}:{}, ", n.name.to_string(), PrettyRRData::new(&n.data))?;
}
write!(f, "]")?;
}
if let Some(ref opt) = self.pkt.opt {
write!(f, " OPT[{:?}]", opt)?;
}
Ok(())
}
}
/// Starts a UDP DNS server
pub fn run(config: Arc<Config>) -> IoFuture<()> {
let local_addr = *config.local.as_ref().unwrap();
let fut = futures::lazy(move || {
info!("ShadowSocks UDP DNS Listening on {}", local_addr);
UdpSocket::bind(&local_addr)
}).and_then(move |l| listen(config, l));
boxed_future(fut)
}
fn listen(config: Arc<Config>, l: UdpSocket) -> impl Future<Item = (), Error = io::Error> + Send {
assert!(!config.server.is_empty());
let mut svr_fut = Vec::with_capacity(config.server.len());
for svr in &config.server {
match *svr.addr() {
ServerAddr::SocketAddr(ref addr) => {
svr_fut.push(boxed_future(futures::finished::<_, io::Error>(vec![*addr])));
}
ServerAddr::DomainName(ref dom, ref port) => {
svr_fut.push(boxed_future(resolve_by_google(config.clone(), &*dom, *port, false)));
}
}
}
let cloned_config = config.clone();
join_all(svr_fut).and_then(move |svr_addrs| {
let mut u = Vec::with_capacity(svr_addrs.len());
for (idx, svr_addr) in svr_addrs.into_iter().enumerate() {
let local_addr = SocketAddr::new(IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)), 0);
let s = UdpSocket::bind(&local_addr)?;
let svr_cfg = Arc::new(cloned_config.server[idx].clone());
u.push((Arc::new(Mutex::new(s)), svr_cfg, svr_addr[0]));
}
Ok(u)
})
.and_then(move |vec_servers| {
let mut f = Vec::with_capacity(1 + vec_servers.len());
let l = Arc::new(Mutex::new(l));
f.push(boxed_future(handle_l2r(config, l.clone(), vec_servers.clone())));
for (svr, cfg, _) in vec_servers {
f.push(boxed_future(handle_r2l(l.clone(), svr, cfg)))
}
join_all(f)
})
.map(|_| ())
}
lazy_static! {
static ref GLOBAL_QUERY_ADDR: Mutex<LruCache<u16, (SocketAddr, Instant)>> = Mutex::new(LruCache::new(1024));
}
fn handle_l2r(config: Arc<Config>,
l: SharedUdpSocket,
server: Vec<(SharedUdpSocket, Arc<ServerConfig>, SocketAddr)>)
-> impl Future<Item = (), Error = io::Error> + Send {
assert!(!server.is_empty());
let mut server_idx: usize = 0;
let server = Arc::new(server);
PacketStream::new(l).for_each(move |(payload, src)| {
let server = server.clone();
let config = config.clone();
let pkt_fut = futures::lazy(move || {
let pkt = Packet::parse(&payload[..]).map_err(|err| {
error!("Failed to parse DNS payload, err: {}", err);
io::Error::new(io::ErrorKind::Other,
"parse DNS packet failed")
})?;
let (ref socket, ref svr_cfg, svr_addr) = server[server_idx % server.len()];
let (ni, _) = server_idx.overflowing_add(1);
server_idx = ni;
debug!("DNS {} -> {} {}", src, svr_cfg.addr(), PrettyPacket::new(&pkt));
trace!("DETAIL {} -> {} {:?}", src, svr_cfg.addr(), pkt);
let mut buf = Vec::new();
Address::SocketAddress(config.dns).write_to_buf(&mut buf);
buf.extend_from_slice(&payload);
let socket = socket.clone();
let id = pkt.header.id;
encrypt_payload(svr_cfg.method(), svr_cfg.key(), &buf).map(move |send_payload| {
(socket,
svr_addr,
send_payload,
id)
})
}).and_then(move |(socket, svr_addr, send_payload, id)| {
SendDgramRc::new(socket, send_payload, svr_addr).map(move |_| {
GLOBAL_QUERY_ADDR.lock().unwrap().insert(id, (src, Instant::now()));
})
});
tokio::spawn(pkt_fut.then(|res| {
match res {
Ok(..) => Ok(()),
Err(err) => {
error!("Failed to handle local -> remote packet, err: {}", err);
Err(())
}
}
}));
Ok(())
})
}
fn handle_r2l(l: SharedUdpSocket,
r: SharedUdpSocket,
svr_cfg: Arc<ServerConfig>)
-> impl Future<Item = (), Error = io::Error> + Send {
PacketStream::new(r).for_each(move |(payload, src)| {
let l = l.clone();
let svr_cfg = svr_cfg.clone();
let pkt_fut = futures::lazy(move || {
decrypt_payload(svr_cfg.method(), svr_cfg.key(), &payload)
}).and_then(move |payload| {
Address::read_from(Cursor::new(payload))
.map_err(From::from)
}).and_then(move |(cur, ..)| {
let pos = cur.position() as usize;
let payload = cur.into_inner();
let pkt = Packet::parse(&payload[pos..]).map_err(|err| {
error!("Failed to parse DNS payload, err: {}", err);
io::Error::new(io::ErrorKind::Other, "parse DNS packet failed")
})?;
let payload = payload[pos..].to_vec();
match GLOBAL_QUERY_ADDR.lock().unwrap().remove(&pkt.header.id) {
Some((cli_addr, start_time)) => {
debug!("DNS {} <- {} {} elapsed: {:?}", cli_addr, src, PrettyPacket::new(&pkt), Instant::now() - start_time);
trace!("DETAIL {} <- {} {:?}", cli_addr, src, pkt);
Ok(Some((cli_addr, payload)))
}
None => {
error!("DNS received packet id={} opcode={:?} but found no local endpoint",
pkt.header.id, pkt.header.opcode);
Ok(None)
}
}
}).and_then(move |opt| {
match opt {
Some((cli_addr, payload)) => {
let f = SendDgramRc::new(l, payload, cli_addr)
.map(|_| ());
boxed_future(f)
}
None => boxed_future(futures::finished(()))
}
});
tokio::spawn(pkt_fut.then(|res| {
match res {
Ok(..) => Ok(()),
Err(err) => {
error!("Failed to handle local <- remote packet, err: {}", err);
Err(())
}
}
}));
Ok(())
})
}
+103 -83
View File
@@ -1,140 +1,160 @@
//! UDP relay local server
use std::io::{self, Cursor, ErrorKind, Read};
use std::net::{IpAddr, Ipv4Addr};
use std::net::SocketAddr;
use std::rc::Rc;
use std::net::{IpAddr, Ipv4Addr};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use futures::{self, Future, Stream};
use tokio_core::net::UdpSocket;
use tokio;
use tokio::net::UdpSocket;
use tokio::util::FutureExt;
use config::{ServerAddr, ServerConfig};
use relay::{boxed_future, BoxIoFuture};
use relay::Context;
use config::{Config, ServerAddr, ServerConfig};
use relay::boxed_future;
use relay::dns_resolver::resolve;
use relay::loadbalancing::server::{LoadBalancer, RoundRobin};
use relay::socks5::{Address, UdpAssociateHeader};
use super::{PacketStream, SendDgramRc};
use super::MAXIMUM_UDP_PAYLOAD_SIZE;
use super::crypto_io::{decrypt_payload, encrypt_payload};
use super::MAXIMUM_UDP_PAYLOAD_SIZE;
use super::{PacketStream, SendDgramRc};
/// Resolves server address to SocketAddr
fn resolve_server_addr(svr_cfg: Rc<ServerConfig>) -> BoxIoFuture<SocketAddr> {
fn resolve_server_addr(config: Arc<Config>,
svr_cfg: Arc<ServerConfig>)
-> impl Future<Item = SocketAddr, Error = io::Error> + Send {
match *svr_cfg.addr() {
// Return directly if it is a SocketAddr
ServerAddr::SocketAddr(ref addr) => boxed_future(futures::finished(*addr)),
// Resolve domain name to SocketAddr
ServerAddr::DomainName(ref dname, port) => {
let fut = resolve(dname, port, false).map(move |vec_ipaddr| {
assert!(!vec_ipaddr.is_empty());
vec_ipaddr[0]
});
let fut = resolve(config, dname, port, false).map(move |vec_ipaddr| {
assert!(!vec_ipaddr.is_empty());
vec_ipaddr[0]
});
boxed_future(fut)
}
}
}
fn listen(l: UdpSocket) -> BoxIoFuture<()> {
let socket = Rc::new(l);
let mut balancer = Context::with(|ctx| RoundRobin::new(ctx.config()));
fn listen(config: Arc<Config>, l: UdpSocket) -> impl Future<Item = (), Error = io::Error> + Send {
let socket = Arc::new(Mutex::new(l));
let mut balancer = RoundRobin::new(&*config);
let fut = PacketStream::new(socket.clone()).for_each(move |(pkt, src)| {
PacketStream::new(socket.clone()).for_each(move |(pkt, src)| {
let svr_cfg = balancer.pick_server();
let svr_cfg_cloned = svr_cfg.clone();
let svr_cfg_cloned_cloned = svr_cfg.clone();
let socket = socket.clone();
let config = config.clone();
let timeout = *svr_cfg.timeout();
let rel = futures::lazy(|| UdpAssociateHeader::read_from(Cursor::new(pkt))).map_err(From::from)
.and_then(
|(cur, header)| if header.frag != 0 {
error!("Received UDP associate with frag != 0, which is not supported by ShadowSocks");
let err = io::Error::new(ErrorKind::Other, "unsupported UDP fragmentation");
Err(err)
} else {
Ok((cur, header.address))
},
)
.and_then(|(mut cur, addr)| {
let svr_cfg = svr_cfg_cloned_cloned;
let mut payload = Vec::new();
cur.read_to_end(&mut payload).unwrap();
resolve_server_addr(svr_cfg).and_then(|remote_addr| {
let local_addr = SocketAddr::new(IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)), 0);
Context::with(|ctx| UdpSocket::bind(&local_addr, ctx.handle()))
.map(|remote_udp| (remote_udp, remote_addr))
let rel = futures::lazy(|| UdpAssociateHeader::read_from(Cursor::new(pkt)))
.map_err(From::from)
.and_then(|(cur, header)| {
if header.frag != 0 {
error!("Received UDP associate with frag != 0, which is not supported by ShadowSocks");
let err = io::Error::new(ErrorKind::Other, "unsupported UDP fragmentation");
Err(err)
} else {
Ok((cur, header.address))
}
})
.map(|(remote_udp, remote_addr)| (remote_udp, remote_addr, payload, addr))
})
.and_then(
move |(remote_udp, remote_addr, payload, addr)| {
.and_then(|(mut cur, addr)| {
let svr_cfg = svr_cfg_cloned_cloned;
let mut payload = Vec::new();
cur.read_to_end(&mut payload).unwrap();
resolve_server_addr(config, svr_cfg)
.and_then(|remote_addr| {
let local_addr = SocketAddr::new(IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)), 0);
UdpSocket::bind(&local_addr)
.map(|remote_udp| (remote_udp, remote_addr))
})
.map(|(remote_udp, remote_addr)| (remote_udp, remote_addr, payload, addr))
})
.and_then(move |(remote_udp, remote_addr, payload, addr)| {
let mut buf = Vec::new();
addr.write_to_buf(&mut buf);
buf.extend_from_slice(&payload);
encrypt_payload(svr_cfg.method(), svr_cfg.key(), &buf)
.map(|payload| (remote_udp, remote_addr, payload, addr))
},
)
.and_then(
move |(remote_udp, remote_addr, payload, addr)| {
info!("UDP ASSOCIATE {} -> {}, payload length {} bytes", src, addr, payload.len());
remote_udp.send_dgram(payload, remote_addr)
})
.and_then(move |(remote_udp, remote_addr, payload, addr)| {
debug!("UDP ASSOCIATE {} -> {}, payload length {} bytes", src, addr, payload.len());
let to = timeout.unwrap_or(Duration::from_secs(5));
let caddr = addr.clone();
remote_udp.send_dgram(payload, &remote_addr)
.deadline(Instant::now() + to)
.map_err(move |err| {
match err.into_inner() {
Some(e) => e,
None => {
error!("Udp associate sending datagram {} -> {} timed out in {:?}", src, caddr, to);
io::Error::new(io::ErrorKind::TimedOut, "udp send timed out")
}
}
})
.map(|(remote_udp, _)| (remote_udp, addr))
},
)
.and_then(
move |(remote_udp, addr)| {
})
.and_then(move |(remote_udp, addr)| {
let buf = vec![0u8; MAXIMUM_UDP_PAYLOAD_SIZE];
let to = timeout.unwrap_or(Duration::from_secs(5));
let caddr = addr.clone();
remote_udp.recv_dgram(buf)
.deadline(Instant::now() + to)
.map_err(move |err| {
match err.into_inner() {
Some(e) => e,
None => {
error!("Udp associate waiting datagram {} <- {} timed out in {:?}", src, caddr, to);
io::Error::new(io::ErrorKind::TimedOut, "udp recv timed out")
}
}
})
.and_then(move |(_remote_udp, buf, n, _from)| {
let svr_cfg = svr_cfg_cloned;
decrypt_payload(svr_cfg.method(), svr_cfg.key(), &buf[..n])
})
.map(|payload| (payload, addr))
},
)
.and_then(move |(payload, addr)| {
Address::read_from(Cursor::new(payload)).map_err(From::from)
.map(|(cur, ..)| (cur, addr))
})
.and_then(move |(mut cur, addr)| {
let payload_len = cur.get_ref().len() - cur.position() as usize;
info!("UDP ASSOCIATE {} <- {}, payload length {} bytes", src, addr, payload_len);
})
.and_then(move |(payload, addr)| {
Address::read_from(Cursor::new(payload))
.map_err(From::from)
.map(|(cur, ..)| (cur, addr))
})
.and_then(move |(mut cur, addr)| {
let payload_len = cur.get_ref().len() - cur.position() as usize;
debug!("UDP ASSOCIATE {} <- {}, payload length {} bytes", src, addr, payload_len);
let mut data = Vec::new();
UdpAssociateHeader::new(0, Address::SocketAddress(src)).write_to_buf(&mut data);
let mut data = Vec::new();
UdpAssociateHeader::new(0, Address::SocketAddress(src)).write_to_buf(&mut data);
cur.read_to_end(&mut data).unwrap();
cur.read_to_end(&mut data).unwrap();
SendDgramRc::new(socket, data, src)
})
.map(|_| ());
SendDgramRc::new(socket, data, src)
})
.map(|_| ());
Context::with(|ctx| {
let handle = ctx.handle();
handle.spawn(rel.map_err(|err| {
error!("Error occurs in UDP relay: {}", err);
}));
});
tokio::spawn(rel.map_err(|err| {
error!("Error occurs in UDP relay: {}", err);
}));
Ok(())
});
boxed_future(fut)
})
}
/// Starts a UDP local server
pub fn run() -> BoxIoFuture<()> {
let fut = futures::lazy(|| {
Context::with(|ctx| {
let local_addr = ctx.config().local.as_ref().unwrap();
info!("ShadowSocks UDP Listening on {}", local_addr);
pub fn run(config: Arc<Config>) -> impl Future<Item = (), Error = io::Error> + Send {
let local_addr = *config.local.as_ref().unwrap();
UdpSocket::bind(local_addr, ctx.handle())
})
}).and_then(|l| listen(l));
futures::lazy(move || {
info!("ShadowSocks UDP Listening on {}", local_addr);
boxed_future(fut)
UdpSocket::bind(&local_addr)
}).and_then(move |l| listen(config, l))
}
+12 -9
View File
@@ -40,12 +40,13 @@
use std::io;
use std::mem;
use std::net::SocketAddr;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use tokio_core::net::UdpSocket;
use tokio::net::UdpSocket;
use futures::{Async, Future, Poll, Stream};
pub mod dns;
pub mod local;
pub mod server;
@@ -58,15 +59,17 @@ mod crypto_io;
/// [here](http://support.microsoft.com/kb/822061/)*
pub const MAXIMUM_UDP_PAYLOAD_SIZE: usize = 65536;
type SharedUdpSocket = Arc<Mutex<UdpSocket>>;
/// UDP `recv_from` stream
pub struct PacketStream {
udp: Rc<UdpSocket>,
udp: SharedUdpSocket,
buf: [u8; MAXIMUM_UDP_PAYLOAD_SIZE],
}
impl PacketStream {
/// Creates a new `PacketStream`
pub fn new(udp: Rc<UdpSocket>) -> PacketStream {
pub fn new(udp: SharedUdpSocket) -> PacketStream {
PacketStream { udp: udp,
buf: [0u8; MAXIMUM_UDP_PAYLOAD_SIZE], }
}
@@ -76,14 +79,14 @@ impl Stream for PacketStream {
type Item = (Vec<u8>, SocketAddr);
type Error = io::Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
let (n, addr) = try_nb!(self.udp.recv_from(&mut self.buf));
let (n, addr) = try_ready!(self.udp.lock().unwrap().poll_recv_from(&mut self.buf));
Ok(Async::Ready(Some((self.buf[..n].to_vec(), addr))))
}
}
enum SendDgramStat<B: AsRef<[u8]>> {
Pending {
udp: Rc<UdpSocket>,
udp: SharedUdpSocket,
buf: B,
addr: SocketAddr,
},
@@ -96,7 +99,7 @@ pub struct SendDgramRc<B: AsRef<[u8]>> {
}
impl<B: AsRef<[u8]>> SendDgramRc<B> {
pub fn new(udp: Rc<UdpSocket>, buf: B, addr: SocketAddr) -> SendDgramRc<B> {
pub fn new(udp: SharedUdpSocket, buf: B, addr: SocketAddr) -> SendDgramRc<B> {
SendDgramRc { stat: SendDgramStat::Pending { udp: udp,
buf: buf,
addr: addr, }, }
@@ -104,13 +107,13 @@ impl<B: AsRef<[u8]>> SendDgramRc<B> {
}
impl<B: AsRef<[u8]>> Future for SendDgramRc<B> {
type Item = (Rc<UdpSocket>, usize, B);
type Item = (SharedUdpSocket, usize, B);
type Error = io::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let n = match self.stat {
SendDgramStat::Pending { ref udp,
ref buf,
ref addr, } => try_nb!(udp.send_to(buf.as_ref(), addr)),
ref addr, } => try_ready!(udp.lock().unwrap().poll_send_to(buf.as_ref(), addr)),
SendDgramStat::Empty => unreachable!(),
};
+79 -59
View File
@@ -2,56 +2,58 @@
use std::io::{self, Cursor, ErrorKind};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use futures::{self, Future, Stream};
use tokio_core::net::UdpSocket;
use tokio;
use tokio::net::UdpSocket;
use tokio::util::FutureExt;
use tokio_io::IoFuture;
use config::ServerConfig;
use relay::{boxed_future, BoxIoFuture};
use relay::Context;
use config::{Config, ServerConfig};
use relay::boxed_future;
use relay::dns_resolver::resolve;
use relay::socks5::Address;
use super::{PacketStream, SendDgramRc};
use super::MAXIMUM_UDP_PAYLOAD_SIZE;
use super::crypto_io::{decrypt_payload, encrypt_payload};
use super::MAXIMUM_UDP_PAYLOAD_SIZE;
use super::{PacketStream, SendDgramRc};
fn resolve_remote_addr(addr: Address) -> BoxIoFuture<SocketAddr> {
fn resolve_remote_addr(config: Arc<Config>, addr: Address) -> impl Future<Item = SocketAddr, Error = io::Error> + Send {
match addr {
Address::SocketAddress(s) => {
Context::with(|ctx| {
if ctx.forbidden_ip().contains(&s.ip()) {
let err = io::Error::new(ErrorKind::Other,
format!("{} is forbidden, failed to connect {}", s.ip(), s));
return boxed_future(futures::done(Err(err)));
}
if config.forbidden_ip.contains(&s.ip()) {
let err = io::Error::new(ErrorKind::Other,
format!("{} is forbidden, failed to connect {}", s.ip(), s));
return boxed_future(futures::done(Err(err)));
}
boxed_future(futures::finished(s))
})
boxed_future(futures::finished(s))
}
Address::DomainNameAddress(dname, port) => {
let fut = resolve(&dname, port, true).map(move |vec_ipaddr| {
assert!(!vec_ipaddr.is_empty());
vec_ipaddr[0]
});
let fut = resolve(config, &dname, port, true).map(move |vec_ipaddr| {
assert!(!vec_ipaddr.is_empty());
vec_ipaddr[0]
});
boxed_future(fut)
}
}
}
fn listen(svr_cfg: Rc<ServerConfig>) -> BoxIoFuture<()> {
fn listen(config: Arc<Config>, svr_cfg: Arc<ServerConfig>) -> impl Future<Item = (), Error = io::Error> + Send {
let listen_addr = *svr_cfg.addr().listen_addr();
info!("ShadowSocks UDP listening on {}", listen_addr);
let fut = futures::lazy(move || Context::with(|ctx| UdpSocket::bind(&listen_addr, ctx.handle())))
.and_then(move |socket| {
let socket = Rc::new(socket);
PacketStream::new(socket.clone()).for_each(move |(pkt, src)| {
let svr_cfg = svr_cfg.clone();
let svr_cfg_cloned = svr_cfg.clone();
let socket = socket.clone();
let rel = futures::lazy(move || decrypt_payload(svr_cfg.method(), svr_cfg.key(), &pkt))
futures::lazy(move || UdpSocket::bind(&listen_addr)).and_then(move |socket| {
let socket = Arc::new(Mutex::new(socket));
PacketStream::new(socket.clone()).for_each(move |(pkt, src)| {
let svr_cfg = svr_cfg.clone();
let svr_cfg_cloned = svr_cfg.clone();
let socket = socket.clone();
let config = config.clone();
let timeout = *svr_cfg.timeout();
let rel = futures::lazy(move || decrypt_payload(svr_cfg.method(), svr_cfg.key(), &pkt))
.and_then(move |payload| {
// Read Address in the front (ShadowSocks protocol)
Address::read_from(Cursor::new(payload))
@@ -62,23 +64,35 @@ fn listen(svr_cfg: Rc<ServerConfig>) -> BoxIoFuture<()> {
payload.drain(..header_len);
let body = payload;
info!("UDP ASSOCIATE {} -> {}, payload length {} bytes", src, addr, body.len());
debug!("UDP ASSOCIATE {} -> {}, payload length {} bytes", src, addr, body.len());
Ok((addr, body))
})
.and_then(|(addr, body)| {
let local_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0);
Context::with(|ctx| UdpSocket::bind(&local_addr, ctx.handle()))
UdpSocket::bind(&local_addr)
.map(|remote_udp| (remote_udp, addr, body))
})
.and_then(|(remote_udp, addr, body)| {
resolve_remote_addr(addr.clone())
.and_then(|addr| remote_udp.send_dgram(body, addr))
resolve_remote_addr(config, addr.clone())
.and_then(|addr| remote_udp.send_dgram(body, &addr))
.map(|(remote_udp, _)| (remote_udp, addr))
})
})
.and_then(|(remote_udp, addr)| {
.and_then(move |(remote_udp, addr)| {
let buf = vec![0u8; MAXIMUM_UDP_PAYLOAD_SIZE];
let to = timeout.unwrap_or(Duration::from_secs(5));
let caddr = addr.clone();
remote_udp.recv_dgram(buf)
.deadline(Instant::now() + to)
.map_err(move |err| {
match err.into_inner() {
Some(e) => e,
None => {
error!("Udp associate waiting datagram {} -> {} timed out in {:?}", src, caddr, to);
io::Error::new(io::ErrorKind::TimedOut, "udp recv timed out")
}
}
})
.and_then(|(_remote_udp, buf, n, _from)| {
let svr_cfg = svr_cfg_cloned;
@@ -89,40 +103,46 @@ fn listen(svr_cfg: Rc<ServerConfig>) -> BoxIoFuture<()> {
})
})
.and_then(move |(buf, addr)| {
info!("UDP ASSOCIATE {} <- {}, payload length {} bytes", src, addr, buf.len());
debug!("UDP ASSOCIATE {} <- {}, payload length {} bytes", src, addr, buf.len());
let to = timeout.unwrap_or(Duration::from_secs(5));
let caddr = addr.clone();
SendDgramRc::new(socket, buf, src)
.deadline(Instant::now() + to)
.map_err(move |err| {
match err.into_inner() {
Some(e) => e,
None => {
error!("Udp associate sending datagram {} <- {} timed out in {:?}", src, caddr, to);
io::Error::new(io::ErrorKind::TimedOut, "udp send timed out")
}
}
})
})
.map(|_| ());
Context::with(|ctx| {
let handle = ctx.handle();
handle.spawn(rel.map_err(|err| {
error!("Udp relay error: {}", err);
}));
});
tokio::spawn(rel.map_err(|err| {
error!("Udp relay error: {}", err);
}));
Ok(())
})
});
boxed_future(fut)
Ok(())
})
})
}
/// Starts a UDP relay server
pub fn run() -> BoxIoFuture<()> {
let mut fut = None;
pub fn run(config: Arc<Config>) -> impl Future<Item = (), Error = io::Error> + Send {
let mut fut: Option<IoFuture<()>> = None;
Context::with(|ctx| {
let config = ctx.config();
for svr in &config.server {
let svr_cfg = Rc::new(svr.clone());
for svr in &config.server {
let svr_cfg = Arc::new(svr.clone());
let svr_fut = listen(svr_cfg);
fut = match fut {
None => Some(svr_fut),
Some(fut) => Some(boxed_future(fut.join(svr_fut).map(|_| ()))),
};
}
let svr_fut = listen(config.clone(), svr_cfg);
fut = match fut {
None => Some(boxed_future(svr_fut)),
Some(fut) => Some(boxed_future(fut.join(svr_fut).map(|_| ()))),
};
}
fut.expect("Should have at least one server")
})
fut.expect("Should have at least one server")
}
+1 -1
View File
@@ -1,8 +1,8 @@
use std::io;
use futures::{Async, Future, Poll};
use tokio_io::AsyncWrite;
use tokio_io::io::{write_all, WriteAll};
use tokio_io::AsyncWrite;
/// Write all bytes without returning the internal bytes buffer
pub struct WriteBytes<W, B>
+75
View File
@@ -0,0 +1,75 @@
extern crate dns_parser;
extern crate env_logger;
extern crate rand;
extern crate shadowsocks;
use std::collections::HashSet;
use std::net::{SocketAddr, UdpSocket};
use std::thread;
use std::time::Duration;
use dns_parser::{Builder, Packet, QueryClass, QueryType};
use shadowsocks::config::{Config, ConfigType};
use shadowsocks::{run_dns, run_server};
const CONFIG: &'static str = r#"{
"server": "127.0.0.1",
"server_port": 8988,
"local_port": 5030,
"local_address": "127.0.0.1",
"password": "abc",
"timeout": 20,
"method": "aes-256-gcm",
"enable_udp": true
}"#;
#[test]
fn dns_relay() {
env_logger::init();
let server_cfg = Config::load_from_str(CONFIG, ConfigType::Server).unwrap();
let dns_cfg = Config::load_from_str(CONFIG, ConfigType::Local).unwrap();
thread::spawn(move || {
run_server(server_cfg);
});
thread::spawn(move || {
run_dns(dns_cfg);
});
thread::sleep(Duration::from_secs(1));
let dns_addr = "127.0.0.1:5030".parse::<SocketAddr>().unwrap();
let local_addr = "0.0.0.0:0".parse::<SocketAddr>().unwrap();
let local = UdpSocket::bind(&local_addr).unwrap();
const LOOP_ROUND: usize = 10;
let mut sent_id = HashSet::<u16, _>::with_capacity(LOOP_ROUND);
for _ in 0..LOOP_ROUND {
let mut id = rand::random::<u16>();
while let Some(..) = sent_id.get(&id) {
id = rand::random::<u16>();
}
sent_id.insert(id);
let mut builder = Builder::new_query(id, false);
builder.add_question("www.example.com", QueryType::A, QueryClass::IN);
let payload = builder.build().unwrap();
local.send_to(&payload, &dns_addr).unwrap();
// println!("SENT {:?}", payload);
let mut buf = [0u8; 65535];
let (len, _) = local.recv_from(&mut buf).unwrap();
let packet = Packet::parse(&buf[..len]).unwrap();
// println!("GOT {:?}", packet);
assert_eq!(packet.header.id, id);
}
}
+9 -17
View File
@@ -2,7 +2,7 @@ extern crate env_logger;
extern crate futures;
extern crate log;
extern crate shadowsocks;
extern crate tokio_core;
extern crate tokio;
extern crate tokio_io;
use std::net::{SocketAddr, ToSocketAddrs};
@@ -10,14 +10,13 @@ use std::thread;
use std::time::Duration;
use futures::Future;
use tokio_core::reactor::Core;
use tokio_io::io::{flush, read_to_end, write_all};
use shadowsocks::{run_local, run_server};
use shadowsocks::config::{Config, ServerConfig};
use shadowsocks::crypto::CipherType;
use shadowsocks::relay::socks5::Address;
use shadowsocks::relay::tcprelay::client::Socks5Client;
use shadowsocks::{run_local, run_server};
pub struct Socks5TestServer {
local_addr: SocketAddr,
@@ -54,12 +53,12 @@ impl Socks5TestServer {
pub fn run(&self) {
let svr_cfg = self.config.clone();
thread::spawn(move || {
run_server(svr_cfg).unwrap();
run_server(svr_cfg);
});
let client_cfg = self.config.clone();
thread::spawn(move || {
run_local(client_cfg).unwrap();
run_local(client_cfg);
});
thread::sleep(Duration::from_secs(1));
@@ -77,12 +76,9 @@ fn socks5_relay_stream() {
let svr = Socks5TestServer::new(SERVER_ADDR, LOCAL_ADDR, PASSWORD, METHOD, false);
svr.run();
let mut lp = Core::new().unwrap();
let handle = lp.handle();
let c = Socks5Client::connect(Address::DomainNameAddress("www.example.com".to_owned(), 80),
*svr.client_addr(),
handle);
*svr.client_addr());
let fut = c.and_then(|c| {
let req = b"GET / HTTP/1.0\r\nHost: www.example.com\r\nAccept: */*\r\n\r\n";
write_all(c, req.to_vec()).and_then(|(c, _)| flush(c))
@@ -93,7 +89,7 @@ fn socks5_relay_stream() {
})
});
lp.run(fut).unwrap();
tokio::run(fut.map_err(|_| ()));
}
#[test]
@@ -107,12 +103,8 @@ fn socks5_relay_aead() {
let svr = Socks5TestServer::new(SERVER_ADDR, LOCAL_ADDR, PASSWORD, METHOD, false);
svr.run();
let mut lp = Core::new().unwrap();
let handle = lp.handle();
let c = Socks5Client::connect(Address::DomainNameAddress("www.example.com".to_owned(), 80),
*svr.client_addr(),
handle);
*svr.client_addr());
let fut = c.and_then(|c| {
let req = b"GET / HTTP/1.0\r\nHost: www.example.com\r\nAccept: */*\r\n\r\n";
write_all(c, req.to_vec()).and_then(|(c, _)| flush(c))
@@ -123,5 +115,5 @@ fn socks5_relay_aead() {
})
});
lp.run(fut).unwrap();
tokio::run(fut.map_err(|_| ()));
}
+7 -12
View File
@@ -4,7 +4,7 @@ extern crate bytes;
extern crate env_logger;
extern crate futures;
extern crate shadowsocks;
extern crate tokio_core;
extern crate tokio;
extern crate tokio_io;
use std::io::Cursor;
@@ -15,14 +15,13 @@ use std::time::Duration;
use bytes::{BufMut, BytesMut};
use futures::Future;
use tokio_core::reactor::Core;
use tokio_io::io::read_to_end;
use shadowsocks::{run_local, run_server};
use shadowsocks::config::{Config, ServerConfig};
use shadowsocks::crypto::CipherType;
use shadowsocks::relay::socks5::{Address, UdpAssociateHeader};
use shadowsocks::relay::tcprelay::client::Socks5Client;
use shadowsocks::{run_local, run_server};
const SERVER_ADDR: &'static str = "127.0.0.1:8093";
const LOCAL_ADDR: &'static str = "127.0.0.1:8291";
@@ -48,14 +47,14 @@ fn get_client_addr() -> SocketAddr {
fn start_server(bar: Arc<Barrier>) {
thread::spawn(move || {
bar.wait();
run_server(get_config()).unwrap();
run_server(get_config());
});
}
fn start_local(bar: Arc<Barrier>) {
thread::spawn(move || {
bar.wait();
run_local(get_config()).unwrap();
run_local(get_config());
});
}
@@ -76,10 +75,7 @@ fn start_udp_echo_server(bar: Arc<Barrier>) {
fn start_udp_request_holder(bar: Arc<Barrier>, addr: Address) {
thread::spawn(move || {
let mut lp = Core::new().unwrap();
let handle = lp.handle();
let c = Socks5Client::udp_associate(addr, get_client_addr(), handle);
let c = Socks5Client::udp_associate(addr, get_client_addr());
let fut = c.and_then(|(c, addr)| {
assert_eq!(addr, Address::SocketAddress(LOCAL_ADDR.parse().unwrap()));
@@ -89,7 +85,7 @@ fn start_udp_request_holder(bar: Arc<Barrier>, addr: Address) {
bar.wait();
lp.run(fut).unwrap();
tokio::run(fut.map_err(|_| ()));
});
}
@@ -136,8 +132,7 @@ fn udp_relay() {
println!("Received buf size={} {:?}", amt, &buf[..amt]);
let cur = Cursor::new(buf[..amt].to_vec());
let (cur, header) = UdpAssociateHeader::read_from(cur).wait()
.expect("Invalid UDP header");
let (cur, header) = UdpAssociateHeader::read_from(cur).wait().expect("Invalid UDP header");
println!("{:?}", header);
let header_len = cur.position() as usize;
let buf = cur.into_inner();