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));
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(
EndpointConfig::default(),
None,
socket,
Arc::new(TokioRuntime),
)?;
ep.set_default_client_config(config);
let ep = Self {
@ -139,7 +146,12 @@ impl Endpoint {
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)?;

View File

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

View File

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

View File

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