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
This commit is contained in:
Jennifer Luu
2025-04-04 02:47:49 +11:00
committed by GitHub
parent b4df068e73
commit 3cf9c31ba7
5 changed files with 71 additions and 11 deletions

View File

@@ -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<S: AsRawFd>(af: AddrFamily, socket: &S) -> i
pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) -> io::Result<UdpSocket> {
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),
};

View File

@@ -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<S: AsRawFd>(af: AddrFamily, socket: &S) -> i
pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) -> io::Result<UdpSocket> {
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),
};

View File

@@ -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<S: AsRawFd>(af: AddrFamily, socket: &S) -> i
pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) -> io::Result<UdpSocket> {
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),
};

View File

@@ -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<S: AsRawFd>(_af: AddrFamily, _socket: &S) ->
pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) -> io::Result<UdpSocket> {
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),
};

View File

@@ -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<UdpSocket> {
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),
};