diff --git a/Cargo.toml b/Cargo.toml
index 68f8bcaa..c5ec1a5e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -62,6 +62,7 @@ openssl = "^0.7.1"
lru-cache = "0.0.7"
libc = "^0.2.7"
hyper = "0.9"
+url = "^1.2"
[dependencies.coio]
git = "https://github.com/zonyitoo/coio-rs.git"
diff --git a/src/lib.rs b/src/lib.rs
index 1ac05b8c..1f96ad26 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -38,6 +38,7 @@ extern crate crypto as rust_crypto;
extern crate ip;
extern crate openssl;
extern crate hyper;
+extern crate url;
extern crate libc;
diff --git a/src/relay/tcprelay/http.rs b/src/relay/tcprelay/http.rs
index 9f8a6519..cd1417c4 100644
--- a/src/relay/tcprelay/http.rs
+++ b/src/relay/tcprelay/http.rs
@@ -22,20 +22,19 @@
/// Http Proxy
use std::io::{self, Read, Write};
-use std::net::{SocketAddr, Shutdown};
+use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6, Shutdown};
use std::time::Duration;
use coio::net::TcpStream;
use hyper::net::NetworkStream;
-use hyper::server::request::Request;
use hyper::server::response::Response;
-use hyper::method::Method;
use hyper::uri::RequestUri;
-use hyper::buffer::BufReader;
use hyper::header::Headers;
use hyper::status::StatusCode;
+use url::Host;
+
use relay::socks5::Address;
#[derive(Debug)]
@@ -75,25 +74,10 @@ impl NetworkStream for HttpStream {
}
}
-fn do_handshake(stream: &mut HttpStream, addr: SocketAddr) -> Result
{
- let mut reader = BufReader::new(stream as &mut NetworkStream);
- let request = match Request::new(&mut reader, addr) {
- Ok(r) => r,
- Err(err) => {
- error!("Failed to create Request: {:?}", err);
- return Err(StatusCode::BadRequest);
- }
- };
- match request.method {
- Method::Connect => {}
- _ => {
- error!("Does not support {:?}", request.method);
- return Err(StatusCode::MethodNotAllowed);
- }
- }
- match &request.uri {
+pub fn get_address(uri: &RequestUri) -> Result {
+ match uri {
&RequestUri::Authority(ref s) => {
match s.parse::() {
Ok(addr) => Ok(Address::SocketAddress(addr)),
@@ -119,6 +103,22 @@ fn do_handshake(stream: &mut HttpStream, addr: SocketAddr) -> Result {
+ 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)
@@ -126,21 +126,11 @@ fn do_handshake(stream: &mut HttpStream, addr: SocketAddr) -> Result io::Result {
- match do_handshake(stream, addr) {
- Ok(r) => {
- stream.write_all(b"HTTP/1.1 200 Connection Established\r\n\r\n")
- .map(|_| r)
- }
- Err(status) => {
- let mut headers = Headers::new();
- let mut resp = Response::new(stream, &mut headers);
- *resp.status_mut() = status;
- try!(resp.start().and_then(|r| r.end()));
-
- let err = io::Error::new(io::ErrorKind::Other, "Handshake error");
- Err(err)
- }
- }
+pub fn write_response(stream: &mut Write, status: StatusCode) -> io::Result<()> {
+ let mut headers = Headers::new();
+ let mut resp = Response::new(stream, &mut headers);
+ *resp.status_mut() = status;
+ try!(resp.start().and_then(|r| r.end()));
+ Ok(())
}
diff --git a/src/relay/tcprelay/local.rs b/src/relay/tcprelay/local.rs
index d133174d..80444639 100644
--- a/src/relay/tcprelay/local.rs
+++ b/src/relay/tcprelay/local.rs
@@ -30,9 +30,14 @@ use std::sync::Arc;
use coio::Scheduler;
use coio::net::{TcpListener, TcpStream, Shutdown};
+use hyper::net::NetworkStream;
+use hyper::server::request::Request;
+use hyper::method::Method;
+use hyper::status::StatusCode;
+
use config::{Config, ClientConfig};
-use relay::socks5;
+use relay::socks5::{self, Address};
use relay::loadbalancing::server::{LoadBalancer, RoundRobin};
use crypto::cipher::CipherType;
@@ -241,50 +246,29 @@ impl TcpRelayLocal {
}
}
- fn handle_http_client(stream: TcpStream, server_addr: SocketAddr, password: Vec, encrypt_method: CipherType) {
- use super::http::handshake;
- use super::http::HttpStream;
-
- let sockname = match stream.peer_addr() {
- Ok(sockname) => sockname,
- Err(err) => {
- error!("Failed to get peer addr: {}", err);
- return;
- }
- };
-
- let stream_writer = match stream.try_clone() {
- Ok(s) => s,
- Err(err) => {
- error!("Failed to clone local stream: {}", err);
- return;
- }
- };
-
- let mut http_stream = HttpStream(stream);
- let addr = match handshake(&mut http_stream, sockname) {
- Ok(addr) => addr,
- Err(err) => {
- error!("Error occurs while doing handshake: {}", err);
- return;
- }
- };
-
- // NOTE: We have already sent Connection Established while doing handshake
-
- let HttpStream(stream) = http_stream; // Extract
+ fn handle_http_connect(stream: TcpStream,
+ stream_writer: TcpStream,
+ addr: Address,
+ server_addr: SocketAddr,
+ password: Vec,
+ encrypt_method: CipherType)
+ -> io::Result<()> {
+ info!("CONNECT (Http) {}", addr);
let mut local_reader = BufReader::new(stream);
let mut local_writer = stream_writer;
- info!("CONNECT (Http) {}", addr);
+ if let Err(err) = local_writer.write_all(b"HTTP/1.1 200 Connection Established\r\n\r\n") {
+ error!("Failed to send handshake: {:?}", err);
+ return Err(err);
+ }
let (mut decrypt_stream, mut encrypt_stream) =
match super::connect_proxy_server(&server_addr, encrypt_method, &password[..], &addr) {
Ok(x) => x,
Err(err) => {
error!("Failed to connect to proxy server: {:?}", err);
- return;
+ return Err(err);
}
};
@@ -337,6 +321,78 @@ impl TcpRelayLocal {
let _ = decrypt_stream.get_mut().shutdown(Shutdown::Both);
let _ = local_writer.shutdown(Shutdown::Both);
});
+
+ Ok(())
+ }
+
+ fn handle_http_client(stream: TcpStream, server_addr: SocketAddr, password: Vec, encrypt_method: CipherType) {
+ use hyper::buffer::BufReader;
+ use super::http::{HttpStream, get_address, write_response};
+
+ let sockname = match stream.peer_addr() {
+ Ok(sockname) => sockname,
+ Err(err) => {
+ error!("Failed to get peer addr: {}", err);
+ return;
+ }
+ };
+
+ let mut stream_writer = match stream.try_clone() {
+ Ok(s) => s,
+ Err(err) => {
+ error!("Failed to clone stream: {:?}", err);
+ return;
+ }
+ };
+
+ let mut http_stream = HttpStream(stream);
+
+ let opt_stream = {
+ let opt_addr = {
+ let mut reader = BufReader::new(&mut http_stream as &mut NetworkStream);
+ let request = match Request::new(&mut reader, sockname) {
+ Ok(r) => r,
+ Err(err) => {
+ error!("Failed to create Request: {:?}", err);
+ let _ = write_response(&mut stream_writer, StatusCode::BadRequest);
+ return;
+ }
+ };
+
+ let addr = match get_address(&request.uri) {
+ Ok(addr) => addr,
+ Err(status) => {
+ let _ = write_response(&mut stream_writer, status);
+ return;
+ }
+ };
+
+ match request.method {
+ Method::Connect => Some(addr),
+ method => {
+ info!("{} (Http) {} (Not supported)", method, addr);
+ let _ = write_response(&mut stream_writer, StatusCode::MethodNotAllowed);
+
+ None
+ }
+ }
+ };
+
+ opt_addr.map(move |addr| {
+ let HttpStream(stream) = http_stream; // Deconstruct
+ (stream, addr)
+ })
+ };
+
+ if let Some((stream, addr)) = opt_stream {
+ // CONNECT
+ let _ = TcpRelayLocal::handle_http_connect(stream,
+ stream_writer,
+ addr,
+ server_addr,
+ password,
+ encrypt_method);
+ }
}
}