mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-17 03:15:37 +00:00
ae54a872ce
usage: --socks5 12345 create an socks5 server on port 12345, can be used by socks5 client to access virtual network.
245 lines
7.8 KiB
Rust
245 lines
7.8 KiB
Rust
use crate::gateway::fast_socks5::consts;
|
|
use crate::gateway::fast_socks5::consts::SOCKS5_ADDR_TYPE_IPV4;
|
|
use crate::gateway::fast_socks5::SocksError;
|
|
use crate::read_exact;
|
|
|
|
use anyhow::Context;
|
|
use std::fmt;
|
|
use std::io;
|
|
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
|
use std::vec::IntoIter;
|
|
use thiserror::Error;
|
|
use tokio::io::{AsyncRead, AsyncReadExt};
|
|
use tokio::net::lookup_host;
|
|
|
|
use tracing::{debug, error};
|
|
|
|
/// SOCKS5 reply code
|
|
#[derive(Error, Debug)]
|
|
pub enum AddrError {
|
|
#[error("DNS Resolution failed")]
|
|
DNSResolutionFailed,
|
|
#[error("Can't read IPv4")]
|
|
IPv4Unreadable,
|
|
#[error("Can't read IPv6")]
|
|
IPv6Unreadable,
|
|
#[error("Can't read port number")]
|
|
PortNumberUnreadable,
|
|
#[error("Can't read domain len")]
|
|
DomainLenUnreadable,
|
|
#[error("Can't read Domain content")]
|
|
DomainContentUnreadable,
|
|
#[error("Malformed UTF-8")]
|
|
Utf8,
|
|
#[error("Unknown address type")]
|
|
IncorrectAddressType,
|
|
#[error("{0}")]
|
|
Custom(String),
|
|
}
|
|
|
|
/// A description of a connection target.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub enum TargetAddr {
|
|
/// Connect to an IP address.
|
|
Ip(SocketAddr),
|
|
/// Connect to a fully qualified domain name.
|
|
///
|
|
/// The domain name will be passed along to the proxy server and DNS lookup
|
|
/// will happen there.
|
|
Domain(String, u16),
|
|
}
|
|
|
|
impl TargetAddr {
|
|
pub async fn resolve_dns(self) -> anyhow::Result<TargetAddr> {
|
|
match self {
|
|
TargetAddr::Ip(ip) => Ok(TargetAddr::Ip(ip)),
|
|
TargetAddr::Domain(domain, port) => {
|
|
debug!("Attempt to DNS resolve the domain {}...", &domain);
|
|
|
|
let socket_addr = lookup_host((&domain[..], port))
|
|
.await
|
|
.context(AddrError::DNSResolutionFailed)?
|
|
.next()
|
|
.ok_or(AddrError::Custom(
|
|
"Can't fetch DNS to the domain.".to_string(),
|
|
))?;
|
|
debug!("domain name resolved to {}", socket_addr);
|
|
|
|
// has been converted to an ip
|
|
Ok(TargetAddr::Ip(socket_addr))
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn is_ip(&self) -> bool {
|
|
match self {
|
|
TargetAddr::Ip(_) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn is_domain(&self) -> bool {
|
|
!self.is_ip()
|
|
}
|
|
|
|
pub fn to_be_bytes(&self) -> anyhow::Result<Vec<u8>> {
|
|
let mut buf = vec![];
|
|
match self {
|
|
TargetAddr::Ip(SocketAddr::V4(addr)) => {
|
|
debug!("TargetAddr::IpV4");
|
|
|
|
buf.extend_from_slice(&[SOCKS5_ADDR_TYPE_IPV4]);
|
|
|
|
debug!("addr ip {:?}", (*addr.ip()).octets());
|
|
buf.extend_from_slice(&(addr.ip()).octets()); // ip
|
|
buf.extend_from_slice(&addr.port().to_be_bytes()); // port
|
|
}
|
|
TargetAddr::Ip(SocketAddr::V6(addr)) => {
|
|
debug!("TargetAddr::IpV6");
|
|
buf.extend_from_slice(&[consts::SOCKS5_ADDR_TYPE_IPV6]);
|
|
|
|
debug!("addr ip {:?}", (*addr.ip()).octets());
|
|
buf.extend_from_slice(&(addr.ip()).octets()); // ip
|
|
buf.extend_from_slice(&addr.port().to_be_bytes()); // port
|
|
}
|
|
TargetAddr::Domain(ref domain, port) => {
|
|
debug!("TargetAddr::Domain");
|
|
if domain.len() > u8::max_value() as usize {
|
|
return Err(SocksError::ExceededMaxDomainLen(domain.len()).into());
|
|
}
|
|
buf.extend_from_slice(&[consts::SOCKS5_ADDR_TYPE_DOMAIN_NAME, domain.len() as u8]);
|
|
buf.extend_from_slice(domain.as_bytes()); // domain content
|
|
buf.extend_from_slice(&port.to_be_bytes());
|
|
// port content (.to_be_bytes() convert from u16 to u8 type)
|
|
}
|
|
}
|
|
Ok(buf)
|
|
}
|
|
}
|
|
|
|
// async-std ToSocketAddrs doesn't supports external trait implementation
|
|
// @see https://github.com/async-rs/async-std/issues/539
|
|
impl std::net::ToSocketAddrs for TargetAddr {
|
|
type Iter = IntoIter<SocketAddr>;
|
|
|
|
fn to_socket_addrs(&self) -> io::Result<IntoIter<SocketAddr>> {
|
|
match *self {
|
|
TargetAddr::Ip(addr) => Ok(vec![addr].into_iter()),
|
|
TargetAddr::Domain(_, _) => Err(io::Error::new(
|
|
io::ErrorKind::Other,
|
|
"Domain name has to be explicitly resolved, please use TargetAddr::resolve_dns().",
|
|
)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for TargetAddr {
|
|
#[inline]
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
match *self {
|
|
TargetAddr::Ip(ref addr) => write!(f, "{}", addr),
|
|
TargetAddr::Domain(ref addr, ref port) => write!(f, "{}:{}", addr, port),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A trait for objects that can be converted to `TargetAddr`.
|
|
pub trait ToTargetAddr {
|
|
/// Converts the value of `self` to a `TargetAddr`.
|
|
fn to_target_addr(&self) -> io::Result<TargetAddr>;
|
|
}
|
|
|
|
impl<'a> ToTargetAddr for (&'a str, u16) {
|
|
fn to_target_addr(&self) -> io::Result<TargetAddr> {
|
|
// try to parse as an IP first
|
|
if let Ok(addr) = self.0.parse::<Ipv4Addr>() {
|
|
return (addr, self.1).to_target_addr();
|
|
}
|
|
|
|
if let Ok(addr) = self.0.parse::<Ipv6Addr>() {
|
|
return (addr, self.1).to_target_addr();
|
|
}
|
|
|
|
Ok(TargetAddr::Domain(self.0.to_owned(), self.1))
|
|
}
|
|
}
|
|
|
|
impl ToTargetAddr for SocketAddr {
|
|
fn to_target_addr(&self) -> io::Result<TargetAddr> {
|
|
Ok(TargetAddr::Ip(*self))
|
|
}
|
|
}
|
|
|
|
impl ToTargetAddr for SocketAddrV4 {
|
|
fn to_target_addr(&self) -> io::Result<TargetAddr> {
|
|
SocketAddr::V4(*self).to_target_addr()
|
|
}
|
|
}
|
|
|
|
impl ToTargetAddr for SocketAddrV6 {
|
|
fn to_target_addr(&self) -> io::Result<TargetAddr> {
|
|
SocketAddr::V6(*self).to_target_addr()
|
|
}
|
|
}
|
|
|
|
impl ToTargetAddr for (Ipv4Addr, u16) {
|
|
fn to_target_addr(&self) -> io::Result<TargetAddr> {
|
|
SocketAddrV4::new(self.0, self.1).to_target_addr()
|
|
}
|
|
}
|
|
|
|
impl ToTargetAddr for (Ipv6Addr, u16) {
|
|
fn to_target_addr(&self) -> io::Result<TargetAddr> {
|
|
SocketAddrV6::new(self.0, self.1, 0, 0).to_target_addr()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum Addr {
|
|
V4([u8; 4]),
|
|
V6([u8; 16]),
|
|
Domain(String), // Vec<[u8]> or Box<[u8]> or String ?
|
|
}
|
|
|
|
/// This function is used by the client & the server
|
|
pub async fn read_address<T: AsyncRead + Unpin>(
|
|
stream: &mut T,
|
|
atyp: u8,
|
|
) -> anyhow::Result<TargetAddr> {
|
|
let addr = match atyp {
|
|
consts::SOCKS5_ADDR_TYPE_IPV4 => {
|
|
debug!("Address type `IPv4`");
|
|
Addr::V4(read_exact!(stream, [0u8; 4]).context(AddrError::IPv4Unreadable)?)
|
|
}
|
|
consts::SOCKS5_ADDR_TYPE_IPV6 => {
|
|
debug!("Address type `IPv6`");
|
|
Addr::V6(read_exact!(stream, [0u8; 16]).context(AddrError::IPv6Unreadable)?)
|
|
}
|
|
consts::SOCKS5_ADDR_TYPE_DOMAIN_NAME => {
|
|
debug!("Address type `domain`");
|
|
let len = read_exact!(stream, [0]).context(AddrError::DomainLenUnreadable)?[0];
|
|
let domain = read_exact!(stream, vec![0u8; len as usize])
|
|
.context(AddrError::DomainContentUnreadable)?;
|
|
// make sure the bytes are correct utf8 string
|
|
let domain = String::from_utf8(domain).context(AddrError::Utf8)?;
|
|
|
|
Addr::Domain(domain)
|
|
}
|
|
_ => return Err(anyhow::anyhow!(AddrError::IncorrectAddressType)),
|
|
};
|
|
|
|
// Find port number
|
|
let port = read_exact!(stream, [0u8; 2]).context(AddrError::PortNumberUnreadable)?;
|
|
// Convert (u8 * 2) into u16
|
|
let port = (port[0] as u16) << 8 | port[1] as u16;
|
|
|
|
// Merge ADDRESS + PORT into a TargetAddr
|
|
let addr: TargetAddr = match addr {
|
|
Addr::V4([a, b, c, d]) => (Ipv4Addr::new(a, b, c, d), port).to_target_addr()?,
|
|
Addr::V6(x) => (Ipv6Addr::from(x), port).to_target_addr()?,
|
|
Addr::Domain(domain) => TargetAddr::Domain(domain, port),
|
|
};
|
|
|
|
Ok(addr)
|
|
}
|