Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
#![crate_name = "rustsocks"]
#![crate_type = "rlib"]
#![feature(slice_patterns)]
#![feature(ip_addr)]
#![feature(read_exact)]

pub use socks4a::Socks4a;
pub use socks4::Socks4;
//pub use socks4a::Socks4a;
//pub use socks4::Socks4;
pub use socks5::Socks5;

pub mod socks4a;
pub mod socks4;
//pub mod socks4a;
//pub mod socks4;
pub mod socks5;

mod util {
use std::io::{IoResult, IoError, IoErrorKind};
use std::io::{Result, Error, ErrorKind};

pub fn io_err<T>(kind: IoErrorKind, desc: &'static str) -> IoResult<T> {
Err(IoError { kind: kind, desc: desc, detail: None })
pub fn io_err<T>(kind: ErrorKind, desc: &'static str) -> Result<T> {
Err(Error::new(kind, desc))
}
}
2 changes: 1 addition & 1 deletion src/socks4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl<'a> Socks4<'a> {
// request failed because client's identd could not confirm the user ID
// string in the request
0x5d => io_err(ConnectionRefused, "Unknown user"),
x => fail!("Unexpected status byte: {}", x)
x => { return io_err(OtherIoError, format!("Unexpected status byte: {}", x)); }
}
}
}
6 changes: 3 additions & 3 deletions src/socks4a.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use util::io_err;
use std::io::{IoResult, TcpStream, ConnectionRefused, ConnectionFailed,
OtherIoError};
use std::io::{Result, OtherIoError};
use std::net::{TcpStream, ConnectionFailed, ConnectionRefused};

pub struct Socks4a<'a> {
socks_host: &'a str,
Expand Down Expand Up @@ -42,7 +42,7 @@ impl<'a> Socks4a<'a> {
// request failed because client's identd could not confirm the user ID
// string in the request
0x5d => io_err(ConnectionRefused, "Unknown user"),
x => fail!("Unexpected status byte: {}", x)
x => io_err(OtherIoError, format!("Unexpected status byte: {}", x))
}
}
}
127 changes: 78 additions & 49 deletions src/socks5.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,58 @@
use util::io_err;
use std::io::{IoResult, TcpStream, ConnectionRefused, ConnectionFailed,
OtherIoError};
use std::io::net::ip::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::io::{Write, Read, ErrorKind};
use std::io::Result as IoResult;
use std::net::{IpAddr, TcpStream};
use std::mem;

enum AuthMethod<'s> {
NoAuth,
UPass(&'s str, &'s str)
}

pub trait ReadU8 {
fn read_u8(&mut self) -> IoResult<u8>;
}

impl ReadU8 for TcpStream {
#[inline]
fn read_u8(&mut self) -> IoResult<u8> {
let mut buf = [0u8];
try!(self.read_exact(&mut buf));
Ok(buf[0])
}
}

pub trait Socks5Destination {
fn write_destination(&self, &mut TcpStream) -> IoResult<()>;
}

impl<'a> Socks5Destination for &'a str {
fn write_destination(&self, stream: &mut TcpStream) -> IoResult<()> {
if self.len() > 255 {
return io_err(OtherIoError, "Domain length is too long");
return io_err(ErrorKind::Other, "Domain length is too long");
}

try!(stream.write([0x03, self.len() as u8]));
try!(stream.write_str(*self));
try!(stream.write(&[0x03, self.len() as u8]));
try!(stream.write(&self.as_bytes()));
Ok(())
}
}

impl<'a> Socks5Destination for IpAddr {
fn write_destination(&self, stream: &mut TcpStream) -> IoResult<()> {
match *self {
Ipv4Addr(a, b, c, d) => try!(stream.write([0x01, a, b, c, d])),
Ipv6Addr(a, b, c, d, e, f, g, h) => {
try!(stream.write_u8(0x04));
for &pair in [a, b, c, d, e, f, g, h].iter() {
try!(stream.write_be_u16(pair));
IpAddr::V4(addr) => {
try!(stream.write_all(&[0x01]));
try!(stream.write_all(&addr.octets()));
},
IpAddr::V6(addr) => {
let segments = addr.segments();
try!(stream.write_all(&[0x04]));
for &pair in segments.iter() {
let array : [u8; 2] = unsafe {
mem::transmute(pair.to_be())
};
try!(stream.write_all(&array));
}
}
}
Expand All @@ -51,69 +72,73 @@ impl<'a> Socks5<'a> {
Socks5 {
socks_host: host,
socks_port: port,
socks_auth: NoAuth
socks_auth: AuthMethod::NoAuth
}
}

pub fn login(&mut self, uname: &'a str, passwd: &'a str) {
self.socks_auth = UPass(uname, passwd);
self.socks_auth = AuthMethod::UPass(uname, passwd);
}

pub fn connect<T: Socks5Destination>(&mut self, destination: T, port: u16)
-> IoResult<TcpStream> {
let mut stream = try!(TcpStream::connect(self.socks_host, self.socks_port));
try!(stream.write([0x05u8]));
let mut stream = try!(TcpStream::connect((self.socks_host, self.socks_port)));
try!(stream.write(&[0x05u8]));
match self.socks_auth {
NoAuth => { try!(stream.write([0x01u8, 0x00])); },
UPass(..) => { try!(stream.write([0x01u8, 0x02])); }
AuthMethod::NoAuth => { try!(stream.write(&[0x01u8, 0x00])); },
AuthMethod::UPass(..) => { try!(stream.write(&[0x01u8, 0x02])); }
}

if try!(stream.read_u8()) != 0x05 {
return io_err(OtherIoError, "Unexpected SOCKS version number");
return io_err(ErrorKind::Other, "Unexpected SOCKS version number");
}

match try!(stream.read_u8()) {
0x00 => {
match self.socks_auth {
NoAuth => { /* Continue */ },
_ => return io_err(OtherIoError,
AuthMethod::NoAuth => { /* Continue */ },
_ => return io_err(ErrorKind::Other,
"Wrong authentication method from server")
}
}
0x02 => {
match self.socks_auth {
UPass(uname, passwd) => {
try!(stream.write([0x01u8, uname.len() as u8]));
try!(stream.write_str(uname));
try!(stream.write([passwd.len() as u8]));
try!(stream.write_str(passwd));
AuthMethod::UPass(uname, passwd) => {
try!(stream.write_all(&[0x01u8, uname.len() as u8]));
//try!(stream.write_str(uname));
try!(write!(stream, "{}", uname));
try!(stream.write_all(&[passwd.len() as u8]));
try!(write!(stream, "{}", passwd));
//try!(stream.write_str(passwd));

if try!(stream.read_u8()) != 0x01 {
return io_err(OtherIoError,
return io_err(ErrorKind::Other,
"Invalid authentication version");
}

if try!(stream.read_u8()) != 0x00 {
return io_err(ConnectionRefused, "Authentication failed");
return io_err(ErrorKind::ConnectionRefused, "Authentication failed");
}
}
_ => { return io_err(OtherIoError,
_ => { return io_err(ErrorKind::Other,
"Wrong authentication method from server");
}
}
}
0xFF => { return io_err(ConnectionRefused,
0xFF => { return io_err(ErrorKind::ConnectionRefused,
"Server refused authentication methods"); }
_ => { return io_err(OtherIoError,
_ => { return io_err(ErrorKind::Other,
"Wrong authentication method from server"); }
}

try!(stream.write([0x05u8, 0x01, 0x00]));
try!(stream.write(&[0x05u8, 0x01, 0x00]));
try!(destination.write_destination(&mut stream));
try!(stream.write_be_u16(port));
let be_port = port.to_be();
let array : [u8; 2] = unsafe { mem::transmute(be_port) };
try!(stream.write_all(&array));

if try!(stream.read_u8()) != 0x05 {
return io_err(OtherIoError, "Invalid SOCKS version number");
return io_err(ErrorKind::Other, "Invalid SOCKS version number");
}

match try!(stream.read_u8()) {
Expand All @@ -122,32 +147,36 @@ impl<'a> Socks5<'a> {

match try!(stream.read_u8()) {
0x01 => {
let mut _ipv4 = [0,.. 4];
try!(stream.read_at_least(4, &mut _ipv4));
let mut _ipv4 = [0; 4];
try!(stream.read_exact(&mut _ipv4));
}
0x03 => {
let addrlen = try!(stream.read_u8());
let _domain = try!(stream.read_exact(addrlen as uint));
let mut _domain = Vec::with_capacity(addrlen as usize);
try!(stream.read_exact(&mut _domain));
}
0x04 => {
let mut _ipv6 = [0,.. 16];
try!(stream.read_at_least(16, &mut _ipv6));
let mut _ipv6 = [0; 16];
try!(stream.read_exact(&mut _ipv6));
}
_ => return io_err(OtherIoError, "Invalid address type"),
_ => return io_err(ErrorKind::Other, "Invalid address type"),
}

let _port = try!(stream.read_be_u16());
let mut _port_slice : [u8; 2] = [0u8; 2];
try!(stream.read_exact(&mut _port_slice));
let _port_be : u16 = unsafe { mem::transmute(_port_slice) };
let _port = u16::from_be(_port_be);
Ok(stream)
}
0x01 => io_err(OtherIoError, "General failure"),
0x02 => io_err(OtherIoError, "Connection not allowed by ruleset"),
0x03 => io_err(ConnectionFailed, "Network unreachable"),
0x04 => io_err(ConnectionFailed, "Host unreachable"),
0x05 => io_err(ConnectionRefused, "Connection refused by destination"),
0x06 => io_err(ConnectionFailed, "TTL expired"),
0x07 => io_err(OtherIoError, "Protocol Error"),
0x08 => io_err(OtherIoError, "Address type not supported"),
_ => io_err(OtherIoError, "Unknown error")
0x01 => io_err(ErrorKind::Other, "General failure"),
0x02 => io_err(ErrorKind::Other, "Connection not allowed by ruleset"),
0x03 => io_err(ErrorKind::NotConnected, "Network unreachable"),
0x04 => io_err(ErrorKind::Other, "Host unreachable"),
0x05 => io_err(ErrorKind::ConnectionRefused, "Connection refused by destination"),
0x06 => io_err(ErrorKind::Other, "TTL expired"),
0x07 => io_err(ErrorKind::Other, "Protocol Error"),
0x08 => io_err(ErrorKind::Other, "Address type not supported"),
_ => io_err(ErrorKind::Other, "Unknown error")
}
}
}