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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ description = "SOCKS proxy clients"
repository = "https://github.com/sfackler/rust-socks"
documentation = "https://docs.rs/socks/0.3.0/socks"
readme = "README.md"
edition = "2018"

[dependencies]
byteorder = "1.0"
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# rust-socks
[![Build Status](https://travis-ci.org/sfackler/rust-socks.svg?branch=master)](https://travis-ci.org/sfackler/rust-socks)
[![Crates.io](https://img.shields.io/crates/v/socks.svg)](https://crates.io/crates/socks)

[Documentation](https://docs.rs/socks/0.2.3/socks)
[Documentation](https://docs.rs/socks/0.3.2/socks)

SOCKS proxy support for Rust.

Expand Down
32 changes: 17 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
//! SOCKS proxy clients
#![doc(html_root_url="https://docs.rs/socks/0.3.0")]
#![doc(html_root_url = "https://docs.rs/socks/0.3.0")]
#![warn(missing_docs)]

extern crate byteorder;

#[cfg(unix)]
extern crate libc;
#[cfg(windows)]
extern crate winapi;
#[cfg(windows)]
extern crate ws2_32;

use std::io;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
use std::vec;

pub use v4::{Socks4Stream, Socks4Listener};
pub use v5::{Socks5Stream, Socks5Listener, Socks5Datagram};
pub use crate::v4::{Socks4Listener, Socks4Stream};
pub use crate::v5::{Socks5Datagram, Socks5Listener, Socks5Stream};

mod v4;
mod v5;
Expand Down Expand Up @@ -141,20 +132,31 @@ impl<'a> ToTargetAddr for &'a str {
let port_str = match parts_iter.next() {
Some(s) => s,
None => {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid socket address"))
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid socket address",
))
}
};

let host = match parts_iter.next() {
Some(s) => s,
None => {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid socket address"))
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid socket address",
))
}
};

let port: u16 = match port_str.parse() {
Ok(p) => p,
Err(_) => return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid port value")),
Err(_) => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid port value",
))
}
};

(host, port).to_target_addr()
Expand Down
72 changes: 46 additions & 26 deletions src/v4.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,49 @@
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{self, Read, Write};
use std::net::{SocketAddr, ToSocketAddrs, SocketAddrV4, SocketAddrV6, TcpStream, Ipv4Addr};
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6, TcpStream, ToSocketAddrs};

use {ToTargetAddr, TargetAddr};
use crate::{TargetAddr, ToTargetAddr};

fn read_response(socket: &mut TcpStream) -> io::Result<SocketAddrV4> {
let mut response = [0u8; 8];
socket.read_exact(&mut response)?;
let mut response = &response[..];

if response.read_u8()? != 0 {
return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid response version"));
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid response version",
));
}

match response.read_u8()? {
90 => {}
91 => return Err(io::Error::new(io::ErrorKind::Other, "request rejected or failed")),
91 => {
return Err(io::Error::new(
io::ErrorKind::Other,
"request rejected or failed",
))
}
92 => {
return Err(io::Error::new(io::ErrorKind::PermissionDenied,
"request rejected because SOCKS server cannot connect to \
idnetd on the client"))
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"request rejected because SOCKS server cannot connect to \
idnetd on the client",
))
}
93 => {
return Err(io::Error::new(io::ErrorKind::PermissionDenied,
"request rejected because the client program and identd \
report different user-ids"))
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"request rejected because the client program and identd \
report different user-ids",
))
}
_ => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid response code",
))
}
_ => return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid response code")),
}

let port = response.read_u16::<BigEndian>()?;
Expand All @@ -52,15 +69,17 @@ impl Socks4Stream {
/// server does not support SOCKS4A, consider performing the DNS lookup
/// locally and passing a `TargetAddr::Ip`.
pub fn connect<T, U>(proxy: T, target: U, userid: &str) -> io::Result<Socks4Stream>
where T: ToSocketAddrs,
U: ToTargetAddr
where
T: ToSocketAddrs,
U: ToTargetAddr,
{
Self::connect_raw(1, proxy, target, userid)
}

fn connect_raw<T, U>(command: u8, proxy: T, target: U, userid: &str) -> io::Result<Socks4Stream>
where T: ToSocketAddrs,
U: ToTargetAddr
where
T: ToSocketAddrs,
U: ToTargetAddr,
{
let mut socket = TcpStream::connect(proxy)?;

Expand All @@ -74,8 +93,10 @@ impl Socks4Stream {
let addr = match addr {
SocketAddr::V4(addr) => addr,
SocketAddr::V6(_) => {
return Err(io::Error::new(io::ErrorKind::InvalidInput,
"SOCKS4 does not support IPv6"));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"SOCKS4 does not support IPv6",
));
}
};
let _ = packet.write_u16::<BigEndian>(addr.port());
Expand Down Expand Up @@ -166,8 +187,9 @@ impl Socks4Listener {
/// The proxy will filter incoming connections based on the value of
/// `target`.
pub fn bind<T, U>(proxy: T, target: U, userid: &str) -> io::Result<Socks4Listener>
where T: ToSocketAddrs,
U: ToTargetAddr
where
T: ToSocketAddrs,
U: ToTargetAddr,
{
Socks4Stream::connect_raw(2, proxy, target, userid).map(Socks4Listener)
}
Expand Down Expand Up @@ -202,19 +224,17 @@ impl Socks4Listener {
#[cfg(test)]
mod test {
use std::io::{Read, Write};
use std::net::{SocketAddr, SocketAddrV4, ToSocketAddrs, TcpStream};
use std::net::{SocketAddr, SocketAddrV4, TcpStream, ToSocketAddrs};

use super::*;

fn google_ip() -> SocketAddrV4 {
"google.com:80"
.to_socket_addrs()
.unwrap()
.filter_map(|a| {
match a {
SocketAddr::V4(a) => Some(a),
SocketAddr::V6(_) => None,
}
.filter_map(|a| match a {
SocketAddr::V4(a) => Some(a),
SocketAddr::V6(_) => None,
})
.next()
.unwrap()
Expand Down
Loading