From 3cf9c31ba79b3b8ce355e4c6f3d29ef20aa23012 Mon Sep 17 00:00:00 2001 From: Jennifer Luu Date: Fri, 4 Apr 2025 02:47:49 +1100 Subject: [PATCH] Apply outbound_bind_addr to UDP traffic in ssserver (#1929) * [linux] Add IP mapping to fix outbound_bind_addr assignment for UDP sockets * [freebsd] Add IP mapping to fix outbound_bind_addr for UDP sockets * [macos] Add IP mapping to fix outbound_bind_addr for UDP sockets * [windows] Add IP mapping to fix outbound_bind_addr for UDP sockets * [other os] Add IP mapping to fix outbound_bind_addr for UDP sockets * Fix up Error class imports for windows * Use inferation for IPv4 SocketAddr --- .../src/net/sys/unix/bsd/freebsd.rs | 18 +++++++++++++++--- .../shadowsocks/src/net/sys/unix/bsd/macos.rs | 16 ++++++++++++++-- .../shadowsocks/src/net/sys/unix/linux/mod.rs | 18 +++++++++++++++--- crates/shadowsocks/src/net/sys/unix/others.rs | 18 +++++++++++++++--- crates/shadowsocks/src/net/sys/windows/mod.rs | 12 ++++++++++++ 5 files changed, 71 insertions(+), 11 deletions(-) diff --git a/crates/shadowsocks/src/net/sys/unix/bsd/freebsd.rs b/crates/shadowsocks/src/net/sys/unix/bsd/freebsd.rs index f2095b3c..fd139ccc 100644 --- a/crates/shadowsocks/src/net/sys/unix/bsd/freebsd.rs +++ b/crates/shadowsocks/src/net/sys/unix/bsd/freebsd.rs @@ -1,6 +1,6 @@ use std::{ - io, mem, - net::{Ipv4Addr, Ipv6Addr, SocketAddr}, + io, mem, io:ErrorKind, + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, os::unix::io::{AsRawFd, RawFd}, pin::Pin, ptr, @@ -19,7 +19,7 @@ use tokio_tfo::TfoStream; use crate::net::{ AcceptOpts, AddrFamily, ConnectOpts, - sys::{set_common_sockopt_after_connect, set_common_sockopt_for_connect, socket_bind_dual_stack}, + sys::{set_common_sockopt_after_connect, set_common_sockopt_for_connect, socket_bind_dual_stack, io::Error}, udp::{BatchRecvMessage, BatchSendMessage}, }; @@ -228,7 +228,19 @@ pub fn set_disable_ip_fragmentation(af: AddrFamily, socket: &S) -> i pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) -> io::Result { let bind_addr = match (af, config.bind_local_addr) { (AddrFamily::Ipv4, Some(SocketAddr::V4(addr))) => addr.into(), + (AddrFamily::Ipv4, Some(SocketAddr::V6(addr))) => { + // Map IPv6 bind_local_addr to IPv4 if AF is IPv4 + match addr.ip().to_ipv4_mapped() { + Some(addr) => SocketAddr::new(IpAddr::from(addr), 0), + None => return Err(Error::new(ErrorKind::InvalidInput, "Invalid IPv6 address")), + } + }, (AddrFamily::Ipv6, Some(SocketAddr::V6(addr))) => addr.into(), + (AddrFamily::Ipv6, Some(SocketAddr::V4(addr))) => { + // Map IPv4 bind_local_addr to IPv6 if AF is IPv6 + let ip_addr: IpAddr = addr.ip().to_ipv6_mapped().into(); + SocketAddr::new(ip_addr, 0) + }, (AddrFamily::Ipv4, ..) => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), (AddrFamily::Ipv6, ..) => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), }; diff --git a/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs b/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs index 5c67933a..386381cf 100644 --- a/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs +++ b/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs @@ -3,7 +3,7 @@ use std::{ collections::HashMap, io::{self, ErrorKind}, mem, - net::{Ipv4Addr, Ipv6Addr, SocketAddr, TcpStream as StdTcpStream}, + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, TcpStream as StdTcpStream}, os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}, pin::Pin, ptr, @@ -23,7 +23,7 @@ use tokio_tfo::TfoStream; use crate::net::{ AcceptOpts, AddrFamily, ConnectOpts, - sys::{set_common_sockopt_after_connect, set_common_sockopt_for_connect, socket_bind_dual_stack}, + sys::{set_common_sockopt_after_connect, set_common_sockopt_for_connect, socket_bind_dual_stack, io::Error}, udp::{BatchRecvMessage, BatchSendMessage}, }; @@ -356,7 +356,19 @@ pub fn set_disable_ip_fragmentation(af: AddrFamily, socket: &S) -> i pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) -> io::Result { let bind_addr = match (af, config.bind_local_addr) { (AddrFamily::Ipv4, Some(SocketAddr::V4(addr))) => addr.into(), + (AddrFamily::Ipv4, Some(SocketAddr::V6(addr))) => { + // Map IPv6 bind_local_addr to IPv4 if AF is IPv4 + match addr.ip().to_ipv4_mapped() { + Some(addr) => SocketAddr::new(IpAddr::from(addr), 0), + None => return Err(Error::new(ErrorKind::InvalidInput, "Invalid IPv6 address")), + } + }, (AddrFamily::Ipv6, Some(SocketAddr::V6(addr))) => addr.into(), + (AddrFamily::Ipv6, Some(SocketAddr::V4(addr))) => { + // Map IPv4 bind_local_addr to IPv6 if AF is IPv6 + let ip_addr: IpAddr = addr.ip().to_ipv6_mapped().into(); + SocketAddr::new(ip_addr, 0) + }, (AddrFamily::Ipv4, ..) => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), (AddrFamily::Ipv6, ..) => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), }; diff --git a/crates/shadowsocks/src/net/sys/unix/linux/mod.rs b/crates/shadowsocks/src/net/sys/unix/linux/mod.rs index 356c5a33..f98227f4 100644 --- a/crates/shadowsocks/src/net/sys/unix/linux/mod.rs +++ b/crates/shadowsocks/src/net/sys/unix/linux/mod.rs @@ -1,6 +1,6 @@ use std::{ - io, mem, - net::{Ipv4Addr, Ipv6Addr, SocketAddr}, + io, mem, io::ErrorKind, + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}, pin::Pin, ptr, @@ -20,7 +20,7 @@ use tokio_tfo::TfoStream; use crate::net::{ AcceptOpts, AddrFamily, ConnectOpts, - sys::{set_common_sockopt_after_connect, set_common_sockopt_for_connect, socket_bind_dual_stack}, + sys::{set_common_sockopt_after_connect, set_common_sockopt_for_connect, socket_bind_dual_stack, io::Error}, udp::{BatchRecvMessage, BatchSendMessage}, }; @@ -287,7 +287,19 @@ pub fn set_disable_ip_fragmentation(af: AddrFamily, socket: &S) -> i pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) -> io::Result { let bind_addr = match (af, config.bind_local_addr) { (AddrFamily::Ipv4, Some(SocketAddr::V4(addr))) => addr.into(), + (AddrFamily::Ipv4, Some(SocketAddr::V6(addr))) => { + // Map IPv6 bind_local_addr to IPv4 if AF is IPv4 + match addr.ip().to_ipv4_mapped() { + Some(addr) => SocketAddr::new(IpAddr::from(addr), 0), + None => return Err(Error::new(ErrorKind::InvalidInput, "Invalid IPv6 address")), + } + }, (AddrFamily::Ipv6, Some(SocketAddr::V6(addr))) => addr.into(), + (AddrFamily::Ipv6, Some(SocketAddr::V4(addr))) => { + // Map IPv4 bind_local_addr to IPv6 if AF is IPv6 + let ip_addr: IpAddr = addr.ip().to_ipv6_mapped().into(); + SocketAddr::new(ip_addr, 0) + }, (AddrFamily::Ipv4, ..) => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), (AddrFamily::Ipv6, ..) => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), }; diff --git a/crates/shadowsocks/src/net/sys/unix/others.rs b/crates/shadowsocks/src/net/sys/unix/others.rs index 15cac8f0..be83f686 100644 --- a/crates/shadowsocks/src/net/sys/unix/others.rs +++ b/crates/shadowsocks/src/net/sys/unix/others.rs @@ -1,6 +1,6 @@ use std::{ - io, - net::{Ipv4Addr, Ipv6Addr, SocketAddr}, + io,io::ErrorKind + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, ops::{Deref, DerefMut}, os::fd::AsRawFd, pin::Pin, @@ -15,7 +15,7 @@ use tokio::{ use crate::net::{ AcceptOpts, AddrFamily, ConnectOpts, - sys::{ErrorKind, set_common_sockopt_after_connect, set_common_sockopt_for_connect}, + sys::{ErrorKind, set_common_sockopt_after_connect, set_common_sockopt_for_connect, io::Error}, }; /// A wrapper of `TcpStream` @@ -82,7 +82,19 @@ pub fn set_disable_ip_fragmentation(_af: AddrFamily, _socket: &S) -> pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) -> io::Result { let bind_addr = match (af, config.bind_local_addr) { (AddrFamily::Ipv4, Some(SocketAddr::V4(addr))) => addr.into(), + (AddrFamily::Ipv4, Some(SocketAddr::V6(addr))) => { + // Map IPv6 bind_local_addr to IPv4 if AF is IPv4 + match addr.ip().to_ipv4_mapped() { + Some(addr) => SocketAddr::new(IpAddr::from(addr), 0), + None => return Err(Error::new(ErrorKind::InvalidInput, "Invalid IPv6 address")), + } + }, (AddrFamily::Ipv6, Some(SocketAddr::V6(addr))) => addr.into(), + (AddrFamily::Ipv6, Some(SocketAddr::V4(addr))) => { + // Map IPv4 bind_local_addr to IPv6 if AF is IPv6 + let ip_addr: IpAddr = addr.ip().to_ipv6_mapped().into(); + SocketAddr::new(ip_addr, 0) + }, (AddrFamily::Ipv4, ..) => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), (AddrFamily::Ipv6, ..) => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), }; diff --git a/crates/shadowsocks/src/net/sys/windows/mod.rs b/crates/shadowsocks/src/net/sys/windows/mod.rs index 05d8008f..74d08cca 100644 --- a/crates/shadowsocks/src/net/sys/windows/mod.rs +++ b/crates/shadowsocks/src/net/sys/windows/mod.rs @@ -468,7 +468,19 @@ pub async fn create_inbound_udp_socket(addr: &SocketAddr, ipv6_only: bool) -> io pub async fn create_outbound_udp_socket(af: AddrFamily, opts: &ConnectOpts) -> io::Result { let bind_addr = match (af, opts.bind_local_addr) { (AddrFamily::Ipv4, Some(SocketAddr::V4(addr))) => addr.into(), + (AddrFamily::Ipv4, Some(SocketAddr::V6(addr))) => { + // Map IPv6 bind_local_addr to IPv4 if AF is IPv4 + match addr.ip().to_ipv4_mapped() { + Some(addr) => SocketAddr::new(IpAddr::from(addr), 0), + None => return Err(io::Error::new(ErrorKind::InvalidInput, "Invalid IPv6 address")), + } + }, (AddrFamily::Ipv6, Some(SocketAddr::V6(addr))) => addr.into(), + (AddrFamily::Ipv6, Some(SocketAddr::V4(addr))) => { + // Map IPv4 bind_local_addr to IPv6 if AF is IPv6 + let ip_addr: IpAddr = addr.ip().to_ipv6_mapped().into(); + SocketAddr::new(ip_addr, 0) + }, (AddrFamily::Ipv4, ..) => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), (AddrFamily::Ipv6, ..) => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), };