mirror of
https://github.com/shadowsocks/shadowsocks-rust.git
synced 2026-02-09 01:59:16 +08:00
Add Socks5Client wrap, fulfill more documents, add tcp connect test
This commit is contained in:
@@ -13,6 +13,7 @@ use clap::{App, Arg};
|
||||
|
||||
use qrcode::QrCode;
|
||||
|
||||
use shadowsocks::VERSION;
|
||||
use shadowsocks::config::{Config, ConfigType, ServerConfig};
|
||||
|
||||
const BLACK: &'static str = "\x1b[40m \x1b[0m";
|
||||
@@ -103,6 +104,7 @@ fn main() {
|
||||
let app = App::new("ssurl")
|
||||
.author("Y. T. Chung <zonyitoo@gmail.com>")
|
||||
.about("Encode and decode ShadowSocks URL")
|
||||
.version(VERSION)
|
||||
.arg(Arg::with_name("ENCODE")
|
||||
.short("e")
|
||||
.long("encode")
|
||||
|
||||
@@ -133,6 +133,7 @@ impl ServerAddr {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse ServerAddr error
|
||||
#[derive(Debug)]
|
||||
pub struct ServerAddrError;
|
||||
|
||||
@@ -169,13 +170,18 @@ impl Display for ServerAddr {
|
||||
/// Configuration for a server
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ServerConfig {
|
||||
/// Server address
|
||||
pub addr: ServerAddr,
|
||||
/// Encryption password (key)
|
||||
pub password: String,
|
||||
/// Encryption type (method)
|
||||
pub method: CipherType,
|
||||
/// Connection timeout
|
||||
pub timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
impl ServerConfig {
|
||||
/// Create a basic config
|
||||
pub fn basic(addr: SocketAddr, password: String, method: CipherType) -> ServerConfig {
|
||||
ServerConfig {
|
||||
addr: ServerAddr::SocketAddr(addr),
|
||||
@@ -206,9 +212,14 @@ impl json::ToJson for ServerConfig {
|
||||
/// Listening address
|
||||
pub type ClientConfig = SocketAddr;
|
||||
|
||||
/// Server config type
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ConfigType {
|
||||
/// Config for local
|
||||
///
|
||||
/// Requires `local` configuration
|
||||
Local,
|
||||
/// Config for server
|
||||
Server,
|
||||
}
|
||||
|
||||
@@ -230,6 +241,7 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration parsing error kind
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ErrorKind {
|
||||
MissingField,
|
||||
|
||||
@@ -44,6 +44,7 @@ extern crate tokio_core;
|
||||
|
||||
extern crate libc;
|
||||
|
||||
/// ShadowSocks version
|
||||
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub mod config;
|
||||
|
||||
@@ -37,8 +37,6 @@ use config::Config;
|
||||
/// Relay server running under local environment.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::SocketAddr;
|
||||
///
|
||||
/// use shadowsocks::relay::RelayLocal;
|
||||
/// use shadowsocks::config::{Config, ServerConfig, ServerAddr};
|
||||
/// use shadowsocks::crypto::CipherType;
|
||||
@@ -51,7 +49,7 @@ use config::Config;
|
||||
/// method: CipherType::Aes256Cfb,
|
||||
/// timeout: None,
|
||||
/// }];
|
||||
/// RelayLocal::run(config);
|
||||
/// RelayLocal::run(config).unwrap();
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct RelayLocal;
|
||||
|
||||
@@ -34,8 +34,6 @@ use config::Config;
|
||||
/// Relay server running on server side.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::SocketAddr;
|
||||
///
|
||||
/// use shadowsocks::relay::RelayServer;
|
||||
/// use shadowsocks::config::{Config, ServerConfig, ServerAddr};
|
||||
/// use shadowsocks::crypto::CipherType;
|
||||
@@ -47,7 +45,7 @@ use config::Config;
|
||||
/// method: CipherType::Aes256Cfb,
|
||||
/// timeout: None,
|
||||
/// }];
|
||||
/// RelayServer::run(config);
|
||||
/// RelayServer::run(config).unwrap();
|
||||
/// ```
|
||||
///
|
||||
#[derive(Clone)]
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
// 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.
|
||||
|
||||
//! Socks5 protocol definition (RFC1928)
|
||||
//!
|
||||
//! Implements [SOCKS Protocol Version 5](https://www.ietf.org/rfc/rfc1928.txt) proxy protocol
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
@@ -61,11 +65,14 @@ const SOCKS5_REPLY_TTL_EXPIRED: u8 = 0x06;
|
||||
const SOCKS5_REPLY_COMMAND_NOT_SUPPORTED: u8 = 0x07;
|
||||
const SOCKS5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED: u8 = 0x08;
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// SOCKS5 command
|
||||
#[derive(Clone, Debug, Copy)]
|
||||
pub enum Command {
|
||||
/// CONNECT command (TCP tunnel)
|
||||
TcpConnect,
|
||||
/// BIND command (Not supported in ShadowSocks)
|
||||
TcpBind,
|
||||
/// UDP ASSOCIATE command
|
||||
UdpAssociate,
|
||||
}
|
||||
|
||||
@@ -90,6 +97,7 @@ impl Command {
|
||||
}
|
||||
}
|
||||
|
||||
/// SOCKS5 reply code
|
||||
#[derive(Clone, Debug, Copy)]
|
||||
pub enum Reply {
|
||||
Succeeded,
|
||||
@@ -139,9 +147,29 @@ impl Reply {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Reply {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Reply::Succeeded => write!(f, "Succeeded"),
|
||||
Reply::AddressTypeNotSupported => write!(f, "Address type not supported"),
|
||||
Reply::CommandNotSupported => write!(f, "Command not supported"),
|
||||
Reply::ConnectionNotAllowed => write!(f, "Connection not allowed"),
|
||||
Reply::ConnectionRefused => write!(f, "Connection refused"),
|
||||
Reply::GeneralFailure => write!(f, "General failure"),
|
||||
Reply::HostUnreachable => write!(f, "Host unreachable"),
|
||||
Reply::NetworkUnreachable => write!(f, "Network unreachable"),
|
||||
Reply::OtherReply(u) => write!(f, "Other reply ({})", u),
|
||||
Reply::TtlExpired => write!(f, "TTL expired"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// SOCKS5 protocol error
|
||||
#[derive(Clone)]
|
||||
pub struct Error {
|
||||
/// Reply code
|
||||
pub reply: Reply,
|
||||
/// Error message
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
@@ -191,9 +219,12 @@ impl From<Error> for io::Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// SOCKS5 address type
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Address {
|
||||
/// Socket address (IP Address)
|
||||
SocketAddress(SocketAddr),
|
||||
/// Domain name address
|
||||
DomainNameAddress(String, u16),
|
||||
}
|
||||
|
||||
@@ -250,13 +281,23 @@ impl From<SocketAddr> for Address {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(String, u16)> for Address {
|
||||
fn from((dn, port): (String, u16)) -> Address {
|
||||
Address::DomainNameAddress(dn, port)
|
||||
}
|
||||
}
|
||||
|
||||
/// TCP request header after handshake
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TcpRequestHeader {
|
||||
/// SOCKS5 command
|
||||
pub command: Command,
|
||||
/// Remote address
|
||||
pub address: Address,
|
||||
}
|
||||
|
||||
impl TcpRequestHeader {
|
||||
/// Creates a request header
|
||||
pub fn new(cmd: Command, addr: Address) -> TcpRequestHeader {
|
||||
TcpRequestHeader {
|
||||
command: cmd,
|
||||
@@ -264,6 +305,7 @@ impl TcpRequestHeader {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read from a reader
|
||||
pub fn read_from<R: Read + 'static>(r: R) -> Box<Future<Item = (R, TcpRequestHeader), Error = Error>> {
|
||||
let fut = read_exact(r, [0u8; 3])
|
||||
.map_err(From::from)
|
||||
@@ -295,6 +337,7 @@ impl TcpRequestHeader {
|
||||
Box::new(fut)
|
||||
}
|
||||
|
||||
/// Write data into a writer
|
||||
pub fn write_to<W: Write + 'static>(&self, w: W) -> BoxIoFuture<W> {
|
||||
let addr = self.address.clone();
|
||||
|
||||
@@ -304,19 +347,24 @@ impl TcpRequestHeader {
|
||||
Box::new(fut)
|
||||
}
|
||||
|
||||
/// Length in bytes
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.address.len() + 3
|
||||
}
|
||||
}
|
||||
|
||||
/// TCP response header
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TcpResponseHeader {
|
||||
/// SOCKS5 reply
|
||||
pub reply: Reply,
|
||||
/// Reply address
|
||||
pub address: Address,
|
||||
}
|
||||
|
||||
impl TcpResponseHeader {
|
||||
/// Creates a response header
|
||||
pub fn new(reply: Reply, address: Address) -> TcpResponseHeader {
|
||||
TcpResponseHeader {
|
||||
reply: reply,
|
||||
@@ -324,6 +372,7 @@ impl TcpResponseHeader {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read from a reader
|
||||
pub fn read_from<R: Read + 'static>(r: R) -> Box<Future<Item = (R, TcpResponseHeader), Error = Error>> {
|
||||
let fut = read_exact(r, [0u8; 3])
|
||||
.map_err(From::from)
|
||||
@@ -351,6 +400,7 @@ impl TcpResponseHeader {
|
||||
Box::new(fut)
|
||||
}
|
||||
|
||||
/// Write to a writer
|
||||
pub fn write_to<W: Write + 'static>(&self, w: W) -> BoxIoFuture<W> {
|
||||
let addr = self.address.clone();
|
||||
let fut = write_all(w, [SOCKS5_VERSION, self.reply.as_u8(), 0x00])
|
||||
@@ -360,6 +410,7 @@ impl TcpResponseHeader {
|
||||
Box::new(fut)
|
||||
}
|
||||
|
||||
/// Length in bytes
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.address.len() + 3
|
||||
@@ -507,21 +558,25 @@ fn get_addr_len(atyp: &Address) -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
// +----+----------+----------+
|
||||
// |VER | NMETHODS | METHODS |
|
||||
// +----+----------+----------+
|
||||
// | 5 | 1 | 1 to 255 |
|
||||
// +----+----------+----------|
|
||||
/// SOCKS5 handshake request packet
|
||||
///
|
||||
/// +----+----------+----------+
|
||||
/// |VER | NMETHODS | METHODS |
|
||||
/// +----+----------+----------+
|
||||
/// | 5 | 1 | 1 to 255 |
|
||||
/// +----+----------+----------|
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HandshakeRequest {
|
||||
pub methods: Vec<u8>,
|
||||
}
|
||||
|
||||
impl HandshakeRequest {
|
||||
/// Creates a handshake request
|
||||
pub fn new(methods: Vec<u8>) -> HandshakeRequest {
|
||||
HandshakeRequest { methods: methods }
|
||||
}
|
||||
|
||||
/// Read from a reader
|
||||
pub fn read_from<R: Read + 'static>(r: R) -> BoxIoFuture<(R, HandshakeRequest)> {
|
||||
let fut = read_exact(r, [0u8, 0u8])
|
||||
.and_then(|(r, buf)| {
|
||||
@@ -541,6 +596,7 @@ impl HandshakeRequest {
|
||||
Box::new(fut)
|
||||
}
|
||||
|
||||
/// Write to a writer
|
||||
pub fn write_to<W: Write + 'static>(self, w: W) -> BoxIoFuture<W> {
|
||||
let fut = write_all(w, [SOCKS5_VERSION, self.methods.len() as u8])
|
||||
.and_then(move |(w, _)| write_all(w, self.methods))
|
||||
@@ -550,21 +606,25 @@ impl HandshakeRequest {
|
||||
}
|
||||
}
|
||||
|
||||
// +----+--------+
|
||||
// |VER | METHOD |
|
||||
// +----+--------+
|
||||
// | 1 | 1 |
|
||||
// +----+--------+
|
||||
/// SOCKS5 handshake response packet
|
||||
///
|
||||
/// +----+--------+
|
||||
/// |VER | METHOD |
|
||||
/// +----+--------+
|
||||
/// | 1 | 1 |
|
||||
/// +----+--------+
|
||||
#[derive(Clone, Debug, Copy)]
|
||||
pub struct HandshakeResponse {
|
||||
pub chosen_method: u8,
|
||||
}
|
||||
|
||||
impl HandshakeResponse {
|
||||
/// Creates a handshake response
|
||||
pub fn new(cm: u8) -> HandshakeResponse {
|
||||
HandshakeResponse { chosen_method: cm }
|
||||
}
|
||||
|
||||
/// Read from a reader
|
||||
pub fn read_from<R: Read + 'static>(r: R) -> BoxIoFuture<(R, HandshakeResponse)> {
|
||||
let fut = read_exact(r, [0u8, 0u8]).and_then(|(r, buf)| {
|
||||
let ver = buf[0];
|
||||
@@ -579,18 +639,25 @@ impl HandshakeResponse {
|
||||
Box::new(fut)
|
||||
}
|
||||
|
||||
/// Write to a writer
|
||||
pub fn write_to<W: Write + 'static>(self, w: W) -> BoxIoFuture<W> {
|
||||
Box::new(write_all(w, [SOCKS5_VERSION, self.chosen_method]).map(|(w, _)| w))
|
||||
}
|
||||
}
|
||||
|
||||
/// UDP ASSOCIATE request header
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UdpAssociateHeader {
|
||||
/// Fragment
|
||||
///
|
||||
/// ShadowSocks does not support fragment, so this frag must be 0x00
|
||||
pub frag: u8,
|
||||
/// Remote address
|
||||
pub address: Address,
|
||||
}
|
||||
|
||||
impl UdpAssociateHeader {
|
||||
/// Creates a header
|
||||
pub fn new(frag: u8, address: Address) -> UdpAssociateHeader {
|
||||
UdpAssociateHeader {
|
||||
frag: frag,
|
||||
@@ -598,6 +665,7 @@ impl UdpAssociateHeader {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read from a reader
|
||||
pub fn read_from<R: Read + 'static>(r: R) -> Box<Future<Item = (R, UdpAssociateHeader), Error = Error>> {
|
||||
let fut = read_exact(r, [0u8; 3])
|
||||
.map_err(From::from)
|
||||
@@ -611,6 +679,7 @@ impl UdpAssociateHeader {
|
||||
Box::new(fut)
|
||||
}
|
||||
|
||||
/// Write to a writer
|
||||
pub fn write_to<W: Write + 'static>(&self, w: W) -> BoxIoFuture<W> {
|
||||
let addr = self.address.clone();
|
||||
let fut = write_all(w, [0x00, 0x00, self.frag])
|
||||
@@ -619,6 +688,8 @@ impl UdpAssociateHeader {
|
||||
Box::new(fut)
|
||||
}
|
||||
|
||||
/// Length in bytes
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
3 + self.address.len()
|
||||
}
|
||||
|
||||
97
src/relay/tcprelay/client.rs
Normal file
97
src/relay/tcprelay/client.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
// The MIT License (MIT)
|
||||
|
||||
// Copyright (c) 2014 Y. T. CHUNG <zonyitoo@gmail.com>
|
||||
|
||||
// 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.
|
||||
|
||||
//! TCP relay client implementation
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use tokio_core::reactor::Handle;
|
||||
use tokio_core::net::TcpStream;
|
||||
|
||||
use futures::{self, Future};
|
||||
|
||||
use relay::socks5::{self, HandshakeRequest, HandshakeResponse, Address, TcpRequestHeader, TcpResponseHeader, Command,
|
||||
Reply};
|
||||
use relay::{BoxIoFuture, boxed_future};
|
||||
|
||||
/// Socks5 proxy client
|
||||
pub struct Socks5Client {
|
||||
stream: TcpStream,
|
||||
}
|
||||
|
||||
impl Socks5Client {
|
||||
pub fn connect<A>(addr: A, proxy: SocketAddr, handle: Handle) -> BoxIoFuture<Socks5Client>
|
||||
where Address: From<A>,
|
||||
A: 'static
|
||||
{
|
||||
let fut = futures::lazy(move || TcpStream::connect(&proxy, &handle))
|
||||
.and_then(move |s| {
|
||||
// 1. Handshake
|
||||
let hs = HandshakeRequest::new(vec![socks5::SOCKS5_AUTH_METHOD_NONE]);
|
||||
trace!("Client connected, going to send handshake: {:?}", hs);
|
||||
|
||||
hs.write_to(s)
|
||||
.and_then(|s| HandshakeResponse::read_from(s))
|
||||
.and_then(|(s, rsp)| {
|
||||
trace!("Got handshake response: {:?}", rsp);
|
||||
assert_eq!(rsp.chosen_method, socks5::SOCKS5_AUTH_METHOD_NONE);
|
||||
Ok(s)
|
||||
})
|
||||
})
|
||||
.and_then(move |s| {
|
||||
// 2. Send request header
|
||||
let h = TcpRequestHeader::new(Command::TcpConnect, From::from(addr));
|
||||
trace!("Going to connect, req: {:?}", h);
|
||||
h.write_to(s)
|
||||
.and_then(|s| TcpResponseHeader::read_from(s).map_err(From::from))
|
||||
.and_then(|(s, rsp)| {
|
||||
trace!("Got response: {:?}", rsp);
|
||||
match rsp.reply {
|
||||
Reply::Succeeded => Ok(s),
|
||||
r => {
|
||||
let err = io::Error::new(io::ErrorKind::Other, format!("{}", r));
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.map(|s| Socks5Client { stream: s });
|
||||
|
||||
boxed_future(fut)
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Socks5Client {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.stream.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Socks5Client {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.stream.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.stream.flush()
|
||||
}
|
||||
}
|
||||
@@ -50,17 +50,25 @@ pub mod local;
|
||||
pub mod server;
|
||||
mod stream;
|
||||
mod http;
|
||||
pub mod client;
|
||||
|
||||
/// Directions in the tunnel
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum TunnelDirection {
|
||||
/// Client -> Server
|
||||
Client2Server,
|
||||
/// Client <- Server
|
||||
Server2Client,
|
||||
}
|
||||
|
||||
/// ReadHalf of TcpStream with decryption
|
||||
pub type DecryptedHalf = DecryptedReader<ReadHalf<TcpStream>>;
|
||||
/// WriteHalf of TcpStream with encryption
|
||||
pub type EncryptedHalf = EncryptedWriter<WriteHalf<TcpStream>>;
|
||||
|
||||
/// Boxed future of DecryptedHalf
|
||||
pub type DecryptedHalfFut = BoxIoFuture<DecryptedHalf>;
|
||||
/// Boxed future of EncryptedHalf
|
||||
pub type EncryptedHalfFut = BoxIoFuture<EncryptedHalf>;
|
||||
|
||||
fn connect_proxy_server(handle: &Handle,
|
||||
@@ -302,7 +310,18 @@ pub fn tunnel<CF, SF>(addr: Address, c2s: CF, s2c: SF) -> BoxIoFuture<()>
|
||||
}
|
||||
});
|
||||
|
||||
Box::new(c2s.join(s2c).map(|_| ()))
|
||||
let fut = c2s.select(s2c)
|
||||
.and_then(|(dir, _)| {
|
||||
match dir {
|
||||
TunnelDirection::Server2Client => trace!("client <- server is closed, abort connection"),
|
||||
TunnelDirection::Client2Server => trace!("server -> client is closed, abort connection"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|(err, _)| err);
|
||||
|
||||
boxed_future(fut)
|
||||
}
|
||||
|
||||
/// Read until EOF, and ignore
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
// 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.
|
||||
|
||||
//! UdpRelay implementation
|
||||
|
||||
pub mod local;
|
||||
pub mod server;
|
||||
|
||||
|
||||
93
tests/test.rs
Normal file
93
tests/test.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
extern crate shadowsocks;
|
||||
extern crate tokio_core;
|
||||
extern crate futures;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate env_logger;
|
||||
|
||||
use std::thread;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::time::Duration;
|
||||
|
||||
use tokio_core::reactor::Core;
|
||||
use tokio_core::io::{read_to_end, write_all, flush};
|
||||
use futures::Future;
|
||||
|
||||
use shadowsocks::relay::tcprelay::client::Socks5Client;
|
||||
use shadowsocks::config::{Config, ServerConfig};
|
||||
use shadowsocks::crypto::CipherType;
|
||||
use shadowsocks::relay::{RelayLocal, RelayServer};
|
||||
use shadowsocks::relay::socks5::Address;
|
||||
|
||||
const SERVER_ADDR: &'static str = "127.0.0.1:8096";
|
||||
const LOCAL_ADDR: &'static str = "127.0.0.1:8008";
|
||||
|
||||
const PASSWORD: &'static str = "test-password";
|
||||
const METHOD: CipherType = CipherType::Aes128Cfb;
|
||||
|
||||
fn get_config() -> Config {
|
||||
let mut cfg = Config::new();
|
||||
cfg.local = Some(LOCAL_ADDR.parse().unwrap());
|
||||
cfg.server = vec![ServerConfig {
|
||||
addr: SERVER_ADDR.parse().unwrap(),
|
||||
password: PASSWORD.to_owned(),
|
||||
method: METHOD,
|
||||
timeout: None,
|
||||
}];
|
||||
cfg
|
||||
}
|
||||
|
||||
fn get_client_addr() -> SocketAddr {
|
||||
LOCAL_ADDR.parse().unwrap()
|
||||
}
|
||||
|
||||
fn start_server(bar: Arc<Barrier>) {
|
||||
thread::spawn(move || {
|
||||
drop(env_logger::init());
|
||||
bar.wait();
|
||||
RelayServer::run(get_config()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
fn start_local(bar: Arc<Barrier>) {
|
||||
thread::spawn(move || {
|
||||
drop(env_logger::init());
|
||||
bar.wait();
|
||||
RelayLocal::run(get_config()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn socks5_relay() {
|
||||
drop(env_logger::init());
|
||||
|
||||
let bar = Arc::new(Barrier::new(3));
|
||||
|
||||
start_server(bar.clone());
|
||||
start_local(bar.clone());
|
||||
|
||||
bar.wait();
|
||||
|
||||
// Wait until all server starts
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
|
||||
let mut lp = Core::new().unwrap();
|
||||
let handle = lp.handle();
|
||||
|
||||
let c = Socks5Client::connect(Address::DomainNameAddress("www.example.com".to_owned(), 80),
|
||||
get_client_addr(),
|
||||
handle);
|
||||
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))
|
||||
.and_then(|c| read_to_end(c, Vec::new()))
|
||||
.map(|(_, buf)| {
|
||||
println!("Got reply from server: {}",
|
||||
unsafe { String::from_utf8_unchecked(buf) });
|
||||
})
|
||||
});
|
||||
|
||||
lp.run(fut).unwrap();
|
||||
}
|
||||
Reference in New Issue
Block a user