Skip to main content

rusty_sockslib/
lib.rs

1#![warn(rust_2018_idioms)]
2#![warn(clippy::all)]
3
4pub mod buffer_pool;
5pub mod config;
6pub mod connection;
7pub mod copy_pump;
8pub mod handshake;
9pub mod helpers;
10pub mod request;
11
12use tokio::net::TcpListener;
13use tracing::{info, warn};
14
15use crate::buffer_pool::BufferPool;
16use crate::config::Config;
17use crate::connection::Connection;
18use crate::helpers::{Helpers, Res};
19
20/// Resolve the listen address from `config`, bind it, log the configuration, and serve forever.
21pub async fn run(config: Config) -> Res<()> {
22    let listen_ip = config.listen_ip()?;
23
24    info!("Version:      {}", env!("CARGO_PKG_VERSION"));
25    info!("Listen IP:    {}", listen_ip);
26    info!("Endpoint IP:  {}", config.endpoint_ip()?);
27    info!("Port:         {}", config.port);
28    info!("Buffer Size:  {}", config.buffer_size);
29    info!("Read Timeout: {}", config.read_timeout);
30    info!("Accept CIDR:  {}", config.accept_cidr);
31
32    let listener = TcpListener::bind(format!("{}:{}", listen_ip, config.port)).await?;
33    info!("Listening on tcp://{}:{} ...", listen_ip, config.port);
34
35    serve(listener, config).await
36}
37
38/// Serve SOCKS5 connections on an already-bound `listener`. Split out from [`run`] so tests can
39/// drive the proxy against an ephemeral loopback port.
40pub async fn serve(listener: TcpListener, config: Config) -> Res<()> {
41    let endpoint_ip = config.endpoint_ip()?;
42    let cidr = Helpers::parse_cidr(&config.accept_cidr)?;
43    let cidr_is_trivial = cidr.is_trivial();
44
45    // Create a buffer pool (doubled so that each half of the connection achieves the desired size).
46    let mut pool = BufferPool::new(2 * config.buffer_size);
47
48    loop {
49        let (stream, _) = listener.accept().await?;
50        let remote_ip = stream.peer_addr()?.ip();
51
52        // Drop connections that do not match the accept CIDR.
53        if !cidr_is_trivial && !Helpers::is_ip_in_cidr(&remote_ip, &cidr)? {
54            warn!("Request from {} does not match {}: dropping connection.", remote_ip, config.accept_cidr);
55            drop(stream);
56            continue;
57        }
58
59        Connection::from(stream, endpoint_ip.clone(), pool.lease(), config.read_timeout).handle();
60    }
61}