Skip to content

Commit

Permalink
Merge branch 'master' into feature_gssapi_auth_support_socks5
Browse files Browse the repository at this point in the history
  • Loading branch information
Rohit Kulkarni committed Oct 6, 2024
2 parents 54b40d7 + 2eeb99c commit 27db782
Show file tree
Hide file tree
Showing 23 changed files with 831 additions and 127 deletions.
22 changes: 15 additions & 7 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,18 @@ jobs:
rust: [stable, beta, nightly]
runs-on: [ubuntu-latest]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: ${{ matrix.rust }}
profile: minimal
override: true
components: clippy
- name: Clippy
run: |
cargo clippy --all-targets --no-default-features --features=tokio -- -D warnings
cargo clippy --all-targets --no-default-features --features=tokio,tor -- -D warnings
cargo clippy --all-targets --no-default-features --features=futures-io -- -D warnings
cargo clippy --all-targets --no-default-features --features=futures-io,tor -- -D warnings
cargo clippy --all-targets --all-features -- -D warnings
- name: Install 3proxy
run: |
cd $HOME
Expand All @@ -27,9 +33,11 @@ jobs:
cd 3proxy-0.8.13 && ln -s Makefile.Linux Makefile && make -j$(nproc)
sudo apt-get update
sudo apt-get install socat -y
- name: Build
run: |
cargo build --examples --all-features
cargo build --verbose --all --all-features
cargo test --lib --verbose --all-features
- name: Run tests
run: |
cargo build --examples
cargo build --verbose --all
cargo test --lib --verbose
env PATH=$HOME/3proxy-0.8.13/src:$PATH tests/integration_tests.sh
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# 0.5.2

* Added SOCKS4 support `bind` and `connect`.
* `tokio` becomes an optional dependency and [`futures-io`](https://github.com/rust-lang/futures-rs/tree/0.3.30/futures-io) traits are supported through the `futures-io` feature.

# 0.5.1

Expand Down
16 changes: 12 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,34 @@ edition = "2018"
travis-ci = { repository = "sticnarf/tokio-socks" }

[features]
default = ["tokio"]
tor = []
gssapi = ["async-trait"]

[[example]]
name = "chainproxy"
required-features = ["tokio"]

[[example]]
name = "socket"
required-features = ["tor"]
required-features = ["tokio", "tor"]

[[example]]
name = "tor"
required-features = ["tor"]
required-features = ["tokio", "tor"]

[dependencies]
futures-util = { version = "0.3", default-features = false }
tokio = { version = "1.0", features = ["io-util", "net"] }
futures-io = { version = "0.3", optional = true }
tokio = { version = "1.0", features = ["io-util", "net"], optional = true }
either = "1"
thiserror = "1.0"
async-trait = { version = "0.1.81", optional = true}

[dev-dependencies]
futures-executor = "0.3"
futures-util = { version = "0.3", default-features = false, features = ["io"] }
tokio = { version = "1.0", features = ["io-util", "rt-multi-thread", "net"] }
once_cell = "1.2.0"
hyper = "0.14"
hyper = "0.14"
smol = "2.0.0"
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# tokio-socks

[![Build Status](https://travis-ci.org/sticnarf/tokio-socks.svg?branch=master)](https://travis-ci.org/sticnarf/tokio-socks)
[![Build Status](https://github.com/sticnarf/tokio-socks/actions/workflows/main.yml/badge.svg)](https://github.com/sticnarf/tokio-socks/actions)
[![Crates Version](https://img.shields.io/crates/v/tokio-socks.svg)](https://crates.io/crates/tokio-socks)
[![docs](https://docs.rs/tokio-socks/badge.svg)](https://docs.rs/tokio-socks)

Expand All @@ -17,6 +17,14 @@ Asynchronous SOCKS proxy support for Rust.
- [X] Chain proxies ([see example](examples/chainproxy.rs))
- [X] SOCKS4

## Compatibility with Other Async Runtimes

By default, the `tokio` feature is enabled, as the crate name suggests.

Users can opt out `tokio` by setting `default-features = false`. The `*_with_socket` functions accept types implementing the `AsyncSocket` trait.

The crate provides `io::Compat` that implements `AsyncSocket` for `futures-io` types (requiring the `futures-io` feature).

## License

This project is licensed under the MIT License - see the [LICENSE](/LICENSE) file for details.
Expand Down
2 changes: 1 addition & 1 deletion examples/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ async fn connect() -> Result<(), Error> {
}

fn main() {
let mut rt = Runtime::new().unwrap();
let rt = Runtime::new().unwrap();
rt.block_on(connect()).unwrap();
}
2 changes: 1 addition & 1 deletion examples/tor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ async fn connect() -> Result<(), Error> {
}

fn main() {
let mut rt = Runtime::new().unwrap();
let rt = Runtime::new().unwrap();
rt.block_on(connect()).unwrap();
}
5 changes: 2 additions & 3 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use_small_heuristics = "Default"
hard_tabs = false
imports_layout = "HorizontalVertical"
merge_imports = true
imports_granularity = "Crate"
group_imports = "StdExternalCrate"
match_block_trailing_comma = true
max_width = 120
newline_style = "Unix"
normalize_comments = true
reorder_imports = true
reorder_modules = true
reorder_impl_items = true
report_todo = "Never"
report_fixme = "Never"
space_after_colon = true
space_before_colon = false
struct_lit_single_line = true
Expand Down
43 changes: 43 additions & 0 deletions src/io/compat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#[cfg(feature = "futures-io")]
mod futures;

/// A compatibility layer for using non-tokio types with this crate.
///
/// Example:
/// ```no_run
/// use smol::net::unix::UnixStream;
/// use tokio_socks::{io::Compat, tcp::Socks5Stream};
/// let socket = Compat::new(UnixStream::connect(proxy_addr)
/// .await
/// .map_err(Error::Io)?); // Compat<UnixStream>
/// let conn =
/// Socks5Stream::connect_with_password_and_socket(socket, target, username, password).await?;
/// // Socks5Stream has implemented futures-io AsyncRead + AsyncWrite.
/// ```
pub struct Compat<S>(S);

#[cfg(feature = "futures-io")]
impl<S> Compat<S> {
pub fn new(inner: S) -> Self {
Compat(inner)
}

/// Consumes the `Compat``, returning the inner value.
pub fn into_inner(self) -> S {
self.0
}
}

#[cfg(feature = "futures-io")]
impl<S> AsRef<S> for Compat<S> {
fn as_ref(&self) -> &S {
&self.0
}
}

#[cfg(feature = "futures-io")]
impl<S> AsMut<S> for Compat<S> {
fn as_mut(&mut self) -> &mut S {
&mut self.0
}
}
46 changes: 46 additions & 0 deletions src/io/compat/futures.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use std::{
io::Result as IoResult,
pin::Pin,
task::{Context, Poll},
};

use futures_io::{AsyncRead, AsyncWrite};

use super::Compat;
use crate::io::AsyncSocket;

impl<S> AsyncSocket for Compat<S>
where S: AsyncRead + AsyncWrite + Unpin
{
fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<IoResult<usize>> {
AsyncRead::poll_read(Pin::new(&mut self.0), cx, buf)
}

fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<IoResult<usize>> {
AsyncWrite::poll_write(Pin::new(&mut self.0), cx, buf)
}
}

impl<S> AsyncRead for Compat<S>
where S: AsyncRead + Unpin
{
fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<IoResult<usize>> {
AsyncRead::poll_read(Pin::new(&mut self.0), cx, buf)
}
}

impl<S> AsyncWrite for Compat<S>
where S: AsyncWrite + Unpin
{
fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<IoResult<usize>> {
AsyncWrite::poll_write(Pin::new(&mut self.0), cx, buf)
}

fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<IoResult<()>> {
AsyncWrite::poll_flush(Pin::new(&mut self.0), cx)
}

fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<IoResult<()>> {
AsyncWrite::poll_close(Pin::new(&mut self.0), cx)
}
}
106 changes: 106 additions & 0 deletions src/io/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//! Asynchronous I/O abstractions for sockets.
#[cfg(feature = "tokio")]
mod tokio;

use std::{
future::Future,
io::{Error, ErrorKind},
mem,
pin::Pin,
task::{Context, Poll},
};

use futures_util::ready;

#[cfg(feature = "futures-io")]
mod compat;
#[cfg(feature = "futures-io")]
pub use compat::Compat;

/// A trait for asynchronous socket I/O.
///
/// Any type that implements tokio's `AsyncRead` and `AsyncWrite` traits
/// has implemented `AsyncSocket` trait.
///
/// Use `FuturesIoCompatExt` to wrap `futures-io` types as `AsyncSocket` types.
pub trait AsyncSocket {
fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize, Error>>;

fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize, Error>>;
}

pub(crate) trait AsyncSocketExt {
fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self>
where Self: Sized;

fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self>
where Self: Sized;
}

impl<S: AsyncSocket> AsyncSocketExt for S {
fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self>
where Self: Sized {
let capacity = buf.len();
ReadExact {
reader: self,
buf,
capacity,
}
}

fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self>
where Self: Sized {
WriteAll { writer: self, buf }
}
}

pub(crate) struct ReadExact<'a, R> {
reader: &'a mut R,
buf: &'a mut [u8],
capacity: usize,
}

impl<R: AsyncSocket + Unpin> Future for ReadExact<'_, R> {
type Output = Result<usize, Error>;

fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = &mut *self;
while !this.buf.is_empty() {
let n = ready!(Pin::new(&mut *this.reader).poll_read(cx, this.buf))?;
{
let (_, rest) = mem::take(&mut this.buf).split_at_mut(n);
this.buf = rest;
}
if n == 0 {
return Poll::Ready(Err(ErrorKind::UnexpectedEof.into()));
}
}
Poll::Ready(Ok(this.capacity))
}
}

pub(crate) struct WriteAll<'a, W> {
writer: &'a mut W,
buf: &'a [u8],
}

impl<W: AsyncSocket + Unpin> Future for WriteAll<'_, W> {
type Output = Result<(), Error>;

fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = &mut *self;
while !this.buf.is_empty() {
let n = ready!(Pin::new(&mut *this.writer).poll_write(cx, this.buf))?;
{
let (_, rest) = mem::take(&mut this.buf).split_at(n);
this.buf = rest;
}
if n == 0 {
return Poll::Ready(Err(ErrorKind::WriteZero.into()));
}
}

Poll::Ready(Ok(()))
}
}
26 changes: 26 additions & 0 deletions src/io/tokio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//! AsyncSocket trait implementation for tokio's AsyncRead + AsyncWrite
//! traits.
use std::{
io::Result as IoResult,
pin::Pin,
task::{Context, Poll},
};

use futures_util::ready;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};

use super::AsyncSocket;

impl<S> AsyncSocket for S
where S: AsyncRead + AsyncWrite
{
fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<IoResult<usize>> {
let mut buf = ReadBuf::new(buf);
ready!(AsyncRead::poll_read(self, cx, &mut buf))?;
Poll::Ready(Ok(buf.filled().len()))
}

fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<IoResult<usize>> {
AsyncWrite::poll_write(self, cx, buf)
}
}
Loading

0 comments on commit 27db782

Please sign in to comment.