mirror of
https://github.com/shadowsocks/shadowsocks-rust.git
synced 2026-11-07 00:13:33 +00:00
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:
Generated
+635
-241
File diff suppressed because it is too large
Load Diff
+18
-13
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)]
|
||||
|
||||
@@ -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;
|
||||
///
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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,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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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>> {
|
||||
|
||||
@@ -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
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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!(),
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user