initial commit
This commit is contained in:
commit
e80d4caec9
9 changed files with 845 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
431
Cargo.lock
generated
Normal file
431
Cargo.lock
generated
Normal file
|
@ -0,0 +1,431 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.159"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_test"
|
||||
version = "1.0.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f901ee573cab6b3060453d2d5f0bae4e6d628c23c0a962ff9b5f1d7c8d4f1ed"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smp-socks"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_test",
|
||||
"socks5-server",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socks5-proto"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d91431c4672e25e372ef46bc554be8f315068c03608f99267a71ad32a12e8c4"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socks5-server"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5223c26981806584cc38c74fddf58808dbdcf4724890471ced69e7a2e8d86345"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"socks5-proto",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.40.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "smp-socks"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
socks5-server = "0.10.1"
|
||||
thiserror = "1.0.64"
|
||||
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread", "io-util"] }
|
||||
toml = { version = "0.8.19", default-features = false, features = ["parse"] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_test = "1.0.177"
|
31
src/config.rs
Normal file
31
src/config.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use std::{collections::HashSet, io::Read, net::Ipv4Addr};
|
||||
|
||||
use crate::{error::AppResult, serde_addr::TargetAddr};
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct Config {
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub local: HashSet<TargetAddr>,
|
||||
pub remote: HashSet<TargetAddr>,
|
||||
}
|
||||
|
||||
pub fn parse() -> AppResult<Config> {
|
||||
if let Some(mut file) = std::env::var_os("CONFIG")
|
||||
.and_then(|path| std::fs::File::open(path).ok())
|
||||
.or_else(|| std::fs::File::open("config.toml").ok())
|
||||
{
|
||||
let mut buf = String::new();
|
||||
file.read_to_string(&mut buf)?;
|
||||
Ok(toml::from_str(&buf)?)
|
||||
} else {
|
||||
let mut local = HashSet::new();
|
||||
local.insert((Ipv4Addr::new(127, 0, 0, 1).into(), 5232).into());
|
||||
Ok(Config {
|
||||
host: "0.0.0.0".to_owned(),
|
||||
port: 5233,
|
||||
local,
|
||||
remote: HashSet::new(),
|
||||
})
|
||||
}
|
||||
}
|
79
src/error.rs
Normal file
79
src/error.rs
Normal file
|
@ -0,0 +1,79 @@
|
|||
use socks5_server::proto::Error as SocksProtoError;
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
pub type AppResult<T> = core::result::Result<T, AppError>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AppError {
|
||||
IoError(std::io::Error),
|
||||
InvalidConfig(toml::de::Error),
|
||||
SocksError(SocksProtoError),
|
||||
DataNotUtf8(std::string::FromUtf8Error),
|
||||
ThirdPartyHost,
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for AppError {
|
||||
#[inline]
|
||||
fn from(value: std::io::Error) -> Self {
|
||||
Self::IoError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<toml::de::Error> for AppError {
|
||||
#[inline]
|
||||
fn from(value: toml::de::Error) -> Self {
|
||||
Self::InvalidConfig(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SocksProtoError> for AppError {
|
||||
#[inline]
|
||||
fn from(value: SocksProtoError) -> Self {
|
||||
Self::SocksError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::string::FromUtf8Error> for AppError {
|
||||
#[inline]
|
||||
fn from(value: std::string::FromUtf8Error) -> Self {
|
||||
Self::DataNotUtf8(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub type HandlerResult<T> = core::result::Result<T, HandlerError>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HandlerError {
|
||||
inner: AppError,
|
||||
stream: TcpStream,
|
||||
}
|
||||
|
||||
impl From<(AppError, TcpStream)> for HandlerError {
|
||||
#[inline]
|
||||
fn from(value: (AppError, TcpStream)) -> Self {
|
||||
Self {
|
||||
inner: value.0,
|
||||
stream: value.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(SocksProtoError, TcpStream)> for HandlerError {
|
||||
#[inline]
|
||||
fn from(value: (SocksProtoError, TcpStream)) -> Self {
|
||||
Self {
|
||||
inner: AppError::SocksError(value.0),
|
||||
stream: value.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(std::io::Error, TcpStream)> for HandlerError {
|
||||
#[inline]
|
||||
fn from(value: (std::io::Error, TcpStream)) -> Self {
|
||||
Self {
|
||||
inner: AppError::IoError(value.0),
|
||||
stream: value.1,
|
||||
}
|
||||
}
|
||||
}
|
58
src/handler.rs
Normal file
58
src/handler.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
config::Config,
|
||||
error::{AppError, HandlerError, HandlerResult},
|
||||
};
|
||||
|
||||
use socks5_server::{
|
||||
connection::state::NeedAuthenticate,
|
||||
proto::{Address, Reply},
|
||||
Command, IncomingConnection,
|
||||
};
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
pub async fn handler(
|
||||
conn: IncomingConnection<(), NeedAuthenticate>,
|
||||
config: Arc<Config>,
|
||||
) -> HandlerResult<()> {
|
||||
let conn = conn.authenticate().await?.0;
|
||||
|
||||
match conn.wait().await? {
|
||||
Command::Associate(cmd, _addr) => {
|
||||
let _ = cmd
|
||||
.reply(Reply::CommandNotSupported, Address::unspecified())
|
||||
.await?
|
||||
.close()
|
||||
.await;
|
||||
}
|
||||
|
||||
Command::Bind(cmd, _addr) => {
|
||||
let _ = cmd
|
||||
.reply(Reply::CommandNotSupported, Address::unspecified())
|
||||
.await?
|
||||
.close()
|
||||
.await;
|
||||
}
|
||||
|
||||
Command::Connect(cmd, addr) => {
|
||||
let target = match addr {
|
||||
Address::DomainAddress(host, port) => {
|
||||
let Ok(host) = String::from_utf8(host) else {
|
||||
let conn = cmd
|
||||
.reply(Reply::GeneralFailure, Address::unspecified())
|
||||
.await?
|
||||
.into_inner();
|
||||
return Err(
|
||||
(std::io::Error::from(std::io::ErrorKind::InvalidData), conn).into(),
|
||||
);
|
||||
};
|
||||
TcpStream::connect((host.as_ref(), port)).await
|
||||
}
|
||||
Address::SocketAddress(addr) => TcpStream::connect(addr).await,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
9
src/host.rs
Normal file
9
src/host.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use crate::{config::Config, error::AppResult};
|
||||
|
||||
pub fn parse_str(value: Vec<u8>, config: Arc<Config>) -> AppResult<SocketAddr> {
|
||||
let host = String::from_utf8(value)?;
|
||||
|
||||
//
|
||||
}
|
36
src/main.rs
Normal file
36
src/main.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
mod config;
|
||||
mod error;
|
||||
mod handler;
|
||||
// mod host;
|
||||
mod serde_addr;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use error::AppResult;
|
||||
use handler::handler;
|
||||
|
||||
use socks5_server as socks;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> AppResult<()> {
|
||||
let config = Arc::new(config::parse()?);
|
||||
let auth = Arc::new(socks::auth::NoAuth);
|
||||
|
||||
let listener = TcpListener::bind((config.host.as_str(), config.port)).await?;
|
||||
let server = socks::Server::new(listener, auth);
|
||||
|
||||
while let Ok((conn, _addr)) = server.accept().await {
|
||||
let config = config.clone();
|
||||
tokio::spawn(async move {
|
||||
match handler(conn, config).await {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
//
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
186
src/serde_addr.rs
Normal file
186
src/serde_addr.rs
Normal file
|
@ -0,0 +1,186 @@
|
|||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs};
|
||||
|
||||
use serde::{de::Visitor, Deserialize};
|
||||
|
||||
pub const DEFAULT_PORT: u16 = 5232;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum TargetAddr {
|
||||
DomainName(String, u16),
|
||||
IpAddress(SocketAddr),
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for TargetAddr {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_str(AddrVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSocketAddrs for TargetAddr {
|
||||
type Iter = AddrIter;
|
||||
|
||||
fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
|
||||
Ok(match self {
|
||||
Self::DomainName(host, port) => (host.as_str(), *port).to_socket_addrs()?.into(),
|
||||
Self::IpAddress(sockaddr) => sockaddr.clone().into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(IpAddr, u16)> for TargetAddr {
|
||||
#[inline]
|
||||
fn from((addr, port): (IpAddr, u16)) -> Self {
|
||||
Self::IpAddress(SocketAddr::new(addr, port))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AddrVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for AddrVisitor {
|
||||
type Value = TargetAddr;
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
let v = v.trim();
|
||||
|
||||
let ipv6 = v.starts_with('[');
|
||||
let mut port_idx = v.len();
|
||||
|
||||
let port = if ipv6 {
|
||||
v.rfind("]:").and_then(|pos| {
|
||||
port_idx = pos;
|
||||
v.get(pos + 2..)
|
||||
})
|
||||
} else {
|
||||
v.rfind(':').and_then(|pos| {
|
||||
port_idx = pos;
|
||||
v.get(pos + 1..)
|
||||
})
|
||||
}
|
||||
.and_then(|port| {
|
||||
port.parse::<u16>()
|
||||
.map_err(|_| {
|
||||
port_idx = v.len();
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
.unwrap_or(DEFAULT_PORT);
|
||||
|
||||
let host = if ipv6 {
|
||||
v.get(1..port_idx)
|
||||
} else {
|
||||
v.get(..port_idx)
|
||||
}
|
||||
.ok_or(serde::de::Error::missing_field("host"))?;
|
||||
|
||||
if ipv6 {
|
||||
let addr = IpAddr::V6(host.parse().map_err(serde::de::Error::custom)?);
|
||||
Ok((addr, port).into())
|
||||
} else {
|
||||
if let Ok(addr) = host.parse::<Ipv4Addr>() {
|
||||
Ok((IpAddr::V4(addr), port).into())
|
||||
} else if let Ok(addr) = host.parse::<Ipv6Addr>() {
|
||||
Ok((IpAddr::V6(addr), port).into())
|
||||
} else {
|
||||
Ok(TargetAddr::DomainName(host.to_owned(), port))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
concat!(
|
||||
"host:port ",
|
||||
"where host is a domain or an ipv4/ipv6 addr, ",
|
||||
"and optional port is u16",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AddrIter {
|
||||
HostResolved(std::vec::IntoIter<SocketAddr>),
|
||||
OneAddress(std::iter::Once<SocketAddr>),
|
||||
}
|
||||
|
||||
impl Iterator for AddrIter {
|
||||
type Item = SocketAddr;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
Self::HostResolved(iter) => iter.next(),
|
||||
Self::OneAddress(iter) => iter.next(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::vec::IntoIter<SocketAddr>> for AddrIter {
|
||||
#[inline]
|
||||
fn from(value: std::vec::IntoIter<SocketAddr>) -> Self {
|
||||
Self::HostResolved(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SocketAddr> for AddrIter {
|
||||
#[inline]
|
||||
fn from(value: SocketAddr) -> Self {
|
||||
Self::OneAddress(std::iter::once(value))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
use serde_test::{assert_de_tokens, Token};
|
||||
|
||||
use super::{TargetAddr, DEFAULT_PORT};
|
||||
|
||||
const LOCALHOST_V4: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
|
||||
const TEST_V6: IpAddr = IpAddr::V6(Ipv6Addr::new(
|
||||
0xfe80, 0x0, 0x0, 0x0, 0x721e, 0xd21f, 0x29a3, 0xf396,
|
||||
));
|
||||
|
||||
#[test]
|
||||
pub fn ipv4_without_port() {
|
||||
let exp: TargetAddr = (LOCALHOST_V4, DEFAULT_PORT).into();
|
||||
assert_de_tokens(&exp, &[Token::Str("127.0.0.1")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn ipv4_with_port() {
|
||||
let exp: TargetAddr = (LOCALHOST_V4, 443).into();
|
||||
assert_de_tokens(&exp, &[Token::Str("127.0.0.1:443")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn ipv6_without_port() {
|
||||
let exp: TargetAddr = (TEST_V6, DEFAULT_PORT).into();
|
||||
assert_de_tokens(&exp, &[Token::Str("fe80::721e:d21f:29a3:f396")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn ipv6_with_port() {
|
||||
let exp: TargetAddr = (TEST_V6, 443).into();
|
||||
assert_de_tokens(&exp, &[Token::Str("[fe80::721e:d21f:29a3:f396]:443")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn domain_without_port() {
|
||||
let exp = TargetAddr::DomainName("smp.dc09.ru".to_owned(), DEFAULT_PORT);
|
||||
assert_de_tokens(&exp, &[Token::Str("smp.dc09.ru")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn domain_with_port() {
|
||||
let exp = TargetAddr::DomainName("xftp.dc09.ru".to_owned(), 443);
|
||||
assert_de_tokens(&exp, &[Token::Str("xftp.dc09.ru:443")]);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue