1
0

fix error on setting dual-stack socket

This commit is contained in:
EAimTY 2023-05-26 21:01:56 +09:00
parent 9a61db3c94
commit 5dd3c920e4
5 changed files with 150 additions and 67 deletions

View File

@ -88,13 +88,20 @@ impl Endpoint {
config.transport_config(Arc::new(tp_cfg)); config.transport_config(Arc::new(tp_cfg));
let socket = UdpSocket::bind(SocketAddr::from(([0, 0, 0, 0], 0)))?; // Try to create an IPv4 socket as the placeholder first, if it fails, try IPv6.
let socket = UdpSocket::bind(SocketAddr::from((Ipv4Addr::UNSPECIFIED, 0)))
.or_else(|err| {
UdpSocket::bind(SocketAddr::from((Ipv6Addr::UNSPECIFIED, 0))).map_err(|_| err)
})
.map_err(|err| Error::Socket("failed to create endpoint UDP socket", err))?;
let mut ep = QuinnEndpoint::new( let mut ep = QuinnEndpoint::new(
EndpointConfig::default(), EndpointConfig::default(),
None, None,
socket, socket,
Arc::new(TokioRuntime), Arc::new(TokioRuntime),
)?; )?;
ep.set_default_client_config(config); ep.set_default_client_config(config);
let ep = Self { let ep = Self {
@ -139,7 +146,12 @@ impl Endpoint {
SocketAddr::from((Ipv6Addr::UNSPECIFIED, 0)) SocketAddr::from((Ipv6Addr::UNSPECIFIED, 0))
}; };
ep.rebind(UdpSocket::bind(bind_addr)?)?; ep.rebind(
UdpSocket::bind(bind_addr).map_err(|err| {
Error::Socket("failed to create endpoint UDP socket", err)
})?,
)
.map_err(|err| Error::Socket("failed to rebind endpoint UDP socket", err))?;
} }
let conn = ep.connect(addr, server_name)?; let conn = ep.connect(addr, server_name)?;

View File

@ -68,6 +68,8 @@ pub enum Error {
LoadNativeCerts(IoError), LoadNativeCerts(IoError),
#[error(transparent)] #[error(transparent)]
Rustls(#[from] RustlsError), Rustls(#[from] RustlsError),
#[error("{0}: {1}")]
Socket(&'static str, IoError),
#[error("timeout establishing connection")] #[error("timeout establishing connection")]
Timeout, Timeout,
#[error("cannot resolve the server name")] #[error("cannot resolve the server name")]

View File

@ -3,7 +3,7 @@ use bytes::Bytes;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use parking_lot::Mutex; use parking_lot::Mutex;
use quinn::VarInt; use quinn::VarInt;
use socket2::Socket; use socket2::{Domain, Protocol, SockAddr, Socket, Type};
use socks5_proto::{Address, Reply}; use socks5_proto::{Address, Reply};
use socks5_server::{ use socks5_server::{
auth::{NoAuth, Password}, auth::{NoAuth, Password},
@ -30,7 +30,6 @@ static SERVER: OnceCell<Server> = OnceCell::new();
pub struct Server { pub struct Server {
inner: Socks5Server, inner: Socks5Server,
addr: SocketAddr,
dual_stack: Option<bool>, dual_stack: Option<bool>,
max_pkt_size: usize, max_pkt_size: usize,
next_assoc_id: AtomicU16, next_assoc_id: AtomicU16,
@ -40,22 +39,38 @@ pub struct Server {
impl Server { impl Server {
pub async fn set_config(cfg: Local) -> Result<(), Error> { pub async fn set_config(cfg: Local) -> Result<(), Error> {
let socket = { let socket = {
let socket = Socket::from(TcpListener::bind(&cfg.server).await?.into_std()?); let domain = match cfg.server {
SocketAddr::V4(_) => Domain::IPV4,
SocketAddr::V6(_) => Domain::IPV6,
};
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))
.map_err(|err| Error::Socket("failed to create socks5 server socket", err))?;
if let Some(dual_stack) = cfg.dual_stack { if let Some(dual_stack) = cfg.dual_stack {
if cfg.server.is_ipv4() && dual_stack { socket.set_only_v6(!dual_stack).map_err(|err| {
return Err(Error::from(IoError::new( Error::Socket("socks5 server dual-stack socket setting error", err)
ErrorKind::Unsupported, })?;
"IPv4 socket cannot be dual stack",
)));
} }
socket.set_only_v6(!dual_stack)?; socket.set_reuse_address(true).map_err(|err| {
} Error::Socket("failed to set socks5 server socket to reuse_address", err)
})?;
socket.set_reuse_address(true)?; socket.set_nonblocking(true).map_err(|err| {
Error::Socket("failed setting socks5 server socket as non-blocking", err)
})?;
TcpListener::from_std(StdTcpListener::from(socket))? socket
.bind(&SockAddr::from(cfg.server))
.map_err(|err| Error::Socket("failed to bind socks5 server socket", err))?;
socket
.listen(i32::MAX)
.map_err(|err| Error::Socket("failed to listen on socks5 server socket", err))?;
TcpListener::from_std(StdTcpListener::from(socket))
.map_err(|err| Error::Socket("failed to create socks5 server socket", err))?
}; };
let auth: Arc<dyn Auth + Send + Sync> = match (cfg.username, cfg.password) { let auth: Arc<dyn Auth + Send + Sync> = match (cfg.username, cfg.password) {
@ -68,7 +83,6 @@ impl Server {
let server = Self { let server = Self {
inner: Socks5Server::new(socket, auth), inner: Socks5Server::new(socket, auth),
addr: cfg.server,
dual_stack: cfg.dual_stack, dual_stack: cfg.dual_stack,
max_pkt_size: cfg.max_packet_size, max_pkt_size: cfg.max_packet_size,
next_assoc_id: AtomicU16::new(0), next_assoc_id: AtomicU16::new(0),
@ -84,11 +98,10 @@ impl Server {
} }
pub async fn start() { pub async fn start() {
let server = SERVER.get().unwrap(); log::warn!("[socks5] server started, listening on {}", Self::addr());
log::warn!("[socks5] server started, listening on {}", server.addr);
loop { loop {
match server.inner.accept().await { match SERVER.get().unwrap().inner.accept().await {
Ok((conn, addr)) => { Ok((conn, addr)) => {
log::debug!("[socks5] [{addr}] connection established"); log::debug!("[socks5] [{addr}] connection established");
tokio::spawn(async move { tokio::spawn(async move {
@ -118,33 +131,55 @@ impl Server {
assoc: Associate<associate::NeedReply>, assoc: Associate<associate::NeedReply>,
_addr: Address, _addr: Address,
) -> Result<(), Error> { ) -> Result<(), Error> {
async fn get_assoc_socket() -> Result<Arc<AssociatedUdpSocket>, IoError> { async fn get_assoc_socket() -> Result<Arc<AssociatedUdpSocket>, Error> {
let socket = Socket::from( let domain = match Server::addr() {
UdpSocket::bind(SERVER.get().unwrap().addr) SocketAddr::V4(_) => Domain::IPV4,
.await? SocketAddr::V6(_) => Domain::IPV6,
.into_std()?, };
);
if let Some(dual_stack) = SERVER.get().unwrap().dual_stack { let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP)).map_err(|err| {
// We already checked that the server address is IPv6 Error::Socket("failed to create socks5 server UDP associate socket", err)
socket.set_only_v6(!dual_stack)?; })?;
if let Some(dual_stack) = Server::dual_stack() {
socket.set_only_v6(!dual_stack).map_err(|err| {
Error::Socket(
"socks5 server UDP associate dual-stack socket setting error",
err,
)
})?;
} }
let socket = AssociatedUdpSocket::from(( socket.set_nonblocking(true).map_err(|err| {
UdpSocket::from_std(StdUdpSocket::from(socket))?, Error::Socket(
SERVER.get().unwrap().max_pkt_size, "failed setting socks5 server UDP associate socket as non-blocking",
)); err,
)
})?;
Ok(Arc::new(socket)) socket
.bind(&SockAddr::from(Server::addr()))
.map_err(|err| {
Error::Socket("failed to bind socks5 server UDP associate socket", err)
})?;
let socket = UdpSocket::from_std(StdUdpSocket::from(socket)).map_err(|err| {
Error::Socket("failed to create socks5 server UDP associate socket", err)
})?;
Ok(Arc::new(AssociatedUdpSocket::from((
socket,
Server::max_pkt_size(),
))))
} }
match get_assoc_socket() match get_assoc_socket().await {
.await Ok(assoc_socket) => {
.and_then(|socket| socket.local_addr().map(|addr| (socket, addr)))
{
Ok((assoc_socket, assoc_addr)) => {
let assoc = assoc let assoc = assoc
.reply(Reply::Succeeded, Address::SocketAddress(assoc_addr)) .reply(
Reply::Succeeded,
Address::SocketAddress(assoc_socket.local_addr().unwrap()),
)
.await?; .await?;
Self::send_pkt(assoc, assoc_socket).await Self::send_pkt(assoc, assoc_socket).await
} }
@ -308,4 +343,16 @@ impl Server {
Err(err) => log::error!("[socks5] [send] {err}"), Err(err) => log::error!("[socks5] [send] {err}"),
} }
} }
fn addr() -> SocketAddr {
SERVER.get().unwrap().inner.local_addr().unwrap()
}
fn dual_stack() -> Option<bool> {
SERVER.get().unwrap().dual_stack
}
fn max_pkt_size() -> usize {
SERVER.get().unwrap().max_pkt_size
}
} }

View File

@ -64,8 +64,8 @@ pub enum Error {
AuthFailed(Uuid), AuthFailed(Uuid),
#[error("received packet from unexpected source")] #[error("received packet from unexpected source")]
UnexpectedPacketSource, UnexpectedPacketSource,
#[error("create UDP session socket failed: {0}")] #[error("{0}: {1}")]
CreateUdpSessionSocket(IoError), Socket(&'static str, IoError),
#[error("{0} resolved to {1} but IPv6 UDP relay disabled")] #[error("{0} resolved to {1} but IPv6 UDP relaying is disabled")]
UdpRelayIpv6Disabled(Address, SocketAddr), UdpRelayIpv6Disabled(Address, SocketAddr),
} }

View File

@ -13,7 +13,7 @@ use quinn::{
}; };
use register_count::{Counter, Register}; use register_count::{Counter, Register};
use rustls::{version, ServerConfig as RustlsServerConfig}; use rustls::{version, ServerConfig as RustlsServerConfig};
use socket2::Socket; use socket2::{Domain, Protocol, SockAddr, Socket, Type};
use std::{ use std::{
collections::{hash_map::Entry, HashMap}, collections::{hash_map::Entry, HashMap},
future::Future, future::Future,
@ -95,23 +95,32 @@ impl Server {
config.transport_config(Arc::new(tp_cfg)); config.transport_config(Arc::new(tp_cfg));
let socket = Socket::from(StdUdpSocket::bind(cfg.server)?); let socket = {
let domain = match cfg.server {
SocketAddr::V4(_) => Domain::IPV4,
SocketAddr::V6(_) => Domain::IPV6,
};
let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))
.map_err(|err| Error::Socket("failed to create endpoint UDP socket", err))?;
if let Some(dual_stack) = cfg.dual_stack { if let Some(dual_stack) = cfg.dual_stack {
if cfg.server.is_ipv4() && dual_stack { socket.set_only_v6(!dual_stack).map_err(|err| {
return Err(Error::from(IoError::new( Error::Socket("endpoint dual-stack socket setting error", err)
ErrorKind::Unsupported, })?;
"IPv4 socket cannot be dual stack",
)));
} }
socket.set_only_v6(!dual_stack)?; socket
} .bind(&SockAddr::from(cfg.server))
.map_err(|err| Error::Socket("failed to bind endpoint UDP socket", err))?;
StdUdpSocket::from(socket)
};
let ep = Endpoint::new( let ep = Endpoint::new(
EndpointConfig::default(), EndpointConfig::default(),
Some(config), Some(config),
StdUdpSocket::from(socket), socket,
Arc::new(TokioRuntime), Arc::new(TokioRuntime),
)?; )?;
@ -492,11 +501,8 @@ impl Connection {
(session.socket_v4.clone(), session.socket_v6.clone()) (session.socket_v4.clone(), session.socket_v6.clone())
} }
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
let session = entry.insert( let session = entry
UdpSession::new(assoc_id, self.clone(), self.udp_relay_ipv6) .insert(UdpSession::new(assoc_id, self.clone(), self.udp_relay_ipv6).await?);
.await
.map_err(Error::CreateUdpSessionSocket)?,
);
(session.socket_v4.clone(), session.socket_v6.clone()) (session.socket_v4.clone(), session.socket_v6.clone())
} }
@ -592,17 +598,33 @@ struct UdpSession {
} }
impl UdpSession { impl UdpSession {
async fn new(assoc_id: u16, conn: Connection, udp_relay_ipv6: bool) -> Result<Self, IoError> { async fn new(assoc_id: u16, conn: Connection, udp_relay_ipv6: bool) -> Result<Self, Error> {
let socket_v4 = let socket_v4 = Arc::new(
Arc::new(UdpSocket::bind(SocketAddr::from((Ipv4Addr::UNSPECIFIED, 0))).await?); UdpSocket::bind(SocketAddr::from((Ipv4Addr::UNSPECIFIED, 0)))
let socket_v6 = if udp_relay_ipv6 { .await
let socket = Socket::from( .map_err(|err| Error::Socket("failed to create UDP associate IPv4 socket", err))?,
UdpSocket::bind(SocketAddr::from((Ipv6Addr::UNSPECIFIED, 0)))
.await?
.into_std()?,
); );
let socket_v6 = if udp_relay_ipv6 {
let socket = Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP))
.map_err(|err| Error::Socket("failed to create UDP associate IPv6 socket", err))?;
socket.set_only_v6(true)?; socket.set_nonblocking(true).map_err(|err| {
Error::Socket(
"failed setting UDP associate IPv6 socket as non-blocking",
err,
)
})?;
socket.set_only_v6(true).map_err(|err| {
Error::Socket("failed setting UDP associate IPv6 socket as IPv6-only", err)
})?;
socket
.bind(&SockAddr::from(SocketAddr::from((
Ipv6Addr::UNSPECIFIED,
0,
))))
.map_err(|err| Error::Socket("failed to bind UDP associate IPv6 socket", err))?;
Some(Arc::new(UdpSocket::from_std(StdUdpSocket::from(socket))?)) Some(Arc::new(UdpSocket::from_std(StdUdpSocket::from(socket))?))
} else { } else {