From f2d6a0b8df1c2e1c8d0d011bf99ef40b43d005b2 Mon Sep 17 00:00:00 2001 From: "Y. T. Chung" Date: Mon, 6 Feb 2017 20:36:26 +0800 Subject: [PATCH] removed http proxy protocol, bump version to v1.1 --- Cargo.lock | 124 +----- Cargo.toml | 5 +- src/config.rs | 41 -- src/lib.rs | 3 - src/relay/tcprelay/http_local.rs | 668 ------------------------------- src/relay/tcprelay/local.rs | 12 +- src/relay/tcprelay/mod.rs | 107 +---- 7 files changed, 4 insertions(+), 956 deletions(-) delete mode 100644 src/relay/tcprelay/http_local.rs diff --git a/Cargo.lock b/Cargo.lock index f6136fdd..8459f079 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ [root] name = "shadowsocks-rust" -version = "1.0.3" +version = "1.1.0" dependencies = [ "base64 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -8,8 +8,6 @@ dependencies = [ "env_logger 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", "ip 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", @@ -24,7 +22,6 @@ dependencies = [ "serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -183,40 +180,6 @@ name = "glob" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "httparse" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hyper" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "httparse 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "idna" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "image" version = "0.12.2" @@ -267,11 +230,6 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "language-tags" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "lazy_static" version = "0.2.2" @@ -310,11 +268,6 @@ name = "lzw" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "matches" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "memchr" version = "1.0.1" @@ -333,14 +286,6 @@ dependencies = [ "toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "mime" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "mio" version = "0.6.4" @@ -523,14 +468,6 @@ name = "rustc-serialize" version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "rustc_version" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "scoped-tls" version = "0.1.0" @@ -541,11 +478,6 @@ name = "scoped_threadpool" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "semver" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "serde" version = "0.9.6" @@ -628,37 +560,6 @@ name = "toml" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "traitobject" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "typeable" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicase" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-bidi" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "unicode-segmentation" version = "1.0.3" @@ -677,15 +578,6 @@ dependencies = [ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "url" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "user32-sys" version = "0.2.0" @@ -752,16 +644,12 @@ dependencies = [ "checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518" "checksum gif 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "01c7c19a035de94bd7afbaa62c241aadfbdf1a70f560b348d2312eafa566ca16" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -"checksum httparse 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a6e7a63e511f9edffbab707141fbb8707d1a3098615fb2adbd5769cdfcc9b17d" -"checksum hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "220407e5a263f110ec30a071787c9535918fdfc97def5680c90013c3f30c38c1" -"checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11" "checksum image 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6fb38e372d9288be8fa60bea6f05342e4a35244c221cd9fe4c01ded02c86e60c" "checksum inflate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e7e0062d2dc2f17d2f13750d95316ae8a2ff909af0fda957084f5defd87c43bb" "checksum ip 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7349baf09910ffb475d8ea8a16f1bbe5ec2be295373feef33fb4fea658b8a0ef" "checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c" "checksum jpeg-decoder 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "5c4ff3d14e7ef3522471ab712832c3dd50001f7fb7aa4cdc48af811d63b531e9" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b" "checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b" "checksum libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "684f330624d8c3784fb9558ca46c4ce488073a8d22450415c5eb4f4cfb0d11b5" @@ -769,10 +657,8 @@ dependencies = [ "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" "checksum lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "656fa4dfcb02bcf1063c592ba3ff6a5303ee1f2afe98c8a889e8b1a77c6dfdb7" "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" -"checksum matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efd7622e3022e1a6eaa602c4cea8912254e5582c9c692e9167714182244801b1" "checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4" "checksum metadeps 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829fffe7ea1d747e23f64be972991bc516b2f1ac2ae4a3b33d8bea150c410151" -"checksum mime 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5c93a4bd787ddc6e7833c519b73a50883deb5863d76d9b71eb8216fb7f94e66" "checksum mio 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "eecdbdd49a849336e77b453f021c89972a2cfb5b51931a0026ae0ac4602de681" "checksum miow 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a78d2605eb97302c10cf944b8d96b0a2a890c52957caf92fcd1f24f69049579" "checksum net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "5edf9cb6be97212423aed9413dd4729d62b370b5e1c571750e882cebbbc1e3e2" @@ -793,10 +679,8 @@ dependencies = [ "checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457" "checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" "checksum rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "237546c689f20bb44980270c73c3b9edd0891c1be49cc1274406134a66d3957b" -"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" "checksum scoped_threadpool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3ef399c8893e8cb7aa9696e895427fab3a6bf265977bb96e126f24ddd2cda85a" -"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" "checksum serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0ae9a3c8b07c09dbe43022486d55a18c629a0618d2241e49829aaef9b6d862f9" "checksum serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cf37ce931677e98b4fa5e6469aaa3ab4b6228309ea33b1b22d3ec055adfc4515" "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" @@ -807,15 +691,9 @@ dependencies = [ "checksum time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "211b63c112206356ef1ff9b19355f43740fc3f85960c598a93d3a3d3ba7beade" "checksum tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3d1be481b55126f02ef88ff86748086473cb537a949fc4a8f4be403a530ae54b" "checksum toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "736b60249cb25337bc196faa43ee12c705e426f3d55c214d73a4e7be06f92cb4" -"checksum traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "07eaeb7689bb7fca7ce15628319635758eda769fed481ecfe6686ddef2600616" -"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" -"checksum unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a5906ca2b98c799f4b1ab4557b76367ebd6ae5ef14930ec841c74aed5f3764" -"checksum unicode-bidi 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b61814f3e7fd0e0f15370f767c7c943e08bc2e3214233ae8f88522b334ceb778" -"checksum unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e28fa37426fceeb5cf8f41ee273faa7c82c47dc8fba5853402841e665fcd86ff" "checksum unicode-segmentation 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5336c5173d8a77ae0b36151c706e32ae10f4985e29d704ad5b5f9565d6d4b6" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" "checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91" -"checksum url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5ba8a749fb4479b043733416c244fa9d1d3af3d7c23804944651c8a448cb87e" "checksum user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ef4711d107b21b410a3a974b1204d9accc8b10dad75d8324b5d755de1617d47" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cac5efe5cb0fa14ec2f84f83c701c562ee63f6dcc680861b21d65c682adfb05f" diff --git a/Cargo.toml b/Cargo.toml index 326efcb1..26ce0590 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "shadowsocks-rust" -version = "1.0.3" +version = "1.1.0" authors = ["Y. T. CHUNG "] description = "shadowsocks is a fast tunnel proxy that helps you bypass firewalls." repository = "https://github.com/zonyitoo/shadowsocks-rust" @@ -52,9 +52,6 @@ ip = "1" openssl = "0.9" lru-cache = "0.1" libc = "0.2" -hyper = "0.10" -url = "1.4" -httparse = "1.2" futures = "0.1" futures-cpupool = "0.1" tokio-core = "0.1" diff --git a/src/config.rs b/src/config.rs index 87b20fc5..cdeeac73 100644 --- a/src/config.rs +++ b/src/config.rs @@ -256,7 +256,6 @@ pub enum ConfigType { pub struct Config { pub server: Vec, pub local: Option, - pub http_proxy: Option, pub enable_udp: bool, pub timeout: Option, pub forbidden_ip: HashSet, @@ -340,7 +339,6 @@ impl Config { Config { server: Vec::new(), local: None, - http_proxy: None, enable_udp: false, timeout: None, forbidden_ip: HashSet::new(), @@ -496,45 +494,6 @@ impl Config { } else if has_local_address ^ has_local_port { panic!("You have to provide `local_address` and `local_port` together"); } - - let has_proxy_addr = o.contains_key("local_http_address"); - let has_proxy_port = o.contains_key("local_http_port"); - - if has_proxy_addr && has_proxy_port { - config.http_proxy = match o.get("local_http_address") { - Some(local_addr) => { - let addr_str = try!(local_addr.as_str() - .ok_or(Error::new(ErrorKind::Malformed, - "`local_http_address` should be a string", - None))); - - let port = try!(o.get("local_http_port") - .unwrap() - .as_u64() - .ok_or(Error::new(ErrorKind::Malformed, - "`local_http_port` should be an integer", - None))) as u16; - - match addr_str.parse::() { - Ok(ip) => Some(SocketAddr::V4(SocketAddrV4::new(ip, port))), - Err(..) => { - match addr_str.parse::() { - Ok(ip) => Some(SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0))), - Err(..) => { - return Err(Error::new(ErrorKind::Malformed, - "`local_http_address` is not a valid IP \ - address", - None)) - } - } - } - } - } - None => None, - }; - } else if has_proxy_addr ^ has_proxy_port { - panic!("You have to provide `local_http_address` and `local_http_port` together"); - } } if let Some(forbidden_ip_conf) = o.get("forbidden_ip") { diff --git a/src/lib.rs b/src/lib.rs index 37d90038..ce815e10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,9 +102,6 @@ extern crate rand; extern crate crypto as rust_crypto; extern crate ip; extern crate openssl; -extern crate hyper; -extern crate url; -extern crate httparse; extern crate futures; extern crate futures_cpupool; diff --git a/src/relay/tcprelay/http_local.rs b/src/relay/tcprelay/http_local.rs deleted file mode 100644 index a74e116f..00000000 --- a/src/relay/tcprelay/http_local.rs +++ /dev/null @@ -1,668 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2014 Y. T. CHUNG - -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -/// Http Proxy - -use std::io::{self, BufRead, BufReader, Write}; -use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; -use std::mem; -use std::str; -use std::fmt; -use std::rc::Rc; - -use hyper::uri::RequestUri; -use hyper::header::{Header, HeaderFormat, Headers, ContentLength}; -use hyper::header::{Connection, ConnectionOption}; -use hyper::status::StatusCode; -use hyper::version::HttpVersion; -use hyper::method::Method; -use hyper; - -use httparse::{self, Request}; - -use url::Host; - -use ip::IpAddr; - -use futures::{self, Future, Poll}; -use futures::stream::Stream; - -use tokio_core::net::{TcpStream, TcpListener}; -use tokio_core::reactor::Handle; -use tokio_core::io::Io; -use tokio_core::io::{ReadHalf, WriteHalf}; -use tokio_core::io::{flush, write_all, copy}; - -use net2::TcpBuilder; - -use config::{Config, ServerConfig}; - -use relay::socks5::Address; -use relay::loadbalancing::server::RoundRobin; -use relay::loadbalancing::server::LoadBalancer; -use relay::{BoxIoFuture, boxed_future}; - -use super::tunnel; -use super::stream::EncryptedWriter; - -#[derive(Debug)] -pub struct HttpRequest { - pub version: HttpVersion, - pub method: Method, - pub request_uri: RequestUri, - pub headers: Headers, -} - -impl HttpRequest { - pub fn from_raw<'headers, 'buf: 'headers>(req: &Request<'headers, 'buf>, - headers: &'headers [httparse::Header]) - -> hyper::Result { - - let mut he = Headers::new(); - for h in headers.iter() { - if h.name.len() == 0 { - break; - } - - he.append_raw(h.name.to_owned(), h.value.to_vec()); - } - - Ok(HttpRequest { - version: if req.version.unwrap() == 1 { - HttpVersion::Http11 - } else { - HttpVersion::Http10 - }, - method: try!(req.method.unwrap().parse::()), - request_uri: try!(req.path.unwrap().parse::()), - headers: he, - }) - } - - pub fn clear_request_uri_host(&mut self) { - let ptr = &mut self.request_uri as *mut RequestUri; - match &mut self.request_uri { - &mut RequestUri::AbsoluteUri(ref url) => { - let mut abs = String::new(); - abs += url.path(); - if let Some(query) = url.query() { - abs += "?"; - abs += query; - } - - if let Some(frag) = url.fragment() { - abs += "#"; - abs += frag; - } - - // Force replace - let unsafe_ref = unsafe { &mut *ptr }; - ::std::mem::replace(unsafe_ref, RequestUri::AbsolutePath(abs)); - } - _ => {} - } - } - - /// Writes request into an EncryptedWriter - pub fn write_to_encrypted(self, w: EncryptedWriter) -> BoxIoFuture> - where W: Write + 'static - { - let fut = futures::lazy(move || { - let mut w = Vec::new(); - try!(write!(w, - "{} {} {}\r\n", - self.method, - self.request_uri, - self.version)); - - for header in self.headers.iter() { - if !header.name().is_empty() { - try!(write!(w, "{}: {}\r\n", header.name(), header.value_string())); - } - } - - try!(write!(w, "\r\n")); - - Ok(w) - }) - .and_then(|buf| w.write_all_encrypted(buf)) - .map(|(w, _)| w); - - Box::new(fut) - } - - /// Get Socks5 address from URI - #[inline] - pub fn get_address(&self) -> Result { - get_address(&self.request_uri) - } -} - -fn get_address(uri: &RequestUri) -> Result { - match uri { - &RequestUri::Authority(ref s) => { - match s.parse::() { - Ok(addr) => Ok(Address::SocketAddress(addr)), - Err(_) => { - let mut sp = s.splitn(2, ':'); - match (sp.next(), sp.next()) { - (Some(host), Some(port)) => { - let port = match port.parse::() { - Ok(port) => port, - Err(err) => { - error!("Failed to parse Url, {}", err); - return Err(StatusCode::BadRequest); - } - }; - - Ok(Address::DomainNameAddress(host.to_owned(), port)) - } - (host, port) => { - error!("Failed to parse Url, {:?}:{:?}", host, port); - return Err(StatusCode::BadRequest); - } - } - } - } - } - &RequestUri::AbsoluteUri(ref uri) => { - if !uri.has_host() { - error!("URI does not have Host: {:?}", uri); - return Err(StatusCode::BadRequest); - } - - let port = uri.port_or_known_default().unwrap_or(80); - - let addr = match uri.host().unwrap() { - Host::Domain(dom) => Address::DomainNameAddress(dom.to_owned(), port), - Host::Ipv4(v4) => Address::SocketAddress(SocketAddr::V4(SocketAddrV4::new(v4, port))), - Host::Ipv6(v6) => Address::SocketAddress(SocketAddr::V6(SocketAddrV6::new(v6, port, 0, 0))), - }; - - Ok(addr) - } - u => { - error!("Invalid Uri {:?}", u); - Err(StatusCode::BadRequest) - } - } -} - -pub fn write_response(w: W, version: HttpVersion, status: StatusCode) -> BoxIoFuture - where W: Write + 'static -{ - let buf = format!("{} {}\r\n\r\n", version, status); - Box::new(write_all(w, buf.into_bytes()).map(|(w, _)| w)) -} - -/// X-Forward-For header -#[derive(Debug, Clone)] -pub struct XForwardFor(pub Vec); - -impl Header for XForwardFor { - fn header_name() -> &'static str { - "X-Forward-For" - } - - fn parse_header(raw: &[Vec]) -> hyper::Result { - let mut ips = Vec::new(); - for raw_h in raw.iter() { - let xfor = try!(str::from_utf8(&raw_h[..])); - for xfor_str in xfor.split(',') { - let trimmed = xfor_str.trim(); - if trimmed.is_empty() { - // Ignore empty string - continue; - } - match trimmed.parse::() { - Ok(i) => ips.push(i), - Err(..) => return Err(hyper::Error::Header), - } - } - } - - Ok(XForwardFor(ips)) - } -} - -impl HeaderFormat for XForwardFor { - fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut first = true; - for ip in &self.0 { - if first { - first = false; - } else { - try!(write!(f, ", ")); - } - - try!(write!(f, "{}", ip)); - } - - Ok(()) - } -} - -/// X-Real-IP header -#[derive(Debug, Clone)] -pub struct XRealIp(pub IpAddr); - -impl Header for XRealIp { - fn header_name() -> &'static str { - "X-Real-IP" - } - - fn parse_header(raw: &[Vec]) -> hyper::Result { - let mut ip = None; - for raw_ip in raw.iter() { - let x_ip = try!(str::from_utf8(&raw_ip[..])); - match x_ip.trim().parse::() { - Ok(i) => { - if let Some(prev_ip) = ip.take() { - if prev_ip != i { - return Err(hyper::Error::Header); - } - } - - ip = Some(i); - } - Err(..) => return Err(hyper::Error::Header), - } - } - - match ip { - Some(ip) => Ok(XRealIp(ip)), - None => Err(hyper::Error::Header), - } - } -} - -impl HeaderFormat for XRealIp { - #[inline] - fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -/// Future for reading HttpRequest -pub enum HttpRequestFut - where R: BufRead -{ - Pending { r: R, buf: Vec }, - Empty, -} - -impl HttpRequestFut - where R: BufRead -{ - pub fn new(r: R) -> HttpRequestFut { - HttpRequestFut::with_buf(r, Vec::new()) - } - - pub fn with_buf(r: R, buf: Vec) -> HttpRequestFut { - HttpRequestFut::Pending { r: r, buf: buf } - } -} - -impl Future for HttpRequestFut - where R: BufRead -{ - type Item = (R, HttpRequest); - type Error = io::Error; - - fn poll(&mut self) -> Poll { - let req = match self { - &mut HttpRequestFut::Pending { ref mut r, ref mut buf } => { - // FIXME: Compiler force me to do this! - let http_req: Option; - loop { - let mut is_eof = false; - loop { - let n = try_nb!(r.read_until(b'\n', buf)); - if n == 0 { - is_eof = true; - break; - } - - if buf.ends_with(b"\r\n\r\n") { - break; - } - } - - // Maximum 128 headers - let mut headers = [httparse::EMPTY_HEADER; 128]; - let headers_ptr = &headers as *const _; - let mut req = Request::new(&mut headers); - match req.parse(&mut buf[..]) { - Ok(httparse::Status::Partial) => { - if is_eof { - // Already EOF! - let err = io::Error::new(io::ErrorKind::UnexpectedEof, "Unexpected Eof"); - return Err(err); - } - } - Ok(httparse::Status::Complete(..)) => { - - // Make borrow checker happy - let headers_ref = unsafe { &*headers_ptr }; - let hreq = match HttpRequest::from_raw(&req, headers_ref) { - Ok(r) => r, - Err(err) => { - error!("HttpRequest::from_raw: {}", err); - let err = io::Error::new(io::ErrorKind::Other, "Hyper error"); - return Err(err); - } - }; - http_req = Some(hreq); - break; - } - Err(err) => { - error!("Request parse: {:?}", err); - let err = io::Error::new(io::ErrorKind::Other, "Hyper error"); - return Err(err); - } - } - } - - http_req.unwrap() - } - &mut HttpRequestFut::Empty => panic!("poll a HttpRequestFut after it's done"), - }; - - match mem::replace(self, HttpRequestFut::Empty) { - HttpRequestFut::Pending { r, .. } => Ok((r, req).into()), - HttpRequestFut::Empty => unreachable!(), - } - } -} - -fn socket_to_ip(addr: &SocketAddr) -> IpAddr { - match *addr { - SocketAddr::V4(ref v4) => IpAddr::V4(v4.ip().clone()), - SocketAddr::V6(ref v6) => IpAddr::V6(v6.ip().clone()), - } -} - -fn preprocess_request(client_addr: Option<&SocketAddr>, req: &mut HttpRequest) -> usize { - // Gets content length for body - let content_length = req.headers.get::().unwrap_or(&ContentLength(0)).0 as usize; - - if let Some(client_addr) = client_addr { - let client_ip = socket_to_ip(client_addr); - - // Set proxy IP info - let xf = if let Some(fw) = req.headers.get_mut::() { - let mut flst = fw.0.clone(); - flst.push(client_ip.clone()); - flst - } else { - vec![client_ip.clone()] - }; - req.headers.set(XForwardFor(xf)); - - // Set real ip - req.headers.set(XRealIp(client_ip)); - } - - // Clears host, which only for proxy - req.clear_request_uri_host(); - - content_length -} - -/// Proxy this HTTP Request to writer -pub fn proxy_request_encrypted((r, w): (R, EncryptedWriter), - client_addr: Option<&SocketAddr>, - mut req: HttpRequest) - -> BoxIoFuture<(R, EncryptedWriter)> - where R: BufRead + 'static, - W: Write + 'static -{ - let content_length = preprocess_request(client_addr, &mut req); - - let fut = req.write_to_encrypted(w) - .and_then(|w| flush(w)) - .and_then(move |w| super::copy_exact_encrypted(r, w, content_length)); - Box::new(fut) -} - -/// Check `Connection` header to determine whether we should keep alive -pub fn should_keep_alive(req: &HttpRequest) -> bool { - let default_keep_alive = req.version >= HttpVersion::Http11; - match req.headers.get::() { - Some(conn) => { - for opt in conn.iter() { - if let &ConnectionOption::KeepAlive = opt { - return true; - } - } - - default_keep_alive - } - None => default_keep_alive, - } -} - -fn handle_connect(handle: Handle, - (r, w): (BufReader>, WriteHalf), - req: HttpRequest, - addr: Address, - svr_cfg: Rc) - -> Box> { - let cloned_addr = addr.clone(); - let http_version = req.version; - let cloned_svr_cfg = svr_cfg.clone(); - - let fut = super::connect_proxy_server(&handle, svr_cfg) - .and_then(move |svr_s| { - trace!("Proxy server connected"); - - // Tell the client that we are ready - let handshake_resp = format!("{} 200 Connection Established\r\n\r\n", http_version); - trace!("Sending HTTP tunnel handshake response"); - write_all(w, handshake_resp.into_bytes()) - .and_then(|(w, _)| flush(w)) - .map(|w| (svr_s, w)) - }) - .and_then(move |(svr_s, w)| { - super::proxy_server_handshake(svr_s, cloned_svr_cfg, addr).and_then(move |(svr_r, svr_w)| { - let rhalf = svr_r.and_then(move |svr_r| copy(svr_r, w)); - let whalf = svr_w.and_then(move |svr_w| svr_w.copy_from_encrypted(r)); - tunnel(cloned_addr, whalf, rhalf) - }) - }); - - Box::new(fut) -} - -fn handle_http_keepalive(r: BufReader>, svr_w: super::EncryptedHalf) -> BoxIoFuture<()> { - let fut = HttpRequestFut::new(r).then(|res| { - match res { - Ok((r, req)) => { - let should_keep_alive = should_keep_alive(&req); - trace!("Going to proxy request: {:?}", req); - trace!("Should keep alive? {}", should_keep_alive); - - let fut = proxy_request_encrypted((r, svr_w), None, req).and_then(move |(r, svr_w)| { - if should_keep_alive { - handle_http_keepalive(r, svr_w) - } else { - futures::finished(()).boxed() - } - }); - Box::new(fut) as BoxIoFuture<()> - } - Err(err) => { - let fut = futures::lazy(|| { - use std::io::ErrorKind; - match err.kind() { - // It is Ok for client to close connection - ErrorKind::UnexpectedEof | ErrorKind::BrokenPipe => Ok(()), - _ => Err(err), - } - }); - Box::new(fut) as BoxIoFuture<()> - } - } - }); - Box::new(fut) -} - -fn handle_http_proxy(handle: Handle, - (r, w): (BufReader>, WriteHalf), - client_addr: &SocketAddr, - req: HttpRequest, - addr: Address, - svr_cfg: Rc) - -> Box> { - trace!("Using HTTP Proxy for {} -> {}", client_addr, addr); - - let should_keep_alive = should_keep_alive(&req); - let fut = super::connect_proxy_server(&handle, svr_cfg.clone()).and_then(move |svr_s| { - trace!("Proxy server connected"); - - let cloned_addr = addr.clone(); - super::proxy_server_handshake(svr_s, svr_cfg, addr).and_then(move |(svr_r, svr_w)| { - - // Just proxy anything to client - let rhalf = svr_r.and_then(move |svr_r| copy(svr_r, w)); - let whalf = svr_w.and_then(move |svr_w| { - // Send the first request to server - trace!("Going to proxy request: {:?}", req); - trace!("Should keep alive? {}", should_keep_alive); - - proxy_request_encrypted((r, svr_w), None, req).and_then(move |(r, svr_w)| { - if should_keep_alive { - handle_http_keepalive(r, svr_w) - } else { - futures::finished(()).boxed() - } - }) - }); - - let fut = rhalf.join(whalf) - .then(move |_| { - trace!("Relay to {} is finished", cloned_addr); - Ok(()) - }); - boxed_future(fut) - }) - }); - - Box::new(fut) -} - -fn handle_client(handle: &Handle, socket: TcpStream, _: SocketAddr, svr_cfg: Rc) -> io::Result<()> { - let cloned_handle = handle.clone(); - let client_addr = try!(socket.peer_addr()); - let fut = futures::lazy(|| Ok(socket.split())) - .and_then(|(r, w)| { - // Process the first request to see whether client wants CONNECT tunnel or normal HTTP proxy - - let r = BufReader::new(r); - - HttpRequestFut::new(r).and_then(move |(r, mut req)| { - trace!("Got HTTP Request, version: {}, method: {}, uri: {}", - req.version, - req.method, - req.request_uri); - - match req.get_address() { - Ok(addr) => { - req.clear_request_uri_host(); - boxed_future(futures::finished((r, w, req, addr))) - } - Err(status_code) => { - error!("Invalid Uri: {}", req.request_uri); - let fut = write_response(w, req.version, status_code).then(|_| { - let err = io::Error::new(io::ErrorKind::Other, "Invalid Uri"); - Err(err) - }); - boxed_future(fut) - } - } - }) - }) - .and_then(move |(r, w, req, addr)| { - match req.method.clone() { - Method::Connect => { - info!("CONNECT (Http) {}", addr); - handle_connect(cloned_handle, (r, w), req, addr, svr_cfg) - } - met => { - info!("{} (Http) {}", met, addr); - handle_http_proxy(cloned_handle, (r, w), &client_addr, req, addr, svr_cfg) - } - } - }); - - handle.spawn(fut.then(|res| { - match res { - Ok(..) => Ok(()), - Err(err) => { - if err.kind() != io::ErrorKind::BrokenPipe { - error!("Failed to handle client: {}", err); - } - - Err(()) - } - } - })); - - Ok(()) -} - -/// TCP local server using HTTP proxy protocol -pub fn run(config: Rc, handle: Handle) -> Box> { - let listener = { - let local_addr = config.http_proxy.as_ref().unwrap(); - - let tcp_builder = match local_addr { - &SocketAddr::V4(..) => TcpBuilder::new_v4(), - &SocketAddr::V6(..) => TcpBuilder::new_v6(), - } - .unwrap_or_else(|err| panic!("Failed to create listener, {}", err)); - - super::reuse_port(&tcp_builder) - .and_then(|builder| builder.reuse_address(true)) - .and_then(|builder| builder.bind(local_addr)) - .unwrap_or_else(|err| panic!("Failed to bind {}, {}", local_addr, err)); - - let listener = tcp_builder.listen(1024) - .and_then(|l| TcpListener::from_listener(l, local_addr, &handle)) - .unwrap_or_else(|err| panic!("Failed to listen, {}", err)); - info!("ShadowSocks HTTP Listening on {}", local_addr); - listener - }; - - let mut servers = RoundRobin::new(&*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_client(&handle, socket, addr, server_cfg) - }); - - Box::new(listening.map_err(|err| { - error!("HTTP server run failed: {}", err); - err - })) -} diff --git a/src/relay/tcprelay/local.rs b/src/relay/tcprelay/local.rs index 577e04e2..e829a70a 100644 --- a/src/relay/tcprelay/local.rs +++ b/src/relay/tcprelay/local.rs @@ -23,8 +23,6 @@ use std::rc::Rc; -use futures::Future; - use tokio_core::reactor::Handle; use config::Config; @@ -32,17 +30,9 @@ use config::Config; use relay::{BoxIoFuture, boxed_future}; use super::socks5_local; -use super::http_local; /// Starts a TCP local server pub fn run(config: Rc, handle: Handle) -> BoxIoFuture<()> { let tcp_fut = socks5_local::run(config.clone(), handle.clone()); - match &config.http_proxy { - &Some(..) => { - let http_fut = http_local::run(config, handle); - boxed_future(tcp_fut.join(http_fut) - .map(|_| ())) - } - &None => tcp_fut, - } + boxed_future(tcp_fut) } diff --git a/src/relay/tcprelay/mod.rs b/src/relay/tcprelay/mod.rs index 4f3299f3..634c54c6 100644 --- a/src/relay/tcprelay/mod.rs +++ b/src/relay/tcprelay/mod.rs @@ -21,11 +21,10 @@ //! TcpRelay implementation -use std::io::{self, Read, Write}; +use std::io::{self, Read}; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::rc::Rc; use std::mem; -use std::cmp; use crypto::cipher; use crypto::CryptoMode; @@ -50,7 +49,6 @@ use self::stream::{EncryptedWriter, DecryptedReader}; pub mod local; mod socks5_local; -mod http_local; pub mod server; mod stream; pub mod client; @@ -165,109 +163,6 @@ pub fn proxy_handshake(remote_stream: TcpStream, boxed_future(fut) } -/// Copy exactly N bytes by encryption -pub enum CopyExactEncrypted - where R: Read, - W: Write -{ - Pending { - reader: R, - writer: EncryptedWriter, - buf: [u8; 4096], - remain: usize, - pos: usize, - cap: usize, - enc_buf: Vec, - }, - Empty, -} - -impl CopyExactEncrypted - where R: Read, - W: Write -{ - pub fn new(r: R, w: EncryptedWriter, amt: usize) -> CopyExactEncrypted { - CopyExactEncrypted::Pending { - reader: r, - writer: w, - buf: [0u8; 4096], - remain: amt, - pos: 0, - cap: 0, - enc_buf: Vec::new(), - } - } -} - -impl Future for CopyExactEncrypted - where R: Read, - W: Write -{ - type Item = (R, EncryptedWriter); - type Error = io::Error; - - fn poll(&mut self) -> Poll { - match self { - &mut CopyExactEncrypted::Empty => panic!("poll after CopyExactEncrypted is finished"), - &mut CopyExactEncrypted::Pending { ref mut reader, - ref mut writer, - ref mut buf, - ref mut remain, - ref mut pos, - ref mut cap, - ref mut enc_buf } => { - loop { - // If our buffer is empty, then we need to read some data to - // continue. - if *pos == *cap && *remain != 0 { - let buf_len = cmp::min(*remain, buf.len()); - let n = try_nb!(reader.read(&mut buf[..buf_len])); - if n == 0 { - // Unexpected EOF! - let err = io::Error::new(io::ErrorKind::UnexpectedEof, "Unexpected Eof"); - return Err(err); - } else { - *pos = 0; - *remain -= n; - - enc_buf.clear(); - try!(writer.cipher_update(&buf[..n], enc_buf)); - *cap = enc_buf.len(); - } - } - - // If our buffer has some data, let's write it out! - while *pos < *cap { - let i = try_nb!(writer.write(&enc_buf[*pos..*cap])); - *pos += i; - } - - // If we've written al the data and we've seen EOF, flush out the - // data and finish the transfer. - // done with the entire transfer. - if *pos == *cap && *remain == 0 { - try_nb!(writer.flush()); - break; // The only path to execute the following logic - } - } - } - } - - match mem::replace(self, CopyExactEncrypted::Empty) { - CopyExactEncrypted::Pending { reader, writer, .. } => Ok((reader, writer).into()), - CopyExactEncrypted::Empty => unreachable!(), - } - } -} - -/// Copy all bytes from reader and write all encrypted data into writer -pub fn copy_exact_encrypted(r: R, w: EncryptedWriter, amt: usize) -> CopyExactEncrypted - where R: Read, - W: Write -{ - CopyExactEncrypted::new(r, w, amt) -} - /// Establish tunnel between server and client pub fn tunnel(addr: Address, c2s: CF, s2c: SF) -> BoxIoFuture<()> where CF: Future + 'static,